amis-rpc-design/node_modules/tldjs/lib/suffix-trie.js
2023-10-07 19:42:30 +08:00

179 lines
3.4 KiB
JavaScript

"use strict";
var VALID_HOSTNAME_VALUE = 0;
/**
* Return min(a, b), handling possible `null` values.
*
* @param {number|null} a
* @param {number|null} b
* @return {number|null}
*/
function minIndex(a, b) {
if (a === null) {
return b;
} else if (b === null) {
return a;
}
return a < b ? a : b;
}
/**
* Insert a public suffix rule in the `trie`.
*
* @param {object} rule
* @param {object} trie
* @return {object} trie (updated)
*/
function insertInTrie(rule, trie) {
var parts = rule.parts;
var node = trie;
for (var i = 0; i < parts.length; i += 1) {
var part = parts[i];
var nextNode = node[part];
if (nextNode === undefined) {
nextNode = Object.create(null);
node[part] = nextNode;
}
node = nextNode;
}
node.$ = VALID_HOSTNAME_VALUE;
return trie;
}
/**
* Recursive lookup of `parts` (starting at `index`) in the tree.
*
* @param {array} parts
* @param {object} trie
* @param {number} index - when to start in `parts` (initially: length - 1)
* @return {number} size of the suffix found (in number of parts matched)
*/
function lookupInTrie(parts, trie, index) {
var part;
var nextNode;
var publicSuffixIndex = null;
// We have a match!
if (trie.$ !== undefined) {
publicSuffixIndex = index + 1;
}
// No more `parts` to look for
if (index === -1) {
return publicSuffixIndex;
}
part = parts[index];
// Check branch corresponding to next part of hostname
nextNode = trie[part];
if (nextNode !== undefined) {
publicSuffixIndex = minIndex(
publicSuffixIndex,
lookupInTrie(parts, nextNode, index - 1)
);
}
// Check wildcard branch
nextNode = trie['*'];
if (nextNode !== undefined) {
publicSuffixIndex = minIndex(
publicSuffixIndex,
lookupInTrie(parts, nextNode, index - 1)
);
}
return publicSuffixIndex;
}
/**
* Contains the public suffix ruleset as a Trie for efficient look-up.
*
* @constructor
*/
function SuffixTrie(rules) {
this.exceptions = Object.create(null);
this.rules = Object.create(null);
if (rules) {
for (var i = 0; i < rules.length; i += 1) {
var rule = rules[i];
if (rule.exception) {
insertInTrie(rule, this.exceptions);
} else {
insertInTrie(rule, this.rules);
}
}
}
}
/**
* Load the trie from JSON (as serialized by JSON.stringify).
*/
SuffixTrie.fromJson = function (json) {
var trie = new SuffixTrie();
trie.exceptions = json.exceptions;
trie.rules = json.rules;
return trie;
};
/**
* Check if `value` is a valid TLD.
*/
SuffixTrie.prototype.hasTld = function (value) {
// All TLDs are at the root of the Trie.
return this.rules[value] !== undefined;
};
/**
* Check if `hostname` has a valid public suffix in `trie`.
*
* @param {string} hostname
* @return {string|null} public suffix
*/
SuffixTrie.prototype.suffixLookup = function (hostname) {
var parts = hostname.split('.');
// Look for a match in rules
var publicSuffixIndex = lookupInTrie(
parts,
this.rules,
parts.length - 1
);
if (publicSuffixIndex === null) {
return null;
}
// Look for exceptions
var exceptionIndex = lookupInTrie(
parts,
this.exceptions,
parts.length - 1
);
if (exceptionIndex !== null) {
return parts.slice(exceptionIndex + 1).join('.');
}
return parts.slice(publicSuffixIndex).join('.');
};
module.exports = SuffixTrie;