WIP - add extractor, generate snippet_data
This commit is contained in:
68
node_modules/@hapi/address/lib/abnf.js
generated
vendored
Executable file
68
node_modules/@hapi/address/lib/abnf.js
generated
vendored
Executable file
@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
/*
|
||||
From RFC 5321:
|
||||
|
||||
Mailbox = Local-part "@" ( Domain / address-literal )
|
||||
|
||||
Local-part = Dot-string / Quoted-string
|
||||
Dot-string = Atom *("." Atom)
|
||||
Atom = 1*atext
|
||||
atext = ALPHA / DIGIT / "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "/" / "=" / "?" / "^" / "_" / "`" / "{" / "|" / "}" / "~"
|
||||
|
||||
Domain = sub-domain *("." sub-domain)
|
||||
sub-domain = Let-dig [Ldh-str]
|
||||
Let-dig = ALPHA / DIGIT
|
||||
Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig
|
||||
|
||||
ALPHA = %x41-5A / %x61-7A ; a-z, A-Z
|
||||
DIGIT = %x30-39 ; 0-9
|
||||
|
||||
From RFC 6531:
|
||||
|
||||
sub-domain =/ U-label
|
||||
atext =/ UTF8-non-ascii
|
||||
|
||||
UTF8-non-ascii = UTF8-2 / UTF8-3 / UTF8-4
|
||||
|
||||
UTF8-2 = %xC2-DF UTF8-tail
|
||||
UTF8-3 = %xE0 %xA0-BF UTF8-tail /
|
||||
%xE1-EC 2( UTF8-tail ) /
|
||||
%xED %x80-9F UTF8-tail /
|
||||
%xEE-EF 2( UTF8-tail )
|
||||
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) /
|
||||
%xF1-F3 3( UTF8-tail ) /
|
||||
%xF4 %x80-8F 2( UTF8-tail )
|
||||
|
||||
UTF8-tail = %x80-BF
|
||||
|
||||
Note: The following are not supported:
|
||||
|
||||
RFC 5321: address-literal, Quoted-string
|
||||
RFC 5322: obs-*, CFWS
|
||||
*/
|
||||
|
||||
|
||||
internals.atext = '[\\w!#\\$%&\'\\*\\+\\-/=\\?\\^`\\{\\|\\}~]'; // _ included in \w
|
||||
|
||||
|
||||
exports.atextRx = new RegExp(`^${internals.atext}+$`);
|
||||
|
||||
|
||||
exports.atomRx = new RegExp([
|
||||
|
||||
internals.atext,
|
||||
|
||||
// %xC2-DF UTF8-tail
|
||||
'(?:[\\xc2-\\xdf][\\x80-\\xbf])',
|
||||
|
||||
// %xE0 %xA0-BF UTF8-tail %xE1-EC 2( UTF8-tail ) %xED %x80-9F UTF8-tail %xEE-EF 2( UTF8-tail )
|
||||
'(?:\\xe0[\\xa0-\\xbf][\\x80-\\xbf])|(?:[\\xe1-\\xec][\\x80-\\xbf]{2})|(?:\\xed[\\x80-\\x9f][\\x80-\\xbf])|(?:[\\xee-\\xef][\\x80-\\xbf]{2})',
|
||||
|
||||
// %xF0 %x90-BF 2( UTF8-tail ) %xF1-F3 3( UTF8-tail ) %xF4 %x80-8F 2( UTF8-tail )
|
||||
'(?:\\xf0[\\x90-\\xbf][\\x80-\\xbf]{2})|(?:[\\xf1-\\xf3][\\x80-\\xbf]{3})|(?:\\xf4[\\x80-\\x8f][\\x80-\\xbf]{2})'
|
||||
|
||||
].join('|'));
|
||||
248
node_modules/@hapi/address/lib/index.js
generated
vendored
Executable file
248
node_modules/@hapi/address/lib/index.js
generated
vendored
Executable file
@ -0,0 +1,248 @@
|
||||
'use strict';
|
||||
|
||||
const Punycode = require('punycode');
|
||||
|
||||
const Abnf = require('./abnf');
|
||||
const Tlds = require('./tlds');
|
||||
|
||||
|
||||
const internals = {
|
||||
nonAsciiRx: /[^\x00-\x7f]/,
|
||||
minDomainSegments: 2,
|
||||
defaultTlds: { allow: Tlds, deny: null }
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
email: {
|
||||
analyze: function (email, options) {
|
||||
|
||||
return internals.email(email, options);
|
||||
},
|
||||
isValid: function (email, options) {
|
||||
|
||||
return !internals.email(email, options);
|
||||
}
|
||||
},
|
||||
domain: {
|
||||
analyze: function (domain, options = {}) {
|
||||
|
||||
internals.options(domain, options);
|
||||
|
||||
if (!domain) {
|
||||
return internals.error('Domain must be a non-empty string');
|
||||
}
|
||||
|
||||
if (domain.length > 256) {
|
||||
return internals.error('Domain too long');
|
||||
}
|
||||
|
||||
const ascii = !internals.nonAsciiRx.test(domain);
|
||||
if (!ascii) {
|
||||
if (options.allowUnicode === false) { // Defaults to true
|
||||
return internals.error('Domain contains forbidden Unicode characters');
|
||||
}
|
||||
|
||||
const normalized = domain.normalize('NFC');
|
||||
domain = Punycode.toASCII(normalized);
|
||||
}
|
||||
|
||||
return internals.domain(domain, options);
|
||||
},
|
||||
isValid: function (domain, options) {
|
||||
|
||||
return !module.exports.domain.analyze(domain, options);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.email = function (email, options = {}) {
|
||||
|
||||
internals.options(email, options);
|
||||
|
||||
if (!email) {
|
||||
return internals.error('Address must be a non-empty string');
|
||||
}
|
||||
|
||||
// Unicode
|
||||
|
||||
const ascii = !internals.nonAsciiRx.test(email);
|
||||
if (!ascii) {
|
||||
if (options.allowUnicode === false) { // Defaults to true
|
||||
return internals.error('Address contains forbidden Unicode characters');
|
||||
}
|
||||
|
||||
const normalized = email.normalize('NFC');
|
||||
email = Punycode.toASCII(normalized);
|
||||
}
|
||||
|
||||
// Basic structure
|
||||
|
||||
const parts = email.split('@');
|
||||
if (parts.length !== 2) {
|
||||
return internals.error(parts.length > 2 ? 'Address cannot contain more than one @ character' : 'Address must contain one @ character');
|
||||
}
|
||||
|
||||
const local = parts[0];
|
||||
const domain = parts[1];
|
||||
|
||||
if (!local) {
|
||||
return internals.error('Address local part cannot be empty');
|
||||
}
|
||||
|
||||
if (!domain) {
|
||||
return internals.error('Domain cannot be empty');
|
||||
}
|
||||
|
||||
if (email.length > 254) { // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
|
||||
return internals.error('Address too long');
|
||||
}
|
||||
|
||||
if (Buffer.byteLength(local, 'utf-8') > 64) { // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1
|
||||
return internals.error('Address local part too long');
|
||||
}
|
||||
|
||||
// Validate parts
|
||||
|
||||
return internals.local(local, ascii) || internals.domain(domain, options);
|
||||
};
|
||||
|
||||
|
||||
internals.options = function (value, options) {
|
||||
|
||||
// Options validation
|
||||
|
||||
if (options.tlds &&
|
||||
options.tlds !== true) {
|
||||
|
||||
if (typeof options.tlds !== 'object') {
|
||||
throw new Error('Invalid options: tlds must be a boolean or an object');
|
||||
}
|
||||
|
||||
if (options.tlds.allow !== undefined &&
|
||||
options.tlds.allow !== true &&
|
||||
options.tlds.allow instanceof Set === false) {
|
||||
|
||||
throw new Error('Invalid options: tlds.allow must be a Set object or true');
|
||||
}
|
||||
|
||||
if (options.tlds.deny) {
|
||||
if (options.tlds.deny instanceof Set === false) {
|
||||
throw new Error('Invalid options: tlds.deny must be a Set object');
|
||||
}
|
||||
|
||||
if (options.tlds.allow instanceof Set) {
|
||||
throw new Error('Invalid options: cannot specify both tlds.allow and tlds.deny lists');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Input validation
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error('Invalid input: value must be a string');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.local = function (local, ascii) {
|
||||
|
||||
const segments = local.split('.');
|
||||
for (const segment of segments) {
|
||||
if (!segment.length) {
|
||||
return internals.error('Address local part contains empty dot-separated segment');
|
||||
}
|
||||
|
||||
if (ascii) {
|
||||
if (!Abnf.atextRx.test(segment)) {
|
||||
return internals.error('Address local part contains invalid character');
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const char of segment) {
|
||||
const binary = Buffer.from(char).toString('binary');
|
||||
if (!Abnf.atomRx.test(binary)) {
|
||||
return internals.error('Address local part contains invalid character');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.tldSegmentRx = /^[a-zA-Z](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/;
|
||||
|
||||
|
||||
internals.domainSegmentRx = /^[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/;
|
||||
|
||||
|
||||
internals.domain = function (domain, options) {
|
||||
|
||||
// https://tools.ietf.org/html/rfc1035 section 2.3.1
|
||||
|
||||
const minDomainSegments = (options.minDomainSegments || internals.minDomainSegments);
|
||||
|
||||
const segments = domain.split('.');
|
||||
if (segments.length < minDomainSegments) {
|
||||
return internals.error('Domain lacks the minimum required number of segments');
|
||||
}
|
||||
|
||||
const tlds = internals.tlds(options);
|
||||
if (tlds) {
|
||||
const tld = segments[segments.length - 1].toLowerCase();
|
||||
if (tlds.deny && tlds.deny.has(tld) ||
|
||||
tlds.allow && !tlds.allow.has(tld)) {
|
||||
|
||||
return internals.error('Domain uses forbidden TLD');
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < segments.length; ++i) {
|
||||
const segment = segments[i];
|
||||
|
||||
if (!segment.length) {
|
||||
return internals.error('Domain contains empty dot-separated segment');
|
||||
}
|
||||
|
||||
if (segment.length > 63) {
|
||||
return internals.error('Domain contains dot-separated segment that is too long');
|
||||
}
|
||||
|
||||
if (i < segments.length - 1) {
|
||||
if (!internals.domainSegmentRx.test(segment)) {
|
||||
return internals.error('Domain contains invalid character');
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!internals.tldSegmentRx.test(segment)) {
|
||||
return internals.error('Domain contains invalid tld character');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.tlds = function (options) {
|
||||
|
||||
if (options.tlds === false) { // Defaults to true
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!options.tlds ||
|
||||
options.tlds === true) {
|
||||
|
||||
return internals.defaultTlds;
|
||||
}
|
||||
|
||||
return {
|
||||
allow: options.tlds.allow === true ? null : options.tlds.allow || Tlds,
|
||||
deny: options.tlds.deny || null
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
internals.error = function (reason) {
|
||||
|
||||
return { error: reason };
|
||||
};
|
||||
1548
node_modules/@hapi/address/lib/tlds.js
generated
vendored
Executable file
1548
node_modules/@hapi/address/lib/tlds.js
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user