From 9dd9e4aa05da6e3a3f62d926b42ade3d4f149c84 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Fri, 7 Jul 2023 14:18:27 +0800 Subject: [PATCH] Implement suffix (reversed) trie --- Build/lib/trie.js | 300 +++++++++++++++++++++++++++++++++++++++++ Build/lib/trie.test.js | 185 +++++++++++++++++++++++++ package.json | 3 + pnpm-lock.yaml | 282 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 760 insertions(+), 10 deletions(-) create mode 100644 Build/lib/trie.js create mode 100644 Build/lib/trie.test.js diff --git a/Build/lib/trie.js b/Build/lib/trie.js new file mode 100644 index 00000000..6908c8a7 --- /dev/null +++ b/Build/lib/trie.js @@ -0,0 +1,300 @@ +/** + * Suffix Trie based on Mnemonist Trie + */ + +const SENTINEL = String.fromCharCode(0); + +class Trie { + size = 0; + root = {}; + + /** + * Method used to add the given prefix to the trie. + * + * @param {string} suffix - Prefix to follow. + * @return {Trie} + */ + add(suffix) { + let node = this.root; + let token; + + for (let i = suffix.length - 1; i >= 0; i--) { + token = suffix[i]; + + node = node[token] || (node[token] = {}); + } + + // Do we need to increase size? + if (!(SENTINEL in node)) this.size++; + node[SENTINEL] = true; + + return this; + } + + /** + * Method used to retrieve every item in the trie with the given prefix. + * + * @param {string} suffix - Prefix to query. + * @param {boolean} [includeEqualWithSuffix] + * @return {string[]} + */ + find(suffix, includeEqualWithSuffix = true) { + let node = this.root; + const matches = []; + let token; + let i; + let l; + + for (let i = suffix.length - 1; i >= 0; i--) { + token = suffix[i]; + + node = node[token]; + + if (node == null) return matches; + } + + // Performing DFS from prefix + const nodeStack = [node]; + + const suffixStack = [suffix]; + let k; + + let $suffix = suffix; + + while (nodeStack.length) { + $suffix = suffixStack.pop(); + node = nodeStack.pop(); + + for (k in node) { + if (k === SENTINEL) { + if (includeEqualWithSuffix) { + matches.push($suffix); + } else if ($suffix !== suffix) { + matches.push($suffix); + } + + + continue; + } + + nodeStack.push(node[k]); + suffixStack.push(k + $suffix); + } + } + + return matches; + } + + toJSON() { + return this.root; + } + + /** + * Method used to clear the trie. + * + * @return {void} + */ + // clear() { + // // Properties + // this.root = {}; + // this.size = 0; + // } + + /** + * Method used to update the value of the given prefix in the trie. + * + * @param {string|array} prefix - Prefix to follow. + * @param {(oldValue: any | undefined) => any} updateFunction - Update value visitor callback. + * @return {Trie} + */ + // update(prefix, updateFunction) { + // let node = this.root; + // let token; + + // for (let i = 0, l = prefix.length; i < l; i++) { + // token = prefix[i]; + + // node = node[token] || (node[token] = {}); + // } + + // // Do we need to increase size? + // if (!(SENTINEL in node)) + // this.size++; + + // node[SENTINEL] = updateFunction(node[SENTINEL]); + + // return this; + // } + + /** + * Method used to delete a prefix from the trie. + * + * @param {string|array} suffix - Prefix to delete. + * @return {boolean} + */ + delete(suffix) { + let node = this.root; + let toPrune = null; + let tokenToPrune = null; + let parent; + let token; + + for (let i = suffix.length - 1; i >= 0; i--) { + token = suffix[i]; + parent = node; + node = node[token]; + + // Prefix does not exist + if (typeof node === 'undefined') + return false; + + // Keeping track of a potential branch to prune + if (toPrune !== null) { + if (Object.keys(node).length > 1) { + toPrune = null; + tokenToPrune = null; + } + } + else { + if (Object.keys(node).length < 2) { + toPrune = parent; + tokenToPrune = token; + } + } + } + + if (!(SENTINEL in node)) return false; + + this.size--; + + if (toPrune) { + delete toPrune[tokenToPrune]; + } else { + delete node[SENTINEL]; + } + + return true; + } + + /** + * Method used to assert whether the given prefix exists in the Trie. + * + * @param {string} suffix - Prefix to check. + * @return {boolean} + */ + has(suffix) { + let node = this.root; + let token; + + for (let i = suffix.length - 1; i >= 0; i--) { + token = suffix[i]; + node = node[token]; + + if (typeof node === 'undefined') + return false; + } + + return SENTINEL in node; + } + + /** + * Method returning an iterator over the trie's prefixes. + * + * @param {string|array} [prefix] - Optional starting prefix. + * @return {Iterator} + */ + // prefixes(prefix) { + // let node = this.root; + // const nodeStack = []; + // const prefixStack = []; + // let token; + // let i; + // let l; + + // const isString = this.mode === 'string'; + + // // Resolving initial prefix + // if (prefix) { + // for (i = 0, l = prefix.length; i < l; i++) { + // token = prefix[i]; + // node = node[token]; + + // // If the prefix does not exist, we return an empty iterator + // if (typeof node === 'undefined') + // return Iterator.empty(); + // } + // } + // else { + // prefix = isString ? '' : []; + // } + + // nodeStack.push(node); + // prefixStack.push(prefix); + + // return new Iterator(() => { + // let currentNode; + // let currentPrefix; + // let hasValue = false; + // let k; + + // while (nodeStack.length) { + // currentNode = nodeStack.pop(); + // currentPrefix = prefixStack.pop(); + + // for (k in currentNode) { + // if (k === SENTINEL) { + // hasValue = true; + // continue; + // } + + // nodeStack.push(currentNode[k]); + // prefixStack.push(isString ? currentPrefix + k : currentPrefix.concat(k)); + // } + + // if (hasValue) + // return { done: false, value: currentPrefix }; + // } + + // return { done: true }; + // }); + // } + + /** + * Convenience known methods. + */ + // inspect() { + // const proxy = new Set(); + + // const iterator = this.prefixes(); + // let step; + + // while ((step = iterator.next(), !step.done)) + // proxy.add(step.value); + + // // Trick so that node displays the name of the constructor + // Object.defineProperty(proxy, 'constructor', { + // value: Trie, + // enumerable: false + // }); + + // return proxy; + // } + /** + * Static .from function taking an arbitrary iterable & converting it into + * a trie. + * + * @param {string[]} iterable - Target iterable. + * @return {Trie} + */ + static from = iterable => { + const trie = new Trie(); + iterable.forEach(i => trie.add(i)); + return trie; + }; +} + +/** + * Exporting. + */ +module.exports.SENTINEL = SENTINEL; +module.exports = Trie; diff --git a/Build/lib/trie.test.js b/Build/lib/trie.test.js new file mode 100644 index 00000000..7ec860bd --- /dev/null +++ b/Build/lib/trie.test.js @@ -0,0 +1,185 @@ +require('chai').should(); + +const Trie = require('./trie'); +const assert = require('assert'); + +var SENTINEL = Trie.SENTINEL; + +describe('Trie', () => { + it('should be possible to add items to a Trie.', () => { + const trie = new Trie(); + + trie.add('sukka'); + trie.add('ukka'); + trie.add('akku'); + + trie.size.should.eq(3); + trie.has('sukka').should.eq(true); + trie.has('ukka').should.eq(true); + trie.has('akku').should.eq(true); + trie.has('noc').should.eq(false); + trie.has('suk').should.eq(false); + trie.has('sukkaw').should.eq(false); + }); + + it('adding the same item several times should not increase size.', () => { + const trie = new Trie(); + + trie.add('rat'); + trie.add('erat'); + trie.add('rat'); + + assert.strictEqual(trie.size, 2); + assert.strictEqual(trie.has('rat'), true); + }); + + it('should be possible to set the null sequence.', () => { + let trie = new Trie(); + + trie.add(''); + trie.size.should.eq(1); + trie.has('').should.eq(true); + + trie = new Trie(Array); + + trie.add([]); + trie.size.should.eq(1); + trie.has([]).should.eq(true); + }); + + it('should be possible to delete items.', () => { + const trie = new Trie(); + + trie.add('rat'); + trie.add('rate'); + trie.add('tar'); + + assert.strictEqual(trie.delete(''), false); + trie.delete('').should.eq(false); + trie.delete('hello').should.eq(false); + + trie.delete('rat').should.eq(true); + trie.has('rat').should.eq(false); + trie.has('rate').should.eq(true); + + trie.size.should.eq(2); + + assert.strictEqual(trie.delete('rate'), true); + + assert.strictEqual(trie.size, 1); + + assert.strictEqual(trie.delete('tar'), true); + + assert.strictEqual(trie.size, 0); + }); + + it('should be possible to check the existence of a sequence in the Trie.', () => { + const trie = new Trie(); + + trie.add('romanesque'); + + assert.strictEqual(trie.has('romanesque'), true); + assert.strictEqual(trie.has('roman'), false); + assert.strictEqual(trie.has(''), false); + }); + + it('should be possible to retrieve items matching the given prefix.', () => { + const trie = new Trie(); + + trie.add('roman'); + trie.add('esqueroman'); + trie.add('sesqueroman'); + trie.add('greek'); + + assert.deepStrictEqual(trie.find('roman'), ['roman', 'esqueroman', 'sesqueroman']); + assert.deepStrictEqual(trie.find('man'), ['roman', 'esqueroman', 'sesqueroman']); + assert.deepStrictEqual(trie.find('esqueroman'), ['esqueroman', 'sesqueroman']); + assert.deepStrictEqual(trie.find('eek'), ['greek']); + assert.deepStrictEqual(trie.find('hello'), []); + assert.deepStrictEqual(trie.find(''), ['greek', 'roman', 'esqueroman', 'sesqueroman']); + }); + + // it('should work with custom tokens.', () => { + // const trie = new Trie(Array); + + // trie.add(['the', 'cat', 'eats', 'the', 'mouse']); + // trie.add(['the', 'mouse', 'eats', 'cheese']); + // trie.add(['hello', 'world']); + + // assert.strictEqual(trie.size, 3); + + // assert.strictEqual(trie.has(['the', 'mouse', 'eats', 'cheese']), true); + // assert.strictEqual(trie.has(['the', 'mouse', 'eats']), false); + + // assert.strictEqual(trie.delete(['hello']), false); + // assert.strictEqual(trie.delete(['hello', 'world']), true); + + // assert.strictEqual(trie.size, 2); + // }); + + // it('should be possible to iterate over the trie\'s prefixes.', () => { + // const trie = new Trie(); + + // trie.add('rat'); + // trie.add('rate'); + + // let prefixes = take(trie.prefixes()); + + // assert.deepStrictEqual(prefixes, ['rat', 'rate']); + + // trie.add('rater'); + // trie.add('rates'); + + // prefixes = take(trie.keys('rate')); + + // assert.deepStrictEqual(prefixes, ['rate', 'rates', 'rater']); + // }); + + // it('should be possible to iterate over the trie\'s prefixes using for...of.', () => { + // const trie = new Trie(); + + // trie.add('rat'); + // trie.add('rate'); + + // const tests = [ + // 'rat', + // 'rate' + // ]; + + // let i = 0; + + // for (const prefix of trie) + // assert.deepStrictEqual(prefix, tests[i++]); + // }); + + it('should be possible to create a trie from an arbitrary iterable.', () => { + const words = ['roman', 'esqueroman']; + + const trie = Trie.from(words); + + assert.strictEqual(trie.size, 2); + assert.deepStrictEqual(trie.has('roman'), true); + }); +}); + +describe('surge domainset dedupe', () => { + it('should not remove same entry', () => { + const trie = Trie.from(['.skk.moe', 'noc.one']); + + trie.find('.skk.moe').should.eql(['.skk.moe']); + trie.find('noc.one').should.eql(['noc.one']); + }); + + it('should remove subdomain', () => { + const trie = Trie.from(['www.noc.one', 'www.sukkaw.com', 'blog.skk.moe', 'image.cdn.skk.moe', 'cdn.sukkaw.net']); + // trie.find('noc.one').should.eql(['www.noc.one']); + trie.find('.skk.moe').should.eql(['image.cdn.skk.moe', 'blog.skk.moe']); + // trie.find('sukkaw.net').should.eql(['cdn.sukkaw.net']); + trie.find('.sukkaw.com').should.eql(['www.sukkaw.com']); + }); + + it('should not remove non-subdomain', () => { + const trie = Trie.from(['skk.moe', 'sukkaskk.moe']); + trie.find('.skk.moe').should.eql([]); + }); +}) diff --git a/package.json b/package.json index 1f4abea1..7322f178 100644 --- a/package.json +++ b/package.json @@ -125,7 +125,10 @@ "undici": "5.22.1" }, "devDependencies": { + "@types/mocha": "^10.0.1", + "chai": "^4.3.7", "eslint-plugin-import": "npm:eslint-plugin-i@2.27.5-4", + "mocha": "^10.2.0", "wireit": "^0.9.5" }, "engines": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1085993..e65ef3f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,9 +43,18 @@ dependencies: version: 5.22.1 devDependencies: + '@types/mocha': + specifier: ^10.0.1 + version: 10.0.1 + chai: + specifier: ^4.3.7 + version: 4.3.7 eslint-plugin-import: specifier: npm:eslint-plugin-i@2.27.5-4 version: /eslint-plugin-i@2.27.5-4(eslint@8.44.0) + mocha: + specifier: ^10.2.0 + version: 10.2.0 wireit: specifier: ^0.9.5 version: 0.9.5 @@ -104,7 +113,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) espree: 9.6.0 globals: 13.20.0 ignore: 5.2.4 @@ -126,7 +135,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -220,13 +229,17 @@ packages: resolution: {integrity: sha512-o0J30wqycjF5miWDKYKKzzOU1ZTLuA42HZ4HE7/zqTOc/jTLdQ5NhYWvsRQo45Nfi1KHoRdNhteSI4BAxTF1Pg==} dev: false + /@types/mocha@10.0.1: + resolution: {integrity: sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==} + dev: true + /@vercel/fetch-retry@5.1.3(node-fetch@2.6.11): resolution: {integrity: sha512-UIbFc4VsEZHOr6dWuE+kxY4NxnOLXFMCWm0fSKRRHUEtrIzaJLzHpWk2QskCXTSzFgFvhkLAvSrBK2XZg7NSzg==} peerDependencies: node-fetch: ^2.6.7 dependencies: async-retry: 1.3.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) node-fetch: 2.6.11 transitivePeerDependencies: - supports-color @@ -264,6 +277,11 @@ packages: uri-js: 4.4.1 dev: false + /ansi-colors@4.1.1: + resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} + engines: {node: '>=6'} + dev: true + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -286,6 +304,10 @@ packages: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + /astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -317,6 +339,12 @@ packages: concat-map: 0.0.1 dev: true + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} @@ -324,6 +352,10 @@ packages: fill-range: 7.0.1 dev: true + /browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + dev: true + /busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -336,6 +368,24 @@ packages: engines: {node: '>=6'} dev: true + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /chai@4.3.7: + resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.2 + deep-eql: 4.1.3 + get-func-name: 2.0.0 + loupe: 2.3.6 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -344,6 +394,10 @@ packages: supports-color: 7.2.0 dev: true + /check-error@1.0.2: + resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + dev: true + /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -386,6 +440,14 @@ packages: string-natural-compare: 3.0.1 dev: false + /cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -416,10 +478,10 @@ packages: supports-color: optional: true dependencies: - ms: 2.1.2 + ms: 2.1.3 dev: true - /debug@4.3.4: + /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} peerDependencies: @@ -429,11 +491,29 @@ packages: optional: true dependencies: ms: 2.1.2 + supports-color: 8.1.1 + + /decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + dev: true + + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true + /diff@5.0.0: + resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} + engines: {node: '>=0.3.1'} + dev: true + /doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -450,7 +530,11 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: false + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} @@ -546,7 +630,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.0 @@ -674,6 +758,11 @@ packages: rimraf: 3.0.2 dev: true + /flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + dev: true + /flatted@3.2.7: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true @@ -710,6 +799,15 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /get-func-name@2.0.0: + resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} + dev: true + /get-tsconfig@4.6.2: resolution: {integrity: sha512-E5XrT4CbbXcXWy+1jChlZmrmCwd5KGx502kDCXJJ7y898TtWW9FwoG5HfOLVRKmlmDGkWN2HM9Ho+/Y8F0sJDg==} dependencies: @@ -730,6 +828,17 @@ packages: is-glob: 4.0.3 dev: true + /glob@7.2.0: + resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -758,7 +867,6 @@ packages: /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - dev: true /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} @@ -779,6 +887,11 @@ packages: resolution: {integrity: sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==} dev: false + /he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: true + /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} @@ -839,7 +952,6 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: false /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -858,6 +970,16 @@ packages: engines: {node: '>=8'} dev: true + /is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + dev: true + + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -916,6 +1038,20 @@ packages: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} dev: false + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + + /loupe@2.3.6: + resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + dependencies: + get-func-name: 2.0.0 + dev: true + /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -942,6 +1078,13 @@ packages: brace-expansion: 1.1.11 dev: true + /minimatch@5.0.1: + resolution: {integrity: sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} @@ -968,9 +1111,47 @@ packages: hasBin: true dev: false + /mocha@10.2.0: + resolution: {integrity: sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==} + engines: {node: '>= 14.0.0'} + hasBin: true + dependencies: + ansi-colors: 4.1.1 + browser-stdout: 1.3.1 + chokidar: 3.5.3 + debug: 4.3.4(supports-color@8.1.1) + diff: 5.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 7.2.0 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 5.0.1 + ms: 2.1.3 + nanoid: 3.3.3 + serialize-javascript: 6.0.0 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + workerpool: 6.2.1 + yargs: 16.2.0 + yargs-parser: 20.2.4 + yargs-unparser: 2.0.0 + dev: true + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /nanoid@3.3.3: + resolution: {integrity: sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true @@ -1075,6 +1256,10 @@ packages: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: false @@ -1115,6 +1300,12 @@ packages: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -1122,6 +1313,11 @@ packages: picomatch: 2.3.1 dev: true + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -1173,6 +1369,10 @@ packages: queue-microtask: 1.2.3 dev: true + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + /semver@7.5.3: resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==} engines: {node: '>=10'} @@ -1181,6 +1381,12 @@ packages: lru-cache: 6.0.0 dev: true + /serialize-javascript@6.0.0: + resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} + dependencies: + randombytes: 2.1.0 + dev: true + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1222,7 +1428,6 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: false /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} @@ -1242,6 +1447,12 @@ packages: has-flag: 4.0.0 dev: true + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -1313,6 +1524,11 @@ packages: prelude-ls: 1.2.1 dev: true + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} @@ -1366,13 +1582,59 @@ packages: proper-lockfile: 4.1.2 dev: true + /workerpool@6.2.1: + resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + /yargs-parser@20.2.4: + resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} + engines: {node: '>=10'} + dev: true + + /yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + dev: true + + /yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.4 + dev: true + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'}