From 1ee1a47a1d7bd47e771a93fd6f522694824acff2 Mon Sep 17 00:00:00 2001 From: c0reme Date: Mon, 13 Oct 2025 13:47:06 +0100 Subject: [PATCH 01/11] feat: upgrade to v2 --- .gitignore | 12 +- XIVAPI.js | 58 - lib/Lib.js | 14 - lib/character.js | 35 - lib/cwl.js | 28 - lib/data.js | 52 - lib/freecompany.js | 37 - lib/linkshell.js | 28 - lib/pvpteam.js | 30 - lib/search.js | 37 - package-lock.json | 2684 ++++++++++++++++++++++++++++++- package.json | 12 +- resources/index.js | 3 - src/index.ts | 367 +++++ src/lib/assets.ts | 32 + src/lib/search.ts | 31 + src/lib/sheets.ts | 62 + src/lib/versions.ts | 18 + src/utils.ts | 50 + test.js | 15 - tests/xivapi-js.test.ts | 204 +++ tsconfig.json | 12 + typings/index.d.ts | 220 --- typings/utils/achievements.d.ts | 82 - typings/utils/character.d.ts | 130 -- typings/utils/cwl.ts | 24 - typings/utils/freecompany.d.ts | 57 - typings/utils/index.d.ts | 6 - typings/utils/item.d.ts | 231 --- typings/utils/linkshell.ts | 25 - typings/utils/pvpteam.ts | 26 - typings/utils/search.d.ts | 72 - utils.js | 51 - 33 files changed, 3469 insertions(+), 1276 deletions(-) delete mode 100644 XIVAPI.js delete mode 100644 lib/Lib.js delete mode 100644 lib/character.js delete mode 100644 lib/cwl.js delete mode 100644 lib/data.js delete mode 100644 lib/freecompany.js delete mode 100644 lib/linkshell.js delete mode 100644 lib/pvpteam.js delete mode 100644 lib/search.js delete mode 100644 resources/index.js create mode 100644 src/index.ts create mode 100644 src/lib/assets.ts create mode 100644 src/lib/search.ts create mode 100644 src/lib/sheets.ts create mode 100644 src/lib/versions.ts create mode 100644 src/utils.ts delete mode 100644 test.js create mode 100644 tests/xivapi-js.test.ts create mode 100644 tsconfig.json delete mode 100644 typings/index.d.ts delete mode 100644 typings/utils/achievements.d.ts delete mode 100644 typings/utils/character.d.ts delete mode 100644 typings/utils/cwl.ts delete mode 100644 typings/utils/freecompany.d.ts delete mode 100644 typings/utils/index.d.ts delete mode 100644 typings/utils/item.d.ts delete mode 100644 typings/utils/linkshell.ts delete mode 100644 typings/utils/pvpteam.ts delete mode 100644 typings/utils/search.d.ts delete mode 100644 utils.js diff --git a/.gitignore b/.gitignore index 30bc162..85e9653 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,11 @@ -/node_modules \ No newline at end of file +# ignore build outputs +dist/ +coverage/ +build/ + +# ignore dependencies +node_modules/ + +# ignore package locks and logs +yarn.lock +*.log \ No newline at end of file diff --git a/XIVAPI.js b/XIVAPI.js deleted file mode 100644 index c868c32..0000000 --- a/XIVAPI.js +++ /dev/null @@ -1,58 +0,0 @@ -const resources = require('./resources/'), - Search = require('./lib/search'), - Data = require('./lib/data'), - Character = require('./lib/character'), - FreeCompany = require('./lib/freecompany'), - Linkshell = require('./lib/linkshell'), - CWL = require('./lib/cwl'), - PvPTeam = require('./lib/pvpteam') - -class XIVAPI { - /*{ - private_key string undefined optional - language string 'en' optional - snake_case bool false optional - staging bool false optional - verbose bool false optional - } - */ - constructor(options = {}, legacyOptions = {}) { - //handle attempted use of old API key - if(typeof(options) === 'string') { - console.error('[xivapi-js] BREAKING CHANGE:\n\ -The previous API keys for XIVAPI have been phased out, and are no longer mandatory. \ -This means you\'ll have to define your key during initialization slightly differently. \ -See how in https://github.com/xivapi/xivapi-js/releases/tag/v0.1.3.\n\ -**This instance of xivapi-js will run WITHOUT an API key**') - options = legacyOptions - } - - this.endpoint = `https://${options.staging ? 'staging.' : ''}xivapi.com` - if(options.language && !resources.languages.includes(options.language)) - throw Error(`Invalid language given, must be one of: ${resources.languages}`) - - this.globalParams = {} - - for (let x of ['private_key', 'language']) { - if(typeof options[x] !== 'undefined') - this.globalParams[x] = options[x] - } - if(options.snake_case) - this.globalParams.snake_case = 1 - - this.verbose = options.verbose - - this.resources = resources - this.utils = require('./utils') - - this.search = Search.bind(this) - this.data = new Data(this) - this.character = new Character(this) - this.freecompany = new FreeCompany(this) - this.linkshell = new Linkshell(this) - this.cwl = new CWL(this) - this.pvpteam = new PvPTeam(this) - } -} - -module.exports = XIVAPI diff --git a/lib/Lib.js b/lib/Lib.js deleted file mode 100644 index 849b197..0000000 --- a/lib/Lib.js +++ /dev/null @@ -1,14 +0,0 @@ -const utils = require('../utils') - -class Lib { - constructor(parent) { - this.parent = parent - - this.req = utils.req.bind(parent) - this.reqJSON = utils.reqJSON.bind(parent) - this.makeCSV = utils.makeCSV - this.throwError = utils.throwError - } -} - -module.exports = Lib diff --git a/lib/character.js b/lib/character.js deleted file mode 100644 index 6bd18b3..0000000 --- a/lib/character.js +++ /dev/null @@ -1,35 +0,0 @@ -// https://xivapi.com/docs/Character -const Lib = require('./Lib') - -class Character extends Lib { - constructor(parent) { - super(parent) - } - - /* - { - server - page - } - */ - async search(name, params={}) { - if(!name) - throw this.throwError('character.search()', 'a name') - return this.req('/character/search', Object.assign(params, {name})) - } - - /* - { - extended - data - } - */ - async get(id, params={}) { - if(!id) - throw this.throwError('character.get()','an ID') - - return this.req(`/character/${id}`, params) - } -} - -module.exports = Character diff --git a/lib/cwl.js b/lib/cwl.js deleted file mode 100644 index d41b723..0000000 --- a/lib/cwl.js +++ /dev/null @@ -1,28 +0,0 @@ -// https://xivapi.com/docs/Linkshell -const Lib = require('./Lib') - -class CWL extends Lib { - constructor(parent) { - super(parent) - } - - /* - { - server string optional - page int optional - } - */ - async search(name, params={}) { - if(typeof(name) === 'undefined') - throw this.throwError('cwl.search()','a name') - return this.req('/linkshell/crossworld/search', Object.assign(params, {name})) - } - - async get(id) { - if(typeof(id) === 'undefined') - throw this.throwError('cwl.get()', 'an ID') - return this.req(`/linkshell/crossworld/${id}`) - } -} - -module.exports = CWL diff --git a/lib/data.js b/lib/data.js deleted file mode 100644 index a178f7a..0000000 --- a/lib/data.js +++ /dev/null @@ -1,52 +0,0 @@ -// https://xivapi.com/docs/Welcome#section-4 -const Lib = require('./Lib') - -class Content extends Lib { - constructor(parent) { - super(parent) - } - - async content() { - return this.req('/content') - } - - /* - { - limit - ids - } - */ - async list(name, params={}) { - if(typeof name==='undefined') - throw this.throwError('data.list()','a name') - - if(params.ids) - params.ids = this.parent.utils.makeCSV(params.ids) - - return this.req(`/${name}`, params) - } - - async get(name, id) { - const missing_params = [] - if(typeof name==='undefined') - missing_params.push('a name') - if(typeof id==='undefined') - missing_params.push('an ID') - if(missing_params.length>0) - throw this.throwError('data.get()', missing_params.join(',')) - - return this.req(`/${name}/${id}`) - } - - servers() { - return this.req('/servers') - } - - datacenters() { - return this.req('/servers/dc') - } - - -} - -module.exports = Content diff --git a/lib/freecompany.js b/lib/freecompany.js deleted file mode 100644 index fbe18e7..0000000 --- a/lib/freecompany.js +++ /dev/null @@ -1,37 +0,0 @@ -// https://xivapi.com/docs/Free-Company -const Lib = require('./Lib') - -class FreeCompany extends Lib { - constructor(parent) { - super(parent) - } - - /* - { - server string optional - page int optional - } - */ - async search(name, params={}) { - if(typeof(name) === 'undefined') - throw this.throwError('freecompany.search()','a name') - return this.req('/freecompany/search', Object.assign(params, {name})) - } - - /* - { - extended - data - } - */ - async get(id, params={}) { - if(typeof(id) === 'undefined') - throw this.throwError('freecompany.get()', 'an ID') - - params.data = this.makeCSV(params.data) - - return this.req(`/freecompany/${id}`, params) - } -} - -module.exports = FreeCompany diff --git a/lib/linkshell.js b/lib/linkshell.js deleted file mode 100644 index 0831f7b..0000000 --- a/lib/linkshell.js +++ /dev/null @@ -1,28 +0,0 @@ -// https://xivapi.com/docs/Linkshell -const Lib = require('./Lib') - -class Linkshell extends Lib { - constructor(parent) { - super(parent) - } - - /* - { - server string optional - page int optional - } - */ - async search(name, params={}) { - if(typeof(name) === 'undefined') - throw this.throwError('linkshell.search()','a name') - return this.req('/linkshell/search', Object.assign(params, {name})) - } - - async get(id) { - if(typeof(id) === 'undefined') - throw this.throwError('linkshell.get()', 'an ID') - return this.req(`/linkshell/${id}`) - } -} - -module.exports = Linkshell diff --git a/lib/pvpteam.js b/lib/pvpteam.js deleted file mode 100644 index 4ec4582..0000000 --- a/lib/pvpteam.js +++ /dev/null @@ -1,30 +0,0 @@ -// https://xivapi.com/docs/PVP-Team -const Lib = require('./Lib') - -class PvPTeam extends Lib { - constructor(parent) { - super(parent) - } - - /* - { - server string optional - page int optional - } - */ - async search(name, params={}) { - if(typeof name==='undefined') - throw this.throwError('pvpteam.search()', 'a name') - - return this.req('/pvpteam/search',Object.assign(params, {name})) - } - - async get(id) { - if(typeof id==='undefined') - throw this.throwError('pvpteam.get()', 'an ID') - - return this.req(`/pvpteam/${id}`) - } -} - -module.exports = PvPTeam diff --git a/lib/search.js b/lib/search.js deleted file mode 100644 index ed9d36b..0000000 --- a/lib/search.js +++ /dev/null @@ -1,37 +0,0 @@ -let { req, reqJSON, makeCSV, throwError } = require('../utils') - -/* -{ - filters - lore - - string_column - string_algo - limit -} -*/ -module.exports = async function(input, params = {}) { - req = req.bind(this) - reqJSON = reqJSON.bind(this) - - if(typeof(input) === 'undefined') - throw throwError('search()', 'any input') - - let path = params.lore ? '/lore' : '/search' - - switch(typeof(input)) { - // GET method - case 'string': - params.indexes = makeCSV(params.indexes) - return req(path, Object.assign(params, {'string': input})) - - // ElasticSearch JSON method - case 'object': - input.indexes = makeCSV(params.indexes) - input.columns = makeCSV(params.columns) - return reqJSON(path, input) - - default: - throw new Error(`Unexpected input type for search: '${typeof(input)}'`) - } -} diff --git a/package-lock.json b/package-lock.json index d68490c..f5d30a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,22 +8,2686 @@ "name": "@xivapi/js", "version": "0.4.5", "license": "MIT", + "devDependencies": { + "@types/node": "^24.7.2", + "ts-node": "^10.9.2", + "typescript": "^5.9.3", + "vitest": "^3.2.4" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", + "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz", + "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz", + "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz", + "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz", + "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz", + "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz", + "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz", + "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz", + "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz", + "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz", + "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz", + "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz", + "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz", + "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz", + "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", + "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz", + "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz", + "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz", + "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz", + "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz", + "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz", + "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.7.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz", + "integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.14.0" + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", + "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.4", + "@rollup/rollup-android-arm64": "4.52.4", + "@rollup/rollup-darwin-arm64": "4.52.4", + "@rollup/rollup-darwin-x64": "4.52.4", + "@rollup/rollup-freebsd-arm64": "4.52.4", + "@rollup/rollup-freebsd-x64": "4.52.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", + "@rollup/rollup-linux-arm-musleabihf": "4.52.4", + "@rollup/rollup-linux-arm64-gnu": "4.52.4", + "@rollup/rollup-linux-arm64-musl": "4.52.4", + "@rollup/rollup-linux-loong64-gnu": "4.52.4", + "@rollup/rollup-linux-ppc64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-musl": "4.52.4", + "@rollup/rollup-linux-s390x-gnu": "4.52.4", + "@rollup/rollup-linux-x64-gnu": "4.52.4", + "@rollup/rollup-linux-x64-musl": "4.52.4", + "@rollup/rollup-openharmony-arm64": "4.52.4", + "@rollup/rollup-win32-arm64-msvc": "4.52.4", + "@rollup/rollup-win32-ia32-msvc": "4.52.4", + "@rollup/rollup-win32-x64-gnu": "4.52.4", + "@rollup/rollup-win32-x64-msvc": "4.52.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", + "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", + "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", + "dev": true, + "license": "MIT", "dependencies": { - "@root/request": "^1.9.2" + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" }, - "devDependencies": {} + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } }, - "node_modules/@root/request": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@root/request/-/request-1.9.2.tgz", - "integrity": "sha512-wVaL9yVV9oDR9UNbPZa20qgY+4Ch6YN8JUkaE4el/uuS5dmhD8Lusm/ku8qJVNtmQA56XLzEDCRS6/vfpiHK2A==" + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } } }, "dependencies": { - "@root/request": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@root/request/-/request-1.9.2.tgz", - "integrity": "sha512-wVaL9yVV9oDR9UNbPZa20qgY+4Ch6YN8JUkaE4el/uuS5dmhD8Lusm/ku8qJVNtmQA56XLzEDCRS6/vfpiHK2A==" + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "dev": true, + "optional": true + }, + "@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "dev": true, + "optional": true + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", + "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz", + "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz", + "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz", + "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-freebsd-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz", + "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-freebsd-x64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz", + "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz", + "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz", + "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz", + "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz", + "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz", + "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz", + "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz", + "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz", + "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz", + "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", + "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz", + "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-openharmony-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz", + "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz", + "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz", + "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz", + "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz", + "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==", + "dev": true, + "optional": true + }, + "@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", + "dev": true, + "requires": { + "@types/deep-eql": "*" + } + }, + "@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true + }, + "@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "@types/node": { + "version": "24.7.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz", + "integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==", + "dev": true, + "requires": { + "undici-types": "~7.14.0" + } + }, + "@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "requires": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + } + }, + "@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "requires": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + } + }, + "@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "requires": { + "tinyrainbow": "^2.0.0" + } + }, + "@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "requires": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + } + }, + "@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "requires": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + } + }, + "@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "requires": { + "tinyspy": "^4.0.3" + } + }, + "@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "requires": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + } + }, + "acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true + }, + "acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "requires": { + "acorn": "^8.11.0" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true + }, + "cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true + }, + "chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "requires": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + } + }, + "check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true + }, + "esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0" + } + }, + "expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true + }, + "fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "requires": {} + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true + }, + "loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true + }, + "magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true + }, + "pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, + "pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true + }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true + }, + "postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "requires": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + } + }, + "rollup": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", + "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.52.4", + "@rollup/rollup-android-arm64": "4.52.4", + "@rollup/rollup-darwin-arm64": "4.52.4", + "@rollup/rollup-darwin-x64": "4.52.4", + "@rollup/rollup-freebsd-arm64": "4.52.4", + "@rollup/rollup-freebsd-x64": "4.52.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", + "@rollup/rollup-linux-arm-musleabihf": "4.52.4", + "@rollup/rollup-linux-arm64-gnu": "4.52.4", + "@rollup/rollup-linux-arm64-musl": "4.52.4", + "@rollup/rollup-linux-loong64-gnu": "4.52.4", + "@rollup/rollup-linux-ppc64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-musl": "4.52.4", + "@rollup/rollup-linux-s390x-gnu": "4.52.4", + "@rollup/rollup-linux-x64-gnu": "4.52.4", + "@rollup/rollup-linux-x64-musl": "4.52.4", + "@rollup/rollup-openharmony-arm64": "4.52.4", + "@rollup/rollup-win32-arm64-msvc": "4.52.4", + "@rollup/rollup-win32-ia32-msvc": "4.52.4", + "@rollup/rollup-win32-x64-gnu": "4.52.4", + "@rollup/rollup-win32-x64-msvc": "4.52.4", + "@types/estree": "1.0.8", + "fsevents": "~2.3.2" + } + }, + "siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true + }, + "stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true + }, + "strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "requires": { + "js-tokens": "^9.0.1" + } + }, + "tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true + }, + "tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true + }, + "tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "requires": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + } + }, + "tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true + }, + "tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true + }, + "tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true + }, + "ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true + }, + "undici-types": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", + "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "vite": { + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", + "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", + "dev": true, + "requires": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "fsevents": "~2.3.3", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + } + }, + "vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "requires": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + } + }, + "vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "requires": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + } + }, + "why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "requires": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index 1e16cd7..1dd61c5 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,5 @@ { + "type": "module", "name": "@xivapi/js", "version": "0.4.5", "description": "A Node.JS wrapper for xivapi.com", @@ -6,12 +7,15 @@ "directories": { "lib": "lib" }, - "dependencies": { - "@root/request": "^1.9.2" + "devDependencies": { + "@types/node": "^24.7.2", + "ts-node": "^10.9.2", + "typescript": "^5.9.3", + "vitest": "^3.2.4" }, - "devDependencies": {}, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build": "tsc", + "test": "vitest" }, "repository": { "type": "git", diff --git a/resources/index.js b/resources/index.js deleted file mode 100644 index 910f1f1..0000000 --- a/resources/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - languages: ['en', 'ja', 'de', 'fr', 'cn', 'kr'] -} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..98dbaa5 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,367 @@ +export * from "./lib/assets"; +export * from "./lib/search"; +export * from "./lib/sheets"; +export * from "./lib/versions"; + +export namespace XIVAPI { + export interface Options { + /** + * Language to use for the API. + */ + language?: keyof typeof Models.SchemaLanguage; + /** + * Whether to enable verbose logging. + * @default false + */ + verbose?: boolean; + } +} + +/** + * Models are used to define the structure of the data returned by the API. + * @see https://v2.xivapi.com/api/docs#models + */ +export namespace Models { + /** + * Query parameters accepted by endpoints that interact with versioned game data. + * @see https://v2.xivapi.com/api/docs#model/versionquery + */ + export interface VersionQuery { + /** + * Game version to utilise for this query. + */ + version?: string | null; + } + + /** + * Query parameters accepted by the asset endpoint. + * @see https://v2.xivapi.com/api/docs#model/assetquery + */ + export interface AssetQuery { + format: string | SchemaFormat; + /** + * Game path of the asset to retrieve. + * @example "ui/icon/051000/051474_hr1.tex" + */ + path: string; + } + + /** + * @see https://v2.xivapi.com/api/docs#model/schemaformat + */ + export enum SchemaFormat { + jpg = "jpg", + png = "png", + webp = "webp", + } + + /** + * General purpose error response structure. + * @see https://v2.xivapi.com/api/docs#model/errorresponse + */ + export interface ErrorResponse { + code: number; + /** + * Description of what went wrong. + */ + message: string; + } + + /** + * @see https://v2.xivapi.com/api/docs#model/statuscode + */ + export type StatusCode = number; + + /** + * Path segments expected by the asset map endpoint. + * @see https://v2.xivapi.com/api/docs#model/mappath + */ + export interface MapPath { + /** + * Index of the map within the territory. This invariably takes the form of a two-digit zero-padded number. See Map's Id field for examples of possible combinations of `territory` and `index`. + * @example "00" + */ + index: string; + /** + * Territory of the map to be retrieved. This typically takes the form of 4 characters, [letter][number][letter][number]. See Map's Id field for examples of possible combinations of `territory` and `index`. + * @example "s1d1" + */ + territory: string; + } + + /** + * Query paramters accepted by the search endpoint. + * @see https://v2.xivapi.com/api/docs#model/searchquery + */ + export interface SearchQuery { + /** + * Continuation token to retrieve further results from a prior search request. If specified, takes priority over query. + */ + cursor?: string | null; + /** + * Maximum number of rows to return. To paginate, provide the cursor token provided in `next` to the `cursor` parameter. + */ + limit?: number | null; + /** + * A query string for searching excel data. + * Queries are formed of clauses, which take the basic form of `[specifier][operation][value]`, i.e. `Name="Example"`. Multiple clauses may be specified by seperating them with whitespace, i.e. `Foo=1 Bar=2`. + * @see https://v2.xivapi.com/docs/guides/search/#query + */ + query?: QueryString; + /** + * List of excel sheets that the query should be run against. At least one must be specified if not querying a cursor. + */ + sheets?: string | null; + } + + /** + * A query string for searching excel data. + * Queries are formed of clauses, which take the basic form of `[specifier][operation][value]`, i.e. `Name="Example"`. Multiple clauses may be specified by seperating them with whitespace, i.e. `Foo=1 Bar=2`. + * @see https://v2.xivapi.com/api/docs#model/querystring + */ + export type QueryString = string | null; + + /** + * Query parameters accepted by endpoints that retrieve excel row data. + * @see https://v2.xivapi.com/api/docs#model/rowreaderquery + */ + export interface RowReaderQuery { + /** + * A filter string for selecting fields within a row. + * Filters are comprised of a comma-seperated list of field paths, i.e. `a,b` will select the fields `a` and `b`. + */ + fields?: FilterString | null; + /** + * Known languages supported by the game data format. **NOTE:** Not all languages that are supported by the format are valid for all editions of the game. For example, the global game client acknowledges the existence of `chs` and `kr`, however does not provide any data for them. + */ + language?: string | SchemaLanguage | null; + /** + * Schema that row data should be read with. + */ + schema?: SchemaSpecifier | null; + /** + * A filter string for selecting fields within a row. + * Filters are comprised of a comma-seperated list of field paths, i.e. `a,b` will select the fields `a` and `b`. + */ + transient?: FilterString | null; + } + + /** + * Known languages supported by the game data format. **NOTE:** Not all languages that are supported by the format are valid for all editions of the game. For example, the global game client acknowledges the existence of `chs` and `kr`, however does not provide any data for them. + * @see https://v2.xivapi.com/api/docs#model/schemalanguage + */ + export enum SchemaLanguage { + none = "none", + en = "en", + ja = "ja", + de = "de", + fr = "fr", + chs = "chs", + cht = "cht", + kr = "kr", + } + + /** + * @see https://v2.xivapi.com/api/docs#model/schemaspecifier + */ + export type SchemaSpecifier = string; // `^.+(@.+)?$` + + /** + * A filter string for selecting fields within a row. + * Filters are comprised of a comma-seperated list of field paths, i.e. `a,b` will select the fields `a` and `b`. + * @see https://v2.xivapi.com/api/docs#model/filterstring + */ + export type FilterString = string; + + /** + * Response structure for the search endpoint. + * @see https://v2.xivapi.com/api/docs#model/searchresponse + */ + export interface SearchResponse { + results: SearchResult[]; + schema: SchemaSpecifier; + next?: string | null; + } + + /** + * Result found by a search query, hydrated with data from the underlying excel row the result represents. + * @see https://v2.xivapi.com/api/docs#model/searchresult + */ + export interface SearchResult { + fields: object; + /** + * ID of this row. + */ + row_id: number; + /** + * Relevance score for this entry. + * These values only loosely represent the relevance of an entry to the search query. No guarantee is given that the discrete values, nor resulting sort order, will remain stable. + */ + score: number; + /** + * Excel sheet this result was found in. + */ + sheet: SchemaSpecifier; + /** + * Subrow ID of this row, when relevant. + */ + subrow_id?: number; + /** + * Field values for this row's transient row, if any is present, according to the current schema and transient filter. + */ + transient?: object; + } + + /** + * Response structure for the list endpoint. + * @see https://v2.xivapi.com/api/docs#model/listresponse + */ + export interface ListResponse { + /** + * Array of sheets known to the API. + * Metadata about a single sheet. + */ + sheets: SheetMetadata[]; + } + + /** + * Metadata about a single sheet. + * @see https://v2.xivapi.com/api/docs#model/sheetmetadata + */ + export interface SheetMetadata { + /** + * The name of the sheet. + */ + name: string; + } + + /** + * Path variables accepted by the sheet endpoint. + * @see https://v2.xivapi.com/api/docs#model/sheetpath + */ + export interface SheetPath { + /** + * Name of the sheet to read. + */ + sheet: SchemaSpecifier; + } + + /** + * Query parameters accepted by the sheet endpoint. + * @see https://v2.xivapi.com/api/docs#model/sheetquery + */ + export interface SheetQuery { + /** + * Fetch rows after the specified row. Behavior is undefined if both `rows` and `after` are provided. + */ + after?: SchemaSpecifier | null; + /** + * Maximum number of rows to return. To paginate, provide the last returned row to the next request's `after` parameter. + */ + limit?: number | null; + /** + * Rows to fetch from the sheet, as a comma-separated list. Behavior is undefined if both `rows` and `after` are provided. + */ + rows?: string; // `^\d+(:\d+)?(,\d+(:\d+)?)*$` + } + + /** + * @see https://v2.xivapi.com/api/docs#model/rowspecifier + */ + export type RowSpecifier = string; // `^\d+(:\d+)?$` + + /** + * Response structure for the sheet endpoint. + * @see https://v2.xivapi.com/api/docs#model/sheetresponse + */ + export interface SheetResponse { + /** + * Array of rows retrieved by the query. + * @see https://v2.xivapi.com/api/docs#model/rowresult + */ + rows: RowResult[]; + /** + * The canonical specifier for the schema used in this response. + */ + schema: SchemaSpecifier; + } + + /** + * Row retrieved by the query. + * @see https://v2.xivapi.com/api/docs#model/rowresult + */ + export interface RowResult { + fields: object; + /** + * ID of this row. + */ + row_id: number; + /** + * Subrow ID of this row, when relevant. + */ + subrow_id?: number | null; + /** + * Field values for this row's transient row, if any is present, according to the current schema and transient filter. + */ + transient?: object; + } + + /** + * Path variables accepted by the row endpoint. + * @see https://v2.xivapi.com/api/docs#model/rowpath + */ + export interface RowPath { + row: RowSpecifier; + /** + * Name of the sheet to read. + */ + sheet: SchemaSpecifier; + } + + /** + * Response structure for the row endpoint. + * @see https://v2.xivapi.com/api/docs#model/rowresponse + */ + export interface RowResponse { + fields: object; + /** + * ID of this row. + */ + row_id: number; + /** + * The canonical specifier for the schema used in this response. + */ + schema: SchemaSpecifier; + /** + * Subrow ID of this row, when relevant. + */ + subrow_id?: number | null; + /** + * Field values for this row's transient row, if any is present, according to the current schema and transient filter. + */ + transient?: object; + } + + /** + * Response structure for the versions endpoint. + * @see https://v2.xivapi.com/api/docs#model/versionsresponse + */ + export interface VersionsResponse { + /** + * Array of versions available in the API. + * Metadata about a single version supported by the API. + */ + versions: VersionMetadata[]; + } + + /** + * Metadata about a single version supported by the API. + * @see https://v2.xivapi.com/api/docs#model/versionmetadata + */ + export interface VersionMetadata { + /** + * Names associated with this version. Version names specified here are accepted by the `version` query parameter throughout the API. + */ + names: string[]; + } +} diff --git a/src/lib/assets.ts b/src/lib/assets.ts new file mode 100644 index 0000000..96a75ba --- /dev/null +++ b/src/lib/assets.ts @@ -0,0 +1,32 @@ +import type {Models} from ".."; +import { CustomError, request } from "../utils"; + +/** + * Endpoints for accessing game data on a file-by-file basis. Commonly useful for fetching icons or other textures to display on the web. + * @see https://v2.xivapi.com/api/docs#tag/assets + */ +export class Assets { + /** + * Read an asset from the game at the specified path, converting it into a usable format. If no valid conversion between the game file type and specified format exists, an error will be returned. + * @param {Models.AssetQuery} params Query parameters accepted by the asset endpoint. + * @returns {Promise} An image of the asset. + * @see https://v2.xivapi.com/api/docs#tag/assets/get/asset + */ + async get(params: Models.AssetQuery): Promise { + const { data, errors } = await request({ path: "/asset", params }); + if (errors) throw new CustomError(errors[0].message); + return data as Buffer; + } + + /** + * Retrieve the specified map, composing it from split source files if necessary. + * @param {Models.MapPath & Models.VersionQuery & Models.AssetQuery} params + * @returns {Promise} An image of the map. + * @see https://v2.xivapi.com/api/docs#tag/assets/get/asset/map/{territory}/{index} + */ + async map(params: Models.MapPath & Models.VersionQuery & Pick): Promise { + const { data, errors } = await request({ path: "/asset/map", params }); + if (errors) throw new CustomError(errors[0].message); + return data as Buffer; + } +} diff --git a/src/lib/search.ts b/src/lib/search.ts new file mode 100644 index 0000000..6d8286d --- /dev/null +++ b/src/lib/search.ts @@ -0,0 +1,31 @@ +import type { Models, XIVAPI } from ".."; +import { CustomError, request } from "../utils"; + +/** + * Endpoints for seaching and filtering the game's static relational data store. + * @see https://v2.xivapi.com/api/docs#tag/search + */ +export class Search { + private readonly options: XIVAPI.Options; + + constructor( + options: XIVAPI.Options = { + language: "en", + verbose: false, + } + ) { + this.options = options; + } + + /** + * Fetch information about rows and their related data that match the provided search query. + * @param {Models.SearchQuery} params Query paramters accepted by the search endpoint. + * @returns {Promise} Response structure for the search endpoint. + * @see https://v2.xivapi.com/api/docs#tag/search/get/search + */ + async get(params: Models.SearchQuery): Promise { + const { data, errors } = await request({ path: "/search", params, options: this.options }); + if (errors) throw new CustomError(errors[0].message); + return data as Models.SearchResponse; + } +} diff --git a/src/lib/sheets.ts b/src/lib/sheets.ts new file mode 100644 index 0000000..7f1473c --- /dev/null +++ b/src/lib/sheets.ts @@ -0,0 +1,62 @@ +import type {Models, XIVAPI} from ".."; +import { CustomError, request } from "../utils"; + +export class Sheets { + private readonly options: XIVAPI.Options; + + /** + * Endpoints for reading data from the game's static relational data store. + * @param {XIVAPI.Options} [options] The options to fetch the sheets with. + * @see https://v2.xivapi.com/api/docs#tag/sheets + */ + public constructor(options: XIVAPI.Options = { + language: "en", + verbose: false, + }) { + this.options = options; + } + + /** + * List known excel sheets that can be read by the API. + * @returns {Promise} Response structure for the list endpoint. + * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet + */ + async all(): Promise { + const { data, errors } = await request({ path: "/sheet", options: this.options }); + if (errors) throw new CustomError(errors[0].message); + return data as Models.ListResponse; + } + + /** + * Read information about one or more rows and their related data. + * @param {Models.SchemaSpecifier} sheet The sheet to fetch the rows from. + * @param {Models.SheetQuery} [params] The parameters to fetch the rows with. + * @returns {Promise} A list of rows with typed fields. + * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet/{sheet} + */ + async list( + sheet: Models.SchemaSpecifier, + params: Models.SheetQuery = {} + ): Promise { + const { data, errors } = await request({ path: `/sheet/${sheet}`, params, options: this.options }); + if (errors) throw new CustomError(errors[0].message); + return data as Models.SheetResponse; + } + + /** + * Read detailed, filterable information from a single sheet row and its related data. + * @param {Models.SchemaSpecifier} sheet Name of the sheet to read. + * @param {number} row Row to read. + * @returns {Promise} A list of rows with typed fields. + * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet/{sheet}/{id} + */ + async get( + sheet: Models.SchemaSpecifier, + row: number, + params: Models.RowReaderQuery = {} + ): Promise { + const { data, errors } = await request({ path: `/sheet/${sheet}/${row}`, params, options: this.options }); + if (errors) throw new CustomError(errors[0].message); + return data as Models.RowResponse; + } +} diff --git a/src/lib/versions.ts b/src/lib/versions.ts new file mode 100644 index 0000000..243dcd5 --- /dev/null +++ b/src/lib/versions.ts @@ -0,0 +1,18 @@ +import type {Models} from ".."; +import { CustomError, request } from "../utils"; + +/** + * Endpoints for querying metadata about the versions recorded by the boilmaster system. + * @see https://v2.xivapi.com/api/docs#tag/versions + */ +export class Versions { + /** + * List versions understood by the API. + * @see https://v2.xivapi.com/api/docs#tag/versions/get/version + */ + async all(): Promise { + const { data, errors } = await request({ path: "/version" }); + if (errors) throw new CustomError(errors[0].message); + return data as Models.VersionsResponse; + } +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..4c5b248 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,50 @@ +import type { Models, XIVAPI } from "."; + +export const endpoint = "https://v2.xivapi.com/api/"; + +export class CustomError extends Error { + constructor(message: string, name: string | null = null) { + super(); + Error.captureStackTrace(this, this.constructor); + this.name = name || "XIVAPIError"; + this.message = message; + } +} + +export interface RequestPayload { + path: string | URL; + data?: unknown; + params?: Record; + errors?: Models.ErrorResponse[]; + options?: XIVAPI.Options; +} + +export const request = async (payload: RequestPayload): Promise => { + const { path, params, options } = payload; + + const url = new URL(path instanceof URL ? path.toString() : path.replace(/^\/+/, ""), endpoint); + if (params) { + url.search = new URLSearchParams(params).toString(); + + if (!params.language) { + if (options && options.language) url.searchParams.set("language", options.language); + } + } + + const response = await fetch(url); + if (options && options.verbose) console.debug(`Requesting ${path} with params:`, params); + + if (response.ok) { + const contentType = response.headers.get("content-type"); + if (contentType && contentType.includes("application/json")) { + payload.data = await response.json(); + } else { + // For binary data (images, etc.) + payload.data = Buffer.from(await response.arrayBuffer()); + } + } else { + payload.errors = [(await response.json()) as Models.ErrorResponse]; + } + + return payload; +}; diff --git a/test.js b/test.js deleted file mode 100644 index 8ff04c6..0000000 --- a/test.js +++ /dev/null @@ -1,15 +0,0 @@ -const XIVAPI = require('./XIVAPI'), - readline = require('readline') - -const xiv = new XIVAPI({ - //snake_case: true, - verbose: true -}) - -let rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - terminal: false -}).on('line', function (cmd) { - console.log(eval(cmd)) -}) diff --git a/tests/xivapi-js.test.ts b/tests/xivapi-js.test.ts new file mode 100644 index 0000000..6309a24 --- /dev/null +++ b/tests/xivapi-js.test.ts @@ -0,0 +1,204 @@ +import { describe, it, expect, beforeEach } from "vitest"; +import { Versions, Assets, Search, Sheets } from "../src/index"; +import { CustomError } from "../src/utils"; + +describe("@xivapi/js", () => { + const API_TIMEOUT = 10000; + + describe("versions", () => { + it("should fetch all versions successfully", async () => { + const versions = new Versions(); + const result = await versions.all(); + + expect(result).toBeDefined(); + expect(result.versions).toBeDefined(); + expect(Array.isArray(result.versions)).toBe(true); + expect(result.versions.length).toBeGreaterThan(0); + + result.versions.forEach(version => { + expect(version.names).toBeDefined(); + expect(Array.isArray(version.names)).toBe(true); + expect(version.names.length).toBeGreaterThan(0); + }); + }, API_TIMEOUT); + }); + + describe("assets", () => { + it("should fetch asset successfully", async () => { + const assetParams = { + path: "ui/icon/051000/051474_hr1.tex", + format: "png" + }; + + const assets = new Assets(); + const result = await assets.get(assetParams); + + expect(result).toBeDefined(); + expect(Buffer.isBuffer(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + }, API_TIMEOUT); + + it("should handle map requests correctly", async () => { + const mapParams = { + territory: "invalid", + index: "00", + version: "latest", + format: "png" + }; + + const assets = new Assets(); + + await expect(assets.map(mapParams)).rejects.toThrow(); + }, API_TIMEOUT); + + it("should throw CustomError when asset fetch fails", async () => { + const assets = new Assets(); + + await expect(assets.get({ + path: "invalid/path/that/does/not/exist.tex", + format: "png" + })).rejects.toThrow(); + }, API_TIMEOUT); + }); + + describe("search", () => { + it("should perform search successfully with default options", async () => { + const searchParams = { + query: 'Name="Iron Sword"', + sheets: "Item", + limit: 5, + }; + + const search = new Search(); + const result = await search.get(searchParams); + + expect(result).toBeDefined(); + expect(result.results).toBeDefined(); + expect(Array.isArray(result.results)).toBe(true); + expect(result.schema).toBeDefined(); + + if (result.results.length > 0) { + const firstResult = result.results[0]; + expect(firstResult.row_id).toBeDefined(); + expect(typeof firstResult.row_id).toBe("number"); + expect(firstResult.score).toBeDefined(); + expect(typeof firstResult.score).toBe("number"); + expect(firstResult.sheet).toBeDefined(); + expect(firstResult.fields).toBeDefined(); + } + }, API_TIMEOUT); + + it("should perform search successfully with custom options", async () => { + const searchParams = { + query: 'Name="Iron Sword"', + sheets: "Item", + limit: 3, + }; + + const customOptions = { + language: "ja" as const, + verbose: true, + }; + + const search = new Search(customOptions); + const result = await search.get(searchParams); + + expect(result).toBeDefined(); + expect(result.results).toBeDefined(); + expect(Array.isArray(result.results)).toBe(true); + expect(result.schema).toBeDefined(); + }, API_TIMEOUT); + + it("should throw CustomError when search fails", async () => { + const search = new Search(); + + await expect(search.get({ + query: "invalid query syntax that should fail", + sheets: "Item" + })).rejects.toThrow(); + }, API_TIMEOUT); + }); + + describe("sheets", () => { + it("should list all sheets successfully with default options", async () => { + const sheets = new Sheets(); + const result = await sheets.all(); + + expect(result).toBeDefined(); + expect(result.sheets).toBeDefined(); + expect(Array.isArray(result.sheets)).toBe(true); + expect(result.sheets.length).toBeGreaterThan(0); + + result.sheets.forEach(sheet => { + expect(sheet.name).toBeDefined(); + expect(typeof sheet.name).toBe("string"); + }); + }, API_TIMEOUT); + + it("should list all sheets successfully with custom options", async () => { + const customOptions = { + language: "fr" as const, + verbose: true, + }; + + const sheets = new Sheets(customOptions); + const result = await sheets.all(); + + expect(result).toBeDefined(); + expect(result.sheets).toBeDefined(); + expect(Array.isArray(result.sheets)).toBe(true); + expect(result.sheets.length).toBeGreaterThan(0); + }, API_TIMEOUT); + + it("should list sheet rows successfully", async () => { + const sheetParams = { limit: 5 }; + + const sheets = new Sheets(); + const result = await sheets.list("Item", sheetParams); + + expect(result).toBeDefined(); + expect(result.rows).toBeDefined(); + expect(Array.isArray(result.rows)).toBe(true); + expect(result.schema).toBeDefined(); + + if (result.rows.length > 0) { + const firstRow = result.rows[0]; + expect(firstRow.row_id).toBeDefined(); + expect(typeof firstRow.row_id).toBe("number"); + expect(firstRow.fields).toBeDefined(); + } + }, API_TIMEOUT); + + it("should get specific sheet row successfully", async () => { + const rowParams = { + fields: "Name,ID", + language: "en", + }; + + const sheets = new Sheets(); + const result = await sheets.get("Item", 1, rowParams); + + expect(result).toBeDefined(); + expect(result.row_id).toBeDefined(); + expect(typeof result.row_id).toBe("number"); + expect(result.schema).toBeDefined(); + expect(result.fields).toBeDefined(); + }, API_TIMEOUT); + + it("should use default parameters when none provided", async () => { + const sheets = new Sheets(); + const result = await sheets.list("Item"); + + expect(result).toBeDefined(); + expect(result.rows).toBeDefined(); + expect(Array.isArray(result.rows)).toBe(true); + expect(result.schema).toBeDefined(); + }, API_TIMEOUT); + + it("should throw CustomError when sheet operation fails", async () => { + const sheets = new Sheets(); + + await expect(sheets.list("NonExistentSheetThatDoesNotExist")).rejects.toThrow(); + }, API_TIMEOUT); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..167a608 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@tsconfig/node16/tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "target": "ES2022", + "declaration": true, + "preserveConstEnums": true, + "outDir": "dist" + }, + "include": ["src/**/*"], + "exclude": ["**/*.spec.ts"] +} diff --git a/typings/index.d.ts b/typings/index.d.ts deleted file mode 100644 index 5b66f75..0000000 --- a/typings/index.d.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { - CharacterGetParams, - CharacterGetResult, - CharacterSearchParams, - CharacterSearchResult, - FreeCompanyGetParams, - FreeCompanyGetResult, - FreeCompanySearchParams, - FreeCompanySearchResult, - LinkshellGetResult, - LinkshellSearchParams, - LinkshellSearchResult, - CWLGetResult, - CWLSearchParams, - CWLSearchResult, - PvPTeamGetResult, - PvPTeamSearchParams, - PvPTeamSearchResult, - SearchIndexResult, - SearchIndexes, - SearchResult, -} from "./utils"; -export * from "./utils"; - -declare module "@xivapi/js" { - type StringAlgo = - | "custom" - | "wildcard" - | "wildcard_plus" - | "fuzzy" - | "term" - | "prefix" - | "match" - | "match_phrase" - | "match_phrase_prefix" - | "multi_match" - | "query_string"; - - interface XIVAPIOptions { - private_key?: string; - language?: "en" | "de" | "fr" | "ja" | "cn" | "ko"; - snake_case?: boolean; - staging?: boolean; - verbose?: boolean; - } - - interface DataSearchParams { - /** - * Search a specific series of indexes separated by commas. - */ - indexes: SearchIndexes[]; - - /** - * Search for lore! This is a special built search endpoint which searches a string through various lore specific content. - * @see https://xivapi.com/docs/Search#lore - */ - lore?: boolean; - - /** - * The column to use in string searches. - * @see https://xivapi.com/docs/Search#filters - */ - filters?: string | string[]; - - /** - * The search algorithm to use for string matching. - * @default "wildcard" - * @see https://xivapi.com/docs/Search - */ - string_algo?: StringAlgo; - - /** - * The column to use in string searches. - */ - string_column?: string; - - /** - * Limit the number of results to show. (from 1 to 100) - */ - limit?: number; - } - - export default class XIVAPI { - private readonly options: XIVAPIOptions; - private readonly endpoint: string; - private readonly globalParams: { [key: string]: string | number }; - - constructor(options: string | XIVAPIOptions); - constructor(options: string | XIVAPIOptions, legacyOptions?: XIVAPIOptions); - - /** - * XIVAPI provides the ability to quickly search all game content via Elasticsearch. - * This search endpoint only searches game content and not: characters, free companies, linkshells or pvp teams. - * Those have their own dedicated search endpoints as they relay to Lodestone. - * @since 0.4.2 - * @see https://xivapi.com/docs/Search - * @example - * ```ts - * const xiv = new XIVAPI(); - * await xiv.search("aiming"); // without params - * await xiv.search("aiming", { indexes: ["Item", "Recipe"] }); // with params - * ``` - */ - public search( - input: string, - params?: DataSearchParams - ): Promise; - - /** - * Obtain game content data of Final Fantasy XIV. - * @since 0.4.2 - * @see https://xivapi.com/docs/Game-Data - */ - public data: { - /** - * Returns information about a specific object including extended information. - * @since 0.4.2 - * @see https://xivapi.com/docs/Game-Data - * @example - * ```ts - * const xiv = new XIVAPI(); - * await xiv.data.get("Item", 1673); - * ``` - */ - get: (name: string, id: string | number) => Promise<{ [key: string]: any }>; - - /** - * Obtain game content data of Final Fantasy XIV. - * @since 0.4.2 - * @see https://xivapi.com/docs/Game-Data - * @example - * ```ts - * const xiv = new XIVAPI(); - * await xiv.data.list("Item", { limit: 100 }); // with limit param - * await xiv.data.list("Item", { ids: [1673, 1674] }); // with ids param - * ``` - */ - list: ( - name: keyof typeof SearchIndexes, - params?: { - /** - * Limit the number of items returned by the API. - * @min 100 - * @max 3000 - */ - limit?: number; - - /** - * Filter the ids down if you want data for a specific series of items. - */ - ids?: number[]; - } - ) => Promise; - - /** - * Returns information about a specific object including extended information. - * @since 0.4.2 - * @see https://xivapi.com/docs/Game-Data#servers - */ - servers: () => Promise; - - /** - * Another list of servers grouped by their data center. - * @since 0.4.2 - * @see https://xivapi.com/docs/Game-Data#data-center - */ - datacenters: () => Promise<{ [key: string]: string[] }>; - }; - - /** - * Search and retrieve character data from The Lodestone. Providing useful information such as character profile data, minions and mounts obtained, achievements obtained and their relative dates. Character friends, their free company, pvp team and much more! - * @since 0.4.2 - * @see https://xivapi.com/docs/Character - */ - public character: { - search: (name: string, params?: CharacterSearchParams) => Promise; - get: (id: string | number, params?: CharacterGetParams) => Promise; - }; - - /** - * Search and retrieve Free Company data from The Lodestone, provides useful information such as profile information and member lists. - * @since 0.4.2 - * @see https://xivapi.com/docs/Free-Company - */ - public freecompany: { - search: (name: string, params?: FreeCompanySearchParams) => Promise; - get: (id: string | number, params?: FreeCompanyGetParams) => Promise; - }; - - /** - * Search and retrieve Linkshell data from The Lodestone. - * @since 0.4.2 - * @see https://xivapi.com/docs/Linkshell - */ - public linkshell: { - search: (name: string, params?: LinkshellSearchParams) => Promise; - get: (id: string | number) => Promise; - }; - - /** - * Search and retrieve CWL data from The Lodestone. - * @since 0.4.4 - * @see https://xivapi.com/docs/Linkshell - */ - public cwl: { - search: (name: string, params?: CWLSearchParams) => Promise; - get: (id: string | number) => Promise; - }; - - /** - * Search and retrieve PVP Team data from The Lodestone. - * @since 0.4.2 - * @see https://xivapi.com/docs/PvP-Team - */ - public pvpteam: { - search: (name: string, params?: PvPTeamSearchParams) => Promise; - get: (id: string | number) => Promise; - }; - } -} diff --git a/typings/utils/achievements.d.ts b/typings/utils/achievements.d.ts deleted file mode 100644 index 48a5e69..0000000 --- a/typings/utils/achievements.d.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { GamePatchData, ItemData, TitleData } from "./item"; -import { SearchResult } from "./search"; - -export interface AchievementsData { - List: { Date: number; ID: number }[]; - Points: number; -} - -export interface AchievementCategoryData { - AchievementsKind: { - ID: number; - Name: string; - Name_de: string; - Name_en: string; - Name_fr: string; - Name_ja: string; - Order: number; - }; - AchievementKindTarget: string; - AchievementKindTargetID: number; - HideCategory: number; - ID: number; - Name: string; - Name_de: string; - Name_en: string; - Name_fr: string; - Name_ja: string; - Order: number; - ShowComplete: number; -} - -export interface AchievementGetResult { - AchievementCategory: AchievementCategoryData; - AchievementCategoryTarget: string; - AchievementCategoryTargetID: number; - AchievementHideCondition: string | null; - AchievementHideConditionTarget: string; - AchievementHideConditionTargetID: number; - AchievementTarget: { ID: number; Type: 1; Value: number } | null; - AchievementTargetID: number | null; - ClassJobRequirements: {}[]; - Data0: number; - Data1: number; - Data2: number; - Data3: number; - Data4: number; - Data5: number; - Data6: number; - Data7: number; - Description: string; - Description_de: string; - Description_en: string; - Description_fr: string; - Description_ja: string; - GameContentLinks: []; - GamePatch: GamePatchData; - ID: number; - Icon: string; - IconHD: string; - ItemID: number; - Item: ItemData | null; - ItemTarget: string; - ItemTargetID: number; - Key: number; - Name: string; - Name_de: string; - Name_en: string; - Name_fr: string; - Name_ja: string; - Order: number; - Patch: number | null; - Points: number; - PostAchievements: []; - PreAchievements: []; - QuestRequirements: {}[]; - QuestRequirementsAll: boolean; - Title: TitleData | null; - TitleTarget: string; - TitleTargetID: number; - Type: number; - Url: string; -} diff --git a/typings/utils/character.d.ts b/typings/utils/character.d.ts deleted file mode 100644 index b2bcb91..0000000 --- a/typings/utils/character.d.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { SearchParams, SearchResult } from "./search"; -import { AchievementsData } from "./achievements"; -import { FreeCompanyGetResult } from "./freecompany"; - -type Omit = Pick>; -export interface CharacterSearchParams extends Omit {} -export interface CharacterGetParams { - /** - * If set to 1, the API will return more data in the response by extending out the data IDs to useful objects. - */ - extended?: 1; - - /** - * By default the `Character`, `ClassJobs`, `Minion` and `Mount` data will return, you can request more data using the `data` query. - * @see https://xivapi.com/docs/Character#character - */ - data?: ("AC" | "FR" | "FC" | "FC" | "FCM" | "MIMO" | "PVP")[]; -} - -export interface BasicCharacterData { - Avatar: string; - FeastMatches: number; - ID: number; - Lang: string | null; - Name: string; - Rank: number | null; - RankIcon: string | null; - Server: string; -} - -export interface CharacterSearchResult extends SearchResult { - Results: BasicCharacterData[]; -} - -export interface ClassJobData { - ClassID: number; - ExpLevel: number; - ExpLevelMax: number; - ExpLevelTogo: number; - IsSpecialised: boolean; - JobID: number; - Level: number; - Name: string; - UnlockedState: { - ID: number; - Name: string; - }; -} - -export interface GearData { - Creator: string | null; - Dye: number | null; - ID: number | null; - Materia: number[]; - Mirage: number | null; -} - -export interface GearsetData { - Attributes: { [key: string]: number }; - ClassID: number; - Gear: { - Body: GearData | undefined; - Bracelets: GearData | undefined; - Earrings: GearData | undefined; - Feet: GearData | undefined; - Hands: GearData | undefined; - Head: GearData | undefined; - Legs: GearData | undefined; - MainHand: GearData | undefined; - Necklace: GearData | undefined; - OffHand: GearData | undefined; - Ring1: GearData | undefined; - Ring2: GearData | undefined; - SoulCrystal: GearData | undefined; - }; - GearKey: string; - JobID: number; - Level: number; -} - -export interface CharacterData { - ActiveClassJob: ClassJobData; - Avatar: string; - Bio: string; - ClassJobs: ClassJobData[]; - ClassJobsBozjan: { Level: number | null; Mettle: number | null; Name: string }; - ClassJobsElemental: { - ExpLevel: number; - ExpLevelMax: number; - ExpLevelTogo: number; - Level: number; - Name: string; - }[]; - DC: string; - FreeCompanyId: string | null; - FreeCompanyName: string | null; - GearSet: GearsetData; - Gender: 1 | 2; - GrandCompany: { - NameID: number; - RankID: number; - }; - GuardianDeity: number; - ID: number; - Lang: string | null; - Name: string; - Nameday: string; - ParseDate: number; - Portrait: string; - PvPTeamId: number | null; - Race: number; - Server: string; - Title: number; - TitleTop: boolean; - Town: number; - Tribe: number; -} - -export interface CharacterGetResult { - Achievements: AchievementsData | null; - AchievementsPublic: boolean | null; - Character: CharacterData; - FreeCompany: FreeCompanyGetResult | null; - FreeCompanyMembers: BasicCharacterData[] | null; - Friends: BasicCharacterData[] | null; - FriendsPublic: boolean | null; - Minions: { Icon: string; Name: string }[] | null; - Mounts: { Icon: string; Name: string }[] | null; - PvPTeam: null; -} diff --git a/typings/utils/cwl.ts b/typings/utils/cwl.ts deleted file mode 100644 index 3aa79ae..0000000 --- a/typings/utils/cwl.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { CharacterSearchResult } from "./character"; -import { SearchParams, SearchResult } from "./search"; - -export interface CWLSearchParams extends SearchParams {} - -export interface CWLSearchResult extends SearchResult { - Results: { - Crest: string[]; - ID: string; - Name: string; - Server: string; - }[]; -} - -export interface CWLGetResult { - CWL: { - ID: string; - Pagination: SearchResult["Pagination"]; - Profile: { - Name: string; - }; - Results: CharacterSearchResult["Results"]; - }; -} diff --git a/typings/utils/freecompany.d.ts b/typings/utils/freecompany.d.ts deleted file mode 100644 index 746cbe6..0000000 --- a/typings/utils/freecompany.d.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { SearchParams, SearchResult } from "./search"; - -export interface FreeCompanySearchParams extends SearchParams {} -export interface FreeCompanyGetParams { - /** - * If set to 1, the API will return more data in the response by extending out the data IDs to useful objects. - */ - extended?: 1; - - /** - * By default the `Character`, `ClassJobs`, `Minion` and `Mount` data will return, you can request more data using the `data` query. - * @see https://xivapi.com/docs/Character#character - */ - data?: ["FCM"]; -} - -export interface FreeCompanySearchResult extends SearchResult { - Results: { - Crest: string[]; - ID: string; - Name: string; - Server: string; - }[]; -} - -export interface FreeCompanyGetResult { - Active: string; - ActiveMemberCount: number; - Crest: string[]; - DC: string; - Estate: { - Greeting: string; - Name: string; - Plot: string; - }; - Focus: { Icon: string; Name: string; Status: boolean }[]; - Formed: number; - GrandCompany: "Maelstrom" | "Order of the Twin Adder" | "Immortal Flames"; - ID: string; - Name: string; - ParseDate: number; - Rank: string; - Rankings: { - Monthly: number; - Weekly: number; - }; - Recruitment: string; - Reputation: [ - { Name: string; Progress: number; Rank: number }, - { Name: string; Progress: number; Rank: number }, - { Name: string; Progress: number; Rank: number } - ]; - Seeking: { Icon: string; Name: string; Status: boolean }[]; - Server: string; - Slogan: string; - Tag: string; -} diff --git a/typings/utils/index.d.ts b/typings/utils/index.d.ts deleted file mode 100644 index 18abed1..0000000 --- a/typings/utils/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./character"; -export * from "./freecompany"; -export * from "./linkshell"; -export * from "./cwl"; -export * from "./pvpteam"; -export * from "./search"; diff --git a/typings/utils/item.d.ts b/typings/utils/item.d.ts deleted file mode 100644 index fcf2c6a..0000000 --- a/typings/utils/item.d.ts +++ /dev/null @@ -1,231 +0,0 @@ -export interface GamePatchData { - Banner: string; - ExName: string; - ExVersion: number; - ID: number; - Name: string; - Name_de: string; - Name_en: string; - Name_fr: string; - Name_ja: string; - Name_kr: string; - ReleaseDate: string; - Version: string; -} - -export interface TitleData { - ID: number; - Icon: string; - IsPrefix: number; - Name: string; - NameFemale: string; - NameFemale_de: string; - NameFemale_en: string; - NameFemale_fr: string; - NameFemale_ja: string; - Name_de: string; - Name_en: string; - Name_fr: string; - Name_ja: string; - Order: number; -} - -export interface ItemActionData { - CondBattle: number; - CondLv: number; - CondPVP: number; - CondPVPOnly: number; - Data0: number; - Data1: number; - Data2: number; - Data3: number; - Data4: number; - Data5: number; - Data6: number; - Data7: number; - Data8: number; - DataHQ0: number; - DataHQ1: number; - DataHQ2: number; - DataHQ3: number; - DataHQ4: number; - DataHQ5: number; - DataHQ6: number; - DataHQ7: number; - DataHQ8: number; - ID: number; - Type: number; -} - -export interface ItemData { - AdditionalData: number; - Adjective: number; - AetherialReduce: number; - AlwaysCollectable: number; - Article: number; - BaseParam0: null; - BaseParam0Target: string; - BaseParam0TargetID: number; - BaseParam1: null; - BaseParam1Target: string; - BaseParam1TargetID: number; - BaseParam2: null; - BaseParam2Target: string; - BaseParam2TargetID: number; - BaseParam3: null; - BaseParam3Target: string; - BaseParam3TargetID: number; - BaseParam4: null; - BaseParam4Target: string; - BaseParam4TargetID: number; - BaseParam5: null; - BaseParam5Target: string; - BaseParam5TargetID: number; - BaseParamModifier: number; - BaseParamSpecial0: null; - BaseParamSpecial0Target: string; - BaseParamSpecial0TargetID: number; - BaseParamSpecial1: null; - BaseParamSpecial1Target: string; - BaseParamSpecial1TargetID: number; - BaseParamSpecial2: null; - BaseParamSpecial2Target: string; - BaseParamSpecial2TargetID: number; - BaseParamSpecial3: null; - BaseParamSpecial3Target: string; - BaseParamSpecial3TargetID: number; - BaseParamSpecial4: null; - BaseParamSpecial4Target: string; - BaseParamSpecial4TargetID: number; - BaseParamSpecial5: null; - BaseParamSpecial5Target: string; - BaseParamSpecial5TargetID: number; - BaseParamValue0: number; - BaseParamValue1: number; - BaseParamValue2: number; - BaseParamValue3: number; - BaseParamValue4: number; - BaseParamValue5: number; - BaseParamValueSpecial0: number; - BaseParamValueSpecial1: number; - BaseParamValueSpecial2: number; - BaseParamValueSpecial3: number; - BaseParamValueSpecial4: number; - BaseParamValueSpecial5: number; - Block: number; - BlockRate: number; - CanBeHq: number; - CastTimeS: number; - ClassJobCategory: null; - ClassJobCategoryTarget: string; - ClassJobCategoryTargetID: number; - ClassJobRepair: null; - ClassJobRepairTarget: string; - ClassJobRepairTargetID: number; - ClassJobUse: null; - ClassJobUseTarget: string; - ClassJobUseTargetID: number; - CooldownS: number; - DamageMag: number; - DamagePhys: number; - DefenseMag: number; - DefensePhys: number; - DelayMs: number; - Description: string; - Description_de: string; - Description_en: string; - Description_fr: string; - Description_ja: string; - Desynth: number; - EquipRestriction: number; - EquipSlotCategory: null; - EquipSlotCategoryTarget: string; - EquipSlotCategoryTargetID: number; - FilterGroup: number; - GrandCompany: null; - GrandCompanyTarget: string; - GrandCompanyTargetID: number; - ID: number; - Icon: string; - IconHD: string; - IconID: number; - IsAdvancedMeldingPermitted: number; - IsCollectable: number; - IsCrestWorthy: number; - IsDyeable: number; - IsGlamourous: number; - IsIndisposable: number; - IsPvP: number; - IsUnique: number; - IsUntradable: number; - ItemAction: ItemActionData; - ItemActionTarget: string; - ItemActionTargetID: string; - ItemGlamour: null; - ItemGlamourTarget: string; - ItemGlamourTargetID: number; - ItemRepair: null; - ItemRepairTarget: string; - ItemRepairTargetID: number; - ItemSearchCategory: null; - ItemSearchCategoryTarget: string; - ItemSearchCategoryTargetID: number; - ItemSeries: null; - ItemSeriesTarget: string; - ItemSeriesTargetID: number; - ItemSortCategory: { - ID: number; - Param: number; - }; - ItemSortCategoryTarget: string; - ItemSortCategoryTargetID: number; - ItemSpecialBonus: null; - ItemSpecialBonusParam: number; - ItemSpecialBonusTarget: string; - ItemSpecialBonusTargetID: number; - ItemUICategory: { - ID: number; - Icon: string; - IconHD: string; - IconID: number; - Name: string; - Name_de: string; - Name_en: string; - Name_fr: string; - Name_ja: string; - OrderMajor: number; - OrderMinor: number; - }; - ItemUICategoryTarget: string; - ItemUICategoryTargetID: number; - LevelEquip: number; - LevelItem: number; - Lot: number; - MateriaSlotCount: number; - MaterializeType: number; - ModelMain: string; - ModelSub: string; - Name: string; - Name_de: string; - Name_en: string; - Name_fr: string; - Name_ja: string; - Plural: string; - Plural_de: string; - Plural_en: string; - Plural_fr: string; - Plural_ja: string; - PossessivePronoun: number; - PriceLow: number; - PriceMid: number; - Pronoun: number; - Rarity: number; - Singular: string; - Singular_de: string; - Singular_en: string; - Singular_fr: string; - Singular_ja: string; - StackSize: number; - StartsWithVowel: number; - SubStatCategory: number; -} diff --git a/typings/utils/linkshell.ts b/typings/utils/linkshell.ts deleted file mode 100644 index d8f7ef3..0000000 --- a/typings/utils/linkshell.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { CharacterSearchResult } from "./character"; -import { SearchParams, SearchResult } from "./search"; - -export interface LinkshellSearchParams extends SearchParams {} - -export interface LinkshellSearchResult extends SearchResult { - Results: { - Crest: string[]; - ID: string; - Name: string; - Server: string; - }[]; -} - -export interface LinkshellGetResult { - Linkshell: { - ID: string; - Pagination: SearchResult["Pagination"]; - Profile: { - Name: string; - Server: string; - }; - Results: CharacterSearchResult["Results"]; - }; -} diff --git a/typings/utils/pvpteam.ts b/typings/utils/pvpteam.ts deleted file mode 100644 index 38e9191..0000000 --- a/typings/utils/pvpteam.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { CharacterSearchResult } from "./character"; -import { SearchParams, SearchResult } from "./search"; - -export interface PvPTeamSearchParams extends SearchParams {} - -export interface PvPTeamSearchResult extends SearchResult { - Results: { - Crest: string[]; - ID: string; - Name: string; - Server: string; - }[]; -} - -export interface PvPTeamGetResult { - PvPTeam: { - ID: string; - Pagination: SearchResult["Pagination"]; - Profile: { - Crest: string[]; - Name: string; - Server: string; - }; - Results: CharacterSearchResult["Results"]; - }; -} diff --git a/typings/utils/search.d.ts b/typings/utils/search.d.ts deleted file mode 100644 index 90de0b2..0000000 --- a/typings/utils/search.d.ts +++ /dev/null @@ -1,72 +0,0 @@ -export enum SearchIndexes { - "Achievement", - "Title", - "Action", - "CraftAction", - "Trait", - "PvPAction", - "PvPTrait", - "Status", - "BNpcName", - "ENpcResident", - "Companion", - "Mount", - "Leve", - "Emote", - "InstanceContent", - "Item", - "Recipe", - "Fate", - "Quest", - "ContentFinderCondition", - "Balloon", - "BuddyEquip", - "Orchestrion", - "PlaceName", - "Weather", - "World", - "Map", - "lore_finder", -} - -export interface SearchResult { - /** - * The pagination data for the search. - */ - Pagination: { - Page: number; - PageNext: number | null; - PagePrev: number | null; - PageTotal: number; - Results: number; - ResultsPerPage: number; - ResultsTotal: number; - }; - - /** - * The results obtained from the search. - */ - Results: unknown[]; -} - -export interface SearchIndexResult extends SearchResult { - Results: { ID: number; Icon: string | null; Name: string | null; Url: string }[]; -} - -interface SearchParams { - /**- - * The name to search for, you can use `+` for spaces or let the API handle it for you. - */ - name?: string; - - /** - * The server to search against, this is case sensitive. - * @see https://xivapi.com/servers - */ - server?: string; - - /** - * Search or move to a specific page. - */ - page?: number; -} diff --git a/utils.js b/utils.js deleted file mode 100644 index f3e6c4d..0000000 --- a/utils.js +++ /dev/null @@ -1,51 +0,0 @@ -const request = require('@root/request') - -module.exports = { - //standard request function - async req(path, params={}) { - let convs = ['snake_case', 'extended'] - for (const c of convs) { - if(typeof params[c] !== 'undefined') - params[c] = params[c] ? 1 : 0 - } - - - params = Object.assign({}, this.globalParams, params) - - if(this.verbose) - console.log(`Requesting ${path} with params: `, params) - - return (await request({ - url: `${this.endpoint + path}${Object.keys(params).length > 0 ? `?${new URLSearchParams(params).toString()}` : ''}`, - json: true - })).body - }, - - //JSON request function - async reqJSON(path, body) { - if(this.verbose) - console.log(`Requesting ${path} with body: `, body) - - return (await request({ - method: 'POST', - url: this.endpoint + path, - body: body, - json: true - })).body - }, - - //handle both comma-separated strings, and string arrays, for CSV params - makeCSV(x) { - if(typeof(x) === 'undefined') - return - - if(Array.isArray(x)) - return x.join(',') - if(typeof(x) === 'string') - return x - }, - - throwError(method, param) { - return Error(`xivapi-js: Can't use ${method} without providing ${param}.`) - } -} From 36c06c3125074c5fe0cb059fbd91548dcef9862a Mon Sep 17 00:00:00 2001 From: c0reme Date: Mon, 13 Oct 2025 19:14:06 +0100 Subject: [PATCH 02/11] refactor: streamline API request handling and enhance search functionality - Consolidated exports in index.ts to simplify module structure. - Updated request function in utils.ts to handle array parameters for query and version options. - Modified sheets.ts and versions.ts to ensure params are passed correctly in API requests. - Expanded test suite in xivapi-js.test.ts to include comprehensive search validation and error handling for various query scenarios. --- src/index.ts | 14 +- src/lib/index.ts | 4 + src/lib/sheets.ts | 2 +- src/lib/versions.ts | 2 +- src/utils.ts | 16 +- tests/xivapi-js.test.ts | 613 +++++++++++++++++++++++++++++++--------- 6 files changed, 503 insertions(+), 148 deletions(-) create mode 100644 src/lib/index.ts diff --git a/src/index.ts b/src/index.ts index 98dbaa5..a192ec2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,12 @@ -export * from "./lib/assets"; -export * from "./lib/search"; -export * from "./lib/sheets"; -export * from "./lib/versions"; +export * from "./lib"; export namespace XIVAPI { export interface Options { + /** + * The supported version of the game to use for the API. + * @default "latest" + */ + version?: string; /** * Language to use for the API. */ @@ -119,7 +121,7 @@ export namespace Models { * Queries are formed of clauses, which take the basic form of `[specifier][operation][value]`, i.e. `Name="Example"`. Multiple clauses may be specified by seperating them with whitespace, i.e. `Foo=1 Bar=2`. * @see https://v2.xivapi.com/api/docs#model/querystring */ - export type QueryString = string | null; + export type QueryString = string | string[] | Record | URLSearchParams | null; /** * Query parameters accepted by endpoints that retrieve excel row data. @@ -171,7 +173,7 @@ export namespace Models { * Filters are comprised of a comma-seperated list of field paths, i.e. `a,b` will select the fields `a` and `b`. * @see https://v2.xivapi.com/api/docs#model/filterstring */ - export type FilterString = string; + export type FilterString = string | string[]; /** * Response structure for the search endpoint. diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..433d6e2 --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1,4 @@ +export * from "./assets"; +export * from "./search"; +export * from "./sheets"; +export * from "./versions"; \ No newline at end of file diff --git a/src/lib/sheets.ts b/src/lib/sheets.ts index 7f1473c..64caf8d 100644 --- a/src/lib/sheets.ts +++ b/src/lib/sheets.ts @@ -22,7 +22,7 @@ export class Sheets { * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet */ async all(): Promise { - const { data, errors } = await request({ path: "/sheet", options: this.options }); + const { data, errors } = await request({ path: "/sheet", params: {}, options: this.options }); if (errors) throw new CustomError(errors[0].message); return data as Models.ListResponse; } diff --git a/src/lib/versions.ts b/src/lib/versions.ts index 243dcd5..cc24fd1 100644 --- a/src/lib/versions.ts +++ b/src/lib/versions.ts @@ -11,7 +11,7 @@ export class Versions { * @see https://v2.xivapi.com/api/docs#tag/versions/get/version */ async all(): Promise { - const { data, errors } = await request({ path: "/version" }); + const { data, errors } = await request({ path: "/version", params: {} }); if (errors) throw new CustomError(errors[0].message); return data as Models.VersionsResponse; } diff --git a/src/utils.ts b/src/utils.ts index 4c5b248..bec0498 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -24,13 +24,24 @@ export const request = async (payload: RequestPayload): Promise const url = new URL(path instanceof URL ? path.toString() : path.replace(/^\/+/, ""), endpoint); if (params) { + const array: Record = { query: " ", fields: ",", transient: "," }; + for (const key in array) { + if (Object.prototype.hasOwnProperty.call(params, key) && Array.isArray(params[key])) { + params[key] = params[key].join(array[key]); + } + } + url.search = new URLSearchParams(params).toString(); - + if (!params.language) { if (options && options.language) url.searchParams.set("language", options.language); } + + if (!params.version) { + if (options && options.version) url.searchParams.set("version", options.version); + } } - + const response = await fetch(url); if (options && options.verbose) console.debug(`Requesting ${path} with params:`, params); @@ -39,7 +50,6 @@ export const request = async (payload: RequestPayload): Promise if (contentType && contentType.includes("application/json")) { payload.data = await response.json(); } else { - // For binary data (images, etc.) payload.data = Buffer.from(await response.arrayBuffer()); } } else { diff --git a/tests/xivapi-js.test.ts b/tests/xivapi-js.test.ts index 6309a24..1e6a0d6 100644 --- a/tests/xivapi-js.test.ts +++ b/tests/xivapi-js.test.ts @@ -1,10 +1,75 @@ -import { describe, it, expect, beforeEach } from "vitest"; +import { describe, it, expect } from "vitest"; import { Versions, Assets, Search, Sheets } from "../src/index"; -import { CustomError } from "../src/utils"; describe("@xivapi/js", () => { const API_TIMEOUT = 10000; + const validateSearchResponse = (result: any) => { + expect(result).toBeDefined(); + expect(result.results).toBeDefined(); + expect(Array.isArray(result.results)).toBe(true); + expect(result.schema).toBeDefined(); + + if (result.results.length > 0) { + const firstResult = result.results[0]; + expect(firstResult.row_id).toBeDefined(); + expect(typeof firstResult.row_id).toBe("number"); + expect(firstResult.score).toBeDefined(); + expect(typeof firstResult.score).toBe("number"); + expect(firstResult.sheet).toBeDefined(); + expect(firstResult.fields).toBeDefined(); + } + }; + + const validateItemData = (result: any, expectedSheet: string = "Item") => { + expect(result.results.length).toBeGreaterThan(0); + + result.results.forEach((item: any) => { + expect(item.sheet).toBe(expectedSheet); + expect(item.fields).toBeDefined(); + expect(typeof item.fields).toBe("object"); + + // Check for common item fields + if (item.fields.Name) { + expect(typeof item.fields.Name).toBe("string"); + expect(item.fields.Name.length).toBeGreaterThan(0); + } + + if (item.fields.ID) { + expect(typeof item.fields.ID).toBe("number"); + expect(item.fields.ID).toBeGreaterThan(0); + } + + if (item.fields.LevelItem) { + expect(typeof item.fields.LevelItem).toBe("number"); + expect(item.fields.LevelItem).toBeGreaterThanOrEqual(0); + } + }); + }; + + const validateActionData = (result: any) => { + expect(result.results.length).toBeGreaterThan(0); + + result.results.forEach((action: any) => { + expect(action.sheet).toBe("Action"); + expect(action.fields).toBeDefined(); + expect(typeof action.fields).toBe("object"); + + // Check for common action fields + if (action.fields.Name) { + expect(typeof action.fields.Name).toBe("string"); + expect(action.fields.Name.length).toBeGreaterThan(0); + } + + if (action.fields.ID) { + expect(typeof action.fields.ID).toBe("number"); + expect(action.fields.ID).toBeGreaterThan(0); + } + }); + }; + + const createSearch = (options = {}) => new Search(options); + describe("versions", () => { it("should fetch all versions successfully", async () => { const versions = new Versions(); @@ -62,143 +127,417 @@ describe("@xivapi/js", () => { }); describe("search", () => { - it("should perform search successfully with default options", async () => { - const searchParams = { - query: 'Name="Iron Sword"', - sheets: "Item", - limit: 5, - }; - - const search = new Search(); - const result = await search.get(searchParams); - - expect(result).toBeDefined(); - expect(result.results).toBeDefined(); - expect(Array.isArray(result.results)).toBe(true); - expect(result.schema).toBeDefined(); - - if (result.results.length > 0) { - const firstResult = result.results[0]; - expect(firstResult.row_id).toBeDefined(); - expect(typeof firstResult.row_id).toBe("number"); - expect(firstResult.score).toBeDefined(); - expect(typeof firstResult.score).toBe("number"); - expect(firstResult.sheet).toBeDefined(); - expect(firstResult.fields).toBeDefined(); - } - }, API_TIMEOUT); - - it("should perform search successfully with custom options", async () => { - const searchParams = { - query: 'Name="Iron Sword"', - sheets: "Item", - limit: 3, - }; - - const customOptions = { - language: "ja" as const, - verbose: true, - }; - - const search = new Search(customOptions); - const result = await search.get(searchParams); - - expect(result).toBeDefined(); - expect(result.results).toBeDefined(); - expect(Array.isArray(result.results)).toBe(true); - expect(result.schema).toBeDefined(); - }, API_TIMEOUT); - - it("should throw CustomError when search fails", async () => { - const search = new Search(); - - await expect(search.get({ - query: "invalid query syntax that should fail", - sheets: "Item" - })).rejects.toThrow(); - }, API_TIMEOUT); + describe("basic search operations", () => { + it("can find items by exact name match", async () => { + const search = createSearch(); + const result = await search.get({ + query: 'Name="Iron War Axe"', + sheets: "Item", + limit: 5, + }); + + validateSearchResponse(result); + validateItemData(result); + + const ironWarAxe = result.results.find((item: any) => item.fields.Name === "Iron War Axe"); + expect(ironWarAxe).toBeDefined(); + if (ironWarAxe) { + expect((ironWarAxe.fields as any).Name).toBe("Iron War Axe"); + } + }, API_TIMEOUT); + + it("can find items using partial text search", async () => { + const search = createSearch(); + const result = await search.get({ + query: 'Name~"sword"', + sheets: "Item", + limit: 5, + }); + + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.Name) { + expect(item.fields.Name.toLowerCase()).toContain("sword"); + } + }); + }, API_TIMEOUT); + + it("can search actions by numeric properties", async () => { + const search = createSearch(); + const result = await search.get({ + query: 'Recast100ms>3000', + sheets: "Action", + limit: 5, + }); + + validateSearchResponse(result); + validateActionData(result); + + result.results.forEach((action: any) => { + if (action.fields.Recast100ms) { + expect(action.fields.Recast100ms).toBeGreaterThan(3000); + } + }); + }, API_TIMEOUT); + }); + + describe("numeric comparisons", () => { + it("can find high-level items (>=50)", async () => { + const search = createSearch(); + const result = await search.get({ + query: 'LevelItem>=50', + sheets: "Item", + limit: 5, + }); + + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.LevelItem) { + expect(item.fields.LevelItem).toBeGreaterThanOrEqual(50); + } + }); + }, API_TIMEOUT); + + it("can find low-level items (<10)", async () => { + const search = createSearch(); + const result = await search.get({ + query: 'LevelItem<10', + sheets: "Item", + limit: 5, + }); + + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.LevelItem) { + expect(item.fields.LevelItem).toBeLessThan(10); + } + }); + }, API_TIMEOUT); + + it("can find items in a level range", async () => { + const search = createSearch(); + const result = await search.get({ + query: 'LevelItem>=90 LevelItem<=99', + sheets: "Item", + limit: 5, + }); + + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.LevelItem) { + expect(item.fields.LevelItem).toBeGreaterThanOrEqual(90); + expect(item.fields.LevelItem).toBeLessThanOrEqual(99); + } + }); + }, API_TIMEOUT); + }); + + describe("boolean and complex queries", () => { + it("can filter by boolean properties", async () => { + const search = createSearch(); + const result = await search.get({ + query: 'IsUntradable=true', + sheets: "Item", + limit: 5, + }); + + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.IsUntradable !== undefined) { + expect(item.fields.IsUntradable).toBe(true); + } + }); + }, API_TIMEOUT); + + it("can combine multiple search criteria", async () => { + const search = createSearch(); + const result = await search.get({ + query: 'Name~"sword" LevelItem>=10 LevelItem<=50', + sheets: "Item", + limit: 5, + }); + + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.Name) { + expect(item.fields.Name.toLowerCase()).toContain("sword"); + } + if (item.fields.LevelItem) { + expect(item.fields.LevelItem).toBeGreaterThanOrEqual(10); + expect(item.fields.LevelItem).toBeLessThanOrEqual(50); + } + }); + }, API_TIMEOUT); + }); + + describe("multi-sheet searches", () => { + it("can search across multiple sheets simultaneously", async () => { + const search = createSearch(); + const result = await search.get({ + query: 'Name~"rainbow"', + sheets: "Action,Item", + limit: 5, + }); + + validateSearchResponse(result); + + if (result.results.length > 0) { + const sheets = new Set(result.results.map(r => r.sheet)); + expect(sheets.size).toBeGreaterThanOrEqual(1); + } + }, API_TIMEOUT); + }); + + describe("pagination", () => { + it("can limit results and get pagination cursor", async () => { + const search = createSearch(); + const result = await search.get({ + query: 'Name~"rainbow"', + sheets: "Item", + limit: 2, + }); + + expect(result).toBeDefined(); + expect(result.results).toBeDefined(); + expect(Array.isArray(result.results)).toBe(true); + expect(result.results.length).toBeLessThanOrEqual(2); + expect(result.schema).toBeDefined(); + + if (result.next) { + expect(typeof result.next).toBe("string"); + expect(result.next.length).toBeGreaterThan(0); + } + }, API_TIMEOUT); + + it("can paginate through results using cursor", async () => { + const search = createSearch(); + + const firstPage = await search.get({ + query: 'Name~"sword"', + sheets: "Item", + limit: 2, + }); + + expect(firstPage).toBeDefined(); + expect(firstPage.results).toBeDefined(); + expect(Array.isArray(firstPage.results)).toBe(true); + + if (firstPage.next) { + const secondPage = await search.get({ + cursor: firstPage.next, + limit: 2, + }); + + expect(secondPage).toBeDefined(); + expect(secondPage.results).toBeDefined(); + expect(Array.isArray(secondPage.results)).toBe(true); + expect(secondPage.schema).toBeDefined(); + } + }, API_TIMEOUT); + }); + + describe("customization", () => { + it("can use custom language and verbose options", async () => { + const search = createSearch({ + language: "ja" as const, + verbose: true, + }); + + const result = await search.get({ + query: 'Name~"sword"', + sheets: "Item", + limit: 3, + }); + + validateSearchResponse(result); + + expect(result.results.length).toBeLessThanOrEqual(3); + expect(result.schema).toBeDefined(); + }, API_TIMEOUT); + }); + + describe("array-based queries", () => { + it("can use string arrays for complex queries", async () => { + const search = createSearch(); + const result = await search.get({ + query: ['Name~"sword"', 'LevelItem>=10', 'LevelItem<=50'], + sheets: "Item", + limit: 5, + }); + + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.Name) { + expect(item.fields.Name.toLowerCase()).toContain("sword"); + } + if (item.fields.LevelItem) { + expect(item.fields.LevelItem).toBeGreaterThanOrEqual(10); + expect(item.fields.LevelItem).toBeLessThanOrEqual(50); + } + }); + }, API_TIMEOUT); + + it("can use string arrays for simple queries", async () => { + const search = createSearch(); + const result = await search.get({ + query: ['Name~"sword"'], + sheets: "Item", + limit: 5, + }); + + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.Name) { + expect(item.fields.Name.toLowerCase()).toContain("sword"); + } + }); + }, API_TIMEOUT); + }); + + describe("error handling", () => { + it("throws error for invalid query syntax", async () => { + const search = createSearch(); + + await expect(search.get({ + query: "invalid query syntax that should fail", + sheets: "Item" + })).rejects.toThrow(); + }, API_TIMEOUT); + }); }); describe("sheets", () => { - it("should list all sheets successfully with default options", async () => { - const sheets = new Sheets(); - const result = await sheets.all(); - - expect(result).toBeDefined(); - expect(result.sheets).toBeDefined(); - expect(Array.isArray(result.sheets)).toBe(true); - expect(result.sheets.length).toBeGreaterThan(0); - - result.sheets.forEach(sheet => { - expect(sheet.name).toBeDefined(); - expect(typeof sheet.name).toBe("string"); - }); - }, API_TIMEOUT); - - it("should list all sheets successfully with custom options", async () => { - const customOptions = { - language: "fr" as const, - verbose: true, - }; - - const sheets = new Sheets(customOptions); - const result = await sheets.all(); - - expect(result).toBeDefined(); - expect(result.sheets).toBeDefined(); - expect(Array.isArray(result.sheets)).toBe(true); - expect(result.sheets.length).toBeGreaterThan(0); - }, API_TIMEOUT); - - it("should list sheet rows successfully", async () => { - const sheetParams = { limit: 5 }; - - const sheets = new Sheets(); - const result = await sheets.list("Item", sheetParams); - - expect(result).toBeDefined(); - expect(result.rows).toBeDefined(); - expect(Array.isArray(result.rows)).toBe(true); - expect(result.schema).toBeDefined(); - - if (result.rows.length > 0) { - const firstRow = result.rows[0]; - expect(firstRow.row_id).toBeDefined(); - expect(typeof firstRow.row_id).toBe("number"); - expect(firstRow.fields).toBeDefined(); - } - }, API_TIMEOUT); - - it("should get specific sheet row successfully", async () => { - const rowParams = { - fields: "Name,ID", - language: "en", - }; - - const sheets = new Sheets(); - const result = await sheets.get("Item", 1, rowParams); - - expect(result).toBeDefined(); - expect(result.row_id).toBeDefined(); - expect(typeof result.row_id).toBe("number"); - expect(result.schema).toBeDefined(); - expect(result.fields).toBeDefined(); - }, API_TIMEOUT); - - it("should use default parameters when none provided", async () => { - const sheets = new Sheets(); - const result = await sheets.list("Item"); - - expect(result).toBeDefined(); - expect(result.rows).toBeDefined(); - expect(Array.isArray(result.rows)).toBe(true); - expect(result.schema).toBeDefined(); - }, API_TIMEOUT); - - it("should throw CustomError when sheet operation fails", async () => { - const sheets = new Sheets(); - - await expect(sheets.list("NonExistentSheetThatDoesNotExist")).rejects.toThrow(); - }, API_TIMEOUT); + describe("listing sheets", () => { + it("can list all available sheets", async () => { + const sheets = new Sheets(); + const result = await sheets.all(); + + expect(result).toBeDefined(); + expect(result.sheets).toBeDefined(); + expect(Array.isArray(result.sheets)).toBe(true); + expect(result.sheets.length).toBeGreaterThan(0); + + result.sheets.forEach(sheet => { + expect(sheet.name).toBeDefined(); + expect(typeof sheet.name).toBe("string"); + }); + }, API_TIMEOUT); + + it("can list sheets with custom language options", async () => { + const sheets = new Sheets({ + language: "fr" as const, + verbose: true, + }); + const result = await sheets.all(); + + expect(result).toBeDefined(); + expect(result.sheets).toBeDefined(); + expect(Array.isArray(result.sheets)).toBe(true); + expect(result.sheets.length).toBeGreaterThan(0); + }, API_TIMEOUT); + }); + + describe("reading sheet data", () => { + it("can list rows from a specific sheet", async () => { + const sheets = new Sheets(); + const result = await sheets.list("Item", { limit: 5 }); + + expect(result).toBeDefined(); + expect(result.rows).toBeDefined(); + expect(Array.isArray(result.rows)).toBe(true); + expect(result.schema).toBeDefined(); + expect(result.rows.length).toBeGreaterThan(0); + + result.rows.forEach((row: any) => { + expect(row.row_id).toBeDefined(); + expect(typeof row.row_id).toBe("number"); + expect(row.fields).toBeDefined(); + expect(typeof row.fields).toBe("object"); + + if (row.fields.Name) { + expect(typeof row.fields.Name).toBe("string"); + expect(row.fields.Name.length).toBeGreaterThan(0); + } + + if (row.fields.ID) { + expect(typeof row.fields.ID).toBe("number"); + expect(row.fields.ID).toBeGreaterThan(0); + } + }); + }, API_TIMEOUT); + + it("can get a specific row with field filtering", async () => { + const sheets = new Sheets(); + const result = await sheets.get("Item", 1, { + fields: "Name", + language: "en", + }); + + expect(result).toBeDefined(); + expect(result.row_id).toBeDefined(); + expect(typeof result.row_id).toBe("number"); + expect(result.schema).toBeDefined(); + expect(result.fields).toBeDefined(); + expect(result.row_id).toBe(1); + + expect((result.fields as any).Name).toBeDefined(); + expect((result.fields as any).Name).toBe("Gil"); + }, API_TIMEOUT); + + it("can get a specific row with array-based field filtering", async () => { + const sheets = new Sheets(); + const result = await sheets.get("Item", 1, { + fields: ["Name", "LevelItem"], + language: "en", + }); + + expect(result).toBeDefined(); + expect(result.row_id).toBeDefined(); + expect(typeof result.row_id).toBe("number"); + expect(result.schema).toBeDefined(); + expect(result.fields).toBeDefined(); + expect(result.row_id).toBe(1); + + expect(result.fields).toBeDefined(); + expect(typeof result.fields).toBe("object"); + expect(Object.keys(result.fields).length).toBe(2); + expect((result.fields as any).Name).toBeDefined(); + expect((result.fields as any).Name).toBe("Gil"); + }, API_TIMEOUT); + + it("can list rows with default parameters", async () => { + const sheets = new Sheets(); + const result = await sheets.list("Item"); + + expect(result).toBeDefined(); + expect(result.rows).toBeDefined(); + expect(Array.isArray(result.rows)).toBe(true); + expect(result.schema).toBeDefined(); + }, API_TIMEOUT); + }); + + describe("error handling", () => { + it("throws error for non-existent sheets", async () => { + const sheets = new Sheets(); + + await expect(sheets.list("NonExistentSheetThatDoesNotExist")).rejects.toThrow(); + }, API_TIMEOUT); + }); }); }); From b981b285bff7ead4c462d70ee925858a71bc377f Mon Sep 17 00:00:00 2001 From: c0reme Date: Tue, 14 Oct 2025 19:04:46 +0100 Subject: [PATCH 03/11] feat: enhance xivapi client with structured sheet access and improved error handling - Introduced a new class structure for xivapi, encapsulating sheet access methods for achievements, items, and more. - Enhanced error handling with a CustomError class for better debugging. - Updated utils to ensure consistent option handling across API requests. - Expanded test coverage to validate new functionalities and ensure robust error handling. --- src/index.ts | 121 ++++- src/lib/assets.ts | 6 +- src/lib/search.ts | 6 +- src/lib/sheets.ts | 32 +- src/utils.ts | 6 +- tests/xivapi-js.test.ts | 1059 ++++++++++++++++++++++++--------------- 6 files changed, 796 insertions(+), 434 deletions(-) diff --git a/src/index.ts b/src/index.ts index a192ec2..ddca4ed 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,116 @@ -export * from "./lib"; +import { Assets, Search, Sheets, Versions } from "./lib"; +import { CustomError } from "./utils"; -export namespace XIVAPI { +export type NumberResolvable = number | string; + +class xivapiSheet { + private readonly type: T; + + constructor(sheet: T) { + this.type = sheet; + } + + /** + * Gets a single row from the sheet. + * @param {NumberResolvable} id The row to fetch. + * @param {Models.RowReaderQuery} [params] The parameters to fetch the row with. + * @returns {Promise} A single row with typed fields. + * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet/{sheet}/{row} + */ + public get( + id: NumberResolvable, + params: Models.RowReaderQuery = {} + ): Promise { + try { + if (typeof id !== "string") id = id.toString(); + return new Sheets().get(this.type, id, params); + } catch (error) { + throw new CustomError(error instanceof Error ? error.message : "Unknown error"); + } + } + + /** + * Gets a list of rows from the sheet. + * @param {Models.SheetQuery} [params] The parameters to fetch the rows with. + * @returns {Promise} A list of rows with typed fields. + * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet/{sheet} + */ + public list(params: Models.SheetQuery = {}): Promise { + try { + return new Sheets().list(this.type, params); + } catch (error) { + throw new CustomError(error instanceof Error ? error.message : "Unknown error"); + } + } +} + +export default class xivapi { + private readonly options: xivapi.Options; + + /** + * A wrapper for the XIVAPI v2 API. + * @param {xivapi.Options} [options] The client options to fetch with. + * @see https://v2.xivapi.com/api/docs + * @since 0.5.0 + */ + constructor( + options: xivapi.Options = { + version: "latest", + language: "en", + verbose: false, + } + ) { + this.options = options; + } + + /** + * @since 0.5.0 + */ + public get achievements() { + return new xivapiSheet("Achievement"); + } + + /** + * @since 0.5.0 + */ + public get items() { + return new xivapiSheet("Item"); + } + + /** + * Raw endpoints for the API. Please consider using the typed endpoints instead. + * @see https://v2.xivapi.com/api/docs + * @since 0.5.0 + */ + public data = { + /** + * @see https://v2.xivapi.com/api/docs#tag/assets + * @since 0.5.0 + */ + assets: () => new Assets(), + + /** + * @see https://v2.xivapi.com/api/docs#tag/search + * @since 0.5.0 + */ + search: () => new Search(this.options), + + /** + * @see https://v2.xivapi.com/api/docs#tag/sheets + * @since 0.5.0 + */ + sheets: () => new Sheets(this.options), + + /** + * @see https://v2.xivapi.com/api/docs#tag/versions + * @since 0.5.0 + */ + versions: () => + new Versions().all().then((versions) => versions.versions.map((version) => version.names[0])), + }; +} + +export namespace xivapi { export interface Options { /** * The supported version of the game to use for the API. @@ -121,7 +231,12 @@ export namespace Models { * Queries are formed of clauses, which take the basic form of `[specifier][operation][value]`, i.e. `Name="Example"`. Multiple clauses may be specified by seperating them with whitespace, i.e. `Foo=1 Bar=2`. * @see https://v2.xivapi.com/api/docs#model/querystring */ - export type QueryString = string | string[] | Record | URLSearchParams | null; + export type QueryString = + | string + | string[] + | Record + | URLSearchParams + | null; /** * Query parameters accepted by endpoints that retrieve excel row data. diff --git a/src/lib/assets.ts b/src/lib/assets.ts index 96a75ba..a860a9c 100644 --- a/src/lib/assets.ts +++ b/src/lib/assets.ts @@ -1,4 +1,4 @@ -import type {Models} from ".."; +import type { Models } from ".."; import { CustomError, request } from "../utils"; /** @@ -24,7 +24,9 @@ export class Assets { * @returns {Promise} An image of the map. * @see https://v2.xivapi.com/api/docs#tag/assets/get/asset/map/{territory}/{index} */ - async map(params: Models.MapPath & Models.VersionQuery & Pick): Promise { + async map( + params: Models.MapPath & Models.VersionQuery & Pick + ): Promise { const { data, errors } = await request({ path: "/asset/map", params }); if (errors) throw new CustomError(errors[0].message); return data as Buffer; diff --git a/src/lib/search.ts b/src/lib/search.ts index 6d8286d..e05bb3d 100644 --- a/src/lib/search.ts +++ b/src/lib/search.ts @@ -1,4 +1,4 @@ -import type { Models, XIVAPI } from ".."; +import type { Models, xivapi} from ".."; import { CustomError, request } from "../utils"; /** @@ -6,10 +6,10 @@ import { CustomError, request } from "../utils"; * @see https://v2.xivapi.com/api/docs#tag/search */ export class Search { - private readonly options: XIVAPI.Options; + private readonly options: xivapi.Options; constructor( - options: XIVAPI.Options = { + options: xivapi.Options = { language: "en", verbose: false, } diff --git a/src/lib/sheets.ts b/src/lib/sheets.ts index 64caf8d..eda781d 100644 --- a/src/lib/sheets.ts +++ b/src/lib/sheets.ts @@ -1,18 +1,20 @@ -import type {Models, XIVAPI} from ".."; +import type { Models, xivapi } from ".."; import { CustomError, request } from "../utils"; export class Sheets { - private readonly options: XIVAPI.Options; - + private readonly options: xivapi.Options; + /** * Endpoints for reading data from the game's static relational data store. * @param {XIVAPI.Options} [options] The options to fetch the sheets with. * @see https://v2.xivapi.com/api/docs#tag/sheets */ - public constructor(options: XIVAPI.Options = { - language: "en", - verbose: false, - }) { + public constructor( + options: xivapi.Options = { + language: "en", + verbose: false, + } + ) { this.options = options; } @@ -38,7 +40,11 @@ export class Sheets { sheet: Models.SchemaSpecifier, params: Models.SheetQuery = {} ): Promise { - const { data, errors } = await request({ path: `/sheet/${sheet}`, params, options: this.options }); + const { data, errors } = await request({ + path: `/sheet/${sheet}`, + params, + options: this.options, + }); if (errors) throw new CustomError(errors[0].message); return data as Models.SheetResponse; } @@ -46,16 +52,20 @@ export class Sheets { /** * Read detailed, filterable information from a single sheet row and its related data. * @param {Models.SchemaSpecifier} sheet Name of the sheet to read. - * @param {number} row Row to read. + * @param {Models.RowSpecifier} row Row to read. * @returns {Promise} A list of rows with typed fields. * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet/{sheet}/{id} */ async get( sheet: Models.SchemaSpecifier, - row: number, + row: Models.RowSpecifier, params: Models.RowReaderQuery = {} ): Promise { - const { data, errors } = await request({ path: `/sheet/${sheet}/${row}`, params, options: this.options }); + const { data, errors } = await request({ + path: `/sheet/${sheet}/${row}`, + params, + options: this.options, + }); if (errors) throw new CustomError(errors[0].message); return data as Models.RowResponse; } diff --git a/src/utils.ts b/src/utils.ts index bec0498..d6a06f3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import type { Models, XIVAPI } from "."; +import type { Models, xivapi } from "."; export const endpoint = "https://v2.xivapi.com/api/"; @@ -16,7 +16,7 @@ export interface RequestPayload { data?: unknown; params?: Record; errors?: Models.ErrorResponse[]; - options?: XIVAPI.Options; + options?: xivapi.Options; } export const request = async (payload: RequestPayload): Promise => { @@ -56,5 +56,7 @@ export const request = async (payload: RequestPayload): Promise payload.errors = [(await response.json()) as Models.ErrorResponse]; } + if (options && options.verbose) console.debug(`${response.ok ? "Success" : "Error"} on ${path} with params:`, params); + return payload; }; diff --git a/tests/xivapi-js.test.ts b/tests/xivapi-js.test.ts index 1e6a0d6..891b1b3 100644 --- a/tests/xivapi-js.test.ts +++ b/tests/xivapi-js.test.ts @@ -1,15 +1,16 @@ import { describe, it, expect } from "vitest"; -import { Versions, Assets, Search, Sheets } from "../src/index"; +import xivapiClient from "../src/index"; describe("@xivapi/js", () => { const API_TIMEOUT = 10000; + const xivapi = new xivapiClient(); const validateSearchResponse = (result: any) => { expect(result).toBeDefined(); expect(result.results).toBeDefined(); expect(Array.isArray(result.results)).toBe(true); expect(result.schema).toBeDefined(); - + if (result.results.length > 0) { const firstResult = result.results[0]; expect(firstResult.row_id).toBeDefined(); @@ -23,23 +24,22 @@ describe("@xivapi/js", () => { const validateItemData = (result: any, expectedSheet: string = "Item") => { expect(result.results.length).toBeGreaterThan(0); - + result.results.forEach((item: any) => { expect(item.sheet).toBe(expectedSheet); expect(item.fields).toBeDefined(); expect(typeof item.fields).toBe("object"); - - // Check for common item fields + if (item.fields.Name) { expect(typeof item.fields.Name).toBe("string"); expect(item.fields.Name.length).toBeGreaterThan(0); } - + if (item.fields.ID) { expect(typeof item.fields.ID).toBe("number"); expect(item.fields.ID).toBeGreaterThan(0); } - + if (item.fields.LevelItem) { expect(typeof item.fields.LevelItem).toBe("number"); expect(item.fields.LevelItem).toBeGreaterThanOrEqual(0); @@ -49,18 +49,17 @@ describe("@xivapi/js", () => { const validateActionData = (result: any) => { expect(result.results.length).toBeGreaterThan(0); - + result.results.forEach((action: any) => { expect(action.sheet).toBe("Action"); expect(action.fields).toBeDefined(); expect(typeof action.fields).toBe("object"); - - // Check for common action fields + if (action.fields.Name) { expect(typeof action.fields.Name).toBe("string"); expect(action.fields.Name.length).toBeGreaterThan(0); } - + if (action.fields.ID) { expect(typeof action.fields.ID).toBe("number"); expect(action.fields.ID).toBeGreaterThan(0); @@ -68,476 +67,710 @@ describe("@xivapi/js", () => { }); }; - const createSearch = (options = {}) => new Search(options); + const createSearch = (options = {}) => { + const client = new xivapiClient(options); + return client.data.search(); + }; describe("versions", () => { - it("should fetch all versions successfully", async () => { - const versions = new Versions(); - const result = await versions.all(); - - expect(result).toBeDefined(); - expect(result.versions).toBeDefined(); - expect(Array.isArray(result.versions)).toBe(true); - expect(result.versions.length).toBeGreaterThan(0); - - result.versions.forEach(version => { - expect(version.names).toBeDefined(); - expect(Array.isArray(version.names)).toBe(true); - expect(version.names.length).toBeGreaterThan(0); - }); - }, API_TIMEOUT); + it( + "should fetch all versions successfully", + async () => { + const result = await xivapi.data.versions(); + + expect(result).toBeDefined(); + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + + result.forEach((version) => { + expect(typeof version).toBe("string"); + expect(version.length).toBeGreaterThan(0); + }); + }, + API_TIMEOUT + ); }); describe("assets", () => { - it("should fetch asset successfully", async () => { - const assetParams = { - path: "ui/icon/051000/051474_hr1.tex", - format: "png" - }; - - const assets = new Assets(); - const result = await assets.get(assetParams); - - expect(result).toBeDefined(); - expect(Buffer.isBuffer(result)).toBe(true); - expect(result.length).toBeGreaterThan(0); - }, API_TIMEOUT); - - it("should handle map requests correctly", async () => { - const mapParams = { - territory: "invalid", - index: "00", - version: "latest", - format: "png" - }; - - const assets = new Assets(); - - await expect(assets.map(mapParams)).rejects.toThrow(); - }, API_TIMEOUT); - - it("should throw CustomError when asset fetch fails", async () => { - const assets = new Assets(); - - await expect(assets.get({ - path: "invalid/path/that/does/not/exist.tex", - format: "png" - })).rejects.toThrow(); - }, API_TIMEOUT); + it( + "should fetch asset successfully", + async () => { + const assetParams = { + path: "ui/icon/051000/051474_hr1.tex", + format: "png", + }; + + const assets = xivapi.data.assets(); + const result = await assets.get(assetParams); + + expect(result).toBeDefined(); + expect(Buffer.isBuffer(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + }, + API_TIMEOUT + ); + + it( + "should handle map requests correctly", + async () => { + const mapParams = { + territory: "invalid", + index: "00", + version: "latest", + format: "png", + }; + + const assets = xivapi.data.assets(); + + await expect(assets.map(mapParams)).rejects.toThrow(); + }, + API_TIMEOUT + ); + + it( + "should throw CustomError when asset fetch fails", + async () => { + const assets = xivapi.data.assets(); + + await expect( + assets.get({ + path: "invalid/path/that/does/not/exist.tex", + format: "png", + }) + ).rejects.toThrow(); + }, + API_TIMEOUT + ); }); describe("search", () => { describe("basic search operations", () => { - it("can find items by exact name match", async () => { - const search = createSearch(); - const result = await search.get({ - query: 'Name="Iron War Axe"', - sheets: "Item", - limit: 5, - }); + it( + "can find items by exact name match", + async () => { + const search = createSearch(); + const result = await search.get({ + query: 'Name="Iron War Axe"', + sheets: "Item", + limit: 5, + }); - validateSearchResponse(result); - validateItemData(result); - - const ironWarAxe = result.results.find((item: any) => item.fields.Name === "Iron War Axe"); - expect(ironWarAxe).toBeDefined(); - if (ironWarAxe) { - expect((ironWarAxe.fields as any).Name).toBe("Iron War Axe"); - } - }, API_TIMEOUT); - - it("can find items using partial text search", async () => { - const search = createSearch(); - const result = await search.get({ - query: 'Name~"sword"', - sheets: "Item", - limit: 5, - }); + validateSearchResponse(result); + validateItemData(result); - validateSearchResponse(result); - validateItemData(result); - - result.results.forEach((item: any) => { - if (item.fields.Name) { - expect(item.fields.Name.toLowerCase()).toContain("sword"); + const ironWarAxe = result.results.find( + (item: any) => item.fields.Name === "Iron War Axe" + ); + expect(ironWarAxe).toBeDefined(); + if (ironWarAxe) { + expect((ironWarAxe.fields as any).Name).toBe("Iron War Axe"); } - }); - }, API_TIMEOUT); - - it("can search actions by numeric properties", async () => { - const search = createSearch(); - const result = await search.get({ - query: 'Recast100ms>3000', - sheets: "Action", - limit: 5, - }); + }, + API_TIMEOUT + ); + + it( + "can find items using partial text search", + async () => { + const search = createSearch(); + const result = await search.get({ + query: 'Name~"sword"', + sheets: "Item", + limit: 5, + }); - validateSearchResponse(result); - validateActionData(result); - - result.results.forEach((action: any) => { - if (action.fields.Recast100ms) { - expect(action.fields.Recast100ms).toBeGreaterThan(3000); - } - }); - }, API_TIMEOUT); + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.Name) { + expect(item.fields.Name.toLowerCase()).toContain("sword"); + } + }); + }, + API_TIMEOUT + ); + + it( + "can search actions by numeric properties", + async () => { + const search = createSearch(); + const result = await search.get({ + query: "Recast100ms>3000", + sheets: "Action", + limit: 5, + }); + + validateSearchResponse(result); + validateActionData(result); + + result.results.forEach((action: any) => { + if (action.fields.Recast100ms) { + expect(action.fields.Recast100ms).toBeGreaterThan(3000); + } + }); + }, + API_TIMEOUT + ); }); describe("numeric comparisons", () => { - it("can find high-level items (>=50)", async () => { - const search = createSearch(); - const result = await search.get({ - query: 'LevelItem>=50', - sheets: "Item", - limit: 5, - }); + it( + "can find high-level items (>=50)", + async () => { + const search = createSearch(); + const result = await search.get({ + query: "LevelItem>=50", + sheets: "Item", + limit: 5, + }); - validateSearchResponse(result); - validateItemData(result); - - result.results.forEach((item: any) => { - if (item.fields.LevelItem) { - expect(item.fields.LevelItem).toBeGreaterThanOrEqual(50); - } - }); - }, API_TIMEOUT); - - it("can find low-level items (<10)", async () => { - const search = createSearch(); - const result = await search.get({ - query: 'LevelItem<10', - sheets: "Item", - limit: 5, - }); + validateSearchResponse(result); + validateItemData(result); - validateSearchResponse(result); - validateItemData(result); - - result.results.forEach((item: any) => { - if (item.fields.LevelItem) { - expect(item.fields.LevelItem).toBeLessThan(10); - } - }); - }, API_TIMEOUT); - - it("can find items in a level range", async () => { - const search = createSearch(); - const result = await search.get({ - query: 'LevelItem>=90 LevelItem<=99', - sheets: "Item", - limit: 5, - }); + result.results.forEach((item: any) => { + if (item.fields.LevelItem) { + expect(item.fields.LevelItem).toBeGreaterThanOrEqual(50); + } + }); + }, + API_TIMEOUT + ); + + it( + "can find low-level items (<10)", + async () => { + const search = createSearch(); + const result = await search.get({ + query: "LevelItem<10", + sheets: "Item", + limit: 5, + }); - validateSearchResponse(result); - validateItemData(result); - - result.results.forEach((item: any) => { - if (item.fields.LevelItem) { - expect(item.fields.LevelItem).toBeGreaterThanOrEqual(90); - expect(item.fields.LevelItem).toBeLessThanOrEqual(99); - } - }); - }, API_TIMEOUT); + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.LevelItem) { + expect(item.fields.LevelItem).toBeLessThan(10); + } + }); + }, + API_TIMEOUT + ); + + it( + "can find items in a level range", + async () => { + const search = createSearch(); + const result = await search.get({ + query: "LevelItem>=90 LevelItem<=99", + sheets: "Item", + limit: 5, + }); + + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.LevelItem) { + expect(item.fields.LevelItem).toBeGreaterThanOrEqual(90); + expect(item.fields.LevelItem).toBeLessThanOrEqual(99); + } + }); + }, + API_TIMEOUT + ); }); describe("boolean and complex queries", () => { - it("can filter by boolean properties", async () => { - const search = createSearch(); - const result = await search.get({ - query: 'IsUntradable=true', - sheets: "Item", - limit: 5, - }); + it( + "can filter by boolean properties", + async () => { + const search = createSearch(); + const result = await search.get({ + query: "IsUntradable=true", + sheets: "Item", + limit: 5, + }); - validateSearchResponse(result); - validateItemData(result); - - result.results.forEach((item: any) => { - if (item.fields.IsUntradable !== undefined) { - expect(item.fields.IsUntradable).toBe(true); - } - }); - }, API_TIMEOUT); - - it("can combine multiple search criteria", async () => { - const search = createSearch(); - const result = await search.get({ - query: 'Name~"sword" LevelItem>=10 LevelItem<=50', - sheets: "Item", - limit: 5, - }); + validateSearchResponse(result); + validateItemData(result); - validateSearchResponse(result); - validateItemData(result); - - result.results.forEach((item: any) => { - if (item.fields.Name) { - expect(item.fields.Name.toLowerCase()).toContain("sword"); - } - if (item.fields.LevelItem) { - expect(item.fields.LevelItem).toBeGreaterThanOrEqual(10); - expect(item.fields.LevelItem).toBeLessThanOrEqual(50); - } - }); - }, API_TIMEOUT); + result.results.forEach((item: any) => { + if (item.fields.IsUntradable !== undefined) { + expect(item.fields.IsUntradable).toBe(true); + } + }); + }, + API_TIMEOUT + ); + + it( + "can combine multiple search criteria", + async () => { + const search = createSearch(); + const result = await search.get({ + query: 'Name~"sword" LevelItem>=10 LevelItem<=50', + sheets: "Item", + limit: 5, + }); + + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.Name) { + expect(item.fields.Name.toLowerCase()).toContain("sword"); + } + if (item.fields.LevelItem) { + expect(item.fields.LevelItem).toBeGreaterThanOrEqual(10); + expect(item.fields.LevelItem).toBeLessThanOrEqual(50); + } + }); + }, + API_TIMEOUT + ); }); describe("multi-sheet searches", () => { - it("can search across multiple sheets simultaneously", async () => { - const search = createSearch(); - const result = await search.get({ - query: 'Name~"rainbow"', - sheets: "Action,Item", - limit: 5, - }); + it( + "can search across multiple sheets simultaneously", + async () => { + const search = createSearch(); + const result = await search.get({ + query: 'Name~"rainbow"', + sheets: "Action,Item", + limit: 5, + }); - validateSearchResponse(result); - - if (result.results.length > 0) { - const sheets = new Set(result.results.map(r => r.sheet)); - expect(sheets.size).toBeGreaterThanOrEqual(1); - } - }, API_TIMEOUT); + validateSearchResponse(result); + + if (result.results.length > 0) { + const sheets = new Set(result.results.map((r) => r.sheet)); + expect(sheets.size).toBeGreaterThanOrEqual(1); + } + }, + API_TIMEOUT + ); }); describe("pagination", () => { - it("can limit results and get pagination cursor", async () => { - const search = createSearch(); - const result = await search.get({ - query: 'Name~"rainbow"', - sheets: "Item", - limit: 2, - }); - - expect(result).toBeDefined(); - expect(result.results).toBeDefined(); - expect(Array.isArray(result.results)).toBe(true); - expect(result.results.length).toBeLessThanOrEqual(2); - expect(result.schema).toBeDefined(); - - if (result.next) { - expect(typeof result.next).toBe("string"); - expect(result.next.length).toBeGreaterThan(0); - } - }, API_TIMEOUT); - - it("can paginate through results using cursor", async () => { - const search = createSearch(); - - const firstPage = await search.get({ - query: 'Name~"sword"', - sheets: "Item", - limit: 2, - }); + it( + "can limit results and get pagination cursor", + async () => { + const search = createSearch(); + const result = await search.get({ + query: 'Name~"rainbow"', + sheets: "Item", + limit: 2, + }); - expect(firstPage).toBeDefined(); - expect(firstPage.results).toBeDefined(); - expect(Array.isArray(firstPage.results)).toBe(true); + expect(result).toBeDefined(); + expect(result.results).toBeDefined(); + expect(Array.isArray(result.results)).toBe(true); + expect(result.results.length).toBeLessThanOrEqual(2); + expect(result.schema).toBeDefined(); - if (firstPage.next) { - const secondPage = await search.get({ - cursor: firstPage.next, + if (result.next) { + expect(typeof result.next).toBe("string"); + expect(result.next.length).toBeGreaterThan(0); + } + }, + API_TIMEOUT + ); + + it( + "can paginate through results using cursor", + async () => { + const search = createSearch(); + + const firstPage = await search.get({ + query: 'Name~"sword"', + sheets: "Item", limit: 2, }); - expect(secondPage).toBeDefined(); - expect(secondPage.results).toBeDefined(); - expect(Array.isArray(secondPage.results)).toBe(true); - expect(secondPage.schema).toBeDefined(); - } - }, API_TIMEOUT); + expect(firstPage).toBeDefined(); + expect(firstPage.results).toBeDefined(); + expect(Array.isArray(firstPage.results)).toBe(true); + + if (firstPage.next) { + const secondPage = await search.get({ + cursor: firstPage.next, + limit: 2, + }); + + expect(secondPage).toBeDefined(); + expect(secondPage.results).toBeDefined(); + expect(Array.isArray(secondPage.results)).toBe(true); + expect(secondPage.schema).toBeDefined(); + } + }, + API_TIMEOUT + ); }); describe("customization", () => { - it("can use custom language and verbose options", async () => { - const search = createSearch({ - language: "ja" as const, - verbose: true, - }); - - const result = await search.get({ - query: 'Name~"sword"', - sheets: "Item", - limit: 3, - }); + it( + "can use custom language and verbose options", + async () => { + const search = createSearch({ + language: "ja" as const, + verbose: true, + }); - validateSearchResponse(result); + const result = await search.get({ + query: 'Name~"sword"', + sheets: "Item", + limit: 3, + }); - expect(result.results.length).toBeLessThanOrEqual(3); - expect(result.schema).toBeDefined(); - }, API_TIMEOUT); + validateSearchResponse(result); + + expect(result.results.length).toBeLessThanOrEqual(3); + expect(result.schema).toBeDefined(); + }, + API_TIMEOUT + ); }); describe("array-based queries", () => { - it("can use string arrays for complex queries", async () => { - const search = createSearch(); - const result = await search.get({ - query: ['Name~"sword"', 'LevelItem>=10', 'LevelItem<=50'], - sheets: "Item", - limit: 5, - }); + it( + "can use string arrays for complex queries", + async () => { + const search = createSearch(); + const result = await search.get({ + query: ['Name~"sword"', "LevelItem>=10", "LevelItem<=50"], + sheets: "Item", + limit: 5, + }); - validateSearchResponse(result); - validateItemData(result); - - result.results.forEach((item: any) => { - if (item.fields.Name) { - expect(item.fields.Name.toLowerCase()).toContain("sword"); - } - if (item.fields.LevelItem) { - expect(item.fields.LevelItem).toBeGreaterThanOrEqual(10); - expect(item.fields.LevelItem).toBeLessThanOrEqual(50); - } - }); - }, API_TIMEOUT); - - it("can use string arrays for simple queries", async () => { - const search = createSearch(); - const result = await search.get({ - query: ['Name~"sword"'], - sheets: "Item", - limit: 5, - }); + validateSearchResponse(result); + validateItemData(result); + + result.results.forEach((item: any) => { + if (item.fields.Name) { + expect(item.fields.Name.toLowerCase()).toContain("sword"); + } + if (item.fields.LevelItem) { + expect(item.fields.LevelItem).toBeGreaterThanOrEqual(10); + expect(item.fields.LevelItem).toBeLessThanOrEqual(50); + } + }); + }, + API_TIMEOUT + ); + + it( + "can use string arrays for simple queries", + async () => { + const search = createSearch(); + const result = await search.get({ + query: ['Name~"sword"'], + sheets: "Item", + limit: 5, + }); - validateSearchResponse(result); - validateItemData(result); + validateSearchResponse(result); + validateItemData(result); - result.results.forEach((item: any) => { - if (item.fields.Name) { - expect(item.fields.Name.toLowerCase()).toContain("sword"); - } - }); - }, API_TIMEOUT); + result.results.forEach((item: any) => { + if (item.fields.Name) { + expect(item.fields.Name.toLowerCase()).toContain("sword"); + } + }); + }, + API_TIMEOUT + ); }); describe("error handling", () => { - it("throws error for invalid query syntax", async () => { - const search = createSearch(); - - await expect(search.get({ - query: "invalid query syntax that should fail", - sheets: "Item" - })).rejects.toThrow(); - }, API_TIMEOUT); + it( + "throws error for invalid query syntax", + async () => { + const search = createSearch(); + + await expect( + search.get({ + query: "invalid query syntax that should fail", + sheets: "Item", + }) + ).rejects.toThrow(); + }, + API_TIMEOUT + ); }); }); describe("sheets", () => { describe("listing sheets", () => { - it("can list all available sheets", async () => { - const sheets = new Sheets(); - const result = await sheets.all(); + it( + "can list all available sheets", + async () => { + const sheets = xivapi.data.sheets(); + const result = await sheets.all(); + + expect(result).toBeDefined(); + expect(result.sheets).toBeDefined(); + expect(Array.isArray(result.sheets)).toBe(true); + expect(result.sheets.length).toBeGreaterThan(0); + + result.sheets.forEach((sheet) => { + expect(sheet.name).toBeDefined(); + expect(typeof sheet.name).toBe("string"); + }); + }, + API_TIMEOUT + ); + + it( + "can list sheets with custom language options", + async () => { + const client = new xivapiClient({ + language: "fr" as const, + verbose: true, + }); + const sheets = client.data.sheets(); + const result = await sheets.all(); + + expect(result).toBeDefined(); + expect(result.sheets).toBeDefined(); + expect(Array.isArray(result.sheets)).toBe(true); + expect(result.sheets.length).toBeGreaterThan(0); + }, + API_TIMEOUT + ); + }); - expect(result).toBeDefined(); - expect(result.sheets).toBeDefined(); - expect(Array.isArray(result.sheets)).toBe(true); - expect(result.sheets.length).toBeGreaterThan(0); - - result.sheets.forEach(sheet => { - expect(sheet.name).toBeDefined(); - expect(typeof sheet.name).toBe("string"); - }); - }, API_TIMEOUT); + describe("reading sheet data", () => { + it( + "can list rows from a specific sheet", + async () => { + const sheets = xivapi.data.sheets(); + const result = await sheets.list("Item", { limit: 5 }); + + expect(result).toBeDefined(); + expect(result.rows).toBeDefined(); + expect(Array.isArray(result.rows)).toBe(true); + expect(result.schema).toBeDefined(); + expect(result.rows.length).toBeGreaterThan(0); + + result.rows.forEach((row: any) => { + expect(row.row_id).toBeDefined(); + expect(typeof row.row_id).toBe("number"); + expect(row.fields).toBeDefined(); + expect(typeof row.fields).toBe("object"); + + if (row.fields.Name) { + expect(typeof row.fields.Name).toBe("string"); + expect(row.fields.Name.length).toBeGreaterThan(0); + } + + if (row.fields.ID) { + expect(typeof row.fields.ID).toBe("number"); + expect(row.fields.ID).toBeGreaterThan(0); + } + }); + }, + API_TIMEOUT + ); + + it( + "can get a specific row with field filtering", + async () => { + const sheets = xivapi.data.sheets(); + const result = await sheets.get("Item", "1", { + fields: "Name", + language: "en", + }); - it("can list sheets with custom language options", async () => { - const sheets = new Sheets({ - language: "fr" as const, - verbose: true, - }); - const result = await sheets.all(); + expect(result).toBeDefined(); + expect(result.row_id).toBeDefined(); + expect(typeof result.row_id).toBe("number"); + expect(result.schema).toBeDefined(); + expect(result.fields).toBeDefined(); + expect(result.row_id).toBe(1); + + expect((result.fields as any).Name).toBeDefined(); + expect((result.fields as any).Name).toBe("Gil"); + }, + API_TIMEOUT + ); + + it( + "can get a specific row with array-based field filtering", + async () => { + const sheets = xivapi.data.sheets(); + const result = await sheets.get("Item", "1", { + fields: ["Name", "LevelItem"], + language: "en", + }); - expect(result).toBeDefined(); - expect(result.sheets).toBeDefined(); - expect(Array.isArray(result.sheets)).toBe(true); - expect(result.sheets.length).toBeGreaterThan(0); - }, API_TIMEOUT); + expect(result).toBeDefined(); + expect(result.row_id).toBeDefined(); + expect(typeof result.row_id).toBe("number"); + expect(result.schema).toBeDefined(); + expect(result.fields).toBeDefined(); + expect(result.row_id).toBe(1); + + expect(result.fields).toBeDefined(); + expect(typeof result.fields).toBe("object"); + expect(Object.keys(result.fields).length).toBe(2); + expect((result.fields as any).Name).toBeDefined(); + expect((result.fields as any).Name).toBe("Gil"); + }, + API_TIMEOUT + ); + + it( + "can list rows with default parameters", + async () => { + const sheets = xivapi.data.sheets(); + const result = await sheets.list("Item"); + + expect(result).toBeDefined(); + expect(result.rows).toBeDefined(); + expect(Array.isArray(result.rows)).toBe(true); + expect(result.schema).toBeDefined(); + }, + API_TIMEOUT + ); }); - describe("reading sheet data", () => { - it("can list rows from a specific sheet", async () => { - const sheets = new Sheets(); - const result = await sheets.list("Item", { limit: 5 }); + describe("error handling", () => { + it( + "throws error for non-existent sheets", + async () => { + const sheets = xivapi.data.sheets(); + + await expect(sheets.list("NonExistentSheetThatDoesNotExist")).rejects.toThrow(); + }, + API_TIMEOUT + ); + }); + }); - expect(result).toBeDefined(); - expect(result.rows).toBeDefined(); - expect(Array.isArray(result.rows)).toBe(true); - expect(result.schema).toBeDefined(); - expect(result.rows.length).toBeGreaterThan(0); - - result.rows.forEach((row: any) => { - expect(row.row_id).toBeDefined(); - expect(typeof row.row_id).toBe("number"); - expect(row.fields).toBeDefined(); - expect(typeof row.fields).toBe("object"); - - if (row.fields.Name) { - expect(typeof row.fields.Name).toBe("string"); - expect(row.fields.Name.length).toBeGreaterThan(0); - } - - if (row.fields.ID) { - expect(typeof row.fields.ID).toBe("number"); - expect(row.fields.ID).toBeGreaterThan(0); - } - }); - }, API_TIMEOUT); + describe("typed sheet accessors", () => { + describe("items", () => { + it( + "can get a specific item by ID", + async () => { + const result = await xivapi.items.get(1, { + fields: "Name", + language: "en", + }); - it("can get a specific row with field filtering", async () => { - const sheets = new Sheets(); - const result = await sheets.get("Item", 1, { - fields: "Name", - language: "en", - }); + expect(result).toBeDefined(); + expect(result.row_id).toBeDefined(); + expect(typeof result.row_id).toBe("number"); + expect(result.schema).toBeDefined(); + expect(result.fields).toBeDefined(); + expect(result.row_id).toBe(1); + expect((result.fields as any).Name).toBe("Gil"); + }, + API_TIMEOUT + ); + + it( + "can list items with parameters", + async () => { + const result = await xivapi.items.list({ limit: 3 }); + + expect(result).toBeDefined(); + expect(result.rows).toBeDefined(); + expect(Array.isArray(result.rows)).toBe(true); + expect(result.schema).toBeDefined(); + expect(result.rows.length).toBeLessThanOrEqual(3); + + result.rows.forEach((row: any) => { + expect(row.row_id).toBeDefined(); + expect(typeof row.row_id).toBe("number"); + expect(row.fields).toBeDefined(); + expect(typeof row.fields).toBe("object"); + }); + }, + API_TIMEOUT + ); + + it( + "can get item with string ID", + async () => { + const result = await xivapi.items.get("1", { + fields: "Name", + language: "en", + }); - expect(result).toBeDefined(); - expect(result.row_id).toBeDefined(); - expect(typeof result.row_id).toBe("number"); - expect(result.schema).toBeDefined(); - expect(result.fields).toBeDefined(); - expect(result.row_id).toBe(1); - - expect((result.fields as any).Name).toBeDefined(); - expect((result.fields as any).Name).toBe("Gil"); - }, API_TIMEOUT); - - it("can get a specific row with array-based field filtering", async () => { - const sheets = new Sheets(); - const result = await sheets.get("Item", 1, { - fields: ["Name", "LevelItem"], - language: "en", - }); + expect(result).toBeDefined(); + expect(result.row_id).toBe(1); + expect((result.fields as any).Name).toBe("Gil"); + }, + API_TIMEOUT + ); + }); - expect(result).toBeDefined(); - expect(result.row_id).toBeDefined(); - expect(typeof result.row_id).toBe("number"); - expect(result.schema).toBeDefined(); - expect(result.fields).toBeDefined(); - expect(result.row_id).toBe(1); - - expect(result.fields).toBeDefined(); - expect(typeof result.fields).toBe("object"); - expect(Object.keys(result.fields).length).toBe(2); - expect((result.fields as any).Name).toBeDefined(); - expect((result.fields as any).Name).toBe("Gil"); - }, API_TIMEOUT); - - it("can list rows with default parameters", async () => { - const sheets = new Sheets(); - const result = await sheets.list("Item"); + describe("achievements", () => { + it( + "can get a specific achievement by ID", + async () => { + const result = await xivapi.achievements.get(1, { + fields: "Name", + language: "en", + }); - expect(result).toBeDefined(); - expect(result.rows).toBeDefined(); - expect(Array.isArray(result.rows)).toBe(true); - expect(result.schema).toBeDefined(); - }, API_TIMEOUT); + expect(result).toBeDefined(); + expect(result.row_id).toBeDefined(); + expect(typeof result.row_id).toBe("number"); + expect(result.schema).toBeDefined(); + expect(result.fields).toBeDefined(); + expect(result.row_id).toBe(1); + expect((result.fields as any).Name).toBe("To Crush Your Enemies I"); + }, + API_TIMEOUT + ); + + it( + "can list achievements with parameters", + async () => { + const result = await xivapi.achievements.list({ limit: 3 }); + + expect(result).toBeDefined(); + expect(result.rows).toBeDefined(); + expect(Array.isArray(result.rows)).toBe(true); + expect(result.schema).toBeDefined(); + expect(result.rows.length).toBeLessThanOrEqual(3); + + result.rows.forEach((row: any) => { + expect(row.row_id).toBeDefined(); + expect(typeof row.row_id).toBe("number"); + expect(row.fields).toBeDefined(); + expect(typeof row.fields).toBe("object"); + }); + }, + API_TIMEOUT + ); }); - describe("error handling", () => { - it("throws error for non-existent sheets", async () => { - const sheets = new Sheets(); + describe("client options", () => { + it( + "can create client with custom options", + async () => { + const client = new xivapiClient({ + language: "ja" as const, + verbose: true, + version: "latest", + }); + + const result = await client.items.get(1, { + fields: "Name", + }); - await expect(sheets.list("NonExistentSheetThatDoesNotExist")).rejects.toThrow(); - }, API_TIMEOUT); + expect(result).toBeDefined(); + expect(result.row_id).toBe(1); + expect((result.fields as any).Name).toBe("Gil"); + }, + API_TIMEOUT + ); }); }); }); From 011916d8026d2a7b0711e0b3d5a6cf5b4f4a4b02 Mon Sep 17 00:00:00 2001 From: c0reme Date: Tue, 14 Oct 2025 19:05:00 +0100 Subject: [PATCH 04/11] docs: update README --- README.md | 136 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 644335c..ecd32b3 100644 --- a/README.md +++ b/README.md @@ -3,75 +3,107 @@ [![npm version](https://badge.fury.io/js/%40xivapi%2Fjs.svg)](https://badge.fury.io/js/%40xivapi%2Fjs) [![license](https://img.shields.io/github/license/xivapi/xivapi-js.svg)](LICENSE) -This is a pure JS wrapper for interacting with [XIVAPI](https://xivapi.com/) and handling all requests in a simple, promise-driven manner. +A pure JS wrapper for interacting with [XIVAPI v2](https://v2.xivapi.com/) +and handling all requests in a simple, promise-driven manner. ## Installation - -Simply add the module to your node project with `npm`: -``` -npm i @xivapi/js +```bash +npm install @xivapi/js ``` -## Usage +## Quick Start -Require and initialize the module in your code: -```js -const XIVAPI = require('@xivapi/js') -const xiv = new XIVAPI() +```javascript +import xivapi from '@xivapi/js'; + +const xiv = new xivapi(); + +// Get a specific item +const item = await xiv.items.get(1); +console.log(item.fields.Name); // "Gil" + +// Search for items +const search = xiv.data.search(); +const data = await search.get({ + query: 'Name~"sword"', + sheets: "Item", + limit: 5 +}); +console.log(data.results); ``` -...and then check out the [wiki](https://github.com/xivapi/xivapi-js/wiki) for usage help! - -If you get really stuck and need some help, or run into any problems/concerns, either open up an issue on github or join the [XIVAPI discord server](https://discord.gg/MFFVHWC) and ping/DM @kaimoe. +## Examples -### Examples: - -Find an item's ID: -```js -const getID = async () => { - //find item - let res = await xiv.search('Stuffed Khloe') - - //return item ID - return res.Results[0].ID -} +### Get Item Data +```javascript +// Get item by ID +const item = await xiv.items.get(1, { + fields: "Name,LevelItem,ItemUICategory", + language: "en" +}); + +// List items with pagination +const items = await xiv.items.list({ limit: 10 }); ``` -Search for an FC and get an list of members: +### Search Game Data ```javascript -const getMembers = async () => { - //find the FC with its name and server - let res = await xiv.freecompany.search('My Fun FC', {server: 'Excalibur'}) - - //get the FC ID - let id = res.Results[0].ID - - //get and return fc members - let fc = await xiv.freecompany.get('9231253336202687179', {data: FCM}) - return fc.FreeCompanyMembers -} +const search = xiv.data.search(); + +// Find items by name +const swords = await search.get({ + query: 'Name~"sword"', + sheets: "Item", + limit: 5 +}); + +// Find high-level items +const highLevelItems = await search.get({ + query: "LevelItem>=90", + sheets: "Item", + limit: 10 +}); + +// Search across multiple sheets +const results = await search.get({ + query: 'Name~"fire"', + sheets: "Action,Item", + limit: 5 +}); ``` -Check for character ownership using a token we generated and provided to the user: -```js -const verifyCharacter = async () => { - //find the character with their name and server - let res = await xiv.character.search('Kai Megumi', {server: 'excalibur'}) //case insensitive server names, btw ;) - - //get the character - let char = res.Results[0] - - //return whether or not the character's lodestone bio matches our token - return char.Bio === 'example_token' -} +### Get Achievement Data +```javascript +// Get achievement by ID +const achievement = await xiv.achievements.get(1, { + fields: "Name,Description", + language: "en" +}); + +// List achievements +const achievements = await xiv.achievements.list({ limit: 5 }); ``` -## Contribute +### Custom Client Options +```javascript +const xiv = new xivapi({ + language: "ja", // Japanese + version: "latest", + verbose: true +}); +``` -Feel free to open up issues/PRs or anything else. +### Get Game Assets +```javascript +const assets = xiv.data.assets(); -Just `git clone https://github.com/xivapi/xivapi-js.git`, run `npm i`, and go to town! +// Get item icon +const icon = await assets.get({ + path: "ui/icon/051000/051474_hr1.tex", + format: "png" +}); +``` ## License -This project is open source, under the terms described in the [MIT License](LICENSE). +MIT License - see [LICENSE](LICENSE) file for details. \ No newline at end of file From 8820c22d4160cb52c08b37d2f20aa9479f5dcbad Mon Sep 17 00:00:00 2001 From: c0reme Date: Wed, 15 Oct 2025 12:02:41 +0100 Subject: [PATCH 05/11] fix: correct type reference --- src/lib/sheets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/sheets.ts b/src/lib/sheets.ts index eda781d..5b538a1 100644 --- a/src/lib/sheets.ts +++ b/src/lib/sheets.ts @@ -6,7 +6,7 @@ export class Sheets { /** * Endpoints for reading data from the game's static relational data store. - * @param {XIVAPI.Options} [options] The options to fetch the sheets with. + * @param {xivapi.Options} [options] The options to fetch the sheets with. * @see https://v2.xivapi.com/api/docs#tag/sheets */ public constructor( From bfed8fa10cfe27d8e748bcd2ceaa34080c17f345 Mon Sep 17 00:00:00 2001 From: c0reme Date: Wed, 15 Oct 2025 12:11:29 +0100 Subject: [PATCH 06/11] feat: add minions and mounts typed sheets --- src/index.ts | 14 ++++++ tests/xivapi-js.test.ts | 99 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 103 insertions(+), 10 deletions(-) diff --git a/src/index.ts b/src/index.ts index ddca4ed..892afea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -70,6 +70,20 @@ export default class xivapi { return new xivapiSheet("Achievement"); } + /** + * @since 0.5.0 + */ + public get minions() { + return new xivapiSheet("Companion"); + } + + /** + * @since 0.5.0 + */ + public get mounts() { + return new xivapiSheet("Mount"); + } + /** * @since 0.5.0 */ diff --git a/tests/xivapi-js.test.ts b/tests/xivapi-js.test.ts index 891b1b3..0d1d518 100644 --- a/tests/xivapi-js.test.ts +++ b/tests/xivapi-js.test.ts @@ -692,29 +692,56 @@ describe("@xivapi/js", () => { }, API_TIMEOUT ); + }); + describe("achievements", () => { it( - "can get item with string ID", + "can get a specific achievement by ID", async () => { - const result = await xivapi.items.get("1", { + const result = await xivapi.achievements.get(1, { fields: "Name", language: "en", }); expect(result).toBeDefined(); + expect(result.row_id).toBeDefined(); + expect(typeof result.row_id).toBe("number"); + expect(result.schema).toBeDefined(); + expect(result.fields).toBeDefined(); expect(result.row_id).toBe(1); - expect((result.fields as any).Name).toBe("Gil"); + expect((result.fields as any).Name).toBe("To Crush Your Enemies I"); + }, + API_TIMEOUT + ); + + it( + "can list achievements with parameters", + async () => { + const result = await xivapi.achievements.list({ limit: 3 }); + + expect(result).toBeDefined(); + expect(result.rows).toBeDefined(); + expect(Array.isArray(result.rows)).toBe(true); + expect(result.schema).toBeDefined(); + expect(result.rows.length).toBeLessThanOrEqual(3); + + result.rows.forEach((row: any) => { + expect(row.row_id).toBeDefined(); + expect(typeof row.row_id).toBe("number"); + expect(row.fields).toBeDefined(); + expect(typeof row.fields).toBe("object"); + }); }, API_TIMEOUT ); }); - describe("achievements", () => { + describe("minions", () => { it( - "can get a specific achievement by ID", + "can get a specific minion by ID", async () => { - const result = await xivapi.achievements.get(1, { - fields: "Name", + const result = await xivapi.minions.get(1, { + fields: "Singular", language: "en", }); @@ -724,15 +751,62 @@ describe("@xivapi/js", () => { expect(result.schema).toBeDefined(); expect(result.fields).toBeDefined(); expect(result.row_id).toBe(1); - expect((result.fields as any).Name).toBe("To Crush Your Enemies I"); + expect((result.fields as any).Singular).toBe("cherry bomb") }, API_TIMEOUT ); it( - "can list achievements with parameters", + "can list minions with parameters", async () => { - const result = await xivapi.achievements.list({ limit: 3 }); + const result = await xivapi.minions.list({ limit: 3 }); + + expect(result).toBeDefined(); + expect(result.rows).toBeDefined(); + expect(Array.isArray(result.rows)).toBe(true); + expect(result.schema).toBeDefined(); + expect(result.rows.length).toBeLessThanOrEqual(3); + + result.rows.forEach((row: any) => { + expect(row.row_id).toBeDefined(); + expect(typeof row.row_id).toBe("number"); + expect(row.fields).toBeDefined(); + expect(typeof row.fields).toBe("object"); + + if (row.fields.Singular) { + expect(typeof row.fields.Singular).toBe("string"); + expect(row.fields.Singular.length).toBeGreaterThan(0); + } + }); + }, + API_TIMEOUT + ); + }); + + describe("mounts", () => { + it( + "can get a specific mount by ID", + async () => { + const result = await xivapi.mounts.get(1, { + fields: "Singular", + language: "en", + }); + + expect(result).toBeDefined(); + expect(result.row_id).toBeDefined(); + expect(typeof result.row_id).toBe("number"); + expect(result.schema).toBeDefined(); + expect(result.fields).toBeDefined(); + expect(result.row_id).toBe(1); + expect((result.fields as any).Singular).toBe("company chocobo") + }, + API_TIMEOUT + ); + + it( + "can list mounts with parameters", + async () => { + const result = await xivapi.mounts.list({ limit: 3 }); expect(result).toBeDefined(); expect(result.rows).toBeDefined(); @@ -745,6 +819,11 @@ describe("@xivapi/js", () => { expect(typeof row.row_id).toBe("number"); expect(row.fields).toBeDefined(); expect(typeof row.fields).toBe("object"); + + if (row.fields.Name) { + expect(typeof row.fields.Name).toBe("string"); + expect(row.fields.Name.length).toBeGreaterThan(0); + } }); }, API_TIMEOUT From c2c3d084ed1cf00a302f49e6a33c8b36a2785768 Mon Sep 17 00:00:00 2001 From: c0reme Date: Fri, 17 Oct 2025 19:51:15 +0100 Subject: [PATCH 07/11] chore: migrate ESLint configuration from .eslintrc.json to eslint.config.js and update dependencies --- .eslintrc.json | 33 - eslint.config.js | 27 + package-lock.json | 2595 ++++++++++++++++++++++++++++----------------- package.json | 7 +- 4 files changed, 1651 insertions(+), 1011 deletions(-) delete mode 100644 .eslintrc.json create mode 100644 eslint.config.js diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 711d631..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "env": { - "es6": true, - "node": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": 2017, - "sourceType": "module" - }, - "rules": { - "indent": [ - "error", - "tab", - { "SwitchCase": 1 } - ], - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "single" - ], - "semi": [ - "error", - "never" - ], - "no-console": [ - 0 - ] - } -} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..e28db0f --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,27 @@ +import js from "@eslint/js" +import globals from "globals" +import tseslint from "typescript-eslint" +import { defineConfig, globalIgnores } from "eslint/config" + +export default defineConfig([ + { + files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], + plugins: { js }, + extends: ["js/recommended"], + languageOptions: { globals: globals.node }, + }, + { files: ["**/*.js"], languageOptions: { sourceType: "commonjs" } }, + globalIgnores(["tests/**/*.test.ts", "dist/**/*.js"]), + tseslint.configs.recommended, + { + rules: { + "@typescript-eslint/no-namespace": "off", + "consistent-return": 2, + "no-console": 0, + quotes: [2, "double"], + semi: [2, "never"], + "linebreak-style": [2, "unix"], + indent: [2, "tab", { SwitchCase: 1 }], + }, + }, +]) diff --git a/package-lock.json b/package-lock.json index f5d30a2..ca2e894 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "@xivapi/js", "version": "0.4.5", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -9,9 +9,13 @@ "version": "0.4.5", "license": "MIT", "devDependencies": { + "@eslint/js": "^9.38.0", "@types/node": "^24.7.2", + "eslint": "^9.37.0", + "globals": "^16.4.0", "ts-node": "^10.9.2", "typescript": "^5.9.3", + "typescript-eslint": "^8.46.1", "vitest": "^3.2.4" } }, @@ -470,6 +474,215 @@ "node": ">=18" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz", + "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", + "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.38.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz", + "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", + "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -498,6 +711,44 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.52.4", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", @@ -858,6 +1109,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.7.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz", @@ -868,6 +1126,264 @@ "undici-types": "~7.14.0" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.1.tgz", + "integrity": "sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.46.1", + "@typescript-eslint/type-utils": "8.46.1", + "@typescript-eslint/utils": "8.46.1", + "@typescript-eslint/visitor-keys": "8.46.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.46.1", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.1.tgz", + "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.46.1", + "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/typescript-estree": "8.46.1", + "@typescript-eslint/visitor-keys": "8.46.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.1.tgz", + "integrity": "sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.46.1", + "@typescript-eslint/types": "^8.46.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.1.tgz", + "integrity": "sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/visitor-keys": "8.46.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.1.tgz", + "integrity": "sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.1.tgz", + "integrity": "sha512-+BlmiHIiqufBxkVnOtFwjah/vrkF4MtKKvpXrKSPLCkCtAp8H01/VV43sfqA98Od7nJpDcFnkwgyfQbOG0AMvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/typescript-estree": "8.46.1", + "@typescript-eslint/utils": "8.46.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.1.tgz", + "integrity": "sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.1.tgz", + "integrity": "sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.46.1", + "@typescript-eslint/tsconfig-utils": "8.46.1", + "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/visitor-keys": "8.46.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.1.tgz", + "integrity": "sha512-vkYUy6LdZS7q1v/Gxb2Zs7zziuXN0wxqsetJdeZdRe/f5dwJFglmuvZBfTUivCtjH725C1jWCDfpadadD95EDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.46.1", + "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/typescript-estree": "8.46.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.1.tgz", + "integrity": "sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@vitest/expect": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", @@ -996,6 +1512,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", @@ -1009,12 +1535,52 @@ "node": ">=0.4.0" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" }, "node_modules/assertion-error": { "version": "2.0.1", @@ -1026,6 +1592,37 @@ "node": ">=12" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -1036,6 +1633,16 @@ "node": ">=8" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/chai": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", @@ -1053,6 +1660,23 @@ "node": ">=18" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -1063,6 +1687,33 @@ "node": ">= 16" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -1070,6 +1721,21 @@ "dev": true, "license": "MIT" }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1098,6 +1764,13 @@ "node": ">=6" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -1157,6 +1830,177 @@ "@esbuild/win32-x64": "0.25.10" } }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz", + "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.4.0", + "@eslint/core": "^0.16.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.37.0", + "@eslint/plugin-kit": "^0.4.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz", + "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -1167,6 +2011,16 @@ "@types/estree": "^1.0.0" } }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/expect-type": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", @@ -1177,6 +2031,67 @@ "node": ">=12.0.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1195,6 +2110,70 @@ } } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1210,38 +2189,289 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, "license": "MIT" }, - "node_modules/magic-string": { - "version": "0.30.19", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", - "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "engines": { + "node": ">=8" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "ISC" - }, - "node_modules/ms": { + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", @@ -1267,6 +2497,96 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -1333,6 +2653,68 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rollup": { "version": "4.52.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", @@ -1375,25 +2757,85 @@ "fsevents": "~2.3.2" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "license": "ISC" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, - "license": "BSD-3-Clause", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/stackback": { - "version": "0.0.2", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, @@ -1406,6 +2848,19 @@ "dev": true, "license": "MIT" }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-literal": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", @@ -1419,6 +2874,19 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -1480,6 +2948,32 @@ "node": ">=14.0.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -1524,6 +3018,19 @@ } } }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -1538,6 +3045,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.1.tgz", + "integrity": "sha512-VHgijW803JafdSsDO8I761r3SHrgk4T00IdyQ+/UsthtgPRsBWQLqoSxOolxTpxRKi1kGXK0bSz4CoAc9ObqJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.46.1", + "@typescript-eslint/parser": "8.46.1", + "@typescript-eslint/typescript-estree": "8.46.1", + "@typescript-eslint/utils": "8.46.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/undici-types": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", @@ -1545,6 +3076,16 @@ "dev": true, "license": "MIT" }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -1723,6 +3264,22 @@ } } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -1740,6 +3297,16 @@ "node": ">=8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -1749,945 +3316,19 @@ "engines": { "node": ">=6" } - } - }, - "dependencies": { - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - } - }, - "@esbuild/aix-ppc64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", - "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", - "dev": true, - "optional": true - }, - "@esbuild/android-arm": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", - "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", - "dev": true, - "optional": true - }, - "@esbuild/android-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", - "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", - "dev": true, - "optional": true - }, - "@esbuild/android-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", - "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", - "dev": true, - "optional": true }, - "@esbuild/darwin-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", - "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", - "dev": true, - "optional": true - }, - "@esbuild/darwin-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", - "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", - "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", - "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", - "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", - "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ia32": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", - "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", - "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-mips64el": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", - "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ppc64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", - "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", - "dev": true, - "optional": true - }, - "@esbuild/linux-riscv64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", - "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", - "dev": true, - "optional": true - }, - "@esbuild/linux-s390x": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", - "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", - "dev": true, - "optional": true - }, - "@esbuild/linux-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", - "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", - "dev": true, - "optional": true - }, - "@esbuild/netbsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", - "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", - "dev": true, - "optional": true - }, - "@esbuild/netbsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", - "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", - "dev": true, - "optional": true - }, - "@esbuild/openbsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", - "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", - "dev": true, - "optional": true - }, - "@esbuild/openbsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", - "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", - "dev": true, - "optional": true - }, - "@esbuild/openharmony-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", - "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", - "dev": true, - "optional": true - }, - "@esbuild/sunos-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", - "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", - "dev": true, - "optional": true - }, - "@esbuild/win32-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", - "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", - "dev": true, - "optional": true - }, - "@esbuild/win32-ia32": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", - "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", - "dev": true, - "optional": true - }, - "@esbuild/win32-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", - "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", - "dev": true, - "optional": true - }, - "@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } - }, - "@rollup/rollup-android-arm-eabi": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", - "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", - "dev": true, - "optional": true - }, - "@rollup/rollup-android-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz", - "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", - "dev": true, - "optional": true - }, - "@rollup/rollup-darwin-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz", - "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", - "dev": true, - "optional": true - }, - "@rollup/rollup-darwin-x64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz", - "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", - "dev": true, - "optional": true - }, - "@rollup/rollup-freebsd-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz", - "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", - "dev": true, - "optional": true - }, - "@rollup/rollup-freebsd-x64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz", - "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz", - "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz", - "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz", - "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-arm64-musl": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz", - "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz", - "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz", - "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz", - "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz", - "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz", - "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-x64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", - "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-x64-musl": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz", - "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", - "dev": true, - "optional": true - }, - "@rollup/rollup-openharmony-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz", - "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", - "dev": true, - "optional": true - }, - "@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz", - "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", - "dev": true, - "optional": true - }, - "@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz", - "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", - "dev": true, - "optional": true - }, - "@rollup/rollup-win32-x64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz", - "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==", - "dev": true, - "optional": true - }, - "@rollup/rollup-win32-x64-msvc": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz", - "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==", - "dev": true, - "optional": true - }, - "@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "@types/chai": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", - "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", - "dev": true, - "requires": { - "@types/deep-eql": "*" - } - }, - "@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true - }, - "@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true - }, - "@types/node": { - "version": "24.7.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz", - "integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==", - "dev": true, - "requires": { - "undici-types": "~7.14.0" - } - }, - "@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", - "dev": true, - "requires": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - } - }, - "@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", - "dev": true, - "requires": { - "@vitest/spy": "3.2.4", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - } - }, - "@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", - "dev": true, - "requires": { - "tinyrainbow": "^2.0.0" - } - }, - "@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", - "dev": true, - "requires": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - } - }, - "@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", - "dev": true, - "requires": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - } - }, - "@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", - "dev": true, - "requires": { - "tinyspy": "^4.0.3" - } - }, - "@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", - "dev": true, - "requires": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" - } - }, - "acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true - }, - "acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "requires": { - "acorn": "^8.11.0" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true - }, - "cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true - }, - "chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", - "dev": true, - "requires": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - } - }, - "check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "requires": { - "ms": "^2.1.3" - } - }, - "deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true - }, - "esbuild": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", - "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", - "dev": true, - "requires": { - "@esbuild/aix-ppc64": "0.25.10", - "@esbuild/android-arm": "0.25.10", - "@esbuild/android-arm64": "0.25.10", - "@esbuild/android-x64": "0.25.10", - "@esbuild/darwin-arm64": "0.25.10", - "@esbuild/darwin-x64": "0.25.10", - "@esbuild/freebsd-arm64": "0.25.10", - "@esbuild/freebsd-x64": "0.25.10", - "@esbuild/linux-arm": "0.25.10", - "@esbuild/linux-arm64": "0.25.10", - "@esbuild/linux-ia32": "0.25.10", - "@esbuild/linux-loong64": "0.25.10", - "@esbuild/linux-mips64el": "0.25.10", - "@esbuild/linux-ppc64": "0.25.10", - "@esbuild/linux-riscv64": "0.25.10", - "@esbuild/linux-s390x": "0.25.10", - "@esbuild/linux-x64": "0.25.10", - "@esbuild/netbsd-arm64": "0.25.10", - "@esbuild/netbsd-x64": "0.25.10", - "@esbuild/openbsd-arm64": "0.25.10", - "@esbuild/openbsd-x64": "0.25.10", - "@esbuild/openharmony-arm64": "0.25.10", - "@esbuild/sunos-x64": "0.25.10", - "@esbuild/win32-arm64": "0.25.10", - "@esbuild/win32-ia32": "0.25.10", - "@esbuild/win32-x64": "0.25.10" - } - }, - "estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "requires": { - "@types/estree": "^1.0.0" - } - }, - "expect-type": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", - "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", - "dev": true - }, - "fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "requires": {} - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true - }, - "loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true - }, - "magic-string": { - "version": "0.30.19", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", - "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true - }, - "pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true - }, - "pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true - }, - "picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true - }, - "picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true - }, - "postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "requires": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - } - }, - "rollup": { - "version": "4.52.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", - "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", - "dev": true, - "requires": { - "@rollup/rollup-android-arm-eabi": "4.52.4", - "@rollup/rollup-android-arm64": "4.52.4", - "@rollup/rollup-darwin-arm64": "4.52.4", - "@rollup/rollup-darwin-x64": "4.52.4", - "@rollup/rollup-freebsd-arm64": "4.52.4", - "@rollup/rollup-freebsd-x64": "4.52.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", - "@rollup/rollup-linux-arm-musleabihf": "4.52.4", - "@rollup/rollup-linux-arm64-gnu": "4.52.4", - "@rollup/rollup-linux-arm64-musl": "4.52.4", - "@rollup/rollup-linux-loong64-gnu": "4.52.4", - "@rollup/rollup-linux-ppc64-gnu": "4.52.4", - "@rollup/rollup-linux-riscv64-gnu": "4.52.4", - "@rollup/rollup-linux-riscv64-musl": "4.52.4", - "@rollup/rollup-linux-s390x-gnu": "4.52.4", - "@rollup/rollup-linux-x64-gnu": "4.52.4", - "@rollup/rollup-linux-x64-musl": "4.52.4", - "@rollup/rollup-openharmony-arm64": "4.52.4", - "@rollup/rollup-win32-arm64-msvc": "4.52.4", - "@rollup/rollup-win32-ia32-msvc": "4.52.4", - "@rollup/rollup-win32-x64-gnu": "4.52.4", - "@rollup/rollup-win32-x64-msvc": "4.52.4", - "@types/estree": "1.0.8", - "fsevents": "~2.3.2" - } - }, - "siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true - }, - "source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true - }, - "stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true - }, - "std-env": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", - "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", - "dev": true - }, - "strip-literal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", - "dev": true, - "requires": { - "js-tokens": "^9.0.1" - } - }, - "tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true - }, - "tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true - }, - "tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "requires": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - } - }, - "tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true - }, - "tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true - }, - "tinyspy": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", - "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", - "dev": true - }, - "ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true - }, - "undici-types": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", - "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", - "dev": true - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "vite": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", - "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", - "dev": true, - "requires": { - "esbuild": "^0.25.0", - "fdir": "^6.5.0", - "fsevents": "~2.3.3", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - } - }, - "vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "requires": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - } - }, - "vitest": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", - "dev": true, - "requires": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" - } - }, - "why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "requires": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true } } } diff --git a/package.json b/package.json index 1dd61c5..20cfa02 100644 --- a/package.json +++ b/package.json @@ -8,14 +8,19 @@ "lib": "lib" }, "devDependencies": { + "@eslint/js": "^9.38.0", "@types/node": "^24.7.2", + "eslint": "^9.37.0", + "globals": "^16.4.0", "ts-node": "^10.9.2", "typescript": "^5.9.3", + "typescript-eslint": "^8.46.1", "vitest": "^3.2.4" }, "scripts": { "build": "tsc", - "test": "vitest" + "test": "vitest", + "lint": "eslint --fix" }, "repository": { "type": "git", From 784a9723a641cb9fd647ff89b8e35b616278a660 Mon Sep 17 00:00:00 2001 From: c0reme Date: Fri, 17 Oct 2025 19:51:32 +0100 Subject: [PATCH 08/11] refactor: restructure XIVAPI client and remove deprecated search functionality - Introduced a new Sheet class for structured access to sheet data. - Updated XIVAPI class to utilize the new Sheet class for achievements, minions, mounts, and items. - Removed the deprecated Search class and its related functionality. - Enhanced error handling and request parameter management in the utils and sheets modules. - Consolidated exports in index.ts for improved module organization. --- src/index.ts | 157 +++++++++++++++++++------------------------- src/lib/assets.ts | 32 ++++----- src/lib/index.ts | 7 +- src/lib/search.ts | 31 --------- src/lib/sheets.ts | 133 ++++++++++++++++++++++++------------- src/lib/versions.ts | 16 ++--- src/utils.ts | 106 ++++++++++++++++-------------- 7 files changed, 236 insertions(+), 246 deletions(-) delete mode 100644 src/lib/search.ts diff --git a/src/index.ts b/src/index.ts index 892afea..5ffa5c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,130 +1,97 @@ -import { Assets, Search, Sheets, Versions } from "./lib"; -import { CustomError } from "./utils"; +import { Assets, Sheet, Sheets, Versions } from "./lib" +import { CustomError, request } from "./utils" -export type NumberResolvable = number | string; +export default class XIVAPI { + private readonly options: XIVAPI.Options -class xivapiSheet { - private readonly type: T; - - constructor(sheet: T) { - this.type = sheet; - } - - /** - * Gets a single row from the sheet. - * @param {NumberResolvable} id The row to fetch. - * @param {Models.RowReaderQuery} [params] The parameters to fetch the row with. - * @returns {Promise} A single row with typed fields. - * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet/{sheet}/{row} - */ - public get( - id: NumberResolvable, - params: Models.RowReaderQuery = {} - ): Promise { - try { - if (typeof id !== "string") id = id.toString(); - return new Sheets().get(this.type, id, params); - } catch (error) { - throw new CustomError(error instanceof Error ? error.message : "Unknown error"); - } - } - - /** - * Gets a list of rows from the sheet. - * @param {Models.SheetQuery} [params] The parameters to fetch the rows with. - * @returns {Promise} A list of rows with typed fields. - * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet/{sheet} - */ - public list(params: Models.SheetQuery = {}): Promise { - try { - return new Sheets().list(this.type, params); - } catch (error) { - throw new CustomError(error instanceof Error ? error.message : "Unknown error"); - } - } -} - -export default class xivapi { - private readonly options: xivapi.Options; - - /** + /** * A wrapper for the XIVAPI v2 API. - * @param {xivapi.Options} [options] The client options to fetch with. + * @param {XIVAPI.Options} [options] The client options to fetch with. * @see https://v2.xivapi.com/api/docs * @since 0.5.0 */ - constructor( - options: xivapi.Options = { - version: "latest", - language: "en", - verbose: false, - } - ) { - this.options = options; - } + constructor( + options: XIVAPI.Options = { + version: "latest", + language: "en", + verbose: false, + } + ) { + this.options = options + } + + /** + * @since 0.5.0 + */ + public get achievements() { + return new Sheet("Achievement") + } - /** + /** * @since 0.5.0 */ - public get achievements() { - return new xivapiSheet("Achievement"); - } + public get minions() { + return new Sheet("Companion") + } - /** + /** * @since 0.5.0 */ - public get minions() { - return new xivapiSheet("Companion"); - } + public get mounts() { + return new Sheet("Mount") + } - /** + /** * @since 0.5.0 */ - public get mounts() { - return new xivapiSheet("Mount"); - } + public get items() { + return new Sheet("Item") + } - /** + /** + * Fetch information about rows and their related data that match the provided search query. + * @param {Models.SearchQuery} params Query paramters accepted by the search endpoint. + * @returns {Promise} Response structure for the search endpoint. + * @see https://v2.xivapi.com/api/docs#tag/search/get/search * @since 0.5.0 */ - public get items() { - return new xivapiSheet("Item"); - } + public async search(params: XIVAPI.SearchParams): Promise { + const { data, errors } = await request({ + path: "/search", + params: params as Record, + }) + if (errors) throw new CustomError(errors[0].message) + return data as Models.SearchResponse + } - /** + /** * Raw endpoints for the API. Please consider using the typed endpoints instead. * @see https://v2.xivapi.com/api/docs * @since 0.5.0 */ - public data = { - /** + public data = { + /** * @see https://v2.xivapi.com/api/docs#tag/assets * @since 0.5.0 */ - assets: () => new Assets(), + assets: () => new Assets(), - /** - * @see https://v2.xivapi.com/api/docs#tag/search - * @since 0.5.0 - */ - search: () => new Search(this.options), - - /** + /** * @see https://v2.xivapi.com/api/docs#tag/sheets * @since 0.5.0 */ - sheets: () => new Sheets(this.options), + sheets: () => new Sheets(this.options), - /** + /** * @see https://v2.xivapi.com/api/docs#tag/versions * @since 0.5.0 */ - versions: () => - new Versions().all().then((versions) => versions.versions.map((version) => version.names[0])), - }; + versions: () => + new Versions().all().then((versions) => versions.versions.map((version) => version.names[0])), + } } -export namespace xivapi { +export namespace XIVAPI { export interface Options { /** * The supported version of the game to use for the API. @@ -141,6 +108,14 @@ export namespace xivapi { */ verbose?: boolean; } + + /** + * Query parameters accepted by the search endpoint. + * @see https://v2.xivapi.com/api/docs#tag/search/get/search + */ + export type SearchParams = Models.SearchQuery & + Models.VersionQuery & + Models.RowReaderQuery & { verbose?: boolean }; } /** diff --git a/src/lib/assets.ts b/src/lib/assets.ts index a860a9c..f1973b4 100644 --- a/src/lib/assets.ts +++ b/src/lib/assets.ts @@ -1,34 +1,34 @@ -import type { Models } from ".."; -import { CustomError, request } from "../utils"; +import type { Models } from ".." +import { CustomError, request } from "../utils" /** * Endpoints for accessing game data on a file-by-file basis. Commonly useful for fetching icons or other textures to display on the web. * @see https://v2.xivapi.com/api/docs#tag/assets */ export class Assets { - /** + /** * Read an asset from the game at the specified path, converting it into a usable format. If no valid conversion between the game file type and specified format exists, an error will be returned. * @param {Models.AssetQuery} params Query parameters accepted by the asset endpoint. * @returns {Promise} An image of the asset. * @see https://v2.xivapi.com/api/docs#tag/assets/get/asset */ - async get(params: Models.AssetQuery): Promise { - const { data, errors } = await request({ path: "/asset", params }); - if (errors) throw new CustomError(errors[0].message); - return data as Buffer; - } + async get(params: Models.AssetQuery): Promise { + const { data, errors } = await request({ path: "/asset", params }) + if (errors) throw new CustomError(errors[0].message) + return data as Buffer + } - /** + /** * Retrieve the specified map, composing it from split source files if necessary. * @param {Models.MapPath & Models.VersionQuery & Models.AssetQuery} params * @returns {Promise} An image of the map. * @see https://v2.xivapi.com/api/docs#tag/assets/get/asset/map/{territory}/{index} */ - async map( - params: Models.MapPath & Models.VersionQuery & Pick - ): Promise { - const { data, errors } = await request({ path: "/asset/map", params }); - if (errors) throw new CustomError(errors[0].message); - return data as Buffer; - } + async map( + params: Models.MapPath & Models.VersionQuery & Pick + ): Promise { + const { data, errors } = await request({ path: "/asset/map", params }) + if (errors) throw new CustomError(errors[0].message) + return data as Buffer + } } diff --git a/src/lib/index.ts b/src/lib/index.ts index 433d6e2..bbd2841 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,4 +1,3 @@ -export * from "./assets"; -export * from "./search"; -export * from "./sheets"; -export * from "./versions"; \ No newline at end of file +export * from "./assets" +export * from "./sheets" +export * from "./versions" \ No newline at end of file diff --git a/src/lib/search.ts b/src/lib/search.ts deleted file mode 100644 index e05bb3d..0000000 --- a/src/lib/search.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { Models, xivapi} from ".."; -import { CustomError, request } from "../utils"; - -/** - * Endpoints for seaching and filtering the game's static relational data store. - * @see https://v2.xivapi.com/api/docs#tag/search - */ -export class Search { - private readonly options: xivapi.Options; - - constructor( - options: xivapi.Options = { - language: "en", - verbose: false, - } - ) { - this.options = options; - } - - /** - * Fetch information about rows and their related data that match the provided search query. - * @param {Models.SearchQuery} params Query paramters accepted by the search endpoint. - * @returns {Promise} Response structure for the search endpoint. - * @see https://v2.xivapi.com/api/docs#tag/search/get/search - */ - async get(params: Models.SearchQuery): Promise { - const { data, errors } = await request({ path: "/search", params, options: this.options }); - if (errors) throw new CustomError(errors[0].message); - return data as Models.SearchResponse; - } -} diff --git a/src/lib/sheets.ts b/src/lib/sheets.ts index 5b538a1..2bed1f5 100644 --- a/src/lib/sheets.ts +++ b/src/lib/sheets.ts @@ -1,72 +1,113 @@ -import type { Models, xivapi } from ".."; -import { CustomError, request } from "../utils"; +import type { Models, XIVAPI } from ".." +import { CustomError, request } from "../utils" + +export class Sheet { + private readonly type: T + + constructor(sheet: T) { + this.type = sheet + } + + /** + * Gets a single row from the sheet. + * @param {string | number} id The row to fetch. + * @param {Models.RowReaderQuery} [params] The parameters to fetch the row with. + * @returns {Promise} A single row with typed fields. + * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet/{sheet}/{row} + */ + public get( + id: string | number, + params: Models.RowReaderQuery = {} + ): Promise { + try { + if (typeof id !== "string") id = id.toString() + return new Sheets().get(this.type, id, params) + } catch (error) { + throw new CustomError(error instanceof Error ? error.message : "Unknown error") + } + } + + /** + * Gets a list of rows from the sheet. + * @param {Models.SheetQuery} [params] The parameters to fetch the rows with. + * @returns {Promise} A list of rows with typed fields. + * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet/{sheet} + */ + public list(params: Models.SheetQuery = {}): Promise { + try { + return new Sheets().list(this.type, params) + } catch (error) { + throw new CustomError(error instanceof Error ? error.message : "Unknown error") + } + } +} export class Sheets { - private readonly options: xivapi.Options; + private readonly options: XIVAPI.Options - /** + /** * Endpoints for reading data from the game's static relational data store. - * @param {xivapi.Options} [options] The options to fetch the sheets with. + * @param {XIVAPI.Options} [options] The options to fetch the sheets with. * @see https://v2.xivapi.com/api/docs#tag/sheets */ - public constructor( - options: xivapi.Options = { - language: "en", - verbose: false, - } - ) { - this.options = options; - } + public constructor( + options: XIVAPI.Options = { + language: "en", + verbose: false, + } + ) { + this.options = options + } - /** + /** * List known excel sheets that can be read by the API. * @returns {Promise} Response structure for the list endpoint. * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet */ - async all(): Promise { - const { data, errors } = await request({ path: "/sheet", params: {}, options: this.options }); - if (errors) throw new CustomError(errors[0].message); - return data as Models.ListResponse; - } + async all(): Promise { + const { data, errors } = await request({ path: "/sheet", params: {}, options: this.options }) + if (errors) throw new CustomError(errors[0].message) + return data as Models.ListResponse + } - /** + /** * Read information about one or more rows and their related data. * @param {Models.SchemaSpecifier} sheet The sheet to fetch the rows from. * @param {Models.SheetQuery} [params] The parameters to fetch the rows with. * @returns {Promise} A list of rows with typed fields. * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet/{sheet} */ - async list( - sheet: Models.SchemaSpecifier, - params: Models.SheetQuery = {} - ): Promise { - const { data, errors } = await request({ - path: `/sheet/${sheet}`, - params, - options: this.options, - }); - if (errors) throw new CustomError(errors[0].message); - return data as Models.SheetResponse; - } + async list( + sheet: Models.SchemaSpecifier, + params: Models.SheetQuery = {} + ): Promise { + const { data, errors } = await request({ + path: `/sheet/${sheet}`, + params: params as Record, + options: this.options, + }) + if (errors) throw new CustomError(errors[0].message) + return data as Models.SheetResponse + } - /** + /** * Read detailed, filterable information from a single sheet row and its related data. * @param {Models.SchemaSpecifier} sheet Name of the sheet to read. * @param {Models.RowSpecifier} row Row to read. * @returns {Promise} A list of rows with typed fields. * @see https://v2.xivapi.com/api/docs#tag/sheets/get/sheet/{sheet}/{id} */ - async get( - sheet: Models.SchemaSpecifier, - row: Models.RowSpecifier, - params: Models.RowReaderQuery = {} - ): Promise { - const { data, errors } = await request({ - path: `/sheet/${sheet}/${row}`, - params, - options: this.options, - }); - if (errors) throw new CustomError(errors[0].message); - return data as Models.RowResponse; - } + async get( + sheet: Models.SchemaSpecifier, + row: Models.RowSpecifier, + params: Models.RowReaderQuery = {} + ): Promise { + const { data, errors } = await request({ + path: `/sheet/${sheet}/${row}`, + params: params as Record, + options: this.options, + }) + if (errors) throw new CustomError(errors[0].message) + return data as Models.RowResponse + } } diff --git a/src/lib/versions.ts b/src/lib/versions.ts index cc24fd1..82b1105 100644 --- a/src/lib/versions.ts +++ b/src/lib/versions.ts @@ -1,18 +1,18 @@ -import type {Models} from ".."; -import { CustomError, request } from "../utils"; +import type { Models } from ".." +import { CustomError, request } from "../utils" /** * Endpoints for querying metadata about the versions recorded by the boilmaster system. * @see https://v2.xivapi.com/api/docs#tag/versions */ export class Versions { - /** + /** * List versions understood by the API. * @see https://v2.xivapi.com/api/docs#tag/versions/get/version */ - async all(): Promise { - const { data, errors } = await request({ path: "/version", params: {} }); - if (errors) throw new CustomError(errors[0].message); - return data as Models.VersionsResponse; - } + async all(): Promise { + const { data, errors } = await request({ path: "/version", params: {} }) + if (errors) throw new CustomError(errors[0].message) + return data as Models.VersionsResponse + } } diff --git a/src/utils.ts b/src/utils.ts index d6a06f3..2df6d55 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,62 +1,68 @@ -import type { Models, xivapi } from "."; +import type { Models, XIVAPI } from "." -export const endpoint = "https://v2.xivapi.com/api/"; +export const endpoint = "https://v2.xivapi.com/api/" export class CustomError extends Error { - constructor(message: string, name: string | null = null) { - super(); - Error.captureStackTrace(this, this.constructor); - this.name = name || "XIVAPIError"; - this.message = message; - } + constructor(message: string, name: string | null = null) { + super() + Error.captureStackTrace(this, this.constructor) + this.name = name || "XIVAPIError" + this.message = message + } } export interface RequestPayload { path: string | URL; data?: unknown; - params?: Record; + params?: Record; errors?: Models.ErrorResponse[]; - options?: xivapi.Options; + options?: XIVAPI.Options; } export const request = async (payload: RequestPayload): Promise => { - const { path, params, options } = payload; - - const url = new URL(path instanceof URL ? path.toString() : path.replace(/^\/+/, ""), endpoint); - if (params) { - const array: Record = { query: " ", fields: ",", transient: "," }; - for (const key in array) { - if (Object.prototype.hasOwnProperty.call(params, key) && Array.isArray(params[key])) { - params[key] = params[key].join(array[key]); - } - } - - url.search = new URLSearchParams(params).toString(); - - if (!params.language) { - if (options && options.language) url.searchParams.set("language", options.language); - } - - if (!params.version) { - if (options && options.version) url.searchParams.set("version", options.version); - } - } - - const response = await fetch(url); - if (options && options.verbose) console.debug(`Requesting ${path} with params:`, params); - - if (response.ok) { - const contentType = response.headers.get("content-type"); - if (contentType && contentType.includes("application/json")) { - payload.data = await response.json(); - } else { - payload.data = Buffer.from(await response.arrayBuffer()); - } - } else { - payload.errors = [(await response.json()) as Models.ErrorResponse]; - } - - if (options && options.verbose) console.debug(`${response.ok ? "Success" : "Error"} on ${path} with params:`, params); - - return payload; -}; + const { path, params, options } = payload + + if (!params?.verbose) { + options!.verbose = params?.verbose as boolean + delete params?.verbose + } + + const url = new URL(path instanceof URL ? path.toString() : path.replace(/^\/+/, ""), endpoint) + if (params) { + const array: Record = { query: " ", fields: ",", transient: "," } + for (const key in array) { + if (Object.prototype.hasOwnProperty.call(params, key) && Array.isArray(params[key])) { + params[key] = (params[key] as string[]).join(array[key]) + } + } + + url.search = new URLSearchParams(params as Record).toString() + + if (!params.language) { + if (options && options.language) url.searchParams.set("language", options.language) + } + + if (!params.version) { + if (options && options.version) url.searchParams.set("version", options.version) + } + } + + const response = await fetch(url) + if (options && options.verbose) console.debug(`Requesting ${path} with params:`, params) + + if (response.ok) { + const contentType = response.headers.get("content-type") + if (contentType && contentType.includes("application/json")) { + payload.data = await response.json() + } else { + payload.data = Buffer.from(await response.arrayBuffer()) + } + } else { + payload.errors = [(await response.json()) as Models.ErrorResponse] + } + + if (options && options.verbose) + console.debug(`${response.ok ? "Success" : "Error"} on ${path} with params:`, params) + + return payload +} From 7acd5d24a33dd1fa0b1b56db334cc46d4437b913 Mon Sep 17 00:00:00 2001 From: c0reme Date: Fri, 17 Oct 2025 19:51:39 +0100 Subject: [PATCH 09/11] docs: enhance README with updated usage examples and deprecation notice for v1 --- README.md | 146 +++++++++++++++++++++++++++--------------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index ecd32b3..340b33e 100644 --- a/README.md +++ b/README.md @@ -3,106 +3,106 @@ [![npm version](https://badge.fury.io/js/%40xivapi%2Fjs.svg)](https://badge.fury.io/js/%40xivapi%2Fjs) [![license](https://img.shields.io/github/license/xivapi/xivapi-js.svg)](LICENSE) -A pure JS wrapper for interacting with [XIVAPI v2](https://v2.xivapi.com/) -and handling all requests in a simple, promise-driven manner. +A JavaScript library for working with [XIVAPI v2](https://v2.xivapi.com/), providing a source of Final Fantasy XIV game data. It lets you fetch, search, and use FFXIV data easily in a promise-based manner. + +> [!WARNING] +> `@xivapi/js@0.4.5` (using XIVAPI v1) is now deprecated. Please use it at your own risk. We strongly recommend you update to the latest version. Migration guide and details: https://v2.xivapi.com/docs/migrate/. + +If you need help or run into any issues, please [open an issue](https://github.com/xivapi/xivapi-js/issues) on GitHub or join the [XIVAPI Discord server](https://discord.gg/MFFVHWC) for support. ## Installation + ```bash -npm install @xivapi/js +npm install @xivapi/js@latest # or use yarn, pnpm, or bun ``` -## Quick Start +## Usage Examples -```javascript +#### 1. Importing and Initialization + +```js import xivapi from '@xivapi/js'; +// Basic instance const xiv = new xivapi(); -// Get a specific item -const item = await xiv.items.get(1); -console.log(item.fields.Name); // "Gil" - -// Search for items -const search = xiv.data.search(); -const data = await search.get({ - query: 'Name~"sword"', - sheets: "Item", - limit: 5 +// With options +const xivCustom = new xivapi({ + version: "7.0", // specify game version + language: "jp", // specify language (jp, en, de, fr) + verbose: true // output more logging }); -console.log(data.results); ``` -## Examples +#### 2. Get an Item -### Get Item Data -```javascript -// Get item by ID -const item = await xiv.items.get(1, { - fields: "Name,LevelItem,ItemUICategory", - language: "en" -}); +```js +// Fetch the Gil item (item ID 1) +const item = await xiv.items.get(1); -// List items with pagination -const items = await xiv.items.list({ limit: 10 }); +console.log(item.Name); // "Gil" (or equivalent in your language) ``` -### Search Game Data -```javascript -const search = xiv.data.search(); +#### 3. Search Example + +```js +// Find all items named "gil" +const params = { + query: 'Name~"gil"', + sheets: "Item" +}; + +const { results } = await xiv.search(params); +console.log(results[0]); + +/* +Output example: +{ + "score": 1, + "sheet": "Item", + "row_id": 1, + "fields": { + "Icon": { + "id": 65002, + "path": "ui/icon/065000/065002.tex", + "path_hr1": "ui/icon/065000/065002_hr1.tex" + }, + "Name": "Gil", + "Singular": "gil" + } +} +*/ +``` -// Find items by name -const swords = await search.get({ - query: 'Name~"sword"', - sheets: "Item", - limit: 5 -}); +#### 4. Using Raw XIVAPI v2 Endpoints -// Find high-level items -const highLevelItems = await search.get({ - query: "LevelItem>=90", - sheets: "Item", - limit: 10 +```js +// Fetch a raw asset file (e.g. icon image) +const asset = await xiv.data.assets.get({ + path: "ui/icon/051000/051474_hr1.tex", + format: "png" // jpg or webp also supported }); -// Search across multiple sheets -const results = await search.get({ - query: 'Name~"fire"', - sheets: "Action,Item", - limit: 5 -}); +// List all quests +const quests = await xiv.data.sheets.list("Quest"); +console.log(quests); + +// List available game versions +const versions = await xiv.data.versions(); +console.log(versions[0]); // e.g. "7.0" ``` -### Get Achievement Data -```javascript -// Get achievement by ID -const achievement = await xiv.achievements.get(1, { - fields: "Name,Description", - language: "en" -}); +## Contributing -// List achievements -const achievements = await xiv.achievements.list({ limit: 5 }); -``` +We welcome all contributions! Whether you'd like to report a bug, suggest a feature, improve the documentation, or submit a pull request, your help is appreciated. -### Custom Client Options -```javascript -const xiv = new xivapi({ - language: "ja", // Japanese - version: "latest", - verbose: true -}); -``` +To get started, clone the repository with: `git clone https://github.com/xivapi/xivapi-js.git` -### Get Game Assets -```javascript -const assets = xiv.data.assets(); +Before opening a pull request, please: +- Make sure your code passes linting and all current tests (`npm run lint && npm test`). +- Clearly explain your changes and reference any relevant issues in your PR description. -// Get item icon -const icon = await assets.get({ - path: "ui/icon/051000/051474_hr1.tex", - format: "png" -}); -``` +If you have questions, suggestions, or want to discuss changes before contributing, feel free to open an issue! ## License From ee8c1b7ade2af9b181d2ac0e314dfaffa577a57f Mon Sep 17 00:00:00 2001 From: c0reme Date: Fri, 17 Oct 2025 19:56:42 +0100 Subject: [PATCH 10/11] fix: improve verbose option handling --- src/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 2df6d55..67d060c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -22,8 +22,8 @@ export interface RequestPayload { export const request = async (payload: RequestPayload): Promise => { const { path, params, options } = payload - if (!params?.verbose) { - options!.verbose = params?.verbose as boolean + if (!options?.verbose && params?.verbose !== undefined) { + options!.verbose = Boolean(params?.verbose) delete params?.verbose } From 8951d1b80a7c67beb2477cb3881f46d47c28d58f Mon Sep 17 00:00:00 2001 From: c0reme Date: Fri, 17 Oct 2025 19:56:57 +0100 Subject: [PATCH 11/11] refactor: simplify search functionality in tests --- tests/xivapi-js.test.ts | 58 ++++++++++++----------------------------- 1 file changed, 16 insertions(+), 42 deletions(-) diff --git a/tests/xivapi-js.test.ts b/tests/xivapi-js.test.ts index 0d1d518..7f98888 100644 --- a/tests/xivapi-js.test.ts +++ b/tests/xivapi-js.test.ts @@ -67,11 +67,6 @@ describe("@xivapi/js", () => { }); }; - const createSearch = (options = {}) => { - const client = new xivapiClient(options); - return client.data.search(); - }; - describe("versions", () => { it( "should fetch all versions successfully", @@ -148,8 +143,7 @@ describe("@xivapi/js", () => { it( "can find items by exact name match", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: 'Name="Iron War Axe"', sheets: "Item", limit: 5, @@ -172,8 +166,7 @@ describe("@xivapi/js", () => { it( "can find items using partial text search", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: 'Name~"sword"', sheets: "Item", limit: 5, @@ -194,8 +187,7 @@ describe("@xivapi/js", () => { it( "can search actions by numeric properties", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: "Recast100ms>3000", sheets: "Action", limit: 5, @@ -218,8 +210,7 @@ describe("@xivapi/js", () => { it( "can find high-level items (>=50)", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: "LevelItem>=50", sheets: "Item", limit: 5, @@ -240,8 +231,7 @@ describe("@xivapi/js", () => { it( "can find low-level items (<10)", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: "LevelItem<10", sheets: "Item", limit: 5, @@ -262,8 +252,7 @@ describe("@xivapi/js", () => { it( "can find items in a level range", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: "LevelItem>=90 LevelItem<=99", sheets: "Item", limit: 5, @@ -287,8 +276,7 @@ describe("@xivapi/js", () => { it( "can filter by boolean properties", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: "IsUntradable=true", sheets: "Item", limit: 5, @@ -309,8 +297,7 @@ describe("@xivapi/js", () => { it( "can combine multiple search criteria", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: 'Name~"sword" LevelItem>=10 LevelItem<=50', sheets: "Item", limit: 5, @@ -337,8 +324,7 @@ describe("@xivapi/js", () => { it( "can search across multiple sheets simultaneously", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: 'Name~"rainbow"', sheets: "Action,Item", limit: 5, @@ -359,8 +345,7 @@ describe("@xivapi/js", () => { it( "can limit results and get pagination cursor", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: 'Name~"rainbow"', sheets: "Item", limit: 2, @@ -383,9 +368,7 @@ describe("@xivapi/js", () => { it( "can paginate through results using cursor", async () => { - const search = createSearch(); - - const firstPage = await search.get({ + const firstPage = await xivapi.search({ query: 'Name~"sword"', sheets: "Item", limit: 2, @@ -396,7 +379,7 @@ describe("@xivapi/js", () => { expect(Array.isArray(firstPage.results)).toBe(true); if (firstPage.next) { - const secondPage = await search.get({ + const secondPage = await xivapi.search({ cursor: firstPage.next, limit: 2, }); @@ -415,12 +398,7 @@ describe("@xivapi/js", () => { it( "can use custom language and verbose options", async () => { - const search = createSearch({ - language: "ja" as const, - verbose: true, - }); - - const result = await search.get({ + const result = await xivapi.search({ query: 'Name~"sword"', sheets: "Item", limit: 3, @@ -439,8 +417,7 @@ describe("@xivapi/js", () => { it( "can use string arrays for complex queries", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: ['Name~"sword"', "LevelItem>=10", "LevelItem<=50"], sheets: "Item", limit: 5, @@ -465,8 +442,7 @@ describe("@xivapi/js", () => { it( "can use string arrays for simple queries", async () => { - const search = createSearch(); - const result = await search.get({ + const result = await xivapi.search({ query: ['Name~"sword"'], sheets: "Item", limit: 5, @@ -489,10 +465,8 @@ describe("@xivapi/js", () => { it( "throws error for invalid query syntax", async () => { - const search = createSearch(); - await expect( - search.get({ + xivapi.search({ query: "invalid query syntax that should fail", sheets: "Item", })