diff --git a/.editorconfig b/.editorconfig index 57daf1e..9452075 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,7 +4,7 @@ root = true end_of_line = lf charset = utf-8 -[*.{js,cjs,mjs,ts}] +[*.{js,cjs,mjs,ts,cts,mts}] insert_final_newline = true trim_trailing_whitespace = true indent_style = space diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 178135c..0000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -/dist/ diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index c8eb0a5..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,115 +0,0 @@ -const { rules: baseStyleRules } = require('eslint-config-airbnb-base/rules/style'); -const { rules: baseVariablesRules } = require('eslint-config-airbnb-base/rules/variables'); -const { rules: tsBaseRules } = require('eslint-config-airbnb-typescript/lib/shared'); - -const forOfStatementRuleIndex = baseStyleRules['no-restricted-syntax'].findIndex((option) => { - return option.selector === 'ForOfStatement'; -}); - -// Rules that apply to both TypeScript and JavaScript -const commonRules = { - // Allow braces in arrow functions if desired - 'arrow-body-style': 0, - - // Generally makes sense, but too strict to enforce - 'class-methods-use-this': 0, - - // Saving 2 characters is not worth the potential errors - curly: ['error', 'all'], - - // A chain of 'if' and 'else if' statements is clearer than multiple individual 'if' blocks - 'no-else-return': ['error', { allowElseIf: true }], - - // Finding good names is hard so allow reuse - 'no-param-reassign': 0, - - // Increment with += 1 is just too long to type - 'no-plusplus': 0, - - // Allow for...of statements because they are natively supported in all target environments. - // Leave the rest of the options intact. - // https://github.com/airbnb/javascript/issues/1122 - 'no-restricted-syntax': [ - baseStyleRules['no-restricted-syntax'][0], - ...baseStyleRules['no-restricted-syntax'].slice(1, forOfStatementRuleIndex), - ...baseStyleRules['no-restricted-syntax'].slice(forOfStatementRuleIndex + 1), - ], - - // This is still the best way to express the private api intent - 'no-underscore-dangle': [baseStyleRules['no-underscore-dangle'][0], { - ...baseStyleRules['no-underscore-dangle'][1], - allowAfterThis: true, - enforceInMethodNames: false, - }], -}; - -const jsRules = { - ...commonRules, - - // Trailing commas on function arguments is just silly - 'comma-dangle': [baseStyleRules['comma-dangle'][0], { - ...baseStyleRules['comma-dangle'][1], - functions: 'never', - }], - - // Finding good names is hard so allow reuse - 'no-shadow': 0, - - // Allow functions to be used before defined because: - // 1) they are hoisted; - // 2) it allows code ordering that moves helper functions to the bottom. - 'no-use-before-define': [baseVariablesRules['no-use-before-define'][0], { - ...baseVariablesRules['no-use-before-define'][1], - functions: false, - }], -}; - -const tsRules = { - ...commonRules, - - // Trailing commas on function arguments is just silly - '@typescript-eslint/comma-dangle': ['error', { - ...tsBaseRules['@typescript-eslint/comma-dangle'][1], - functions: 'never', - }], - - // Finding good names is hard so allow reuse - '@typescript-eslint/no-shadow': 0, - - // Allow functions to be used before defined because: - // 1) they are hoisted; - // 2) it allows code ordering that moves helper functions to the bottom. - '@typescript-eslint/no-use-before-define': ['error', { - ...tsBaseRules['@typescript-eslint/no-use-before-define'][1], - functions: false, - }], -}; - -module.exports = { - root: true, - extends: [ - 'airbnb-base', - ], - overrides: [ - // Enable typescript-eslint only for TypeScript source files - { - files: ['./**/*.ts'], - parser: '@typescript-eslint/parser', - parserOptions: { - tsconfigRootDir: __dirname, - project: true, - }, - plugins: [ - '@typescript-eslint', - ], - extends: [ - 'airbnb-typescript/base', - ], - rules: tsRules, - }, - ], - parserOptions: { - ecmaVersion: 2020, - }, - rules: jsRules, -}; diff --git a/.mocharc.js b/.mocharc.js deleted file mode 100644 index afab8a1..0000000 --- a/.mocharc.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - // We don't need to validate TypeScript in tests so we use ts-node/register/transpile-only - // instead of ts-node/register. - "require": "ts-node/register/transpile-only", - "extension": ["ts"], - "spec": [ - "test/**/*Test.*" - ], -}; diff --git a/README.md b/README.md index 12e4130..3c0ae0d 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ import { functionToTest } from '../src/SomethingToTest'; // Adapt based on your testing framework. This example uses Mocha and Chai's syntax. -it('should produce a success response', () => { +it('should produce a success response', async () => { const server = newServer({ get: ['/my/url', { // status: 200 is the default @@ -65,10 +65,9 @@ it('should produce a success response', () => { server.install(/* optional context; defaults to globalThis */); // Do something that send()s an XMLHttpRequest to '/my/url' and returns a Promise - return functionToTest().then((result) => { - // This assumes the returned Promise resolves to the parsed JSON response - assert.equal(result.message, 'Success!'); - }); + // that resolves to the parsed JSON response + const result = await functionToTest(); + assert.equal(result.message, 'Success!'); } finally { // Restore the original XMLHttpRequest server.remove(); @@ -178,7 +177,7 @@ import { functionToTest } from '../src/SomethingToTest'; // Adapt based on your testing framework. This example uses Mocha and Chai's syntax. -it('should produce a success response', () => { +it('should produce a success response', async () => { // Get a "local" MockXhr subclass const MockXhr = newMockXhr(); @@ -194,10 +193,9 @@ it('should produce a success response', () => { global.XMLHttpRequest = MockXhr; // Do something that send()s an XMLHttpRequest to '/my/url' and returns a Promise - return functionToTest().then((result) => { - // This assumes the returned Promise resolves to the parsed JSON response - assert.equal(result.message, 'Success!'); - }); + // that resolves to the parsed JSON response + const result = await functionToTest(); + assert.equal(result.message, 'Success!'); } finally { // Restore the original XMLHttpRequest delete global.XMLHttpRequest; diff --git a/build/.eslintrc.js b/build/.eslintrc.js deleted file mode 100644 index 88b52dc..0000000 --- a/build/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - rules: { - // We want to print errors to console during the build - 'no-console': 0, - }, -}; diff --git a/build/build.ts b/build/build.ts new file mode 100644 index 0000000..92d2dd0 --- /dev/null +++ b/build/build.ts @@ -0,0 +1,119 @@ +import { openSync, readFileSync, readdirSync, renameSync, writeSync } from 'node:fs'; +import { rollup } from 'rollup'; +import typescript from '@rollup/plugin-typescript'; + +import type { ModuleFormat, OutputOptions, RollupBuild } from 'rollup'; +import { join } from 'node:path'; + +const packageRoot = join(import.meta.dirname, '..'); + +const version = process.env.VERSION ?? + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8')).version as string; + +const banner = `/** + * mock-xmlhttprequest v${version} + * (c) ${new Date().getFullYear()} Bertrand Guay-Paquet + * @license MIT + */ +`; + +const entryPoint = join(packageRoot, 'src', 'index.ts'); + +const commonOutputOptions: Partial = { + banner, + generatedCode: 'es2015', + preserveModules: true, +}; + +const tsconfigPath = join(packageRoot, 'tsconfig.json'); + +const outDir = join(packageRoot, 'dist'); + +const outputs = [ + { + format: 'esm' as ModuleFormat, + jsExt: 'mjs', + dtsExt: 'mts', + }, { + format: 'cjs' as ModuleFormat, + jsExt: 'cjs', + dtsExt: 'cts', + }, +]; + +await build(); + +async function build() { + let buildFailed = false; + for (const output of outputs) { + let codeBundle: RollupBuild | undefined; + try { + // rollup() currently produces this warning: + // [plugin typescript] @rollup/plugin-typescript TS5096: Option 'allowImportingTsExtensions' + // can only be used when either 'noEmit' or 'emitDeclarationOnly' is set. + // However the actual outputs are fine. We'll ignore this warning for now and re-evaluate when + // TypeScript 5.7 is released with the new "Path Rewriting for Relative Paths" feature. + // See also https://github.com/rollup/plugins/discussions/1536 + const dir = join(outDir, output.format); + const outputOptions: OutputOptions = { + ...commonOutputOptions, + format: output.format, + preserveModules: true, + dir, + entryFileNames: `[name].${output.jsExt}`, + }; + + codeBundle = await rollup({ + input: entryPoint, + plugins: [ + typescript({ + tsconfig: tsconfigPath, + declarationDir: outputOptions.dir, + compilerOptions: { + declaration: true, + }, + }), + ], + }); + + await codeBundle.write(outputOptions); + + fixupDeclarationFiles(dir, output.dtsExt); + } catch (e) { + buildFailed = true; + console.error(e); + } finally { + if (codeBundle) { + await codeBundle.close(); + } + } + } + + process.exit(buildFailed ? 1 : 0); +} + +function fixupDeclarationFiles(dir: string, newFileExtension: string) { + const files = readdirSync(dir, { recursive: true }) as string[]; + for (const file of files) { + if (file.endsWith('.d.ts')) { + const filePath = join(dir, file); + + let fileContent = readFileSync(filePath, 'utf8'); + + // Change the '.ts' file extension in imports of the declaration files + // e.g. import ... from './RequestData.ts'; => import ... from './RequestData.mts'; + fileContent = fileContent.replaceAll(/(['"][^'"]*\.)ts(['"])/g, `$1${newFileExtension}$2`); + + const fd = openSync(filePath, 'w'); + + // Add the copyright header + writeSync(fd, banner); + writeSync(fd, '\n'); + writeSync(fd, fileContent); + + // Rename the declaration file to the new file extension + renameSync(filePath, join(dir, `${file.slice(0, -2)}${newFileExtension}`)); + } + } +} diff --git a/build/rollup.config.ts b/build/rollup.config.ts deleted file mode 100644 index 65bdf30..0000000 --- a/build/rollup.config.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import typescript from '@rollup/plugin-typescript'; - -import type { OutputOptions, RollupOptions } from 'rollup'; - -function resolvePath(path: string) { - return fileURLToPath(new URL(path, import.meta.url)); -} - -const version = process.env.VERSION - || JSON.parse(readFileSync(resolvePath('../package.json')).toString()).version; - -// eslint-disable-next-line operator-linebreak -const banner = -`/** - * mock-xmlhttprequest v${version} - * (c) ${new Date().getFullYear()} Bertrand Guay-Paquet - * @license MIT - */`; - -const commonOutputOptions: Partial = { - banner, - generatedCode: 'es2015', - name: 'MockXMLHttpRequest', - sourcemap: true, -}; - -const outputOptions: OutputOptions[] = [ - { - ...commonOutputOptions, - format: 'cjs', - preserveModules: true, - dir: resolvePath('../dist/cjs'), - entryFileNames: '[name].cjs', - }, - { - ...commonOutputOptions, - format: 'es', - preserveModules: true, - dir: resolvePath('../dist/esm'), - entryFileNames: '[name].mjs', - }, -]; - -const config: RollupOptions = { - input: resolvePath('../src/index.ts'), - output: outputOptions, - plugins: [ - typescript({ - tsconfig: resolvePath('../tsconfig.json'), - }), - ], -}; - -export default config; diff --git a/build/tsconfig.dts.json b/build/tsconfig.dts.json deleted file mode 100644 index 08b3938..0000000 --- a/build/tsconfig.dts.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "declarationMap": true, /* Create sourcemaps for d.ts files. */ - "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "outDir": "../dist/types", /* Specify an output folder for all emitted files. */ - }, -} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..e9c3f61 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,263 @@ +import eslint from '@eslint/js'; +import stylistic from '@stylistic/eslint-plugin'; +import tseslint from 'typescript-eslint'; + +const goodPracticesRules = { + // Enforce consistent brace style for all control statements + 'curly': 'error', + + // Require the use of `===` and `!==` + 'eqeqeq': 'error', + + // Require `for-in` loops to include an `if` statement + 'guard-for-in': 'error', + + // Disallow the use of `console` + 'no-console': 'warn', + + // Disallow `else` blocks after `return` statements in `if` statements + 'no-else-return': 'error', + + // Disallow `new` operators with the `String`, `Number`, and `Boolean` objects + 'no-new-wrappers': 'error', + + // Enforce template literal expressions to be of `string` type, but allow numbers + '@typescript-eslint/restrict-template-expressions': ['error', { + allowNumber: true, + }], +}; + +const styleRules = { + // Enforce camelcase naming convention + 'camelcase': ['error', { properties: 'never', ignoreDestructuring: false }], + + // Require constructor names to begin with a capital letter + 'new-cap': 'error', + + // Disallow calls to the `Object` constructor without an argument + 'no-object-constructor': 'error', + + // Disallow dangling underscores in identifiers, except after `this`. + // This is still the best way to express the private api intent. + 'no-underscore-dangle': ['error', { + allowAfterThis: true, + }], + + // Disallow using Object.assign with an object literal as the first + // argument and prefer the use of object spread instead + 'prefer-object-spread': 'error', + + // Disallow Unicode byte order mark (BOM) + 'unicode-bom': 'error', + + // Enforce consistent indentation + '@stylistic/indent': ['error', 2, { SwitchCase: 1 }], + + // Enforce consistent spacing inside array brackets + '@stylistic/array-bracket-spacing': 'error', + + // Enforce consistent spacing before and after the arrow in arrow functions + '@stylistic/arrow-spacing': 'error', + + // Enforce spaces inside of blocks after opening block and before closing block + '@stylistic/block-spacing': 'error', + + // Enforce consistent brace style for blocks + '@stylistic/brace-style': ['error', '1tbs', { allowSingleLine: true }], + + // Require trailing commas in multiline object literals + '@stylistic/comma-dangle': ['error', { + arrays: 'always-multiline', + objects: 'always-multiline', + imports: 'always-multiline', + exports: 'always-multiline', + }], + + // Enforce consistent spacing before and after commas + '@stylistic/comma-spacing': 'error', + + // Enforce consistent comma style + '@stylistic/comma-style': 'error', + + // Enforce consistent spacing inside computed property brackets + '@stylistic/computed-property-spacing': 'error', + + // Enforce consistent newlines before and after dots + '@stylistic/dot-location': ['error', 'property'], + + // Require newline at the end of files + '@stylistic/eol-last': 'error', + + // Enforce line breaks between arguments of a function call + '@stylistic/function-call-argument-newline': ['error', 'consistent'], + + // Disallow spacing between function identifiers and their invocations + '@stylistic/func-call-spacing': 'error', + + // Enforce consistent line breaks inside function parentheses for multiline arguments + '@stylistic/function-paren-newline': ['error', 'multiline-arguments'], + + // Enforce the location of arrow function bodies + '@stylistic/implicit-arrow-linebreak': 'error', + + // Enforce consistent spacing between property names and type annotations in types and interfaces + '@stylistic/key-spacing': 'error', + + // Enforce consistent spacing before and after keywords + '@stylistic/keyword-spacing': 'error', + + // Enforce a maximum line length + '@stylistic/max-len': ['error', 100, 2, { + ignoreComments: true, // Lines with multiple eslint-disable-next-line entries are too long + ignoreUrls: true, + ignoreRegExpLiterals: true, + ignoreStrings: true, + ignoreTemplateLiterals: true, + }], + + // Disallow parentheses when invoking a constructor with no arguments + '@stylistic/new-parens': 'error', + + // Disallow unnecessary semicolons + '@stylistic/no-extra-semi': 'error', + + // Disallow mixed spaces and tabs for indentation + '@stylistic/no-mixed-spaces-and-tabs': 'error', + + // Disallow multiple spaces + '@stylistic/no-multi-spaces': 'error', + + // Disallow multiple empty lines, only one newline at the end, and no new lines at the beginning + '@stylistic/no-multiple-empty-lines': ['error', { max: 1, maxBOF: 0, maxEOF: 0 }], + + // Disallow whitespace before properties + '@stylistic/no-whitespace-before-property': 'error', + + // Enforce consistent spacing inside braces + '@stylistic/object-curly-spacing': ['error', 'always'], + + // Enforce placing object properties on separate lines, unless they fit in a single line + '@stylistic/object-property-newline': ['error', { + allowAllPropertiesOnSameLine: true, + }], + + // Enforce consistent linebreak style for operators + '@stylistic/operator-linebreak': 'error', + + // Enforce spacing between rest and spread operators and their expressions + '@stylistic/rest-spread-spacing': 'error', + + // Disallow all tabs + '@stylistic/no-tabs': 'error', + + // Disallow trailing whitespace at the end of lines + '@stylistic/no-trailing-spaces': 'error', + + // Enforce the consistent use of either backticks, double, or single quotes + '@stylistic/quotes': ['error', 'single', { avoidEscape: true }], + + // Require quotes around object literal, type literal, interfaces and enums property names + '@stylistic/quote-props': ['error', 'consistent-as-needed', { keywords: false }], + + // Require or disallow semicolons instead of ASI + '@stylistic/semi': 'error', + + // Enforce consistent spacing before and after semicolons + '@stylistic/semi-spacing': 'error', + + // Enforce location of semicolons + '@stylistic/semi-style': 'error', + + // Enforce consistent spacing before blocks + '@stylistic/space-before-blocks': 'error', + + // Enforce consistent spacing before function parenthesis + '@stylistic/space-before-function-paren': ['error', { + anonymous: 'always', + named: 'never', + asyncArrow: 'always', + }], + + // Enforce consistent spacing inside parentheses + '@stylistic/space-in-parens': 'error', + + // Require spacing around infix operators + '@stylistic/space-infix-ops': 'error', + + // Enforce consistent spacing before or after unary operators + '@stylistic/space-unary-ops': 'error', + + // Enforce spacing around colons of switch statements + '@stylistic/switch-colon-spacing': 'error', + + // Disallow spacing between template tags and their literals + '@stylistic/template-tag-spacing': 'error', + + // Disallow spacing around embedded expressions of template strings + // enforce usage of spacing in template strings + '@stylistic/template-curly-spacing': 'error', + + // Require spacing after and disallow spacing before the `*` in `yield*` expressions + '@stylistic/yield-star-spacing': 'error', +}; + +export default tseslint.config( + eslint.configs.recommended, + // ...tseslint.configs.recommended, type checked replaces this + ...tseslint.configs.strictTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + { + ignores: [ + 'dist/', + 'integration_tests/', // too difficult to configure with nested package.json + ], + }, + { + plugins: { + '@stylistic': stylistic, + }, + languageOptions: { + parserOptions: { + ecmaVersion: 2021, + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: { ...goodPracticesRules, ...styleRules }, + }, + { + // Non TypeScript files don't get TypeScript rules + files: [ + '**/*.js', + '**/*.cjs', + '**/*.mjs', + ], + ...tseslint.configs.disableTypeChecked, + }, + { + // Build script rules + files: ['build/**/*.ts'], + rules: { + // We want to print to console during the build + 'no-console': 0, + }, + }, + { + // Unit test rules + files: ['test/**/*.ts'], + rules: { + // Often need mocks, etc. in tests + '@typescript-eslint/no-empty-function': 0, + + // The test suite functions return promises that don't need awaiting + '@typescript-eslint/no-floating-promises': [ + 'error', + { + allowForKnownSafeCalls: [ + { from: 'package', name: ['describe', 'it', 'test'], package: 'node:test' }, + ], + }, + ], + }, + } +); diff --git a/integration_tests/cjs/index.js b/integration_tests/cjs/index.js index a85c035..14284ae 100644 --- a/integration_tests/cjs/index.js +++ b/integration_tests/cjs/index.js @@ -1,10 +1,8 @@ const assert = require('node:assert'); -// eslint-disable-next-line import/no-unresolved const { newServer } = require('mock-xmlhttprequest'); function functionToTest() { return new Promise((resolve, reject) => { - // eslint-disable-next-line no-undef const xhr = new XMLHttpRequest(); xhr.open('GET', '/my/url'); xhr.onload = () => resolve(JSON.parse(xhr.response)); diff --git a/integration_tests/esm/index.js b/integration_tests/esm/index.js index 1b0dfca..d132dcc 100644 --- a/integration_tests/esm/index.js +++ b/integration_tests/esm/index.js @@ -1,10 +1,8 @@ import assert from 'node:assert'; -// eslint-disable-next-line import/no-unresolved import { newServer } from 'mock-xmlhttprequest'; function functionToTest() { return new Promise((resolve, reject) => { - // eslint-disable-next-line no-undef const xhr = new XMLHttpRequest(); xhr.open('GET', '/my/url'); xhr.onload = () => resolve(JSON.parse(xhr.response)); diff --git a/package-lock.json b/package-lock.json index eb792ea..5812d71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,84 +9,195 @@ "version": "8.3.0", "license": "MIT", "devDependencies": { - "@rollup/plugin-typescript": "^11.1.5", - "@types/chai": "^4.3.1", - "@types/mocha": "^10.0.0", - "@types/node": "^20.9.2", - "@typescript-eslint/eslint-plugin": "^6.12.0", - "@typescript-eslint/parser": "^6.12.0", - "chai": "^4.3.4", - "cross-env": "^7.0.3", - "eslint": "^8.18.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-plugin-import": "^2.22.1", - "mocha": "^10.0.0", - "rimraf": "^5.0.5", + "@arethetypeswrong/cli": "^0.16.4", + "@rollup/plugin-typescript": "^12.1.0", + "@stylistic/eslint-plugin": "^2.9.0", + "@types/node": "^22.0.0", + "eslint": "^9.0.0", + "rimraf": "^6.0.1", "rollup": "^4.5.0", - "ts-node": "^10.8.2", "tslib": "^2.6.2", - "typescript": "~5.2.2" + "typescript": "^5.6.3", + "typescript-eslint": "^8.8.1" }, "engines": { "node": ">=16.0.0" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "node_modules/@andrewbranch/untar.js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz", + "integrity": "sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==", + "dev": true + }, + "node_modules/@arethetypeswrong/cli": { + "version": "0.16.4", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/cli/-/cli-0.16.4.tgz", + "integrity": "sha512-qMmdVlJon5FtA+ahn0c1oAVNxiq4xW5lqFiTZ21XHIeVwAVIQ+uRz4UEivqRMsjVV1grzRgJSKqaOrq1MvlVyQ==", "dev": true, + "license": "MIT", + "dependencies": { + "@arethetypeswrong/core": "0.16.4", + "chalk": "^4.1.2", + "cli-table3": "^0.6.3", + "commander": "^10.0.1", + "marked": "^9.1.2", + "marked-terminal": "^7.1.0", + "semver": "^7.5.4" + }, + "bin": { + "attw": "dist/index.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, - "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==", + "node_modules/@arethetypeswrong/core": { + "version": "0.16.4", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.16.4.tgz", + "integrity": "sha512-RI3HXgSuKTfcBf1hSEg1P9/cOvmI0flsMm6/QL3L3wju4AlHDqd55JFPfXs4pzgEAgy5L9pul4/HPPz99x2GvA==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "@andrewbranch/untar.js": "^1.0.3", + "cjs-module-lexer": "^1.2.3", + "fflate": "^0.8.2", + "lru-cache": "^10.4.3", + "semver": "^7.5.4", + "typescript": "5.6.1-rc", + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@arethetypeswrong/core/node_modules/typescript": { + "version": "5.6.1-rc", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.1-rc.tgz", + "integrity": "sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "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.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "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.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/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/@eslint/core": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", + "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -94,33 +205,105 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/js": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", - "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", + "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", + "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "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/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@humanwhocodes/module-importer": { @@ -128,6 +311,7 @@ "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" }, @@ -136,17 +320,26 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true + "node_modules/@humanwhocodes/retry": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.0.tgz", + "integrity": "sha512-xnRgu9DxZbkWak/te3fcytNyp8MTbuiZIaueg2rgEvBuN55n04nwLYLU9TX/VVlusc9L2ZNXi99nUFNkHXtr5g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -159,41 +352,19 @@ "node": ">=12" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -206,68 +377,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "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, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@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" @@ -281,6 +396,7 @@ "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" } @@ -290,6 +406,7 @@ "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" @@ -298,23 +415,14 @@ "node": ">= 8" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@rollup/plugin-typescript": { - "version": "11.1.5", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.5.tgz", - "integrity": "sha512-rnMHrGBB0IUEv69Q8/JGRD/n4/n6b3nfpufUu26axhUcboUzv/twfZU8fIBbTOphRAe0v8EyxzeDpKXqGHfyDA==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.1.1.tgz", + "integrity": "sha512-t7O653DpfB5MbFrqPe/VcKFFkvRuFNp9qId3xq4Eth5xlyymzxNpye2z8Hrl0RIMuXTSr5GGcFpkdlMeacUiFQ==", "dev": true, + "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^5.0.1", + "@rollup/pluginutils": "^5.1.0", "resolve": "^1.22.1" }, "engines": { @@ -335,14 +443,15 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.5.tgz", - "integrity": "sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz", + "integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" + "picomatch": "^4.0.2" }, "engines": { "node": ">=14.0.0" @@ -357,258 +466,341 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.5.1.tgz", - "integrity": "sha512-YaN43wTyEBaMqLDYeze+gQ4ZrW5RbTEGtT5o1GVDkhpdNcsLTnLRcLccvwy3E9wiDKWg9RIhuoy3JQKDRBfaZA==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.4.tgz", + "integrity": "sha512-jfUJrFct/hTA0XDM5p/htWKoNNTbDLY0KRwEt6pyOA6k2fmk0WVwl65PdUdJZgzGEHWx+49LilkcSaumQRyNQw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.5.1.tgz", - "integrity": "sha512-n1bX+LCGlQVuPlCofO0zOKe1b2XkFozAVRoczT+yxWZPGnkEAKTTYVOGZz8N4sKuBnKMxDbfhUsB1uwYdup/sw==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.4.tgz", + "integrity": "sha512-j4nrEO6nHU1nZUuCfRKoCcvh7PIywQPUCBa2UsootTHvTHIoIu2BzueInGJhhvQO/2FTRdNYpf63xsgEqH9IhA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.5.1.tgz", - "integrity": "sha512-QqJBumdvfBqBBmyGHlKxje+iowZwrHna7pokj/Go3dV1PJekSKfmjKrjKQ/e6ESTGhkfPNLq3VXdYLAc+UtAQw==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.4.tgz", + "integrity": "sha512-GmU/QgGtBTeraKyldC7cDVVvAJEOr3dFLKneez/n7BvX57UdhOqDsVwzU7UOnYA7AAOt+Xb26lk79PldDHgMIQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.5.1.tgz", - "integrity": "sha512-RrkDNkR/P5AEQSPkxQPmd2ri8WTjSl0RYmuFOiEABkEY/FSg0a4riihWQGKDJ4LnV9gigWZlTMx2DtFGzUrYQw==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.4.tgz", + "integrity": "sha512-N6oDBiZCBKlwYcsEPXGDE4g9RoxZLK6vT98M8111cW7VsVJFpNEqvJeIPfsCzbf0XEakPslh72X0gnlMi4Ddgg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.5.1.tgz", - "integrity": "sha512-ZFPxvUZmE+fkB/8D9y/SWl/XaDzNSaxd1TJUSE27XAKlRpQ2VNce/86bGd9mEUgL3qrvjJ9XTGwoX0BrJkYK/A==", + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.4.tgz", + "integrity": "sha512-py5oNShCCjCyjWXCZNrRGRpjWsF0ic8f4ieBNra5buQz0O/U6mMXCpC1LvrHuhJsNPgRt36tSYMidGzZiJF6mw==", "cpu": [ - "arm" + "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "freebsd" ] }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.5.1.tgz", - "integrity": "sha512-FEuAjzVIld5WVhu+M2OewLmjmbXWd3q7Zcx+Rwy4QObQCqfblriDMMS7p7+pwgjZoo9BLkP3wa9uglQXzsB9ww==", + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.4.tgz", + "integrity": "sha512-L7VVVW9FCnTTp4i7KrmHeDsDvjB4++KOBENYtNYAiYl96jeBThFfhP6HVxL74v4SiZEVDH/1ILscR5U9S4ms4g==", "cpu": [ - "arm64" + "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "freebsd" ] }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.5.1.tgz", - "integrity": "sha512-f5Gs8WQixqGRtI0Iq/cMqvFYmgFzMinuJO24KRfnv7Ohi/HQclwrBCYkzQu1XfLEEt3DZyvveq9HWo4bLJf1Lw==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.4.tgz", + "integrity": "sha512-10ICosOwYChROdQoQo589N5idQIisxjaFE/PAnX2i0Zr84mY0k9zul1ArH0rnJ/fpgiqfu13TFZR5A5YJLOYZA==", "cpu": [ - "arm64" + "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.5.1.tgz", - "integrity": "sha512-CWPkPGrFfN2vj3mw+S7A/4ZaU3rTV7AkXUr08W9lNP+UzOvKLVf34tWCqrKrfwQ0NTk5GFqUr2XGpeR2p6R4gw==", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.4.tgz", + "integrity": "sha512-ySAfWs69LYC7QhRDZNKqNhz2UKN8LDfbKSMAEtoEI0jitwfAG2iZwVqGACJT+kfYvvz3/JgsLlcBP+WWoKCLcw==", "cpu": [ - "x64" + "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.5.1.tgz", - "integrity": "sha512-ZRETMFA0uVukUC9u31Ed1nx++29073goCxZtmZARwk5aF/ltuENaeTtRVsSQzFlzdd4J6L3qUm+EW8cbGt0CKQ==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.4.tgz", + "integrity": "sha512-uHYJ0HNOI6pGEeZ/5mgm5arNVTI0nLlmrbdph+pGXpC9tFHFDQmDMOEqkmUObRfosJqpU8RliYoGz06qSdtcjg==", "cpu": [ - "x64" + "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.5.1.tgz", - "integrity": "sha512-ihqfNJNb2XtoZMSCPeoo0cYMgU04ksyFIoOw5S0JUVbOhafLot+KD82vpKXOurE2+9o/awrqIxku9MRR9hozHQ==", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.4.tgz", + "integrity": "sha512-38yiWLemQf7aLHDgTg85fh3hW9stJ0Muk7+s6tIkSUOMmi4Xbv5pH/5Bofnsb6spIwD5FJiR+jg71f0CH5OzoA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ] }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.5.1.tgz", - "integrity": "sha512-zK9MRpC8946lQ9ypFn4gLpdwr5a01aQ/odiIJeL9EbgZDMgbZjjT/XzTqJvDfTmnE1kHdbG20sAeNlpc91/wbg==", + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.4.tgz", + "integrity": "sha512-q73XUPnkwt9ZNF2xRS4fvneSuaHw2BXuV5rI4cw0fWYVIWIBeDZX7c7FWhFQPNTnE24172K30I+dViWRVD9TwA==", "cpu": [ - "ia32" + "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ] }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.5.1.tgz", - "integrity": "sha512-5I3Nz4Sb9TYOtkRwlH0ow+BhMH2vnh38tZ4J4mggE48M/YyJyp/0sPSxhw1UeS1+oBgQ8q7maFtSeKpeRJu41Q==", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.4.tgz", + "integrity": "sha512-Aie/TbmQi6UXokJqDZdmTJuZBCU3QBDA8oTKRGtd4ABi/nHgXICulfg1KI6n9/koDsiDbvHAiQO3YAUNa/7BCw==", "cpu": [ - "x64" + "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ] }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.4.tgz", + "integrity": "sha512-P8MPErVO/y8ohWSP9JY7lLQ8+YMHfTI4bAdtCi3pC2hTeqFJco2jYspzOzTUB8hwUWIIu1xwOrJE11nP+0JFAQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "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 + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.4.tgz", + "integrity": "sha512-K03TljaaoPK5FOyNMZAAEmhlyO49LaE4qCsr0lYHUKyb6QacTNF9pnfPpXnFlFD3TXuFbFbz7tJ51FujUXkXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "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 + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.4.tgz", + "integrity": "sha512-VJYl4xSl/wqG2D5xTYncVWW+26ICV4wubwN9Gs5NrqhJtayikwCXzPL8GDsLnaLU3WwhQ8W02IinYSFJfyo34Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "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 + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.4.tgz", + "integrity": "sha512-ku2GvtPwQfCqoPFIJCqZ8o7bJcj+Y54cZSr43hHca6jLwAiCbZdBUOrqE6y29QFajNAzzpIOwsckaTFmN6/8TA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@types/chai": { - "version": "4.3.11", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", - "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", - "dev": true + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.4.tgz", + "integrity": "sha512-V3nCe+eTt/W6UYNr/wGvO1fLpHUrnlirlypZfKCT1fG6hWfqhPgQV/K/mRBXBpxc0eKLIF18pIOFVPh0mqHjlg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.4.tgz", + "integrity": "sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@stylistic/eslint-plugin": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.10.1.tgz", + "integrity": "sha512-U+4yzNXElTf9q0kEfnloI9XbOyD4cnEQCxjUI94q0+W++0GAEQvJ/slwEj9lwjDHfGADRSr+Tco/z0XJvmDfCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.12.2", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "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 - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/mocha": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.5.tgz", - "integrity": "sha512-JUI82qwkRhYJYesuKSeFy46fKbhLaV9RU1gAh2PHmyoEECvlTf5UYeIivYlMszp1WT2CwJ4ziC3zoxsodhsGwg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.3.tgz", - "integrity": "sha512-nk5wXLAXGBKfrhLB0cyHGbSqopS+nz0BUgZkUQqSHSSgdee0kssp1IAqlQOu333bW+gMNs2QREx7iynm19Abxw==", + "version": "22.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.7.tgz", + "integrity": "sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.8" } }, - "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", - "dev": true - }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.12.0.tgz", - "integrity": "sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz", + "integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.12.0", - "@typescript-eslint/type-utils": "6.12.0", - "@typescript-eslint/utils": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0", - "debug": "^4.3.4", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/type-utils": "8.13.0", + "@typescript-eslint/utils": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -617,26 +809,27 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.12.0.tgz", - "integrity": "sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz", + "integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "6.12.0", - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/typescript-estree": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0", + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/typescript-estree": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", "debug": "^4.3.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -645,16 +838,17 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz", - "integrity": "sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz", + "integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0" + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -662,26 +856,24 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.12.0.tgz", - "integrity": "sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz", + "integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "6.12.0", - "@typescript-eslint/utils": "6.12.0", + "@typescript-eslint/typescript-estree": "8.13.0", + "@typescript-eslint/utils": "8.13.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, "peerDependenciesMeta": { "typescript": { "optional": true @@ -689,12 +881,13 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz", - "integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz", + "integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==", "dev": true, + "license": "MIT", "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -702,21 +895,23 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz", - "integrity": "sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz", + "integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/visitor-keys": "6.12.0", + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -729,58 +924,65 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.12.0.tgz", - "integrity": "sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz", + "integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.12.0", - "@typescript-eslint/types": "6.12.0", - "@typescript-eslint/typescript-estree": "6.12.0", - "semver": "^7.5.4" + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/typescript-estree": "8.13.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.57.0 || ^9.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz", - "integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz", + "integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.12.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "8.13.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "node_modules/@typescript-eslint/visitor-keys/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/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -793,24 +995,17 @@ "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.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", - "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "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", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -822,22 +1017,33 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { @@ -845,6 +1051,7 @@ "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" }, @@ -855,341 +1062,183 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "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 + "dev": true, + "license": "Python-2.0" }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "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": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "balanced-match": "^1.0.0" } }, - "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "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": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" + "fill-range": "^7.1.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "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": ">=8" + "node": ">=6" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "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": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", "dev": true, + "license": "ISC", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" }, - "engines": { - "node": ">= 0.4" + "bin": { + "highlight": "bin/highlight" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" + "string-width": "^4.2.0" }, "engines": { - "node": ">= 0.4" + "node": "10.* || >= 12.*" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" + "optionalDependencies": { + "@colors/colors": "1.5.0" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "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 - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/chai": { - "version": "4.3.10", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", - "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.0.8" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/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, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/color-convert": { @@ -1197,6 +1246,7 @@ "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" }, @@ -1208,49 +1258,32 @@ "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 + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } }, "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 - }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true - }, - "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 - }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", "dev": true, - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1261,12 +1294,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1277,210 +1311,53 @@ } } }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "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 - }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } + "license": "MIT" }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } + "license": "MIT" }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", "dev": true, - "dependencies": { - "hasown": "^2.0.0" - } + "license": "MIT" }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -1490,6 +1367,7 @@ "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" }, @@ -1498,260 +1376,144 @@ } }, "node_modules/eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", - "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz", + "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.54.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.7.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.14.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", + "@humanwhocodes/retry": "^0.4.0", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-config-airbnb-base/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-config-airbnb-typescript": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz", - "integrity": "sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig==", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-config-airbnb-base": "^15.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.13.0 || ^6.0.0", - "@typescript-eslint/parser": "^5.0.0 || ^6.0.0", - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.3" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "node_modules/eslint/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": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", - "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "brace-expansion": "^1.1.7" }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "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, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": "*" } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "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" }, @@ -1764,6 +1526,7 @@ "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" }, @@ -1776,6 +1539,7 @@ "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" } @@ -1784,13 +1548,15 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true + "dev": true, + "license": "MIT" }, "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" } @@ -1799,13 +1565,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -1822,6 +1590,7 @@ "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" }, @@ -1833,40 +1602,52 @@ "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 + "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 + "dev": true, + "license": "MIT" }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "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": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "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" }, @@ -1879,6 +1660,7 @@ "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" @@ -1890,64 +1672,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "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.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } + "license": "ISC" }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -1959,18 +1710,13 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": 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" @@ -1984,33 +1730,7 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2020,65 +1740,30 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dev": true, + "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2089,6 +1774,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -2096,455 +1782,120 @@ "node": ">=10.13.0" } }, - "node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/glob/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, + "license": "ISC", "dependencies": { - "define-properties": "^1.1.3" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": "20 || >=22" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "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, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, "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", "engines": { "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "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, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-negative-zero": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "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": "MIT", "engines": { - "node": ">=8" + "node": ">= 4" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.8.19" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -2553,73 +1904,70 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "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, - "dependencies": { - "which-typed-array": "^1.1.11" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "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": { - "call-bind": "^1.0.2" + "is-extglob": "^2.1.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "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 + "dev": true, + "license": "ISC" }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=14" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/js-yaml": { @@ -2627,6 +1975,7 @@ "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" }, @@ -2638,37 +1987,29 @@ "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 + "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 + "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 - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } + "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" } @@ -2678,6 +2019,7 @@ "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" @@ -2691,6 +2033,7 @@ "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" }, @@ -2705,335 +2048,192 @@ "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 + "dev": true, + "license": "MIT" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "license": "ISC" + }, + "node_modules/marked": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", + "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 16" } }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "node_modules/marked-terminal": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.2.1.tgz", + "integrity": "sha512-rQ1MoMFXZICWNsKMiiHwP/Z+92PLKskTPXj+e7uwXmuMPkNn7iTqC+IvDekVm1MPeC9wYQeLxeFaOvudRR/XbQ==", "dev": true, + "license": "MIT", "dependencies": { - "get-func-name": "^2.0.1" + "ansi-escapes": "^7.0.0", + "ansi-regex": "^6.1.0", + "chalk": "^5.3.0", + "cli-highlight": "^2.1.11", + "cli-table3": "^0.6.5", + "node-emoji": "^2.1.3", + "supports-hyperlinks": "^3.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <15" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/marked-terminal/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "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 - }, "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.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "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.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" } }, - "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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "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, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, + "license": "MIT", "engines": { - "node": ">= 14.0.0" + "node": ">=8.6" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "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": ">=10" - } - }, - "node_modules/mocha/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 - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "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 - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, + "license": "ISC", "engines": { - "node": ">= 0.4" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "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/object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" } }, - "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "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/node-emoji": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", + "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, - "dependencies": { - "wrappy": "1" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -3044,6 +2244,7 @@ "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" }, @@ -3059,6 +2260,7 @@ "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" }, @@ -3069,11 +2271,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "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" }, @@ -3081,29 +2291,46 @@ "node": ">=6" } }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, "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-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "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" } @@ -3112,58 +2339,44 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.3.tgz", - "integrity": "sha512-B7gr+F6MkqB3uzINHXNctGieGsRTMwIBgxkp0yq/5BwcuDzD4A8wQpHQW6vDAm1uKSLQghmRdD9sKqf2vJ1cEg==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", "dev": true, + "license": "ISC", "engines": { - "node": "*" + "node": "20 || >=22" } }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -3174,6 +2387,7 @@ "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" } @@ -3183,6 +2397,7 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -3205,51 +2420,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + ], + "license": "MIT" }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3259,6 +2438,7 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, + "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -3276,6 +2456,7 @@ "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" } @@ -3285,80 +2466,41 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/rimraf": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", - "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", "dev": true, + "license": "ISC", "dependencies": { - "glob": "^10.3.7" + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" }, "bin": { "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/rollup": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.4.tgz", + "integrity": "sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA==", "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "@types/estree": "1.0.6" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.5.1.tgz", - "integrity": "sha512-0EQribZoPKpb5z1NW/QYm3XSR//Xr8BeEXU49Lc/mQmpmVVG5jPUVrpc2iptup/0WMrY9mzas0fxH+TjYvG2CA==", - "dev": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -3367,18 +2509,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.5.1", - "@rollup/rollup-android-arm64": "4.5.1", - "@rollup/rollup-darwin-arm64": "4.5.1", - "@rollup/rollup-darwin-x64": "4.5.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.5.1", - "@rollup/rollup-linux-arm64-gnu": "4.5.1", - "@rollup/rollup-linux-arm64-musl": "4.5.1", - "@rollup/rollup-linux-x64-gnu": "4.5.1", - "@rollup/rollup-linux-x64-musl": "4.5.1", - "@rollup/rollup-win32-arm64-msvc": "4.5.1", - "@rollup/rollup-win32-ia32-msvc": "4.5.1", - "@rollup/rollup-win32-x64-msvc": "4.5.1", + "@rollup/rollup-android-arm-eabi": "4.24.4", + "@rollup/rollup-android-arm64": "4.24.4", + "@rollup/rollup-darwin-arm64": "4.24.4", + "@rollup/rollup-darwin-x64": "4.24.4", + "@rollup/rollup-freebsd-arm64": "4.24.4", + "@rollup/rollup-freebsd-x64": "4.24.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.4", + "@rollup/rollup-linux-arm-musleabihf": "4.24.4", + "@rollup/rollup-linux-arm64-gnu": "4.24.4", + "@rollup/rollup-linux-arm64-musl": "4.24.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.4", + "@rollup/rollup-linux-riscv64-gnu": "4.24.4", + "@rollup/rollup-linux-s390x-gnu": "4.24.4", + "@rollup/rollup-linux-x64-gnu": "4.24.4", + "@rollup/rollup-linux-x64-musl": "4.24.4", + "@rollup/rollup-win32-arm64-msvc": "4.24.4", + "@rollup/rollup-win32-ia32-msvc": "4.24.4", + "@rollup/rollup-win32-x64-msvc": "4.24.4", "fsevents": "~2.3.2" } }, @@ -3401,70 +2549,17 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, - "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "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" - } - ] - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3472,49 +2567,12 @@ "node": ">=10" } }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "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" }, @@ -3527,29 +2585,17 @@ "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/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -3557,11 +2603,15 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", "dev": true, + "license": "MIT", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, "engines": { "node": ">=8" } @@ -3571,6 +2621,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3586,6 +2637,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3595,61 +2647,66 @@ "node": ">=8" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/strip-ansi": { + "node_modules/string-width/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/strip-ansi-cjs": { @@ -3658,6 +2715,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3665,13 +2723,14 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-json-comments": { @@ -3679,6 +2738,7 @@ "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" }, @@ -3691,6 +2751,7 @@ "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" }, @@ -3698,11 +2759,29 @@ "node": ">=8" } }, + "node_modules/supports-hyperlinks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz", + "integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3714,13 +2793,38 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } }, "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" }, @@ -3729,92 +2833,31 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", + "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" } }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "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/ts-node/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, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" }, "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" }, @@ -3822,146 +2865,87 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">= 0.4" + "node": ">=14.17" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "node_modules/typescript-eslint": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.13.0.tgz", + "integrity": "sha512-vIMpDRJrQd70au2G8w34mPps0ezFSPMEX4pXkTzUkrNbRX+36ais2ksGWN0esZL+ZMaFJEneOBHzCgSqle7DHw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "@typescript-eslint/eslint-plugin": "8.13.0", + "@typescript-eslint/parser": "8.13.0", + "@typescript-eslint/utils": "8.13.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "license": "MIT", "engines": { - "node": ">=14.17" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, "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", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "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" }, @@ -3972,52 +2956,41 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "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, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, + "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "node_modules/wrap-ansi": { + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -4030,50 +3003,83 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -4088,43 +3094,21 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, + "license": "ISC", "engines": { "node": ">=10" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "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, + "license": "MIT", "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index ef27767..cf22bd9 100644 --- a/package.json +++ b/package.json @@ -2,29 +2,33 @@ "name": "mock-xmlhttprequest", "version": "8.3.0", "description": "XMLHttpRequest mock for testing", + "type": "module", "exports": { - "import": "./dist/esm/index.mjs", - "require": "./dist/cjs/index.cjs", - "types": "./dist/types/index.d.ts" + "import": { + "types": "./dist/esm/index.d.mts", + "default": "./dist/esm/index.mjs" + }, + "require": { + "types": "./dist/cjs/index.d.cts", + "default": "./dist/cjs/index.cjs" + } }, "main": "./dist/cjs/index.cjs", - "types": "./dist/types/index.d.ts", + "types": "./dist/cjs/index.d.cts", "files": [ "dist/**/*" ], "scripts": { - "build": "npm run build:src && npm run build:dts", + "build": "node --experimental-strip-types ./build/build.ts", "build:clean": "rimraf dist", - "build:dts": "tsc -p build/tsconfig.dts.json", - "build:src": "rollup --config build/rollup.config.ts --configPlugin \"typescript={tsconfig: \\\"./build/tsconfig.json\\\"}\"", "tsc:validate": "npm run tsc:validate:src && npm run tsc:validate:test && npm run tsc:validate:build", "tsc:validate:src": "tsc --noEmit", "tsc:validate:test": "tsc --noEmit -p test/tsconfig.json", "tsc:validate:build": "tsc --noEmit -p build/tsconfig.json", "lint": "eslint .", - "test": "cross-env TS_NODE_PROJECT='test/tsconfig.json' mocha", - "test:ci": "npm run test && npm run lint", - "test:watch": "npm run test -- --watch", + "test": "node --experimental-strip-types --test", + "test:types": "attw --pack .", + "test:ci": "npm run test && npm run lint && npm run test:types", "prepare": "npm run build:clean && npm run build", "prepublishOnly": "npm run lint && npm run tsc:validate && npm run test" }, @@ -48,24 +52,16 @@ }, "homepage": "https://github.com/berniegp/mock-xmlhttprequest#readme", "devDependencies": { - "@rollup/plugin-typescript": "^11.1.5", - "@types/chai": "^4.3.1", - "@types/mocha": "^10.0.0", - "@types/node": "^20.9.2", - "@typescript-eslint/eslint-plugin": "^6.12.0", - "@typescript-eslint/parser": "^6.12.0", - "chai": "^4.3.4", - "cross-env": "^7.0.3", - "eslint": "^8.18.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-plugin-import": "^2.22.1", - "mocha": "^10.0.0", - "rimraf": "^5.0.5", + "@arethetypeswrong/cli": "^0.16.4", + "@rollup/plugin-typescript": "^12.1.0", + "@stylistic/eslint-plugin": "^2.9.0", + "@types/node": "^22.0.0", + "eslint": "^9.0.0", + "rimraf": "^6.0.1", "rollup": "^4.5.0", - "ts-node": "^10.8.2", "tslib": "^2.6.2", - "typescript": "~5.2.2" + "typescript": "^5.6.3", + "typescript-eslint": "^8.8.1" }, "engines": { "node": ">=16.0.0" diff --git a/src/Factories.ts b/src/Factories.ts index b721187..0918ba0 100644 --- a/src/Factories.ts +++ b/src/Factories.ts @@ -1,12 +1,12 @@ -import MockXhr from './MockXhr'; -import MockXhrServer from './MockXhrServer'; +import MockXhr from './MockXhr.ts'; +import MockXhrServer from './MockXhrServer.ts'; -import type { OnCreateCallback, OnSendCallback } from './MockXhr'; -import type { UrlMatcher, RequestHandler } from './MockXhrServer'; +import type { OnCreateCallback, OnSendCallback } from './MockXhr.ts'; +import type { UrlMatcher, RequestHandler } from './MockXhrServer.ts'; /** * Create a new "local" MockXhr subclass. Using a subclass of `MockXhr` in each test case makes it - * easier to ensure they are self-contained. For example if you set the onSend static propertiy on + * easier to ensure they are self-contained. For example if you set the onSend static property on * a subclass, this will only affect that subclass and not the others created in your other test * cases. You therefore don't need to add cleanup code to revert the changes made to the subclass. * diff --git a/src/HeadersContainer.ts b/src/HeadersContainer.ts index 31bf3c3..c615818 100644 --- a/src/HeadersContainer.ts +++ b/src/HeadersContainer.ts @@ -78,7 +78,6 @@ export default class HeadersContainer { */ addHeader(name: string, value: string) { name = name.toUpperCase(); - value = value ?? ''; const currentValue = this._headers.get(name); if (currentValue !== undefined) { value = `${currentValue}, ${value}`; diff --git a/src/MockXhr.ts b/src/MockXhr.ts index 95e4d5c..8f8e28c 100644 --- a/src/MockXhr.ts +++ b/src/MockXhr.ts @@ -1,19 +1,20 @@ -import HeadersContainer from './HeadersContainer'; -import MockXhrRequest from './MockXhrRequest'; -import RequestData from './RequestData'; -import XhrEvent from './XhrEvent'; -import XhrProgressEvent from './XhrProgressEvent'; -import * as Utils from './Utils'; -import XhrEventTarget from './XhrEventTarget'; +import HeadersContainer from './HeadersContainer.ts'; +import MockXhrRequest from './MockXhrRequest.ts'; +import RequestData from './RequestData.ts'; +import XhrEvent from './XhrEvent.ts'; +import XhrProgressEvent from './XhrProgressEvent.ts'; +import * as Utils from './Utils.ts'; +import XhrEventTarget from './XhrEventTarget.ts'; -import type { MockXhrResponseReceiver } from './MockXhrResponseReceiver'; -import type { TXhrProgressEventNames } from './XhrProgressEventsNames'; +import type { MockXhrResponseReceiver } from './MockXhrResponseReceiver.ts'; +import type { TXhrProgressEventNames } from './XhrProgressEventsNames.ts'; interface MockXhrResponse { isNetworkError?: boolean, status: number, statusMessage: string, headers: HeadersContainer, + // eslint-disable-next-line @typescript-eslint/no-explicit-any body?: any, } @@ -53,38 +54,26 @@ export default class MockXhr extends XhrEventTarget implements XMLHttpRequest, MockXhrResponseReceiver { private _authorRequestHeaders: HeadersContainer; - private _requestMethod?: string; - private _requestUrl?: string; private _readyState: number; - private _timeout: number; - private _crossOriginCredentials: boolean; - private _currentRequest?: MockXhrRequest; - private readonly _uploadObject: XhrEventTarget; responseURL: string; - private _responseType: XMLHttpRequestResponseType; - private _response: MockXhrResponse; private _sendFlag?: boolean; - private _uploadListenerFlag?: boolean; - private _uploadCompleteFlag?: boolean; private _timedOutFlag?: boolean; - private _timeoutReference: number; - - private _timeoutTask: any; + private _timeoutTask?: NodeJS.Timeout; constructor() { super(); @@ -110,30 +99,22 @@ export default class MockXhr //------- static readonly UNSENT = 0; - static readonly OPENED = 1; - static readonly HEADERS_RECEIVED = 2; - static readonly LOADING = 3; - static readonly DONE = 4; readonly UNSENT = MockXhr.UNSENT; - readonly OPENED = MockXhr.OPENED; - readonly HEADERS_RECEIVED = MockXhr.HEADERS_RECEIVED; - readonly LOADING = MockXhr.LOADING; - readonly DONE = MockXhr.DONE; get onreadystatechange() { - return this._getEventHandlerProperty('readystatechange') as ((this: XMLHttpRequest, ev: Event) => any); + return this._getEventHandlerProperty('readystatechange') as ((this: XMLHttpRequest, ev: Event) => unknown); } - set onreadystatechange(value: ((this: XMLHttpRequest, ev: Event) => any) | null) { + set onreadystatechange(value: ((this: XMLHttpRequest, ev: Event) => unknown) | null) { this._setEventHandlerProperty('readystatechange', value); } @@ -200,8 +181,8 @@ export default class MockXhr } const requestBodyLength = request.getRequestBodySize(); if (requestBodyTransmitted > requestBodyLength) { - throw new Error(`Mock usage error detected: upload progress "requestBodyTransmitted" (${requestBodyTransmitted}) ` - + `is greater than "requestBodyLength" (${requestBodyLength})`); + throw new Error('Mock usage error detected: upload progress "requestBodyTransmitted" ' + + `(${requestBodyTransmitted}) is greater than "requestBodyLength" (${requestBodyLength})`); } // Don't throttle events based on elapsed time because it would make tests much slower and @@ -260,10 +241,9 @@ export default class MockXhr downloadProgress(request: RequestData, receivedBytesLength: number, length: number) { // Only act if the originating request is the current active request if (this._currentRequest?.requestData === request) { - if (this._readyState !== MockXhr.HEADERS_RECEIVED - && this._readyState !== MockXhr.LOADING) { - throw new Error(`Mock usage error detected: readyState is ${this._readyState}, but it must be ` - + `HEADERS_RECEIVED (${MockXhr.HEADERS_RECEIVED}) or LOADING (${MockXhr.LOADING})`); + if (this._readyState !== MockXhr.HEADERS_RECEIVED && this._readyState !== MockXhr.LOADING) { + throw new Error(`Mock usage error detected: readyState is ${this._readyState}, but it must ` + + `be HEADERS_RECEIVED (${MockXhr.HEADERS_RECEIVED}) or LOADING (${MockXhr.LOADING})`); } if (this._readyState === MockXhr.HEADERS_RECEIVED) { @@ -283,17 +263,17 @@ export default class MockXhr * @param request Originating request * @param body Response body */ - setResponseBody(request: RequestData, body: any) { + setResponseBody(request: RequestData, body: unknown) { // Only act if the originating request is the current active request if (this._currentRequest?.requestData === request) { if (!this._sendFlag) { throw new Error('Mock usage error detected: call send() first (the "send() flag" is not set)'); } - if (this._readyState !== MockXhr.OPENED - && this._readyState !== MockXhr.HEADERS_RECEIVED - && this._readyState !== MockXhr.LOADING) { - throw new Error(`Mock usage error detected: readyState is ${this._readyState}, but it must be ` - + `OPENED (${MockXhr.OPENED}), HEADERS_RECEIVED (${MockXhr.HEADERS_RECEIVED}) or LOADING (${MockXhr.LOADING})`); + if (this._readyState !== MockXhr.OPENED && + this._readyState !== MockXhr.HEADERS_RECEIVED && + this._readyState !== MockXhr.LOADING) { + throw new Error(`Mock usage error detected: readyState is ${this._readyState}, but it must be ` + + `OPENED (${MockXhr.OPENED}), HEADERS_RECEIVED (${MockXhr.HEADERS_RECEIVED}) or LOADING (${MockXhr.LOADING})`); } if (this._readyState === MockXhr.OPENED) { @@ -462,8 +442,8 @@ export default class MockXhr * @see {@link https://xhr.spec.whatwg.org/#dom-xmlhttprequest-withcredentials} */ set withCredentials(value: boolean) { - if ((this._readyState !== MockXhr.UNSENT && this._readyState !== MockXhr.OPENED) - || this._sendFlag) { + if ((this._readyState !== MockXhr.UNSENT && this._readyState !== MockXhr.OPENED) || + this._sendFlag) { throwError('InvalidStateError'); } this._crossOriginCredentials = !!value; @@ -481,7 +461,7 @@ export default class MockXhr * @param body Request body * @see {@link https://xhr.spec.whatwg.org/#the-send()-method} */ - send(body: any = null) { + send(body: unknown = null) { if (this._readyState !== MockXhr.OPENED || this._sendFlag) { throwError('InvalidStateError'); } @@ -489,8 +469,8 @@ export default class MockXhr body = null; } - if (body !== null) { - let extractedContentType = null; + if (body) { + let extractedContentType: null | string = null; // Document body type not supported @@ -501,10 +481,15 @@ export default class MockXhr contentType = 'text/plain;charset=UTF-8'; } else if (typeof FormData !== 'undefined' && body instanceof FormData) { contentType = 'multipart/form-data; boundary=-----MochXhr1234'; - } else if (body.type) { - // As specified for Blob - contentType = body.type; + } else { + // As specified for Blob, but don't check with instanceof Blob to make mocks easier to do + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + const blobType = (body as any).type; + if (typeof blobType === 'string') { + contentType = blobType; + } } + extractedContentType = contentType; } @@ -522,8 +507,10 @@ export default class MockXhr const requestData = new RequestData( new HeadersContainer(this._authorRequestHeaders), - this._requestMethod as string, - this._requestUrl as string, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this._requestMethod!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this._requestUrl!, body, this._crossOriginCredentials ); @@ -532,7 +519,7 @@ export default class MockXhr this._uploadCompleteFlag = false; this._timedOutFlag = false; this._uploadCompleteFlag = req.body === null; - this._sendFlag = true; + this._sendFlag = true as boolean; this._fireProgressEvent('loadstart', 0, 0); if (!this._uploadCompleteFlag && this._uploadListenerFlag) { @@ -565,9 +552,9 @@ export default class MockXhr abort() { this._terminateFetchController(); - if ((this._readyState === MockXhr.OPENED && this._sendFlag) - || this._readyState === MockXhr.HEADERS_RECEIVED - || this._readyState === MockXhr.LOADING) { + if ((this._readyState === MockXhr.OPENED && this._sendFlag) || + this._readyState === MockXhr.HEADERS_RECEIVED || + this._readyState === MockXhr.LOADING) { this._requestErrorSteps('abort'); } @@ -676,13 +663,15 @@ export default class MockXhr return null; } try { - return JSON.parse(this._response.body); - } catch (e) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return JSON.parse(this._response.body as string); + } catch { return null; } } // Other responseTypes are sent as-is. They can be given directly by setResponseBody() anyway. + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._response.body; } @@ -715,6 +704,7 @@ export default class MockXhr // The response body is not converted to a document response. To get a document // response, pass it directly as the response body in setResponseBody(). + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._response.body ?? ''; } @@ -783,7 +773,9 @@ export default class MockXhr if (this._response.isNetworkError) { return; } - const length = this._response.body?.length ?? 0; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const length = (this._response.body?.length ?? 0) as number; this._fireProgressEvent('progress', length, length); this._readyState = MockXhr.DONE; this._sendFlag = false; @@ -845,7 +837,8 @@ export default class MockXhr // The spec allows access to a text response while it's being received (i.e. LOADING state). // This library current offers no way to simulate this. - return this._response.body?.toString() ?? ''; + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + return (this._response.body?.toString() ?? '') as string; } //---------- @@ -855,8 +848,9 @@ export default class MockXhr protected _callOnSend(onSend?: OnSendCallback) { // Saves the callback and request data in case they change before then() executes if (onSend) { - const request = this._currentRequest as MockXhrRequest; - Promise.resolve().then(() => onSend.call(request, request, this)); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const request = this._currentRequest!; + void Promise.resolve().then(() => { onSend.call(request, request, this); }); } } @@ -893,7 +887,7 @@ export default class MockXhr const delay = Math.max(0, this._timeout - (Date.now() - this._timeoutReference)); this._timeoutTask = setTimeout(() => { if (this._sendFlag) { - this._currentRequest!.setRequestTimeout(); + this._currentRequest?.setRequestTimeout(); } delete this._timeoutTask; }, delay); diff --git a/src/MockXhrRequest.ts b/src/MockXhrRequest.ts index 78e2c0a..7882537 100644 --- a/src/MockXhrRequest.ts +++ b/src/MockXhrRequest.ts @@ -1,6 +1,6 @@ -import RequestData from './RequestData'; +import RequestData from './RequestData.ts'; -import type { MockXhrResponseReceiver } from './MockXhrResponseReceiver'; +import type { MockXhrResponseReceiver } from './MockXhrResponseReceiver.ts'; /** * A request produced by MockXhr.send() and methods to respond to it. @@ -10,10 +10,13 @@ import type { MockXhrResponseReceiver } from './MockXhrResponseReceiver'; * the last one is considered. Responses to previous MockXhrRequests are ignored. */ export default class MockXhrRequest { - constructor( - private readonly _requestData: RequestData, - private readonly _responseReceiver: MockXhrResponseReceiver - ) {} + private readonly _requestData: RequestData; + private readonly _responseReceiver: MockXhrResponseReceiver; + + constructor(requestData: RequestData, responseReceiver: MockXhrResponseReceiver) { + this._requestData = requestData; + this._responseReceiver = responseReceiver; + } get requestData() { return this._requestData; } @@ -26,6 +29,7 @@ export default class MockXhrRequest { get url() { return this._requestData.url; } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return get body() { return this._requestData.body; } get withCredentials() { return this._requestData.withCredentials; } @@ -61,7 +65,7 @@ export default class MockXhrRequest { respond( status?: number, headers?: Record | null, - body?: any, + body?: unknown, statusText?: string ) { this.setResponseHeaders(status, headers, statusText); @@ -98,7 +102,7 @@ export default class MockXhrRequest { * * @param body Response body (default null) */ - setResponseBody(body: any = null) { + setResponseBody(body: unknown = null) { this._responseReceiver.setResponseBody(this._requestData, body); } diff --git a/src/MockXhrResponseReceiver.ts b/src/MockXhrResponseReceiver.ts index 8fc11e1..257ffa1 100644 --- a/src/MockXhrResponseReceiver.ts +++ b/src/MockXhrResponseReceiver.ts @@ -1,4 +1,4 @@ -import type RequestData from './RequestData'; +import type RequestData from './RequestData.ts'; /** * Methods for responding to MockXhr requests @@ -15,7 +15,7 @@ export interface MockXhrResponseReceiver { downloadProgress(request: RequestData, receivedBytesLength: number, length: number): void; - setResponseBody(request: RequestData, body: any): void; + setResponseBody(request: RequestData, body: unknown): void; setNetworkError(request: RequestData): void; diff --git a/src/MockXhrServer.ts b/src/MockXhrServer.ts index 3e92f16..b7f5e61 100644 --- a/src/MockXhrServer.ts +++ b/src/MockXhrServer.ts @@ -1,7 +1,7 @@ -import type MockXhr from './MockXhr'; -import { getBodyByteSize, normalizeHTTPMethodName } from './Utils'; +import type MockXhr from './MockXhr.ts'; +import { getBodyByteSize, normalizeHTTPMethodName } from './Utils.ts'; -import type MockXhrRequest from './MockXhrRequest'; +import type MockXhrRequest from './MockXhrRequest.ts'; export type UrlMatcher = ((url: string) => boolean) | string | RegExp; @@ -9,7 +9,7 @@ export interface RequestHandlerResponse { status: number; statusText: string; headers: Record; - body: any; + body: unknown; } type RequestHandlerCallback = (request: MockXhrRequest) => void; @@ -28,11 +28,18 @@ interface Route { count: number, } -interface RequestLogEntry { +interface RequestLogEntryInternal { method: string; url: string; headers: Record; - body?: any + body?: unknown; +} + +// Isolate the any type to the external facing API +interface RequestLogEntry extends RequestLogEntryInternal { + // Changing the body type to unknown is a breaking change with little to no benefit to users + // eslint-disable-next-line @typescript-eslint/no-explicit-any + body?: any; } /** @@ -52,15 +59,15 @@ export default class MockXhrServer { private _requests: RequestLogEntry[]; - private _routes: Record; + private _routes: Record; private _xhrFactory: () => MockXhr; - private _savedContext?: any; + private _savedContext?: { XMLHttpRequest?: unknown }; private _savedContextHadXMLHttpRequest?: boolean; - private _savedXMLHttpRequest?: any; + private _savedXMLHttpRequest?: unknown; private _defaultRoute?: { handler: RequestHandler; count: number; }; @@ -110,7 +117,7 @@ export default class MockXhrServer { * @param context Context object (e.g. global, window) * @returns this */ - install(context: any = globalThis) { + install(context: { XMLHttpRequest?: unknown } = globalThis) { this._savedContext = context; // Distinguish between an undefined and a missing XMLHttpRequest property @@ -254,6 +261,7 @@ export default class MockXhrServer { method: request.method, url: request.url, headers: request.requestHeaders.getHash(), + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment body: request.body, }); @@ -297,7 +305,7 @@ export default class MockXhrServer { if (nextTransmitted < responseBodySize) { responseTransmitted = nextTransmitted; request.downloadProgress(responseTransmitted, responseBodySize); - Promise.resolve().then(() => responsePhase()); + void Promise.resolve().then(() => { responsePhase(); }); } else { // Final operation for this request request.setResponseBody(handler.body); @@ -319,7 +327,7 @@ export default class MockXhrServer { if (nextTransmitted < requestBodySize) { requestTransmitted = nextTransmitted; request.uploadProgress(requestTransmitted); - Promise.resolve().then(() => requestPhase()); + void Promise.resolve().then(() => { requestPhase(); }); } else { responsePhase(); } diff --git a/src/RequestData.ts b/src/RequestData.ts index f59592c..aa5cedc 100644 --- a/src/RequestData.ts +++ b/src/RequestData.ts @@ -1,17 +1,29 @@ -import HeadersContainer from './HeadersContainer'; -import { getBodyByteSize } from './Utils'; +import HeadersContainer from './HeadersContainer.ts'; +import { getBodyByteSize } from './Utils.ts'; /** * Request parameters from MockXhr.send() */ export default class RequestData { + private readonly _requestHeaders: HeadersContainer; + private readonly _method: string; + private readonly _url: string; + private readonly _body: unknown; + private readonly _credentialsMode: boolean; + constructor( - private readonly _requestHeaders: HeadersContainer, - private readonly _method: string, - private readonly _url: string, - private readonly _body: any = null, - private readonly _credentialsMode: boolean = false - ) {} + requestHeaders: HeadersContainer, + method: string, + url: string, + body: unknown = null, + credentialsMode = false + ) { + this._requestHeaders = requestHeaders; + this._method = method; + this._url = url; + this._body = body; + this._credentialsMode = credentialsMode; + } /** * @returns Request headers container @@ -22,7 +34,9 @@ export default class RequestData { get url() { return this._url; } - get body() { return this._body; } + // Changing the return type to unknown is a breaking change with little to no benefit to users + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any + get body() { return this._body as any; } get withCredentials() { return this._credentialsMode; } diff --git a/src/Utils.ts b/src/Utils.ts index cb74081..a66390f 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1,17 +1,23 @@ -export function getBodyByteSize(body?: string | FormData | Blob | BufferSource | null) { +export function getBodyByteSize(body?: unknown) { if (!body) { return 0; } if (typeof body === 'string') { return getStringByteLength(body); - } else if ((typeof FormData !== 'undefined' && body instanceof FormData) - || (body.constructor && body.constructor.name === 'FormData')) { + } else if ((typeof FormData !== 'undefined' && body instanceof FormData) || + (body.constructor.name === 'FormData')) { // A FormData has field-value pairs. This testing code only sums the individual sizes of the // values. The full multipart/form-data encoding also adds headers, encoding, etc. which we // don't reproduce here. return [...(body as FormData).values()].reduce((sum, value) => { - const valueSize = (value as File).size ?? getStringByteLength(String(value.toString())); + // We don't know if value really is a file, but we want to make mocks easier to achieve so we + // just check for a size property. + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const valueSize = (value as File).size ?? + // Value should already a string if there's no size property, but just in case it's not... + // eslint-disable-next-line @typescript-eslint/no-base-to-string + getStringByteLength(value.toString()); return sum + valueSize; }, 0); } @@ -52,9 +58,9 @@ export function isHeaderName(headerName?: string) { * @returns Whether headerValue is a valid header value */ export function isHeaderValue(headerValue: string) { - return typeof headerValue === 'string' - && headerValue.trim().length === headerValue.length - && headerValue.indexOf('\0') === -1; + return typeof headerValue === 'string' && + headerValue.trim().length === headerValue.length && + !headerValue.includes('\0'); } // Disallowed request headers for setRequestHeader() diff --git a/src/XhrEvent.ts b/src/XhrEvent.ts index cb24004..74a9462 100644 --- a/src/XhrEvent.ts +++ b/src/XhrEvent.ts @@ -2,8 +2,12 @@ * XMLHttpRequest Event */ export default class XhrEvent { + readonly type: string; + /** * @param type Event type */ - constructor(readonly type: string) {} + constructor(type: string) { + this.type = type; + } } diff --git a/src/XhrEventTarget.ts b/src/XhrEventTarget.ts index 0d43195..b7fce53 100644 --- a/src/XhrEventTarget.ts +++ b/src/XhrEventTarget.ts @@ -58,7 +58,8 @@ export default class XhrEventTarget implements XMLHttpRequestEventTarget { */ addEventListener( type: K, - listener: (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => any, + listener: + (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => unknown, options?: boolean | AddEventListenerOptions ): void; addEventListener( @@ -80,9 +81,9 @@ export default class XhrEventTarget implements XMLHttpRequestEventTarget { // append listener to eventTarget’s event listener list. // See https://dom.spec.whatwg.org/#add-an-event-listener if (listeners.every(({ isEventHandlerProperty, listener, useCapture }) => { - return isEventHandlerProperty - || listenerEntry.listener !== listener - || listenerEntry.useCapture !== useCapture; + return isEventHandlerProperty || + listenerEntry.listener !== listener || + listenerEntry.useCapture !== useCapture; })) { listeners.push(listenerEntry); this._listeners.set(type, listeners); @@ -100,7 +101,8 @@ export default class XhrEventTarget implements XMLHttpRequestEventTarget { */ removeEventListener( type: K, - listener: (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => any, + listener: + (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => unknown, options?: boolean | EventListenerOptions ): void; removeEventListener( @@ -118,9 +120,9 @@ export default class XhrEventTarget implements XMLHttpRequestEventTarget { if (listeners) { const listenerEntry = makeListenerEntry(listener, false, options); const index = listeners.findIndex(({ isEventHandlerProperty, listener, useCapture }) => { - return !isEventHandlerProperty - && listenerEntry.listener === listener - && listenerEntry.useCapture === useCapture; + return !isEventHandlerProperty && + listenerEntry.listener === listener && + listenerEntry.useCapture === useCapture; }); if (index >= 0) { listeners[index].removed = true; @@ -207,7 +209,7 @@ interface EventMock { type: string; } -type EventHandlerProperty = ((this: XMLHttpRequest, ev: ProgressEvent) => any); +type EventHandlerProperty = ((this: XMLHttpRequest, ev: ProgressEvent) => unknown); interface EventListenerEntry { listener: EventListenerOrEventListenerObject, diff --git a/src/XhrProgressEvent.ts b/src/XhrProgressEvent.ts index 197bba6..f2b50c9 100644 --- a/src/XhrProgressEvent.ts +++ b/src/XhrProgressEvent.ts @@ -1,6 +1,6 @@ -import XhrEvent from './XhrEvent'; +import XhrEvent from './XhrEvent.ts'; -import type { TXhrProgressEventNames } from './XhrProgressEventsNames'; +import type { TXhrProgressEventNames } from './XhrProgressEventsNames.ts'; /** * XMLHttpRequest ProgressEvent diff --git a/src/index.ts b/src/index.ts index 623a088..58e4f05 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ // Access to the request mock and server classes -export { newMockXhr, newServer } from './Factories'; +export { newMockXhr, newServer } from './Factories.ts'; // Factory methods -export { default as MockXhr } from './MockXhr'; -export { default as MockXhrServer } from './MockXhrServer'; +export { default as MockXhr } from './MockXhr.ts'; +export { default as MockXhrServer } from './MockXhrServer.ts'; diff --git a/test/.eslintrc.js b/test/.eslintrc.js deleted file mode 100644 index 30e5e7f..0000000 --- a/test/.eslintrc.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - rules: { - // Arrow functions can't access the Mocha context so we allow unnamed functions - 'func-names': 0, - - // Don't force spreading test classes over multiple files - 'max-classes-per-file': 0, - }, -}; diff --git a/test/FactoriesTest.ts b/test/Factories.test.ts similarity index 57% rename from test/FactoriesTest.ts rename to test/Factories.test.ts index 40b3c29..dead206 100644 --- a/test/FactoriesTest.ts +++ b/test/Factories.test.ts @@ -1,8 +1,11 @@ -import { assert } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; -import MockXhr from '../src/MockXhr'; -import MockXhrRequest from '../src/MockXhrRequest'; -import { newMockXhr, newServer } from '../src/Factories'; +import MockXhr from '../src/MockXhr.ts'; +import MockXhrRequest from '../src/MockXhrRequest.ts'; +import { newMockXhr, newServer } from '../src/Factories.ts'; + +interface Globals { XMLHttpRequest?: typeof XMLHttpRequest } describe('Factories', () => { describe('newMockXhr()', () => { @@ -33,12 +36,12 @@ describe('Factories', () => { const xhr = new LocalMockXhr2(); - assert.instanceOf(xhr, MockXhr); + assert.ok(xhr instanceof MockXhr); assert.strictEqual(onCreate1Count, 0, 'onCreate() from first mock not called'); assert.strictEqual(onCreate2Count, 1, 'onCreate() from second mock called'); }); - it('should isolate MockXMLHttpRequest.onSend()', () => { + it('should isolate MockXMLHttpRequest.onSend()', async () => { let onSend1Count = 0; const LocalMockXhr1 = newMockXhr(); LocalMockXhr1.onSend = () => { onSend1Count += 1; }; @@ -51,21 +54,25 @@ describe('Factories', () => { xhr.open('GET', '/url'); xhr.send(); - return Promise.resolve().then(() => { - assert.strictEqual(onSend1Count, 0, 'onSend() from first mock not called'); - assert.strictEqual(onSend2Count, 1, 'onSend() from second mock called'); - }); + await Promise.resolve(); + assert.strictEqual(onSend1Count, 0, 'onSend() from first mock not called'); + assert.strictEqual(onSend2Count, 1, 'onSend() from second mock called'); }); - it('should isolate MockXMLHttpRequest.timeoutEnabled', (done) => { + it('should isolate MockXMLHttpRequest.timeoutEnabled', (context) => { try { + context.mock.timers.enable(); MockXhr.timeoutEnabled = false; const LocalMockXhr = newMockXhr(); const xhr = new LocalMockXhr(); + let timedOut = false; + xhr.addEventListener('timeout', () => { timedOut = true; }); xhr.open('GET', '/url'); xhr.send(); xhr.timeout = 1; - xhr.addEventListener('timeout', () => { done(); }); + + context.mock.timers.tick(1); + assert.strictEqual(timedOut, true); } finally { MockXhr.timeoutEnabled = true; } @@ -91,7 +98,7 @@ describe('Factories', () => { const xhr = new LocalMockXhr(); - assert.instanceOf(xhr, MockXhr); + assert.ok(xhr instanceof MockXhr); assert.deepEqual(calls, ['global', 'subclass'], 'hooks called in the right order'); assert.deepEqual(args, [xhr, xhr], 'correct parameters for callbacks'); } finally { @@ -99,7 +106,7 @@ describe('Factories', () => { } }); - it('onSend()', () => { + it('onSend()', async () => { try { const LocalMockXhr = newMockXhr(); const xhr = new LocalMockXhr(); @@ -107,51 +114,50 @@ describe('Factories', () => { const thisValues: MockXhrRequest[] = []; const args: MockXhrRequest[] = []; - const done = new Promise((resolve) => { - MockXhr.onSend = function onSend(arg) { - calls.push('global'); - thisValues.push(this); - args.push(arg); - }; - - LocalMockXhr.onSend = function onSendLocal(arg) { - calls.push('subclass'); - thisValues.push(this); - args.push(arg); - }; - - xhr.onSend = function onSendXhr(arg) { - calls.push('xhr'); - thisValues.push(this); - args.push(arg); - resolve(true); - }; - }); + MockXhr.onSend = function onSend(arg) { + calls.push('global'); + thisValues.push(this); + args.push(arg); + }; + + LocalMockXhr.onSend = function onSendLocal(arg) { + calls.push('subclass'); + thisValues.push(this); + args.push(arg); + }; + + xhr.onSend = function onSendXhr(arg) { + calls.push('xhr'); + thisValues.push(this); + args.push(arg); + }; xhr.open('GET', '/url'); xhr.send(); - return done.then(() => { - const req = xhr.currentRequest; - assert.instanceOf(req, MockXhrRequest); - assert.deepEqual(calls, ['global', 'subclass', 'xhr'], 'hooks called in the right order'); - assert.deepEqual(thisValues, [req, req, req], 'correct contexts for callbacks'); - assert.deepEqual(args, [req, req, req], 'correct parameters for callbacks'); - }); + await Promise.resolve(); + const req = xhr.currentRequest; + assert.ok(req instanceof MockXhrRequest); + assert.deepEqual(calls, ['global', 'subclass', 'xhr'], 'hooks called in the right order'); + assert.deepEqual(thisValues, [req, req, req], 'correct contexts for callbacks'); + assert.deepEqual(args, [req, req, req], 'correct parameters for callbacks'); } finally { delete MockXhr.onSend; } }); }); - it('should work with the low-level quick start code', () => { - const global: any = {}; - function functionToTest(): Promise { + it('should work with the low-level quick start code', async () => { + const global: Globals = {}; + function functionToTest(): Promise<{ message: string }> { return new Promise((resolve, reject) => { - const xhr = new global.XMLHttpRequest() as MockXhr; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const xhr = new global.XMLHttpRequest!() as MockXhr; xhr.open('GET', '/my/url'); - xhr.onload = () => resolve(JSON.parse(xhr.response)); - xhr.onerror = () => reject(xhr.statusText); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + xhr.onload = () => { resolve(JSON.parse(xhr.response)); }; + // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors + xhr.onerror = () => { reject(xhr.statusText); }; xhr.send(); }); } @@ -167,14 +173,13 @@ describe('Factories', () => { }; try { - // Install in the global context so "new XMLHttpRequest()" uses the XMLHttpRequest mock + // Install in the global context so "new XMLHttpRequest()" creates MockXhr instances global.XMLHttpRequest = MockXhr; // Do something that send()s an XMLHttpRequest to '/my/url' and returns a Promise - return functionToTest().then((result) => { - // This assumes the returned Promise resolves to the parsed JSON response - assert.equal(result.message, 'Success!'); - }); + // that resolves to the parsed JSON response + const result = await functionToTest(); + assert.equal(result.message, 'Success!'); } finally { // Restore the original XMLHttpRequest delete global.XMLHttpRequest; @@ -183,31 +188,32 @@ describe('Factories', () => { }); describe('newServer()', () => { - it('should isolate MockXMLHttpRequest.timeoutEnabled', (done) => { + it('should isolate MockXMLHttpRequest.timeoutEnabled', (context) => { + context.mock.timers.enable(); const server = newServer(); const xhr = server.xhrFactory(); - let gotTimeoutEvent = false; server.disableTimeout(); - xhr.addEventListener('timeout', () => { gotTimeoutEvent = true; }); + let timedOut = false; + xhr.addEventListener('timeout', () => { timedOut = true; }); xhr.open('GET', '/url'); xhr.send(); xhr.timeout = 1; // Wait to make sure the timeout has no effect - setTimeout(() => { - assert.isFalse(gotTimeoutEvent, 'there should be no timeout event'); - done(); - }, 40); + context.mock.timers.tick(20); + assert.strictEqual(timedOut, false, 'there should be no timeout event'); }); - it('should work with the quick start code', () => { - function functionToTest(): Promise { + it('should work with the quick start code', async () => { + function functionToTest(): Promise<{ message: string }> { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', '/my/url'); - xhr.onload = () => resolve(JSON.parse(xhr.response)); - xhr.onerror = () => reject(xhr.statusText); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + xhr.onload = () => { resolve(JSON.parse(xhr.response)); }; + // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors + xhr.onerror = () => { reject(xhr.statusText); }; xhr.send(); }); } @@ -226,10 +232,9 @@ describe('Factories', () => { server.install(/* optional context; defaults to globalThis */); // Do something that send()s an XMLHttpRequest to '/my/url' and returns a Promise - return functionToTest().then((result) => { - // This assumes the returned Promise resolves to the parsed JSON response - assert.equal(result.message, 'Success!'); - }); + // that resolves to the parsed JSON response + const result = await functionToTest(); + assert.equal(result.message, 'Success!'); } finally { // Restore the original XMLHttpRequest server.remove(); diff --git a/test/HeadersContainerTest.ts b/test/HeadersContainer.test.ts similarity index 95% rename from test/HeadersContainerTest.ts rename to test/HeadersContainer.test.ts index 4db8446..2340c49 100644 --- a/test/HeadersContainerTest.ts +++ b/test/HeadersContainer.test.ts @@ -1,6 +1,7 @@ -import { assert } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; -import HeadersContainer from '../src/HeadersContainer'; +import HeadersContainer from '../src/HeadersContainer.ts'; describe('HeadersContainer', () => { describe('constructor', () => { @@ -90,7 +91,7 @@ describe('HeadersContainer', () => { headers.addHeader('HEADER', '1'); headers.addHeader('header-2', 'a'); assert.deepEqual(headers.getHash(), { - header: '1', + 'header': '1', 'header-2': 'a', }); }); diff --git a/test/MockXhr.test.ts b/test/MockXhr.test.ts new file mode 100644 index 0000000..faba699 --- /dev/null +++ b/test/MockXhr.test.ts @@ -0,0 +1,1585 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; + +import { recordEvents } from './TestUtils.ts'; +import HeadersContainer from '../src/HeadersContainer.ts'; +import MockXhr from '../src/MockXhr.ts'; +import MockXhrRequest from '../src/MockXhrRequest.ts'; +import RequestData from '../src/RequestData.ts'; +import { upperCaseMethods } from '../src/Utils.ts'; +import XhrEventTarget from '../src/XhrEventTarget.ts'; + +// eslint-disable-next-line @typescript-eslint/no-extraneous-class +class FormDataMock {} + +describe('MockXhr', () => { + // Asserts that the response is a network error + function assertNetworkErrorResponse(xhr: MockXhr) { + assert.strictEqual(xhr.getAllResponseHeaders(), '', 'Response headers'); + assert.strictEqual(xhr.status, 0, 'xhr.status == 0'); + assert.strictEqual(xhr.statusText, '', 'empty xhr.statusText'); + assert.strictEqual(xhr.response, '', 'empty xhr.response'); + assert.strictEqual(xhr.responseText, '', 'empty xhr.responseText'); + } + + function assertSameRequest(req1: RequestData, req2: RequestData) { + assert.strictEqual(req1.requestHeaders.getAll(), req2.requestHeaders.getAll(), 'headers'); + assert.strictEqual(req1.method, req2.method, 'method'); + assert.strictEqual(req1.url, req2.url, 'url'); + assert.deepEqual(req1.body, req2.body, 'body'); + assert.strictEqual(req1.withCredentials, req2.withCredentials, 'withCredentials'); + } + + describe('states', () => { + it('should have state constants', () => { + assert.strictEqual(MockXhr.UNSENT, 0); + assert.strictEqual(MockXhr.OPENED, 1); + assert.strictEqual(MockXhr.HEADERS_RECEIVED, 2); + assert.strictEqual(MockXhr.LOADING, 3); + assert.strictEqual(MockXhr.DONE, 4); + }); + + it('should have a readyState attribute', () => { + const xhr = new MockXhr(); + assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'initial value'); + }); + + it('should not call readyState listeners added in dispatchEvent() listeners', () => { + const xhr = new MockXhr(); + + xhr.onreadystatechange = () => { + xhr.addEventListener('readystatechange', () => { + assert.fail('listener added in callback should not be called'); + }); + }; + + xhr.open('GET', '/url'); + }); + + it('should not call readyState listeners removed in dispatchEvent() listeners', () => { + const xhr = new MockXhr(); + + function callback1() { + xhr.removeEventListener('readystatechange', callback2); + xhr.removeEventListener('readystatechange', callback3); + xhr.onreadystatechange = null; + } + function callback2() { + assert.fail('listener added in callback should not be called'); + } + function callback3() { + assert.fail('listener added in callback should not be called'); + } + function callback4() { + assert.fail('listener added in callback should not be called'); + } + xhr.addEventListener('readystatechange', callback1); + xhr.addEventListener('readystatechange', callback2); + xhr.addEventListener('readystatechange', callback3, { once: true }); + xhr.onreadystatechange = callback4; + + xhr.open('GET', '/url'); + }); + }); + + describe('request', () => { + describe('open()', () => { + it('should change state', () => { + const xhr = new MockXhr(); + const events = recordEvents(xhr); + + xhr.open('GET', '/url'); + + assert.deepEqual(events, ['readystatechange(1)'], 'readystatechange fired'); + }); + + it('should be re-entrant', async () => { + const xhr = new MockXhr(); + const events = recordEvents(xhr); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.open('POST', '/url2'); + xhr.send(); + + const request = await onSend; + assert.strictEqual(request.method, 'POST', 'second method'); + assert.strictEqual(request.url, '/url2', 'second url'); + assert.strictEqual(xhr.readyState, MockXhr.OPENED); + assert.deepEqual(events, [ + 'readystatechange(1)', + 'loadstart(0,0,false)', + ], 'readystatechange fired'); + }); + + it('should reject async = false', () => { + const xhr = new MockXhr(); + const events = recordEvents(xhr); + assert.throws(() => { xhr.open('GET', '/url', false); }, 'sync false throws'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any + assert.throws(() => { xhr.open('GET', '/url', null as any); }, 'sync null throws'); + assert.throws(() => { xhr.open('GET', '/url', undefined); }, 'sync undefined throws'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any + assert.throws(() => { xhr.open('GET', '/url', '' as any); }, 'sync empty string throws'); + assert.strictEqual(events.length, 0, 'no events fired'); + }); + + it('should reject non-methods', () => { + const xhr = new MockXhr(); + const tryMethod = (method: string) => { + return () => { xhr.open(method, '/url'); }; + }; + const events = recordEvents(xhr); + assert.throws(tryMethod('\\'), 'non-method throws'); + assert.throws(tryMethod(';'), 'non-method throws'); + assert.strictEqual(events.length, 0, 'no events fired'); + }); + + it('should reject forbidden methods', () => { + const xhr = new MockXhr(); + const tryMethod = (method: string) => { + return () => { xhr.open(method, '/url'); }; + }; + const events = recordEvents(xhr); + assert.throws(tryMethod('CONNECT'), 'forbidden method throws'); + assert.throws(tryMethod('TRACE'), 'forbidden method throws'); + assert.throws(tryMethod('TRACK'), 'forbidden method throws'); + assert.strictEqual(events.length, 0, 'no events fired'); + }); + + it('should normalize method names', async () => { + const onSends = upperCaseMethods.map((method) => { + const xhr = new MockXhr(); + xhr.open(method.toLowerCase(), 'url'); + const promise = new Promise((resolve) => { + xhr.onSend = resolve; + }); + xhr.send(); + return promise; + }); + + const requests = await Promise.all(onSends); + requests.forEach((request, i) => { + assert.strictEqual(request.method, upperCaseMethods[i]); + }); + }); + }); + + describe('setRequestHeader()', () => { + it('should record header value', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.setRequestHeader('Head', '1'); + xhr.send(); + + const request = await onSend; + assert.strictEqual(request.requestHeaders.getHeader('HEAD'), '1', 'header is case insensitive'); + }); + + it('should record empty header value', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.setRequestHeader('empty-value', ''); + xhr.setRequestHeader('2-empty-values', ''); + xhr.setRequestHeader('2-empty-values', ''); + xhr.setRequestHeader('empty-mid-value', 'a'); + xhr.setRequestHeader('empty-mid-value', ''); + xhr.setRequestHeader('empty-mid-value', 'b'); + xhr.send(); + + const request = await onSend; + assert.strictEqual(request.requestHeaders.getHeader('empty-value'), ''); + assert.strictEqual(request.requestHeaders.getHeader('2-empty-values'), ', '); + assert.strictEqual(request.requestHeaders.getHeader('empty-mid-value'), 'a, , b'); + }); + + it('should throw InvalidStateError if not opened', () => { + assert.throws(() => { + new MockXhr().setRequestHeader('Head', '1'); + }); + }); + + const forbiddenHeaders = [ + 'Accept-Charset', + 'Accept-Encoding', + 'Access-Control-Request-Headers', + 'Access-Control-Request-Method', + 'Connection', + 'Content-Length', + 'Cookie', + 'Cookie2', + 'Date', + 'DNT', + 'Expect', + 'Host', + 'Keep-Alive', + 'Origin', + 'Referer', + 'TE', + 'Trailer', + 'Transfer-Encoding', + 'Upgrade', + 'Via', + ]; + forbiddenHeaders.forEach((header) => { + it(`should reject forbidden header ${header}`, async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.setRequestHeader(header, '1'); + xhr.send(); + + const request = await onSend; + assert.strictEqual( + request.requestHeaders.getHeader(header), + null, + 'Forbidden header not set' + ); + }); + }); + }); + + describe('timeout attribute', function () { + it('can get and set its value', () => { + const xhr = new MockXhr(); + const timeout = 10; + assert.strictEqual(xhr.timeout, 0, 'initial value is 0'); + xhr.timeout = timeout; + assert.strictEqual(xhr.timeout, timeout); + }); + + it('will trigger a timeout if set before send()', (context) => { + context.mock.timers.enable(); + const xhr = new MockXhr(); + + let timedOut = false; + xhr.addEventListener('timeout', () => { timedOut = true; }); + xhr.open('GET', '/url'); + const events = recordEvents(xhr); + xhr.timeout = 1; + xhr.send(); + + context.mock.timers.tick(1); + + assert.strictEqual(timedOut, true); + assert.deepEqual(events, [ + 'loadstart(0,0,false)', + 'readystatechange(4)', + 'timeout(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + }); + + it('will trigger a timeout if set after send()', (context) => { + context.mock.timers.enable(); + const xhr = new MockXhr(); + + xhr.open('GET', '/url'); + xhr.send(); + xhr.timeout = 1; + let timedOut = false; + xhr.addEventListener('timeout', () => { timedOut = true; }); + + context.mock.timers.tick(1); + + assert.strictEqual(timedOut, true); + }); + + it('measures timeout delay relative to send()', (context) => { + context.mock.timers.enable(); + const xhr = new MockXhr(); + + xhr.open('GET', '/url'); + xhr.send(); + + // Advance the clock relative to send() + context.mock.timers.tick(20); + + let timedOut = false; + xhr.ontimeout = () => { timedOut = true; }; + xhr.timeout = 50; + assert.strictEqual(timedOut, false); + + // Still not timed out. 20 + 29 = 49 < 50 + context.mock.timers.tick(29); + assert.strictEqual(timedOut, false); + + // 50ms since send() + context.mock.timers.tick(1); + assert.strictEqual(timedOut, true); + }); + + it('measures timeout delay relative to send() and clamps to 0', (context) => { + context.mock.timers.enable(); + const xhr = new MockXhr(); + + xhr.open('GET', '/url'); + xhr.send(); + + // Advance the clock relative to send() + context.mock.timers.tick(10); + + let timedOut = false; + xhr.ontimeout = () => { timedOut = true; }; + xhr.timeout = 10; + context.mock.timers.tick(0); + assert.strictEqual(timedOut, true); + }); + + it('has no effect when the response is sent fast enough', async (context) => { + context.mock.timers.enable(); + const xhr = new MockXhr(); + + const onSend = new Promise((resolve) => { + xhr.onSend = (request) => { + request.respond(); + resolve(request); + }; + }); + let timedOut = false; + xhr.addEventListener('timeout', () => { timedOut = true; }); + xhr.open('GET', '/url'); + xhr.send(); + xhr.timeout = 1; + + await onSend; + + // Move past the timeout + context.mock.timers.tick(20); + assert.strictEqual(timedOut, false, 'there should be no timeout event'); + }); + + it('can be cancelled', (context) => { + context.mock.timers.enable(); + const xhr = new MockXhr(); + + let timedOut = false; + xhr.addEventListener('timeout', () => { timedOut = true; }); + xhr.open('GET', '/url'); + xhr.send(); + xhr.timeout = 1; + + context.mock.timers.tick(0); + xhr.timeout = 0; + + // Wait to make sure the timeout has no effect + context.mock.timers.tick(20); + assert.strictEqual(timedOut, false, 'there should be no timeout event'); + }); + + it('is cancelled by open()', (context) => { + context.mock.timers.enable(); + const xhr = new MockXhr(); + + let timedOut = false; + xhr.addEventListener('timeout', () => { timedOut = true; }); + xhr.open('GET', '/url'); + xhr.send(); + xhr.timeout = 1; + xhr.open('GET', '/url'); + + // Wait to make sure the timeout has no effect + context.mock.timers.tick(20); + assert.strictEqual(timedOut, false, 'there should be no timeout event'); + }); + + it('can be disabled per instance', (context) => { + context.mock.timers.enable(); + const xhr = new MockXhr(); + + xhr.timeoutEnabled = false; + let timedOut = false; + xhr.addEventListener('timeout', () => { timedOut = true; }); + xhr.open('GET', '/url'); + xhr.send(); + xhr.timeout = 1; + + // Wait to make sure the timeout has no effect + context.mock.timers.tick(20); + assert.strictEqual(timedOut, false, 'there should be no timeout event'); + }); + + it('can be disabled on subclass', (context) => { + try { + context.mock.timers.enable(); + class LocalMockXhr extends MockXhr {} + const xhr = new LocalMockXhr(); + + LocalMockXhr.timeoutEnabled = false; + let timedOut = false; + xhr.addEventListener('timeout', () => { timedOut = true; }); + xhr.open('GET', '/url'); + xhr.send(); + xhr.timeout = 1; + + // Wait to make sure the timeout has no effect + context.mock.timers.tick(20); + assert.strictEqual(timedOut, false, 'there should be no timeout event'); + } finally { + MockXhr.timeoutEnabled = true; + } + }); + + it('can be disabled globally', (context) => { + try { + context.mock.timers.enable(); + const xhr = new MockXhr(); + + MockXhr.timeoutEnabled = false; + let timedOut = false; + xhr.addEventListener('timeout', () => { timedOut = true; }); + xhr.open('GET', '/url'); + xhr.send(); + xhr.timeout = 1; + + // Wait to make sure the timeout has no effect + context.mock.timers.tick(20); + assert.strictEqual(timedOut, false, 'there should be no timeout event'); + } finally { + MockXhr.timeoutEnabled = true; + } + }); + }); + + describe('withCredentials attribute', () => { + it('should initially return false', () => { + const xhr = new MockXhr(); + assert.strictEqual(xhr.withCredentials, false, 'initial value'); + }); + + it('should throw if set when state is not unsent or opened or if the send() flag is set', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + assert.throws(() => { xhr.withCredentials = true; }); + request.respond(); + assert.throws(() => { xhr.withCredentials = true; }); + }); + + it('can get and set its value', () => { + const xhr = new MockXhr(); + xhr.withCredentials = true; + assert.strictEqual(xhr.withCredentials, true, 'value set'); + }); + }); + + it('should have an upload attribute', () => { + const xhr = new MockXhr(); + assert.ok(xhr.upload instanceof XhrEventTarget, 'initial value'); + }); + + describe('send()', () => { + it('should capture RequestData', async () => { + const xhr = new MockXhr(); + const requestData = new RequestData( + new HeadersContainer().addHeader('test', 'ok'), + 'POST', + '/url', + { body: 'body' }, + true + ); + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open(requestData.method, requestData.url); + xhr.setRequestHeader('test', 'ok'); + xhr.withCredentials = requestData.withCredentials; + xhr.send(requestData.body); + + const request = await onSend; + assertSameRequest(request.requestData, requestData); + }); + + it('should set Content-Type for string body', async () => { + const xhr = new MockXhr(); + const body = 'body'; + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('POST', '/url'); + xhr.send(body); + + const request = await onSend; + assert.strictEqual( + request.requestHeaders.getHeader('Content-Type'), + 'text/plain;charset=UTF-8', + 'Content-Type set' + ); + }); + + it('should handle FormData body', async () => { + // The FormData code path of send() requires FormData in the global context. + const savedFormData = globalThis.FormData; + globalThis.FormData = FormDataMock as unknown as typeof globalThis.FormData; + try { + const xhr = new MockXhr(); + const body = new FormDataMock(); + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('POST', '/url'); + xhr.send(body); + + const request = await onSend; + assert.strictEqual( + request.requestHeaders.getHeader('Content-Type'), + 'multipart/form-data; boundary=-----MochXhr1234', + 'Content-Type set' + ); + } finally { + globalThis.FormData = savedFormData; + } + }); + + it('should use body mime type in request header', async () => { + const xhr = new MockXhr(); + const body = { type: 'image/jpeg' }; + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('POST', '/url'); + xhr.send(body); + + const request = await onSend; + assert.strictEqual( + request.requestHeaders.getHeader('Content-Type'), + body.type, + 'Content-Type set' + ); + }); + + it('should not set Content-Type for null body', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + assert.strictEqual(request.body, null, 'Recorded null body'); + assert.strictEqual( + request.requestHeaders.getHeader('Content-Type'), + null, + 'Content-Type not set' + ); + }); + + it('should fire loadstart events', () => { + const xhr = new MockXhr(); + xhr.open('POST', '/url'); + const events = recordEvents(xhr); + xhr.send('body'); + + assert.deepEqual(events, ['loadstart(0,0,false)', 'upload.loadstart(0,4,true)'], 'fired events'); + }); + + it('should handle re-open() during loadstart event handler', async () => { + try { + const xhr = new MockXhr(); + + // Add onSend callbacks + let onSendCount = 0; + MockXhr.onSend = () => { onSendCount += 1; }; + let onSendXhrCount = 0; + xhr.onSend = () => { onSendXhrCount += 1; }; + + // re-open() during the loadstart event handler aborts send() + xhr.open('GET', '/url'); + xhr.addEventListener('loadstart', () => { + // Open a new request + xhr.open('GET', '/url'); + }); + xhr.send(); + + await Promise.resolve(); + assert.strictEqual(xhr.readyState, MockXhr.OPENED, 'final state OPENED'); + assert.strictEqual(onSendCount, 0, 'onSend() should not be called'); + assert.strictEqual(onSendXhrCount, 0, 'onSend() should not be called'); + } finally { + delete MockXhr.onSend; + } + }); + }); + + describe('abort()', () => { + it('should follow the steps for open()-abort() sequence', () => { + const xhr = new MockXhr(); + xhr.open('GET', '/url'); + const events = recordEvents(xhr); + xhr.abort(); + + assert.strictEqual(events.length, 0, 'no abort event'); + assert.strictEqual(xhr.readyState, MockXhr.OPENED, 'final state OPENED'); + }); + + it('should follow the steps for open()-send()-abort() sequence', () => { + const xhr = new MockXhr(); + xhr.open('GET', '/url'); + xhr.send(); + const events = recordEvents(xhr); + xhr.abort(); + + assert.deepEqual(events, [ + 'readystatechange(4)', + 'abort(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); + }); + + it('should follow the steps for open()-send()-HEADERS_RECEIVED-abort() sequence', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + request.setResponseHeaders(); + const events = recordEvents(xhr); + xhr.abort(); + assert.deepEqual(events, [ + 'readystatechange(4)', + 'abort(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); + }); + + it('should follow the steps for open()-send()-LOADING-abort() sequence', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + request.setResponseHeaders(); + request.downloadProgress(2, 8); + const events = recordEvents(xhr); + xhr.abort(); + assert.deepEqual(events, [ + 'readystatechange(4)', + 'abort(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); + }); + + it('should follow the steps for open()-send()-DONE-abort() sequence', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + request.respond(); + const events = recordEvents(xhr); + xhr.abort(); + assert.deepEqual(events, [], 'no fired events'); + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); + }); + + it('should fire upload abort for send(body)-abort() sequence', () => { + const xhr = new MockXhr(); + xhr.open('POST', '/url'); + const events = recordEvents(xhr); + xhr.send('body'); + xhr.abort(); + + assert.deepEqual(events, [ + 'loadstart(0,0,false)', + 'upload.loadstart(0,4,true)', + 'readystatechange(4)', + 'upload.abort(0,0,false)', + 'upload.loadend(0,0,false)', + 'abort(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + }); + + it('should handle abort() during loadstart event handler', async () => { + try { + const xhr = new MockXhr(); + + // Add onSend callbacks + let onSendCalled = false; + xhr.onSend = () => { onSendCalled = true; }; + let onSendXhrCalled = false; + MockXhr.onSend = () => { onSendXhrCalled = true; }; + + // Aborted send() during the loadstart event handler + xhr.open('GET', '/url'); + xhr.addEventListener('loadstart', () => { + // Abort the request + xhr.abort(); + }); + xhr.send(); + + await Promise.resolve(); + assert.ok(!onSendCalled, 'onSend() should not be called'); + assert.ok(!onSendXhrCalled, 'onSend() should not be called'); + assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); + } finally { + delete MockXhr.onSend; + } + }); + + it('should handle nested open() during abort()', () => { + const xhr = new MockXhr(); + const states: number[] = []; + let doAbort = false; + xhr.onreadystatechange = () => { + states.push(xhr.readyState); + if (doAbort) { + xhr.open('GET', '/url'); + } + }; + + xhr.open('GET', '/url'); + xhr.send(); + doAbort = true; + xhr.abort(); + + assert.deepEqual(states, [MockXhr.OPENED, MockXhr.DONE, MockXhr.OPENED]); + }); + + it('should handle nested open()-send() during abort()', () => { + const xhr = new MockXhr(); + const states: number[] = []; + let doAbort = false; + xhr.onreadystatechange = () => { + states.push(xhr.readyState); + if (doAbort) { + doAbort = false; + xhr.open('GET', '/url'); + xhr.send(); + } + }; + + xhr.open('GET', '/url'); + xhr.send(); + doAbort = true; + xhr.abort(); + + assert.deepEqual(states, [MockXhr.OPENED, MockXhr.DONE, MockXhr.OPENED]); + }); + }); + }); + + describe('response', () => { + const validResponseTypes = ['', 'arraybuffer', 'blob', 'document', 'json', 'text'] as const; + + it('should have a status attribute', () => { + const xhr = new MockXhr(); + assert.strictEqual(xhr.status, 0, 'initial value'); + }); + + it('should have a statusText attribute', () => { + const xhr = new MockXhr(); + assert.strictEqual(xhr.statusText, '', 'initial value'); + }); + + describe('overrideMimeType()', () => { + it('should throw if set when state is loading or done', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + request.setResponseHeaders(); + request.downloadProgress(0, 4); + assert.throws(() => { xhr.overrideMimeType('text/plain'); }); + request.setResponseBody('body'); + assert.throws(() => { xhr.overrideMimeType('text/plain'); }); + }); + }); + + describe('responseType attribute', () => { + it('should initially return the empty string', () => { + const xhr = new MockXhr(); + assert.strictEqual(xhr.responseType, '', 'initial value'); + }); + + it('should throw if set when state is loading or done', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + request.setResponseHeaders(); + request.downloadProgress(0, 4); + assert.throws(() => { xhr.responseType = 'text'; }); + request.setResponseBody('body'); + assert.throws(() => { xhr.responseType = 'text'; }); + }); + + validResponseTypes.forEach((value) => { + it(`should accept value '${value}'`, () => { + const xhr = new MockXhr(); + xhr.responseType = value; + assert.strictEqual(xhr.responseType, value, 'responseType was set'); + }); + }); + + it('should ignore invalid values', () => { + const xhr = new MockXhr(); + + // @ts-expect-error We specifically want to test an invalid type here + xhr.responseType = 'value'; + assert.strictEqual(xhr.responseType, '', 'responseType was not set'); + }); + }); + + describe('response attribute', () => { + it('should be initially empty', () => { + const xhr = new MockXhr(); + assert.strictEqual(xhr.response, '', 'initial value'); + }); + + it('should return the empty string before loading state with text responseType', () => { + const xhr = new MockXhr(); + xhr.open('GET', '/url'); + assert.strictEqual(xhr.response, '', 'empty string before loading state'); + }); + + it('should return the text response with text responseType', async () => { + const xhr = new MockXhr(); + const body = 'body'; + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + request.setResponseBody(body); + assert.strictEqual(xhr.response, 'body', 'text response'); + }); + + it('should return null if state is not done with non-text responseType', () => { + const xhr = new MockXhr(); + xhr.open('GET', '/url'); + xhr.responseType = 'json'; + xhr.send(); + assert.strictEqual(xhr.response, null, 'state is not done'); + }); + + validResponseTypes.forEach((value) => { + const data = value === '' || value === 'text' ? ['empty string', ''] : ['null', null]; + it(`should return ${data[0]} with null body and "${value}" responseType`, async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.responseType = value; + xhr.send(); + + const request = await onSend; + request.respond(); + assert.strictEqual(xhr.response, data[1], 'responseType was set'); + }); + }); + + it('should return the response body as-is with arraybuffer responseType', async () => { + const xhr = new MockXhr(); + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.responseType = 'arraybuffer'; + xhr.send(); + + const request = await onSend; + const body = { body: 'test' }; + request.setResponseBody(body); + assert.strictEqual(xhr.response, body, 'passthrough response'); + }); + + it('should return the response body as-is with blob responseType', async () => { + const xhr = new MockXhr(); + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.responseType = 'blob'; + xhr.send(); + + const request = await onSend; + const body = { body: 'test' }; + request.setResponseBody(body); + assert.strictEqual(xhr.response, body, 'passthrough response'); + }); + + it('should return the response body as-is with document responseType', async () => { + const xhr = new MockXhr(); + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.responseType = 'document'; + xhr.send(); + + const request = await onSend; + const body = { body: 'test' }; + request.setResponseBody(body); + assert.strictEqual(xhr.response, body, 'passthrough response'); + }); + + it('should return the json response with json responseType', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.responseType = 'json'; + xhr.send(); + + const request = await onSend; + request.setResponseBody('{"a": 1}'); + assert.deepEqual(xhr.response, { a: 1 }, 'json response'); + }); + + it('should return null for invalid json response with json responseType', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.responseType = 'json'; + xhr.send(); + + const request = await onSend; + request.setResponseBody('{'); + assert.strictEqual(xhr.response, null, 'null response'); + }); + }); + + describe('responseText attribute', () => { + it('should be initially empty', () => { + const xhr = new MockXhr(); + assert.strictEqual(xhr.responseText, '', 'initial value'); + }); + + it('should throw if accessed with non-text responseType', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.responseType = 'json'; + xhr.send(); + + const request = await onSend; + request.respond(); + assert.throws(() => { return xhr.responseText; }); + }); + + it('should return the empty string before loading', () => { + const xhr = new MockXhr(); + xhr.open('GET', '/url'); + assert.strictEqual(xhr.responseText, '', 'empty string before loading'); + }); + + it('should return the text response', async () => { + const xhr = new MockXhr(); + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + const body = 'body'; + request.setResponseBody(body); + assert.strictEqual(xhr.responseText, body, 'text response'); + }); + }); + + describe('responseXML attribute', () => { + it('should be initially null', () => { + const xhr = new MockXhr(); + assert.strictEqual(xhr.responseXML, null, 'initial value'); + }); + + it('should throw if accessed with non-document responseType', () => { + const xhr = new MockXhr(); + xhr.responseType = 'json'; + assert.throws(() => { return xhr.responseXML; }); + }); + + it('should return null if state is not done', () => { + const xhr = new MockXhr(); + xhr.open('GET', '/url'); + xhr.send(); + assert.strictEqual(xhr.responseXML, null, 'state is not done'); + }); + + it('should return the response body as-is with the document responseType', async () => { + const xhr = new MockXhr(); + xhr.responseType = 'document'; + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + const body = { body: 'test' }; + request.setResponseBody(body); + assert.strictEqual(xhr.responseXML, body, 'passthrough response'); + }); + }); + }); + + describe('Lifecycle hooks', () => { + describe('onCreate()', () => { + it('should be called', () => { + try { + const args: MockXhr[] = []; + + MockXhr.onCreate = (arg) => { + args.push(arg); + }; + + const xhr = new MockXhr(); + + assert.ok(xhr instanceof MockXhr); + assert.deepEqual(args, [xhr], 'correct parameters for callbacks'); + } finally { + delete MockXhr.onCreate; + } + }); + }); + + describe('onSend()', () => { + it('should be called in order', async () => { + try { + class LocalMockXhr extends MockXhr {} + const xhr = new LocalMockXhr(); + const calls: string[] = []; + const thisValues: unknown[] = []; + const argValues: unknown[] = []; + + const onSend = new Promise((resolve) => { + MockXhr.onSend = function onSend(...args) { + calls.push('global'); + thisValues.push(this); + argValues.push(args); + }; + + LocalMockXhr.onSend = function onSendLocal(...args) { + calls.push('subclass'); + thisValues.push(this); + argValues.push(args); + }; + + xhr.onSend = function onSendXhr(...args) { + calls.push('xhr'); + thisValues.push(this); + argValues.push(args); + resolve(true); + }; + }); + xhr.open('GET', '/url'); + xhr.send(); + + await onSend; + const req = xhr.currentRequest; + assert.ok(req instanceof MockXhrRequest); + assert.deepEqual(calls, ['global', 'subclass', 'xhr'], 'hooks called in the right order'); + assert.deepEqual(thisValues, [req, req, req], 'correct contexts for callbacks'); + assert.deepEqual(argValues, [[req, xhr], [req, xhr], [req, xhr]], 'correct parameters for callbacks'); + } finally { + delete MockXhr.onSend; + } + }); + + it('should call all callback stages even if they are the same function', async () => { + try { + class LocalMockXhr extends MockXhr {} + const xhr = new LocalMockXhr(); + let callCount = 0; + + const onSend = new Promise((resolve) => { + const onSend = () => { + if (++callCount === 3) { + resolve(true); + } + }; + MockXhr.onSend = onSend; + LocalMockXhr.onSend = onSend; + xhr.onSend = onSend; + }); + xhr.open('GET', '/url'); + xhr.send(); + + await onSend; + assert.strictEqual(callCount, 3); + } finally { + delete MockXhr.onSend; + } + }); + + it('should defensively copy the callback', async () => { + try { + class LocalMockXhr extends MockXhr {} + const xhr = new LocalMockXhr(); + const calls: string[] = []; + + const onSend = new Promise((resolve) => { + MockXhr.onSend = () => { calls.push('global'); }; + LocalMockXhr.onSend = () => { calls.push('subclass'); }; + xhr.onSend = () => { calls.push('xhr'); resolve(true); }; + }); + xhr.open('GET', '/url'); + xhr.send(); + delete MockXhr.onSend; + delete LocalMockXhr.onSend; + delete xhr.onSend; + + await onSend; + assert.deepEqual(calls, ['global', 'subclass', 'xhr'], 'hooks called in the right order'); + } finally { + delete MockXhr.onSend; + } + }); + + it('should be called for each send() and have versioned requests', async () => { + const xhr = new MockXhr(); + const requests: RequestData[] = []; + + let status = 200; + const onSend = new Promise((resolve) => { + xhr.onSend = (request) => { + requests.push(request.requestData); + request.respond(status++); + resolve(request); + }; + }); + xhr.open('GET', '/url1'); + xhr.setRequestHeader('header1', 'val1'); + xhr.send(); + xhr.open('POST', '/url2'); + xhr.setRequestHeader('header2', 'val2'); + xhr.send({ body: 1 }); + + await onSend; + assertSameRequest(requests[0], new RequestData( + new HeadersContainer().addHeader('header1', 'val1'), + 'GET', + '/url1' + )); + assertSameRequest(requests[1], new RequestData( + new HeadersContainer().addHeader('header2', 'val2'), + 'POST', + '/url2', + { body: 1 } + )); + assert.strictEqual(xhr.status, 201, 'received 2nd response'); + }); + }); + }); + + describe('MockXhrResponseReceiver interface', () => { + it('getRequestBodySize() should return the body size', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('POST', '/url'); + xhr.send('body'); + + const request = await onSend; + assert.strictEqual(request.getRequestBodySize(), 4); + }); + + describe('uploadProgress()', () => { + it('should fire upload progress events', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('POST', '/url'); + const events = recordEvents(xhr); // Add listeners BEFORE send() + xhr.send('body'); + + const request = await onSend; + request.uploadProgress(2); + request.uploadProgress(3); + assert.deepEqual(events, [ + 'loadstart(0,0,false)', + 'upload.loadstart(0,4,true)', + 'upload.progress(2,4,true)', + 'upload.progress(3,4,true)', + ], 'fired events'); + }); + + it('should not fire upload progress events if the upload listener flag is unset', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('POST', '/url'); + xhr.send('body'); + + // Add listeners AFTER send() + const events = recordEvents(xhr); + + const request = await onSend; + request.uploadProgress(2); + assert.deepEqual(events, [], 'no fired events'); + }); + }); + + describe('respond()', () => { + it('should set response headers and body', async () => { + const xhr = new MockXhr(); + const status = 201; + const headers = { test: 'ok' }; + const responseBody = 'response'; + const statusText = 'all good!'; + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + const events = recordEvents(xhr); + request.respond(status, headers, responseBody, statusText); + assert.deepEqual(xhr.getResponseHeadersHash(), headers, 'Response headers'); + assert.strictEqual(xhr.status, status, 'xhr.status'); + assert.strictEqual(xhr.statusText, statusText, 'xhr.statusText'); + assert.strictEqual(xhr.response, responseBody, 'xhr.response'); + assert.strictEqual(xhr.responseText, responseBody, 'xhr.responseText'); + assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); + assert.deepEqual(events, [ + // setResponseHeaders() + 'readystatechange(2)', + // setResponseBody() + 'readystatechange(3)', + 'progress(8,8,true)', + 'readystatechange(4)', + 'load(8,8,true)', + 'loadend(8,8,true)', + ], 'fired events'); + }); + }); + + describe('setResponseHeaders()', () => { + it('should set response state and headers', async () => { + const xhr = new MockXhr(); + const statusText = 'Custom Created'; + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + const events = recordEvents(xhr); + request.setResponseHeaders(201, { 'R-Header': '123' }, statusText); + assert.strictEqual(xhr.getAllResponseHeaders(), 'r-header: 123\r\n', 'Response headers'); + assert.strictEqual(xhr.status, 201, 'xhr.status'); + assert.strictEqual(xhr.statusText, statusText, 'xhr.statusText'); + assert.strictEqual(xhr.readyState, MockXhr.HEADERS_RECEIVED, 'readyState HEADERS_RECEIVED'); + assert.strictEqual(xhr.response, '', 'no response yet'); + assert.strictEqual(xhr.responseText, '', 'no response yet'); + assert.strictEqual(xhr.readyState, MockXhr.HEADERS_RECEIVED, 'readyState HEADERS_RECEIVED'); + assert.deepEqual(events, ['readystatechange(2)'], 'fired event'); + }); + + it('should fire upload progress events for non-empty body', async () => { + const xhr = new MockXhr(); + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('POST', '/url'); + + // Must add an upload listener before send() for upload progress events to fire + const events = recordEvents(xhr); + xhr.send('body'); + + const request = await onSend; + request.setResponseHeaders(); + assert.deepEqual(events, [ + // send() + 'loadstart(0,0,false)', + 'upload.loadstart(0,4,true)', + // setResponseHeaders() + 'upload.progress(4,4,true)', + 'upload.load(4,4,true)', + 'upload.loadend(4,4,true)', + 'readystatechange(2)', + ], 'fired events'); + }); + }); + + describe('downloadProgress()', () => { + it('should provide download progress events', async () => { + const xhr = new MockXhr(); + xhr.open('GET', '/url'); + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.send(); + + const request = await onSend; + request.setResponseHeaders(); + const events = recordEvents(xhr); + request.downloadProgress(2, 8); + request.downloadProgress(4, 8); + assert.strictEqual(xhr.readyState, MockXhr.LOADING, 'readyState LOADING'); + assert.deepEqual(events, [ + // downloadProgress() + 'readystatechange(3)', + 'progress(2,8,true)', + // downloadProgress() + 'readystatechange(3)', + 'progress(4,8,true)', + ], 'fired events'); + }); + }); + + describe('setResponseBody()', () => { + it('should set response body and default headers', async () => { + const xhr = new MockXhr(); + const responseBody = 'response'; + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + const events = recordEvents(xhr); + request.setResponseBody(responseBody); + assert.strictEqual(xhr.getAllResponseHeaders(), 'content-length: 8\r\n', 'Response headers'); + assert.strictEqual(xhr.status, 200, 'xhr.status'); + assert.strictEqual(xhr.statusText, 'OK', 'xhr.statusText'); + assert.strictEqual(xhr.response, responseBody, 'xhr.response'); + assert.strictEqual(xhr.responseText, responseBody, 'xhr.responseText'); + assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); + assert.deepEqual(events, [ + // automatic call to setResponseHeaders() + 'readystatechange(2)', + // respond() events - end of body + 'readystatechange(3)', + 'progress(8,8,true)', + 'readystatechange(4)', + 'load(8,8,true)', + 'loadend(8,8,true)', + ], 'fired events'); + }); + + it('should set response body if called after setResponseHeaders()', async () => { + const xhr = new MockXhr(); + const responseBody = 'response'; + + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + request.setResponseHeaders(); + const events = recordEvents(xhr); + request.setResponseBody(responseBody); + assert.strictEqual(xhr.response, responseBody, 'xhr.response'); + assert.strictEqual(xhr.responseText, responseBody, 'xhr.responseText'); + assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); + assert.deepEqual(events, [ + 'readystatechange(3)', + 'progress(8,8,true)', + 'readystatechange(4)', + 'load(8,8,true)', + 'loadend(8,8,true)', + ], 'fired events'); + }); + }); + + describe('setNetworkError()', () => { + it('should error the request', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + const events = recordEvents(xhr); + request.setNetworkError(); + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); + assert.deepEqual(events, [ + 'readystatechange(4)', + 'error(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + }); + + it('should error the request after setResponseHeaders()', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + request.setResponseHeaders(); + const events = recordEvents(xhr); + request.setNetworkError(); + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); + assert.deepEqual(events, [ + 'readystatechange(4)', + 'error(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + }); + + it('should error the request after downloadProgress()', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + request.setResponseHeaders(); + request.downloadProgress(2, 8); + const events = recordEvents(xhr); + request.setNetworkError(); + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); + assert.deepEqual(events, [ + 'readystatechange(4)', + 'error(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + }); + + it('should error the request and fire upload events for non-empty body', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('POST', '/url'); + const events = recordEvents(xhr); // Add listeners BEFORE send() + xhr.send('body'); + + const request = await onSend; + request.setNetworkError(); + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); + assert.deepEqual(events, [ + 'loadstart(0,0,false)', + 'upload.loadstart(0,4,true)', + 'readystatechange(4)', + 'upload.error(0,0,false)', + 'upload.loadend(0,0,false)', + 'error(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + }); + }); + + describe('setRequestTimeout()', () => { + it('should time out the request', async () => { + const xhr = new MockXhr(); + let events: string[] = []; + const onSend = new Promise((resolve) => { + xhr.onSend = (request) => { + events = recordEvents(xhr); + request.setRequestTimeout(); + resolve(request); + }; + }); + xhr.open('GET', '/url'); + xhr.send(); + xhr.timeoutEnabled = false; + xhr.timeout = 1; + + await onSend; + + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); + assert.deepEqual(events, [ + 'readystatechange(4)', + 'timeout(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + }); + + it('should throw if timeout === 0', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { xhr.onSend = resolve; }); + xhr.open('GET', '/url'); + xhr.send(); + + const request = await onSend; + assert.throws(() => { request.setRequestTimeout(); }); + }); + + it('should time out the request after setResponseHeaders()', async () => { + const xhr = new MockXhr(); + + let events: string[] = []; + const onSend = new Promise((resolve) => { + xhr.onSend = (request) => { + request.setResponseHeaders(); + events = recordEvents(xhr); + request.setRequestTimeout(); + resolve(request); + }; + }); + xhr.open('GET', '/url'); + xhr.send(); + xhr.timeoutEnabled = false; + xhr.timeout = 1; + + await onSend; + + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); + assert.deepEqual(events, [ + 'readystatechange(4)', + 'timeout(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + }); + + it('should time out the request after downloadProgress()', async () => { + const xhr = new MockXhr(); + + let events: string[] = []; + const onSend = new Promise((resolve) => { + xhr.onSend = (request) => { + request.setResponseHeaders(); + request.downloadProgress(2, 8); + events = recordEvents(xhr); + request.setRequestTimeout(); + resolve(request); + }; + }); + xhr.open('GET', '/url'); + xhr.send(); + xhr.timeoutEnabled = false; + xhr.timeout = 1; + + await onSend; + + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); + assert.deepEqual(events, [ + 'readystatechange(4)', + 'timeout(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + }); + + it('should time out the request and fire upload events for non-empty body', async () => { + const xhr = new MockXhr(); + const onSend = new Promise((resolve) => { + xhr.onSend = (request) => { + request.setRequestTimeout(); + resolve(request); + }; + }); + xhr.open('POST', '/url'); + const events = recordEvents(xhr); // Add listeners BEFORE send() + xhr.send('body'); + xhr.timeoutEnabled = false; + xhr.timeout = 1; + + await onSend; + + assertNetworkErrorResponse(xhr); + assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); + assert.deepEqual(events, [ + 'loadstart(0,0,false)', + 'upload.loadstart(0,4,true)', + 'readystatechange(4)', + 'upload.timeout(0,0,false)', + 'upload.loadend(0,0,false)', + 'timeout(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + }); + }); + }); +}); diff --git a/test/MockXhrServerTest.ts b/test/MockXhrServer.test.ts similarity index 54% rename from test/MockXhrServerTest.ts rename to test/MockXhrServer.test.ts index 60fe514..a3e696f 100644 --- a/test/MockXhrServerTest.ts +++ b/test/MockXhrServer.test.ts @@ -1,18 +1,19 @@ -import { assert } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; -import { recordEvents } from './TestUtils'; -import HeadersContainer from '../src/HeadersContainer'; -import MockXhr, { type OnSendCallback } from '../src/MockXhr'; -import MockXhrRequest from '../src/MockXhrRequest'; -import MockXhrServer from '../src/MockXhrServer'; -import RequestData from '../src/RequestData'; -import { getBodyByteSize, getStatusText, upperCaseMethods } from '../src/Utils'; +import { recordEvents } from './TestUtils.ts'; +import HeadersContainer from '../src/HeadersContainer.ts'; +import MockXhr, { type OnSendCallback } from '../src/MockXhr.ts'; +import MockXhrRequest from '../src/MockXhrRequest.ts'; +import MockXhrServer from '../src/MockXhrServer.ts'; +import RequestData from '../src/RequestData.ts'; +import { getBodyByteSize, getStatusText, upperCaseMethods } from '../src/Utils.ts'; import type { RequestHandlerResponse } from '../src/MockXhrServer'; describe('MockXhrServer', () => { function makeTestHarness() { - const onSendPromises: Promise[] = []; + const loadendPromises: Promise[] = []; class MockXhrClass extends MockXhr { static onSend?: OnSendCallback; @@ -22,31 +23,31 @@ describe('MockXhrServer', () => { method: string, url: string, headers: Record = {}, - body: any = null, + body: unknown = null, delayedSend = false ) { const xhr = new MockXhrClass(); xhr.open(method, url); - Object.entries(headers).forEach(([name, value]) => xhr.setRequestHeader(name, value)); - onSendPromises.push(new Promise((resolve) => { - xhr.addEventListener('loadend', () => resolve(xhr)); + Object.entries(headers).forEach(([name, value]) => { xhr.setRequestHeader(name, value); }); + loadendPromises.push(new Promise((resolve) => { + xhr.addEventListener('loadend', () => { resolve(xhr); }); })); if (delayedSend) { - // Run in an empty callstack to give users a chance to modify the xhr before send(). + // Run in an empty callstack to give tests a chance to modify the xhr before send(). // e.g. to add (upload) listeners. - Promise.resolve().then(() => xhr.send(body)); + void Promise.resolve().then(() => { xhr.send(body); }); } else { xhr.send(body); } return xhr; } - function waitForResponses() { - return Promise.all(onSendPromises); + function allResponsesPromise() { + return Promise.all(loadendPromises); } - return { MockXhrClass, doRequest, waitForResponses }; + return { MockXhrClass, doRequest, allResponsesPromise }; } function assertResponse(xhr: MockXhr, response: Partial) { @@ -72,14 +73,14 @@ describe('MockXhrServer', () => { } describe('constructor', () => { - it('should add routes', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should add routes', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const handlerFn = (request: MockXhrRequest) => { request.respond(); }; // eslint-disable-next-line @typescript-eslint/no-unused-vars const server = new MockXhrServer(MockXhrClass, { - get: ['/get', { status: 200 }], + 'get': ['/get', { status: 200 }], 'my-method': ['/my-method', { status: 201 }], - post: ['/post', [handlerFn, { status: 404 }]], + 'post': ['/post', [handlerFn, { status: 404 }]], }); doRequest('GET', '/get'); @@ -87,12 +88,11 @@ describe('MockXhrServer', () => { doRequest('POST', '/post'); doRequest('POST', '/post'); - return waitForResponses().then((xhrs) => { - assert.strictEqual(xhrs[0].status, 200); - assert.strictEqual(xhrs[1].status, 201); - assert.strictEqual(xhrs[2].status, 200); - assert.strictEqual(xhrs[3].status, 404); - }); + const xhrs = await allResponsesPromise(); + assert.strictEqual(xhrs[0].status, 200); + assert.strictEqual(xhrs[1].status, 201); + assert.strictEqual(xhrs[2].status, 200); + assert.strictEqual(xhrs[3].status, 404); }); }); @@ -106,7 +106,7 @@ describe('MockXhrServer', () => { it('should provide a factory method for MockXhr', () => { const { MockXhrClass } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); - assert.instanceOf(server.xhrFactory(), MockXhrClass, 'factory method returns MockXhr'); + assert.ok(server.xhrFactory() instanceof MockXhrClass, 'factory method returns MockXhr'); }); }); @@ -129,7 +129,7 @@ describe('MockXhrServer', () => { }); it('should set and restore XMLHttpRequest on context argument', () => { - const context: any = { XMLHttpRequest: 1 }; + const context: { XMLHttpRequest?: unknown } = { XMLHttpRequest: 1 }; const { MockXhrClass } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass).install(context); @@ -140,26 +140,26 @@ describe('MockXhrServer', () => { }); it('should set and restore undefined XMLHttpRequest on context argument', () => { - const context: any = { XMLHttpRequest: undefined }; + const context: { XMLHttpRequest?: unknown } = { XMLHttpRequest: undefined }; const { MockXhrClass } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass).install(context); assert.strictEqual(context.XMLHttpRequest, MockXhrClass, 'XMLHttpRequest property replaced'); server.remove(); - assert.isTrue('XMLHttpRequest' in context, 'XMLHttpRequest property restored'); - assert.isUndefined(context.XMLHttpRequest, 'XMLHttpRequest property restored as undefined'); + assert.ok('XMLHttpRequest' in context, 'XMLHttpRequest property restored'); + assert.strictEqual(context.XMLHttpRequest, undefined, 'XMLHttpRequest property restored as undefined'); }); it('should set and delete missing XMLHttpRequest on context argument', () => { - const context: any = {}; + const context: { XMLHttpRequest?: unknown } = {}; const { MockXhrClass } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass).install(context); assert.strictEqual(context.XMLHttpRequest, MockXhrClass, 'XMLHttpRequest property replaced'); server.remove(); - assert.isFalse('XMLHttpRequest' in context, 'XMLHttpRequest property deleted'); + assert.ok(!('XMLHttpRequest' in context), 'XMLHttpRequest property deleted'); }); it('should throw if remove() is called without install()', () => { @@ -178,8 +178,8 @@ describe('MockXhrServer', () => { }); describe('addHandler()', () => { - it('should support response hash handler', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support response hash handler', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const response = { status: 201, @@ -191,26 +191,24 @@ describe('MockXhrServer', () => { server.addHandler('method', '/path', response); const xhr = doRequest('method', '/path'); - return waitForResponses().then(() => { - assertResponse(xhr, response); - }); + await allResponsesPromise(); + assertResponse(xhr, response); }); - it('should support Function handler', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support Function handler', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const handlerFn = (request: MockXhrRequest) => { request.respond(201); }; server.addHandler('method', '/path', handlerFn); const xhr = doRequest('method', '/path'); - return waitForResponses().then(() => { - assert.strictEqual(xhr.status, 201); - }); + await allResponsesPromise(); + assert.strictEqual(xhr.status, 201); }); - it('should support \'error\' handler', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support \'error\' handler', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); server.addHandler('method', '/path', 'error'); @@ -218,14 +216,14 @@ describe('MockXhrServer', () => { let onerrorRaised = false; xhr.onerror = () => { onerrorRaised = true; }; - return waitForResponses().then(() => { - assertNetworkErrorResponse(xhr); - assert.isTrue(onerrorRaised); - }); + await allResponsesPromise(); + assertNetworkErrorResponse(xhr); + assert.ok(onerrorRaised); }); - it('should support \'timeout\' handler', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support \'timeout\' handler', async (context) => { + context.mock.timers.enable(); + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); server.addHandler('method', '/path', 'timeout'); @@ -234,14 +232,14 @@ describe('MockXhrServer', () => { xhr.ontimeout = () => { ontimeoutRaised = true; }; xhr.timeout = 1; - return waitForResponses().then(() => { - assertNetworkErrorResponse(xhr); - assert.isTrue(ontimeoutRaised); - }); + context.mock.timers.tick(1); + await allResponsesPromise(); + assertNetworkErrorResponse(xhr); + assert.ok(ontimeoutRaised); }); - it('should support Function url matcher', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support Function url matcher', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const status = 201; @@ -249,26 +247,25 @@ describe('MockXhrServer', () => { server.addHandler('method', matcher, { status }); const xhr = doRequest('method', '/my/object/somewhere'); - return waitForResponses().then(() => { - assert.strictEqual(xhr.status, status); - }); + await allResponsesPromise(); + assert.strictEqual(xhr.status, status); }); - it('should support regex url matcher', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support regex url matcher', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const status = 201; server.addHandler('method', /.*\/object\/.*/i, { status }); const xhr = doRequest('method', '/my/object/somewhere'); - return waitForResponses().then(() => { - assert.strictEqual(xhr.status, status); - }); + await allResponsesPromise(); + assert.strictEqual(xhr.status, status); }); - it('should support array of handlers', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support array of handlers', async (context) => { + context.mock.timers.enable(); + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const response = { status: 201, @@ -287,17 +284,17 @@ describe('MockXhrServer', () => { doRequest('method', '/path'); doRequest('method', '/path'); - return waitForResponses().then((xhrs) => { - assert.isTrue(ontimeoutRaised); - assertNetworkErrorResponse(xhrs[0]); - assert.strictEqual(xhrs[1].status, 404); - assertResponse(xhrs[2], response); - assertResponse(xhrs[3], response); - }); + context.mock.timers.tick(1); + const xhrs = await allResponsesPromise(); + assert.ok(ontimeoutRaised); + assertNetworkErrorResponse(xhrs[0]); + assert.strictEqual(xhrs[1].status, 404); + assertResponse(xhrs[2], response); + assertResponse(xhrs[3], response); }); - it('should normalize method names', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should normalize method names', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const status = 201; @@ -307,15 +304,14 @@ describe('MockXhrServer', () => { }); upperCaseMethods.forEach((method) => doRequest(method.toLowerCase(), '/path')); - return waitForResponses().then((xhrs) => { - xhrs.forEach((xhr) => { - assert.strictEqual(xhr.status, status); - }); + const xhrs = await allResponsesPromise(); + xhrs.forEach((xhr) => { + assert.strictEqual(xhr.status, status); }); }); - it('should pick first matched handler', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should pick first matched handler', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const status = 201; @@ -323,25 +319,23 @@ describe('MockXhrServer', () => { server.addHandler('method', '/path', { status: status + 1 }); const xhr = doRequest('method', '/path'); - return waitForResponses().then(() => { - assert.strictEqual(xhr.status, status); - }); + await allResponsesPromise(); + assert.strictEqual(xhr.status, status); }); - it('should handle having no matching handler', () => { + it('should handle having no matching handler', async () => { const { MockXhrClass, doRequest } = makeTestHarness(); // eslint-disable-next-line @typescript-eslint/no-unused-vars const server = new MockXhrServer(MockXhrClass); const xhr = doRequest('method', '/path'); - return Promise.resolve().then(() => { - assert.strictEqual(xhr.readyState, MockXhr.OPENED, 'final state UNSENT'); - }); + await Promise.resolve(); + assert.strictEqual(xhr.readyState, MockXhr.OPENED, 'final state UNSENT'); }); - it('should coexist with routes given in the constructor', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should coexist with routes given in the constructor', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass, { get: ['/get', { status: 201 }], }); @@ -350,10 +344,9 @@ describe('MockXhrServer', () => { doRequest('GET', '/get'); doRequest('method', '/path'); - return waitForResponses().then((xhrs) => { - assert.strictEqual(xhrs[0].status, 201); - assert.strictEqual(xhrs[1].status, 404); - }); + const xhrs = await allResponsesPromise(); + assert.strictEqual(xhrs[0].status, 201); + assert.strictEqual(xhrs[1].status, 404); }); }); @@ -361,24 +354,23 @@ describe('MockXhrServer', () => { const methods = ['get', 'post', 'put', 'delete'] as const; methods.forEach((method) => { - it(`should support ${method}()`, () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it(`should support ${method}()`, async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const status = 201; server[method]('/path', { status }); const xhr = doRequest(method, '/path'); - return waitForResponses().then(() => { - assert.strictEqual(xhr.status, status); - }); + await allResponsesPromise(); + assert.strictEqual(xhr.status, status); }); }); }); describe('setDefaultHandler()', () => { - it('should support response hash handler', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support response hash handler', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const response = { status: 201, @@ -390,39 +382,36 @@ describe('MockXhrServer', () => { server.setDefaultHandler(response); const xhr = doRequest('method', '/path'); - return waitForResponses().then(() => { - assertResponse(xhr, response); - }); + await allResponsesPromise(); + assertResponse(xhr, response); }); - it('should support response hash handler with null body', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support response hash handler with null body', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const response = { body: null }; server.setDefaultHandler(response); const xhr = doRequest('method', '/path'); - return waitForResponses().then(() => { - assertResponse(xhr, response); - }); + await allResponsesPromise(); + assertResponse(xhr, response); }); - it('should support Function handler', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support Function handler', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const handlerFn = (request: MockXhrRequest) => { request.respond(201); }; server.setDefaultHandler(handlerFn); const xhr = doRequest('method', '/path'); - return waitForResponses().then(() => { - assert.strictEqual(xhr.status, 201); - }); + await allResponsesPromise(); + assert.strictEqual(xhr.status, 201); }); - it('should support \'error\' handler', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support \'error\' handler', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); server.setDefaultHandler('error'); @@ -430,14 +419,14 @@ describe('MockXhrServer', () => { let onerrorRaised = false; xhr.onerror = () => { onerrorRaised = true; }; - return waitForResponses().then(() => { - assertNetworkErrorResponse(xhr); - assert.isTrue(onerrorRaised); - }); + await allResponsesPromise(); + assertNetworkErrorResponse(xhr); + assert.ok(onerrorRaised); }); - it('should support \'timeout\' handler', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support \'timeout\' handler', async (context) => { + context.mock.timers.enable(); + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); server.setDefaultHandler('timeout'); @@ -446,14 +435,15 @@ describe('MockXhrServer', () => { xhr.ontimeout = () => { ontimeoutRaised = true; }; xhr.timeout = 1; - return waitForResponses().then(() => { - assertNetworkErrorResponse(xhr); - assert.isTrue(ontimeoutRaised); - }); + context.mock.timers.tick(1); + await allResponsesPromise(); + assertNetworkErrorResponse(xhr); + assert.ok(ontimeoutRaised); }); - it('should support array of handlers', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should support array of handlers', async (context) => { + context.mock.timers.enable(); + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const response = { status: 201, @@ -472,17 +462,17 @@ describe('MockXhrServer', () => { doRequest('method', '/path'); doRequest('method', '/path'); - return waitForResponses().then((xhrs) => { - assert.isTrue(ontimeoutRaised); - assertNetworkErrorResponse(xhrs[0]); - assert.strictEqual(xhrs[1].status, 404); - assertResponse(xhrs[2], response); - assertResponse(xhrs[3], response); - }); + context.mock.timers.tick(1); + const xhrs = await allResponsesPromise(); + assert.ok(ontimeoutRaised); + assertNetworkErrorResponse(xhrs[0]); + assert.strictEqual(xhrs[1].status, 404); + assertResponse(xhrs[2], response); + assertResponse(xhrs[3], response); }); - it('should have lowest precedence', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should have lowest precedence', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const status = 201; @@ -490,32 +480,30 @@ describe('MockXhrServer', () => { server.setDefaultHandler({ status: status + 1 }); const xhr = doRequest('method', '/path'); - return waitForResponses().then(() => { - assert.strictEqual(xhr.status, status); - }); + await allResponsesPromise(); + assert.strictEqual(xhr.status, status); }); }); describe('setDefault404()', () => { - it('should return 404 for unmatched requests', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should return 404 for unmatched requests', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); server.setDefault404(); const xhr = doRequest('method', '/path'); - return waitForResponses().then(() => { - assert.strictEqual(xhr.status, 404); - }); + await allResponsesPromise(); + assert.strictEqual(xhr.status, 404); }); }); describe('aborted requests support', () => { - it('should handle aborted requests', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should handle aborted requests', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); - let requestData: RequestData; + let requestData: RequestData | undefined; server.get('/get', (request) => { requestData = new RequestData( new HeadersContainer(request.requestHeaders), @@ -529,23 +517,23 @@ describe('MockXhrServer', () => { const xhr = doRequest('GET', '/get'); xhr.abort(); - return waitForResponses().then(() => { - assert.strictEqual(requestData.requestHeaders.getAll(), ''); - assert.strictEqual(requestData.method, 'GET'); - assert.strictEqual(requestData.url, '/get'); - assert.deepEqual(requestData.body, null); - assert.strictEqual(requestData.withCredentials, false); - assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); - assert.strictEqual(xhr.getAllResponseHeaders(), '', 'Response headers'); - assert.strictEqual(xhr.status, 0, 'xhr.status == 0'); - assert.strictEqual(xhr.statusText, '', 'empty xhr.statusText'); - assert.strictEqual(xhr.response, '', 'empty xhr.response'); - assert.strictEqual(xhr.responseText, '', 'empty xhr.responseText'); - }); - }); - - it('should handle resent aborted requests', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + await allResponsesPromise(); + assert.ok(requestData); + assert.strictEqual(requestData.requestHeaders.getAll(), ''); + assert.strictEqual(requestData.method, 'GET'); + assert.strictEqual(requestData.url, '/get'); + assert.deepEqual(requestData.body, null); + assert.strictEqual(requestData.withCredentials, false); + assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); + assert.strictEqual(xhr.getAllResponseHeaders(), '', 'Response headers'); + assert.strictEqual(xhr.status, 0, 'xhr.status == 0'); + assert.strictEqual(xhr.statusText, '', 'empty xhr.statusText'); + assert.strictEqual(xhr.response, '', 'empty xhr.response'); + assert.strictEqual(xhr.responseText, '', 'empty xhr.responseText'); + }); + + it('should handle resent aborted requests', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); server.get('/get', {}); server.setDefault404(); @@ -555,15 +543,14 @@ describe('MockXhrServer', () => { xhr.open('GET', '/404-path'); xhr.send(); - return waitForResponses().then(() => { - assert.strictEqual(xhr.status, 404); - }); + await allResponsesPromise(); + assert.strictEqual(xhr.status, 404); }); }); describe('progressRate', () => { - it('should produce upload and download progress events', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + it('should produce upload and download progress events', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const response = { status: 201, @@ -577,32 +564,31 @@ describe('MockXhrServer', () => { const xhr = doRequest('method', '/path', {}, 'request', true); const events = recordEvents(xhr); - return waitForResponses().then(() => { - assertResponse(xhr, response); - assert.deepEqual(events, [ - 'loadstart(0,0,false)', - 'upload.loadstart(0,7,true)', - 'upload.progress(3,7,true)', - 'upload.progress(6,7,true)', - 'upload.progress(7,7,true)', - 'upload.load(7,7,true)', - 'upload.loadend(7,7,true)', - 'readystatechange(2)', - 'readystatechange(3)', - 'progress(3,9,true)', - 'readystatechange(3)', - 'progress(6,9,true)', - 'readystatechange(3)', - 'progress(9,9,true)', - 'readystatechange(4)', - 'load(9,9,true)', - 'loadend(9,9,true)', - ], 'fired events'); - }); - }); - - it('should work with empty request and response bodies', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + await allResponsesPromise(); + assertResponse(xhr, response); + assert.deepEqual(events, [ + 'loadstart(0,0,false)', + 'upload.loadstart(0,7,true)', + 'upload.progress(3,7,true)', + 'upload.progress(6,7,true)', + 'upload.progress(7,7,true)', + 'upload.load(7,7,true)', + 'upload.loadend(7,7,true)', + 'readystatechange(2)', + 'readystatechange(3)', + 'progress(3,9,true)', + 'readystatechange(3)', + 'progress(6,9,true)', + 'readystatechange(3)', + 'progress(9,9,true)', + 'readystatechange(4)', + 'load(9,9,true)', + 'loadend(9,9,true)', + ], 'fired events'); + }); + + it('should work with empty request and response bodies', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const response = {}; @@ -611,22 +597,21 @@ describe('MockXhrServer', () => { const xhr = doRequest('GET', '/path', {}, null, true); const events = recordEvents(xhr); - return waitForResponses().then(() => { - assertResponse(xhr, response); - assert.deepEqual(events, [ - 'loadstart(0,0,false)', - 'readystatechange(2)', - 'readystatechange(3)', - 'progress(0,0,false)', - 'readystatechange(4)', - 'load(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - }); - }); - - it('can be modified while a request is in progress', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + await allResponsesPromise(); + assertResponse(xhr, response); + assert.deepEqual(events, [ + 'loadstart(0,0,false)', + 'readystatechange(2)', + 'readystatechange(3)', + 'progress(0,0,false)', + 'readystatechange(4)', + 'load(0,0,false)', + 'loadend(0,0,false)', + ], 'fired events'); + }); + + it('can be modified while a request is in progress', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const response = { status: 201, @@ -643,32 +628,31 @@ describe('MockXhrServer', () => { xhr.addEventListener('progress', () => { server.progressRate = 0; }, { once: true }); const events = recordEvents(xhr); - return waitForResponses().then(() => { - assertResponse(xhr, response); - assert.deepEqual(events, [ - 'loadstart(0,0,false)', - 'upload.loadstart(0,7,true)', - 'upload.progress(2,7,true)', - // progressRate = 10 - 'upload.progress(7,7,true)', - 'upload.load(7,7,true)', - 'upload.loadend(7,7,true)', - // progressRate = 2 - 'readystatechange(2)', - 'readystatechange(3)', - 'progress(2,9,true)', - // progressRate = 0 - 'readystatechange(3)', - 'progress(9,9,true)', - 'readystatechange(4)', - 'load(9,9,true)', - 'loadend(9,9,true)', - ], 'fired events'); - }); - }); - - it('can be disabled while a request is in progress', () => { - const { MockXhrClass, doRequest, waitForResponses } = makeTestHarness(); + await allResponsesPromise(); + assertResponse(xhr, response); + assert.deepEqual(events, [ + 'loadstart(0,0,false)', + 'upload.loadstart(0,7,true)', + 'upload.progress(2,7,true)', + // progressRate = 10 + 'upload.progress(7,7,true)', + 'upload.load(7,7,true)', + 'upload.loadend(7,7,true)', + // progressRate = 2 + 'readystatechange(2)', + 'readystatechange(3)', + 'progress(2,9,true)', + // progressRate = 0 + 'readystatechange(3)', + 'progress(9,9,true)', + 'readystatechange(4)', + 'load(9,9,true)', + 'loadend(9,9,true)', + ], 'fired events'); + }); + + it('can be disabled while a request is in progress', async () => { + const { MockXhrClass, doRequest, allResponsesPromise } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); const response = { status: 201, @@ -683,29 +667,28 @@ describe('MockXhrServer', () => { xhr.upload.addEventListener('progress', () => { server.progressRate = 0; }, { once: true }); const events = recordEvents(xhr); - return waitForResponses().then(() => { - assertResponse(xhr, response); - assert.deepEqual(events, [ - 'loadstart(0,0,false)', - 'upload.loadstart(0,7,true)', - 'upload.progress(2,7,true)', - // progressRate = 0 - 'upload.progress(7,7,true)', - 'upload.load(7,7,true)', - 'upload.loadend(7,7,true)', - 'readystatechange(2)', - 'readystatechange(3)', - 'progress(9,9,true)', - 'readystatechange(4)', - 'load(9,9,true)', - 'loadend(9,9,true)', - ], 'fired events'); - }); + await allResponsesPromise(); + assertResponse(xhr, response); + assert.deepEqual(events, [ + 'loadstart(0,0,false)', + 'upload.loadstart(0,7,true)', + 'upload.progress(2,7,true)', + // progressRate = 0 + 'upload.progress(7,7,true)', + 'upload.load(7,7,true)', + 'upload.loadend(7,7,true)', + 'readystatechange(2)', + 'readystatechange(3)', + 'progress(9,9,true)', + 'readystatechange(4)', + 'load(9,9,true)', + 'loadend(9,9,true)', + ], 'fired events'); }); }); describe('getRequestLog()', () => { - it('should return all received requests', () => { + it('should return all received requests', async () => { const { MockXhrClass, doRequest } = makeTestHarness(); const server = new MockXhrServer(MockXhrClass); server.addHandler('method', '/path', (request) => { request.respond(404); }); @@ -713,18 +696,17 @@ describe('MockXhrServer', () => { doRequest('GET', '/path2'); doRequest('POST', '/post', { header: '123' }, 12345); - return Promise.resolve().then(() => { - const log = server.getRequestLog(); - assert.strictEqual(log.length, 3, 'handler called'); - assert.deepEqual(log[0], { - method: 'method', url: '/path1', headers: {}, body: null, - }); - assert.deepEqual(log[1], { - method: 'GET', url: '/path2', headers: {}, body: null, - }); - assert.deepEqual(log[2], { - method: 'POST', url: '/post', headers: { header: '123' }, body: 12345, - }); + await Promise.resolve(); + const log = server.getRequestLog(); + assert.strictEqual(log.length, 3, 'handler called'); + assert.deepEqual(log[0], { + method: 'method', url: '/path1', headers: {}, body: null, + }); + assert.deepEqual(log[1], { + method: 'GET', url: '/path2', headers: {}, body: null, + }); + assert.deepEqual(log[2], { + method: 'POST', url: '/post', headers: { header: '123' }, body: 12345, }); }); }); diff --git a/test/MockXhrTest.ts b/test/MockXhrTest.ts deleted file mode 100644 index 1b57d22..0000000 --- a/test/MockXhrTest.ts +++ /dev/null @@ -1,1617 +0,0 @@ -import { assert } from 'chai'; - -import { recordEvents } from './TestUtils'; -import HeadersContainer from '../src/HeadersContainer'; -import MockXhr from '../src/MockXhr'; -import MockXhrRequest from '../src/MockXhrRequest'; -import RequestData from '../src/RequestData'; -import { upperCaseMethods } from '../src/Utils'; -import XhrEventTarget from '../src/XhrEventTarget'; - -class FormDataMock {} - -describe('MockXhr', () => { - // Asserts that the response is a network error - function assertNetworkErrorResponse(xhr: MockXhr) { - assert.strictEqual(xhr.getAllResponseHeaders(), '', 'Response headers'); - assert.strictEqual(xhr.status, 0, 'xhr.status == 0'); - assert.strictEqual(xhr.statusText, '', 'empty xhr.statusText'); - assert.strictEqual(xhr.response, '', 'empty xhr.response'); - assert.strictEqual(xhr.responseText, '', 'empty xhr.responseText'); - } - - function assertSameRequest(req1: RequestData, req2: RequestData) { - assert.strictEqual(req1.requestHeaders.getAll(), req2.requestHeaders.getAll(), 'headers'); - assert.strictEqual(req1.method, req2.method, 'method'); - assert.strictEqual(req1.url, req2.url, 'url'); - assert.deepEqual(req1.body, req2.body, 'body'); - assert.strictEqual(req1.withCredentials, req2.withCredentials, 'withCredentials'); - } - - describe('states', () => { - it('should have state constants', () => { - assert.strictEqual(MockXhr.UNSENT, 0); - assert.strictEqual(MockXhr.OPENED, 1); - assert.strictEqual(MockXhr.HEADERS_RECEIVED, 2); - assert.strictEqual(MockXhr.LOADING, 3); - assert.strictEqual(MockXhr.DONE, 4); - }); - - it('should have a readyState attribute', () => { - const xhr = new MockXhr(); - assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'initial value'); - }); - - it('should not call readyState listeners added in dispatchEvent() listeners', () => { - const xhr = new MockXhr(); - - xhr.onreadystatechange = () => { - xhr.addEventListener('readystatechange', () => { - assert.fail('listener added in callback should not be called'); - }); - }; - - xhr.open('GET', '/url'); - }); - - it('should not call readyState listeners removed in dispatchEvent() listeners', () => { - const xhr = new MockXhr(); - - function callback1() { - xhr.removeEventListener('readystatechange', callback2); - xhr.removeEventListener('readystatechange', callback3); - xhr.onreadystatechange = null; - } - function callback2() { - assert.fail('listener added in callback should not be called'); - } - function callback3() { - assert.fail('listener added in callback should not be called'); - } - function callback4() { - assert.fail('listener added in callback should not be called'); - } - xhr.addEventListener('readystatechange', callback1); - xhr.addEventListener('readystatechange', callback2); - xhr.addEventListener('readystatechange', callback3, { once: true }); - xhr.onreadystatechange = callback4; - - xhr.open('GET', '/url'); - }); - }); - - describe('request', () => { - describe('open()', () => { - it('should change state', () => { - const xhr = new MockXhr(); - const events = recordEvents(xhr); - - xhr.open('GET', '/url'); - - assert.deepEqual(events, ['readystatechange(1)'], 'readystatechange fired'); - }); - - it('should be re-entrant', () => { - const xhr = new MockXhr(); - const events = recordEvents(xhr); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.open('POST', '/url2'); - xhr.send(); - - return done.then((request) => { - assert.strictEqual(request.method, 'POST', 'second method'); - assert.strictEqual(request.url, '/url2', 'second url'); - assert.strictEqual(xhr.readyState, MockXhr.OPENED); - assert.deepEqual(events, [ - 'readystatechange(1)', - 'loadstart(0,0,false)', - ], 'readystatechange fired'); - }); - }); - - it('should reject async = false', () => { - const xhr = new MockXhr(); - const events = recordEvents(xhr); - assert.throws(() => { xhr.open('GET', '/url', false); }, null, null, 'sync false throws'); - assert.throws(() => { xhr.open('GET', '/url', null as any); }, null, null, 'sync null throws'); - assert.throws(() => { xhr.open('GET', '/url', undefined); }, null, null, 'sync undefined throws'); - assert.throws(() => { xhr.open('GET', '/url', '' as any); }, null, null, 'sync empty string throws'); - assert.lengthOf(events, 0, 'no events fired'); - }); - - it('should reject non-methods', () => { - const xhr = new MockXhr(); - const tryMethod = (method: string) => { - return () => { xhr.open(method, '/url'); }; - }; - const events = recordEvents(xhr); - assert.throws(tryMethod('\\'), null, null, 'non-method throws'); - assert.throws(tryMethod(';'), null, null, 'non-method throws'); - assert.lengthOf(events, 0, 'no events fired'); - }); - - it('should reject forbidden methods', () => { - const xhr = new MockXhr(); - const tryMethod = (method: string) => { - return () => { xhr.open(method, '/url'); }; - }; - const events = recordEvents(xhr); - assert.throws(tryMethod('CONNECT'), null, null, 'forbidden method throws'); - assert.throws(tryMethod('TRACE'), null, null, 'forbidden method throws'); - assert.throws(tryMethod('TRACK'), null, null, 'forbidden method throws'); - assert.lengthOf(events, 0, 'no events fired'); - }); - - it('should normalize method names', () => { - const promises = upperCaseMethods.map((method) => { - const xhr = new MockXhr(); - xhr.open(method.toLowerCase(), 'url'); - const promise: Promise = new Promise((resolve) => { - xhr.onSend = resolve; - }); - xhr.send(); - return promise; - }); - - return Promise.all(promises).then((requests) => { - requests.forEach((request, i) => { - assert.strictEqual(request.method, upperCaseMethods[i]); - }); - }); - }); - }); - - describe('setRequestHeader()', () => { - it('should record header value', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.setRequestHeader('Head', '1'); - xhr.send(); - - return done.then((request) => { - assert.strictEqual(request.requestHeaders.getHeader('HEAD'), '1', 'header is case insensitive'); - }); - }); - - it('should record empty header value', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.setRequestHeader('empty-value', ''); - xhr.setRequestHeader('2-empty-values', ''); - xhr.setRequestHeader('2-empty-values', ''); - xhr.setRequestHeader('empty-mid-value', 'a'); - xhr.setRequestHeader('empty-mid-value', ''); - xhr.setRequestHeader('empty-mid-value', 'b'); - xhr.send(); - - return done.then((request) => { - assert.strictEqual(request.requestHeaders.getHeader('empty-value'), ''); - assert.strictEqual(request.requestHeaders.getHeader('2-empty-values'), ', '); - assert.strictEqual(request.requestHeaders.getHeader('empty-mid-value'), 'a, , b'); - }); - }); - - it('should throw InvalidStateError if not opened', () => { - assert.throws(() => { - new MockXhr().setRequestHeader('Head', '1'); - }); - }); - - const forbiddenHeaders = [ - 'Accept-Charset', - 'Accept-Encoding', - 'Access-Control-Request-Headers', - 'Access-Control-Request-Method', - 'Connection', - 'Content-Length', - 'Cookie', - 'Cookie2', - 'Date', - 'DNT', - 'Expect', - 'Host', - 'Keep-Alive', - 'Origin', - 'Referer', - 'TE', - 'Trailer', - 'Transfer-Encoding', - 'Upgrade', - 'Via', - ]; - forbiddenHeaders.forEach((header) => { - it(`should reject forbidden header ${header}`, () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.setRequestHeader(header, '1'); - xhr.send(); - - return done.then((request) => { - assert.strictEqual( - request.requestHeaders.getHeader(header), - null, - 'Forbidden header not set' - ); - }); - }); - }); - }); - - describe('timeout attribute', function () { - this.slow(100); - - function mockSetTimeout() { - const calls: [Function, number][] = []; - const mock = (handler: Function, timeout: number) => { calls.push([handler, timeout]); }; - const saved = globalThis.setTimeout; - globalThis.setTimeout = mock as typeof global.setTimeout; - return { calls, restore: () => { globalThis.setTimeout = saved; } }; - } - - it('can get and set its value', () => { - const xhr = new MockXhr(); - const timeout = 10; - assert.strictEqual(xhr.timeout, 0, 'initial value is 0'); - xhr.timeout = timeout; - assert.strictEqual(xhr.timeout, timeout); - }); - - it('will trigger a timeout if set before send()', () => { - const xhr = new MockXhr(); - const done = new Promise((resolve) => { xhr.addEventListener('timeout', resolve); }); - xhr.open('GET', '/url'); - const events = recordEvents(xhr); - xhr.timeout = 1; - xhr.send(); - - return done.then(() => { - assert.deepEqual(events, [ - 'loadstart(0,0,false)', - 'readystatechange(4)', - 'timeout(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - }); - }); - - it('will trigger a timeout if set after send()', (done) => { - const xhr = new MockXhr(); - xhr.open('GET', '/url'); - xhr.send(); - xhr.timeout = 1; - xhr.addEventListener('timeout', () => { done(); }); - }); - - it('measures timeout delay relative to send()', (done) => { - const xhr = new MockXhr(); - const delay = 100; - - xhr.open('GET', '/url'); - xhr.send(); - - setTimeout(() => { - const { calls, restore } = mockSetTimeout(); - try { - xhr.timeout = delay; - } finally { - restore(); - } - const setTimeoutArg = calls[0][1]; - assert.isAtMost(setTimeoutArg, delay - 20); - done(); - }, 20); - }); - - it('measures timeout delay relative to send() and clamp to 0', (done) => { - const xhr = new MockXhr(); - - xhr.open('GET', '/url'); - xhr.send(); - - setTimeout(() => { - const { calls, restore } = mockSetTimeout(); - try { - xhr.timeout = 1; - } finally { - restore(); - } - const setTimeoutArg = calls[0][1]; - assert.strictEqual(setTimeoutArg, 0, 'timeout delay clamped to 0'); - done(); - }, 20); - }); - - it('has no effect when the response is sent fast enough', (done) => { - const xhr = new MockXhr(); - let gotTimeoutEvent = false; - - xhr.onSend = (request) => { request.respond(); }; - xhr.addEventListener('timeout', () => { gotTimeoutEvent = true; }); - xhr.open('GET', '/url'); - xhr.send(); - xhr.timeout = 1; - - // Wait to make sure the timeout has no effect - setTimeout(() => { - assert.isFalse(gotTimeoutEvent, 'there should be no timeout event'); - done(); - }, 20); - }); - - it('can be cancelled', (done) => { - const xhr = new MockXhr(); - let gotTimeoutEvent = false; - - xhr.addEventListener('timeout', () => { gotTimeoutEvent = true; }); - xhr.open('GET', '/url'); - xhr.send(); - xhr.timeout = 1; - Promise.resolve().then(() => { xhr.timeout = 0; }); - - // Wait to make sure the timeout has no effect - setTimeout(() => { - assert.isFalse(gotTimeoutEvent, 'there should be no timeout event'); - done(); - }, 20); - }); - - it('is cancelled by open()', (done) => { - const xhr = new MockXhr(); - let gotTimeoutEvent = false; - - xhr.addEventListener('timeout', () => { gotTimeoutEvent = true; }); - xhr.open('GET', '/url'); - xhr.send(); - xhr.timeout = 1; - xhr.open('GET', '/url'); - - // Wait to make sure the timeout has no effect - setTimeout(() => { - assert.isFalse(gotTimeoutEvent, 'there should be no timeout event'); - done(); - }, 20); - }); - - it('can be disabled per instance', (done) => { - const xhr = new MockXhr(); - let gotTimeoutEvent = false; - - xhr.timeoutEnabled = false; - xhr.addEventListener('timeout', () => { gotTimeoutEvent = true; }); - xhr.open('GET', '/url'); - xhr.send(); - xhr.timeout = 1; - - // Wait to make sure the timeout has no effect - setTimeout(() => { - assert.isFalse(gotTimeoutEvent, 'there should be no timeout event'); - done(); - }, 20); - }); - - it('can be disabled on subclass', (done) => { - try { - class LocalMockXhr extends MockXhr {} - const xhr = new LocalMockXhr(); - let gotTimeoutEvent = false; - - LocalMockXhr.timeoutEnabled = false; - xhr.addEventListener('timeout', () => { gotTimeoutEvent = true; }); - xhr.open('GET', '/url'); - xhr.send(); - xhr.timeout = 1; - - // Wait to make sure the timeout has no effect - setTimeout(() => { - assert.isFalse(gotTimeoutEvent, 'there should be no timeout event'); - done(); - }, 20); - } finally { - MockXhr.timeoutEnabled = true; - } - }); - - it('can be disabled globally', (done) => { - try { - const xhr = new MockXhr(); - let gotTimeoutEvent = false; - - MockXhr.timeoutEnabled = false; - xhr.addEventListener('timeout', () => { gotTimeoutEvent = true; }); - xhr.open('GET', '/url'); - xhr.send(); - xhr.timeout = 1; - - // Wait to make sure the timeout has no effect - setTimeout(() => { - assert.isFalse(gotTimeoutEvent, 'there should be no timeout event'); - done(); - }, 20); - } finally { - MockXhr.timeoutEnabled = true; - } - }); - }); - - describe('withCredentials attribute', () => { - it('should initially return false', () => { - const xhr = new MockXhr(); - assert.strictEqual(xhr.withCredentials, false, 'initial value'); - }); - - it('should throw if set when state is not unsent or opened or if the send() flag is set', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - assert.throws(() => { xhr.withCredentials = true; }); - request.respond(); - assert.throws(() => { xhr.withCredentials = true; }); - }); - }); - - it('can get and set its value', () => { - const xhr = new MockXhr(); - xhr.withCredentials = true; - assert.strictEqual(xhr.withCredentials, true, 'value set'); - }); - }); - - it('should have an upload attribute', () => { - const xhr = new MockXhr(); - assert.instanceOf(xhr.upload, XhrEventTarget, 'initial value'); - }); - - describe('send()', () => { - it('should capture RequestData', () => { - const xhr = new MockXhr(); - const requestData = new RequestData( - new HeadersContainer().addHeader('test', 'ok'), - 'POST', - '/url', - { body: 'body' }, - true - ); - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open(requestData.method, requestData.url); - xhr.setRequestHeader('test', 'ok'); - xhr.withCredentials = requestData.withCredentials; - xhr.send(requestData.body); - - return done.then((request) => { - assertSameRequest(request.requestData, requestData); - }); - }); - - it('should set Content-Type for string body', () => { - const xhr = new MockXhr(); - const body = 'body'; - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('POST', '/url'); - xhr.send(body); - - return done.then((request) => { - assert.strictEqual( - request.requestHeaders.getHeader('Content-Type'), - 'text/plain;charset=UTF-8', - 'Content-Type set' - ); - }); - }); - - it('should handle FormData body', () => { - // The FormData code path of send() requires FormData in the global context. - const savedFormData = globalThis.FormData; - globalThis.FormData = FormDataMock as unknown as typeof globalThis.FormData; - try { - const xhr = new MockXhr(); - const body = new FormDataMock(); - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('POST', '/url'); - xhr.send(body); - - return done.then((request) => { - assert.strictEqual( - request.requestHeaders.getHeader('Content-Type'), - 'multipart/form-data; boundary=-----MochXhr1234', - 'Content-Type set' - ); - }); - } finally { - globalThis.FormData = savedFormData; - } - }); - - it('should use body mime type in request header', () => { - const xhr = new MockXhr(); - const body = { type: 'image/jpeg' }; - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('POST', '/url'); - xhr.send(body); - - return done.then((request) => { - assert.strictEqual( - request.requestHeaders.getHeader('Content-Type'), - body.type, - 'Content-Type set' - ); - }); - }); - - it('should not set Content-Type for null body', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - assert.strictEqual(request.body, null, 'Recorded null body'); - assert.strictEqual( - request.requestHeaders.getHeader('Content-Type'), - null, - 'Content-Type not set' - ); - }); - }); - - it('should fire loadstart events', () => { - const xhr = new MockXhr(); - xhr.open('POST', '/url'); - const events = recordEvents(xhr); - xhr.send('body'); - - assert.deepEqual(events, ['loadstart(0,0,false)', 'upload.loadstart(0,4,true)'], 'fired events'); - }); - - it('should handle re-open() during loadstart event handler', () => { - try { - const xhr = new MockXhr(); - - // Add onSend callbacks - let onSendCount = 0; - MockXhr.onSend = () => { onSendCount += 1; }; - let onSendXhrCount = 0; - xhr.onSend = () => { onSendXhrCount += 1; }; - - // re-open() during the loadstart event handler aborts send() - xhr.open('GET', '/url'); - xhr.addEventListener('loadstart', () => { - // Open a new request - xhr.open('GET', '/url'); - }); - xhr.send(); - - return Promise.resolve().then(() => { - assert.strictEqual(xhr.readyState, MockXhr.OPENED, 'final state OPENED'); - assert.strictEqual(onSendCount, 0, 'onSend() should not be called'); - assert.strictEqual(onSendXhrCount, 0, 'onSend() should not be called'); - }); - } finally { - delete MockXhr.onSend; - } - }); - }); - - describe('abort()', () => { - it('should follow the steps for open()-abort() sequence', () => { - const xhr = new MockXhr(); - xhr.open('GET', '/url'); - const events = recordEvents(xhr); - xhr.abort(); - - assert.lengthOf(events, 0, 'no abort event'); - assert.strictEqual(xhr.readyState, MockXhr.OPENED, 'final state OPENED'); - }); - - it('should follow the steps for open()-send()-abort() sequence', () => { - const xhr = new MockXhr(); - xhr.open('GET', '/url'); - xhr.send(); - const events = recordEvents(xhr); - xhr.abort(); - - assert.deepEqual(events, [ - 'readystatechange(4)', - 'abort(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); - }); - - it('should follow the steps for open()-send()-HEADERS_RECEIVED-abort() sequence', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - request.setResponseHeaders(); - const events = recordEvents(xhr); - xhr.abort(); - - assert.deepEqual(events, [ - 'readystatechange(4)', - 'abort(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); - }); - }); - - it('should follow the steps for open()-send()-LOADING-abort() sequence', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - request.setResponseHeaders(); - request.downloadProgress(2, 8); - const events = recordEvents(xhr); - xhr.abort(); - - assert.deepEqual(events, [ - 'readystatechange(4)', - 'abort(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); - }); - }); - - it('should follow the steps for open()-send()-DONE-abort() sequence', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - request.respond(); - const events = recordEvents(xhr); - xhr.abort(); - - assert.deepEqual(events, [], 'no fired events'); - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); - }); - }); - - it('should fire upload abort for send(body)-abort() sequence', () => { - const xhr = new MockXhr(); - xhr.open('POST', '/url'); - const events = recordEvents(xhr); - xhr.send('body'); - xhr.abort(); - - assert.deepEqual(events, [ - 'loadstart(0,0,false)', - 'upload.loadstart(0,4,true)', - 'readystatechange(4)', - 'upload.abort(0,0,false)', - 'upload.loadend(0,0,false)', - 'abort(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - }); - - it('should handle abort() during loadstart event handler', () => { - try { - const xhr = new MockXhr(); - - // Add onSend callbacks - let onSendCalled = false; - xhr.onSend = () => { - onSendCalled = true; - }; - let onSendXhrCalled = false; - MockXhr.onSend = () => { - onSendXhrCalled = true; - }; - - // Aborted send() during the loadstart event handler - xhr.open('GET', '/url'); - xhr.addEventListener('loadstart', () => { - // Abort the request - xhr.abort(); - }); - xhr.send(); - - return Promise.resolve().then(() => { - assert.isFalse(onSendCalled, 'onSend() should not be called'); - assert.isFalse(onSendXhrCalled, 'onSend() should not be called'); - assert.strictEqual(xhr.readyState, MockXhr.UNSENT, 'final state UNSENT'); - }); - } finally { - delete MockXhr.onSend; - } - }); - - it('should handle nested open() during abort()', () => { - const xhr = new MockXhr(); - const states: number[] = []; - let doAbort = false; - xhr.onreadystatechange = () => { - states.push(xhr.readyState); - if (doAbort) { - xhr.open('GET', '/url'); - } - }; - - xhr.open('GET', '/url'); - xhr.send(); - doAbort = true; - xhr.abort(); - - assert.deepEqual(states, [MockXhr.OPENED, MockXhr.DONE, MockXhr.OPENED]); - }); - - it('should handle nested open()-send() during abort()', () => { - const xhr = new MockXhr(); - const states: number[] = []; - let doAbort = false; - xhr.onreadystatechange = () => { - states.push(xhr.readyState); - if (doAbort) { - doAbort = false; - xhr.open('GET', '/url'); - xhr.send(); - } - }; - - xhr.open('GET', '/url'); - xhr.send(); - doAbort = true; - xhr.abort(); - - assert.deepEqual(states, [MockXhr.OPENED, MockXhr.DONE, MockXhr.OPENED]); - }); - }); - }); - - describe('response', () => { - const validResponseTypes = ['', 'arraybuffer', 'blob', 'document', 'json', 'text'] as const; - - it('should have a status attribute', () => { - const xhr = new MockXhr(); - assert.strictEqual(xhr.status, 0, 'initial value'); - }); - - it('should have a statusText attribute', () => { - const xhr = new MockXhr(); - assert.strictEqual(xhr.statusText, '', 'initial value'); - }); - - describe('overrideMimeType()', () => { - it('should throw if set when state is loading or done', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - request.setResponseHeaders(); - request.downloadProgress(0, 4); - assert.throws(() => { xhr.overrideMimeType('text/plain'); }); - request.setResponseBody('body'); - assert.throws(() => { xhr.overrideMimeType('text/plain'); }); - }); - }); - }); - - describe('responseType attribute', () => { - it('should initially return the empty string', () => { - const xhr = new MockXhr(); - assert.strictEqual(xhr.responseType, '', 'initial value'); - }); - - it('should throw if set when state is loading or done', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - request.setResponseHeaders(); - request.downloadProgress(0, 4); - assert.throws(() => { xhr.responseType = 'text'; }); - request.setResponseBody('body'); - assert.throws(() => { xhr.responseType = 'text'; }); - }); - }); - - validResponseTypes.forEach((value) => { - it(`should accept value '${value}'`, () => { - const xhr = new MockXhr(); - xhr.responseType = value; - assert.strictEqual(xhr.responseType, value, 'responseType was set'); - }); - }); - - it('should ignore invalid values', () => { - const xhr = new MockXhr(); - - // @ts-ignore - xhr.responseType = 'value'; - assert.strictEqual(xhr.responseType, '', 'responseType was not set'); - }); - }); - - describe('response attribute', () => { - it('should be initially empty', () => { - const xhr = new MockXhr(); - assert.strictEqual(xhr.response, '', 'initial value'); - }); - - it('should return the empty string before loading state with text responseType', () => { - const xhr = new MockXhr(); - xhr.open('GET', '/url'); - assert.strictEqual(xhr.response, '', 'empty string before loading state'); - }); - - it('should return the text response with text responseType', () => { - const xhr = new MockXhr(); - const body = 'body'; - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - request.setResponseBody(body); - assert.strictEqual(xhr.response, 'body', 'text response'); - }); - }); - - it('should return null if state is not done with non-text responseType', () => { - const xhr = new MockXhr(); - xhr.open('GET', '/url'); - xhr.responseType = 'json'; - xhr.send(); - assert.strictEqual(xhr.response, null, 'state is not done'); - }); - - validResponseTypes.forEach((value) => { - const data = value === '' || value === 'text' ? ['empty string', ''] : ['null', null]; - it(`should return ${data[0]} with null body and "${value}" responseType`, () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.responseType = value; - xhr.send(); - - return done.then((request) => { - request.respond(); - assert.strictEqual(xhr.response, data[1], 'responseType was set'); - }); - }); - }); - - it('should return the response body as-is with arraybuffer responseType', () => { - const xhr = new MockXhr(); - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.responseType = 'arraybuffer'; - xhr.send(); - - return done.then((request) => { - const body = { body: 'test' }; - request.setResponseBody(body); - assert.strictEqual(xhr.response, body, 'passthrough response'); - }); - }); - - it('should return the response body as-is with blob responseType', () => { - const xhr = new MockXhr(); - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.responseType = 'blob'; - xhr.send(); - - return done.then((request) => { - const body = { body: 'test' }; - request.setResponseBody(body); - assert.strictEqual(xhr.response, body, 'passthrough response'); - }); - }); - - it('should return the response body as-is with document responseType', () => { - const xhr = new MockXhr(); - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.responseType = 'document'; - xhr.send(); - - return done.then((request) => { - const body = { body: 'test' }; - request.setResponseBody(body); - assert.strictEqual(xhr.response, body, 'passthrough response'); - }); - }); - - it('should return the json response with json responseType', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.responseType = 'json'; - xhr.send(); - - return done.then((request) => { - request.setResponseBody('{"a": 1}'); - assert.deepEqual(xhr.response, { a: 1 }, 'json response'); - }); - }); - - it('should return null for invalid json response with json responseType', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.responseType = 'json'; - xhr.send(); - - return done.then((request) => { - request.setResponseBody('{'); - assert.strictEqual(xhr.response, null, 'null response'); - }); - }); - }); - - describe('responseText attribute', () => { - it('should be initially empty', () => { - const xhr = new MockXhr(); - assert.strictEqual(xhr.responseText, '', 'initial value'); - }); - - it('should throw if accessed with non-text responseType', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.responseType = 'json'; - xhr.send(); - - return done.then((request) => { - request.respond(); - assert.throws(() => { return xhr.responseText; }); - }); - }); - - it('should return the empty string before loading', () => { - const xhr = new MockXhr(); - xhr.open('GET', '/url'); - assert.strictEqual(xhr.responseText, '', 'empty string before loading'); - }); - - it('should return the text response', () => { - const xhr = new MockXhr(); - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - const body = 'body'; - request.setResponseBody(body); - assert.strictEqual(xhr.responseText, body, 'text response'); - }); - }); - }); - - describe('responseXML attribute', () => { - it('should be initially null', () => { - const xhr = new MockXhr(); - assert.strictEqual(xhr.responseXML, null, 'initial value'); - }); - - it('should throw if accessed with non-document responseType', () => { - const xhr = new MockXhr(); - xhr.responseType = 'json'; - assert.throws(() => { return xhr.responseXML; }); - }); - - it('should return null if state is not done', () => { - const xhr = new MockXhr(); - xhr.open('GET', '/url'); - xhr.send(); - assert.strictEqual(xhr.responseXML, null, 'state is not done'); - }); - - it('should return the response body as-is with the document responseType', () => { - const xhr = new MockXhr(); - xhr.responseType = 'document'; - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - const body = { body: 'test' }; - request.setResponseBody(body); - assert.strictEqual(xhr.responseXML, body, 'passthrough response'); - }); - }); - }); - }); - - describe('Lifecycle hooks', () => { - describe('onCreate()', () => { - it('should be called', () => { - try { - const args: MockXhr[] = []; - - MockXhr.onCreate = (arg) => { - args.push(arg); - }; - - const xhr = new MockXhr(); - - assert.instanceOf(xhr, MockXhr); - assert.deepEqual(args, [xhr], 'correct parameters for callbacks'); - } finally { - delete MockXhr.onCreate; - } - }); - }); - - describe('onSend()', () => { - it('should be called in order', () => { - try { - class LocalMockXhr extends MockXhr {} - const xhr = new LocalMockXhr(); - const calls: string[] = []; - const thisValues: any[] = []; - const argValues: any[] = []; - - const done = new Promise((resolve) => { - MockXhr.onSend = function onSend(...args) { - calls.push('global'); - thisValues.push(this); - argValues.push(args); - }; - - LocalMockXhr.onSend = function onSendLocal(...args) { - calls.push('subclass'); - thisValues.push(this); - argValues.push(args); - }; - - xhr.onSend = function onSendXhr(...args) { - calls.push('xhr'); - thisValues.push(this); - argValues.push(args); - resolve(true); - }; - }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then(() => { - const req = xhr.currentRequest; - assert.instanceOf(req, MockXhrRequest); - assert.deepEqual(calls, ['global', 'subclass', 'xhr'], 'hooks called in the right order'); - assert.deepEqual(thisValues, [req, req, req], 'correct contexts for callbacks'); - assert.deepEqual(argValues, [[req, xhr], [req, xhr], [req, xhr]], 'correct parameters for callbacks'); - }); - } finally { - delete MockXhr.onSend; - } - }); - - it('should call all callback stages even if they are the same function', () => { - try { - class LocalMockXhr extends MockXhr {} - const xhr = new LocalMockXhr(); - let callCount = 0; - - const done = new Promise((resolve) => { - const onSend = () => { - if (++callCount === 3) { - resolve(true); - } - }; - MockXhr.onSend = onSend; - LocalMockXhr.onSend = onSend; - xhr.onSend = onSend; - }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then(() => { - assert.strictEqual(callCount, 3); - }); - } finally { - delete MockXhr.onSend; - } - }); - - it('should defensively copy the callback', () => { - try { - class LocalMockXhr extends MockXhr {} - const xhr = new LocalMockXhr(); - const calls: string[] = []; - - const done = new Promise((resolve) => { - MockXhr.onSend = () => { calls.push('global'); }; - LocalMockXhr.onSend = () => { calls.push('subclass'); }; - xhr.onSend = () => { calls.push('xhr'); resolve(true); }; - }); - xhr.open('GET', '/url'); - xhr.send(); - delete MockXhr.onSend; - delete LocalMockXhr.onSend; - delete xhr.onSend; - - return done.then(() => { - assert.deepEqual(calls, ['global', 'subclass', 'xhr'], 'hooks called in the right order'); - }); - } finally { - delete MockXhr.onSend; - } - }); - - it('should be called for each send() and have versioned requests', () => { - const xhr = new MockXhr(); - const requests: RequestData[] = []; - - let status = 200; - xhr.onSend = (request) => { - requests.push(request.requestData); - request.respond(status++); - }; - xhr.open('GET', '/url1'); - xhr.setRequestHeader('header1', 'val1'); - xhr.send(); - xhr.open('POST', '/url2'); - xhr.setRequestHeader('header2', 'val2'); - xhr.send({ body: 1 }); - - return Promise.resolve().then(() => { - assertSameRequest(requests[0], new RequestData( - new HeadersContainer().addHeader('header1', 'val1'), - 'GET', - '/url1' - )); - assertSameRequest(requests[1], new RequestData( - new HeadersContainer().addHeader('header2', 'val2'), - 'POST', - '/url2', - { body: 1 } - )); - assert.strictEqual(xhr.status, 201, 'received 2nd response'); - }); - }); - }); - }); - - describe('MockXhrResponseReceiver interface', () => { - it('getRequestBodySize() should return the body size', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('POST', '/url'); - xhr.send('body'); - - return done.then((request) => { - assert.strictEqual(request.getRequestBodySize(), 4); - }); - }); - - describe('uploadProgress()', () => { - it('should fire upload progress events', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('POST', '/url'); - const events = recordEvents(xhr); // Add listeners BEFORE send() - xhr.send('body'); - - return done.then((request) => { - request.uploadProgress(2); - request.uploadProgress(3); - assert.deepEqual(events, [ - 'loadstart(0,0,false)', - 'upload.loadstart(0,4,true)', - 'upload.progress(2,4,true)', - 'upload.progress(3,4,true)', - ], 'fired events'); - }); - }); - - it('should not fire upload progress events if the upload listener flag is unset', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('POST', '/url'); - xhr.send('body'); - - // Add listeners AFTER send() - const events = recordEvents(xhr); - - return done.then((request) => { - request.uploadProgress(2); - assert.deepEqual(events, [], 'no fired events'); - }); - }); - }); - - describe('respond()', () => { - it('should set response headers and body', () => { - const xhr = new MockXhr(); - const status = 201; - const headers = { test: 'ok' }; - const responseBody = 'response'; - const statusText = 'all good!'; - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - const events = recordEvents(xhr); - request.respond(status, headers, responseBody, statusText); - - assert.deepEqual(xhr.getResponseHeadersHash(), headers, 'Response headers'); - assert.strictEqual(xhr.status, status, 'xhr.status'); - assert.strictEqual(xhr.statusText, statusText, 'xhr.statusText'); - assert.strictEqual(xhr.response, responseBody, 'xhr.response'); - assert.strictEqual(xhr.responseText, responseBody, 'xhr.responseText'); - assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); - assert.deepEqual(events, [ - // setResponseHeaders() - 'readystatechange(2)', - // setResponseBody() - 'readystatechange(3)', - 'progress(8,8,true)', - 'readystatechange(4)', - 'load(8,8,true)', - 'loadend(8,8,true)', - ], 'fired events'); - }); - }); - }); - - describe('setResponseHeaders()', () => { - it('should set response state and headers', () => { - const xhr = new MockXhr(); - const statusText = 'Custom Created'; - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - const events = recordEvents(xhr); - request.setResponseHeaders(201, { 'R-Header': '123' }, statusText); - - assert.strictEqual(xhr.getAllResponseHeaders(), 'r-header: 123\r\n', 'Response headers'); - assert.strictEqual(xhr.status, 201, 'xhr.status'); - assert.strictEqual(xhr.statusText, statusText, 'xhr.statusText'); - assert.strictEqual(xhr.readyState, MockXhr.HEADERS_RECEIVED, 'readyState HEADERS_RECEIVED'); - assert.strictEqual(xhr.response, '', 'no response yet'); - assert.strictEqual(xhr.responseText, '', 'no response yet'); - assert.strictEqual(xhr.readyState, MockXhr.HEADERS_RECEIVED, 'readyState HEADERS_RECEIVED'); - assert.deepEqual(events, ['readystatechange(2)'], 'fired event'); - }); - }); - - it('should fire upload progress events for non-empty body', () => { - const xhr = new MockXhr(); - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('POST', '/url'); - - // Must add an upload listener before send() for upload progress events to fire - const events = recordEvents(xhr); - xhr.send('body'); - - return done.then((request) => { - request.setResponseHeaders(); - assert.deepEqual(events, [ - // send() - 'loadstart(0,0,false)', - 'upload.loadstart(0,4,true)', - // setResponseHeaders() - 'upload.progress(4,4,true)', - 'upload.load(4,4,true)', - 'upload.loadend(4,4,true)', - 'readystatechange(2)', - ], 'fired events'); - }); - }); - }); - - describe('downloadProgress()', () => { - it('should provide download progress events', () => { - const xhr = new MockXhr(); - xhr.open('GET', '/url'); - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.send(); - - return done.then((request) => { - request.setResponseHeaders(); - const events = recordEvents(xhr); - request.downloadProgress(2, 8); - request.downloadProgress(4, 8); - - assert.strictEqual(xhr.readyState, MockXhr.LOADING, 'readyState LOADING'); - assert.deepEqual(events, [ - // downloadProgress() - 'readystatechange(3)', - 'progress(2,8,true)', - // downloadProgress() - 'readystatechange(3)', - 'progress(4,8,true)', - ], 'fired events'); - }); - }); - }); - - describe('setResponseBody()', () => { - it('should set response body and default headers', () => { - const xhr = new MockXhr(); - const responseBody = 'response'; - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - const events = recordEvents(xhr); - request.setResponseBody(responseBody); - - assert.strictEqual(xhr.getAllResponseHeaders(), 'content-length: 8\r\n', 'Response headers'); - assert.strictEqual(xhr.status, 200, 'xhr.status'); - assert.strictEqual(xhr.statusText, 'OK', 'xhr.statusText'); - assert.strictEqual(xhr.response, responseBody, 'xhr.response'); - assert.strictEqual(xhr.responseText, responseBody, 'xhr.responseText'); - assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); - assert.deepEqual(events, [ - // automatic call to setResponseHeaders() - 'readystatechange(2)', - // respond() events - end of body - 'readystatechange(3)', - 'progress(8,8,true)', - 'readystatechange(4)', - 'load(8,8,true)', - 'loadend(8,8,true)', - ], 'fired events'); - }); - }); - - it('should set response body if called after setResponseHeaders()', () => { - const xhr = new MockXhr(); - const responseBody = 'response'; - - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - request.setResponseHeaders(); - const events = recordEvents(xhr); - request.setResponseBody(responseBody); - - assert.strictEqual(xhr.response, responseBody, 'xhr.response'); - assert.strictEqual(xhr.responseText, responseBody, 'xhr.responseText'); - assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); - assert.deepEqual(events, [ - 'readystatechange(3)', - 'progress(8,8,true)', - 'readystatechange(4)', - 'load(8,8,true)', - 'loadend(8,8,true)', - ], 'fired events'); - }); - }); - }); - - describe('setNetworkError()', () => { - it('should error the request', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - const events = recordEvents(xhr); - request.setNetworkError(); - - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); - assert.deepEqual(events, [ - 'readystatechange(4)', - 'error(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - }); - }); - - it('should error the request after setResponseHeaders()', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - request.setResponseHeaders(); - const events = recordEvents(xhr); - request.setNetworkError(); - - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); - assert.deepEqual(events, [ - 'readystatechange(4)', - 'error(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - }); - }); - - it('should error the request after downloadProgress()', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - request.setResponseHeaders(); - request.downloadProgress(2, 8); - const events = recordEvents(xhr); - request.setNetworkError(); - - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); - assert.deepEqual(events, [ - 'readystatechange(4)', - 'error(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - }); - }); - - it('should error the request and fire upload events for non-empty body', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('POST', '/url'); - const events = recordEvents(xhr); // Add listeners BEFORE send() - xhr.send('body'); - - return done.then((request) => { - request.setNetworkError(); - - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); - assert.deepEqual(events, [ - 'loadstart(0,0,false)', - 'upload.loadstart(0,4,true)', - 'readystatechange(4)', - 'upload.error(0,0,false)', - 'upload.loadend(0,0,false)', - 'error(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - }); - }); - }); - - describe('setRequestTimeout()', () => { - it('should time out the request', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - xhr.timeoutEnabled = false; - xhr.timeout = 1; - - return done.then((request) => { - const events = recordEvents(xhr); - request.setRequestTimeout(); - - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); - assert.deepEqual(events, [ - 'readystatechange(4)', - 'timeout(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - }); - }); - - it('should throw if timeout === 0', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - - return done.then((request) => { - assert.throws(() => request.setRequestTimeout()); - }); - }); - - it('should time out the request after setResponseHeaders()', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - xhr.timeoutEnabled = false; - xhr.timeout = 1; - - return done.then((request) => { - request.setResponseHeaders(); - const events = recordEvents(xhr); - request.setRequestTimeout(); - - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); - assert.deepEqual(events, [ - 'readystatechange(4)', - 'timeout(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - }); - }); - - it('should time out the request after downloadProgress()', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('GET', '/url'); - xhr.send(); - xhr.timeoutEnabled = false; - xhr.timeout = 1; - - return done.then((request) => { - request.setResponseHeaders(); - request.downloadProgress(2, 8); - const events = recordEvents(xhr); - request.setRequestTimeout(); - - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); - assert.deepEqual(events, [ - 'readystatechange(4)', - 'timeout(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - }); - }); - - it('should time out the request and fire upload events for non-empty body', () => { - const xhr = new MockXhr(); - const done: Promise = new Promise((resolve) => { xhr.onSend = resolve; }); - xhr.open('POST', '/url'); - const events = recordEvents(xhr); // Add listeners BEFORE send() - xhr.send('body'); - xhr.timeoutEnabled = false; - xhr.timeout = 1; - - return done.then((request) => { - request.setRequestTimeout(); - - assertNetworkErrorResponse(xhr); - assert.strictEqual(xhr.readyState, MockXhr.DONE, 'readyState DONE'); - assert.deepEqual(events, [ - 'loadstart(0,0,false)', - 'upload.loadstart(0,4,true)', - 'readystatechange(4)', - 'upload.timeout(0,0,false)', - 'upload.loadend(0,0,false)', - 'timeout(0,0,false)', - 'loadend(0,0,false)', - ], 'fired events'); - }); - }); - }); - }); -}); diff --git a/test/TestUtils.ts b/test/TestUtils.ts index b2b7279..d5f7f06 100644 --- a/test/TestUtils.ts +++ b/test/TestUtils.ts @@ -1,8 +1,7 @@ -/* eslint-disable import/prefer-default-export */ -import { XHR_PROGRESS_EVENT_NAMES } from '../src/XhrProgressEventsNames'; +import { XHR_PROGRESS_EVENT_NAMES } from '../src/XhrProgressEventsNames.ts'; -import type MockXhr from '../src/MockXhr'; -import type XhrProgressEvent from '../src/XhrProgressEvent'; +import type MockXhr from '../src/MockXhr.ts'; +import type XhrProgressEvent from '../src/XhrProgressEvent.ts'; /** * diff --git a/test/UtilsTest.ts b/test/Utils.test.ts similarity index 77% rename from test/UtilsTest.ts rename to test/Utils.test.ts index 14c2e39..6dba872 100644 --- a/test/UtilsTest.ts +++ b/test/Utils.test.ts @@ -1,10 +1,11 @@ -import { assert } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; -import * as Utils from '../src/Utils'; +import * as Utils from '../src/Utils.ts'; class BlobMock { - constructor(array: any[]) { - assert.isArray(array); + constructor(array: unknown[]) { + assert.ok(Array.isArray(array)); } static testSize = 0; @@ -13,13 +14,13 @@ class BlobMock { } class FormDataMock { - private _values: any[]; + private _values: unknown[]; constructor() { this._values = []; } - append(name: any, value: any) { + append(name: unknown, value: unknown) { this._values.push(value); } @@ -88,53 +89,53 @@ describe('Utils', () => { describe('isHeaderName', () => { ['Content-Length', 'CONNECT', 'MyMethod', '!#$%&\'*+-.^_`|~'].forEach((method) => { it(`accepts '${method}' as a header name`, () => { - assert.isTrue(Utils.isHeaderName(method)); + assert.ok(Utils.isHeaderName(method)); }); }); it('rejects invalid header names', () => { - assert.isFalse(Utils.isHeaderName('\\')); - assert.isFalse(Utils.isHeaderName(';')); + assert.ok(!Utils.isHeaderName('\\')); + assert.ok(!Utils.isHeaderName(';')); }); }); describe('isHeaderValue', () => { ['value', '', 'gzip , chunked', 'abrowser/0.001 (C O M M E N T)', '", , ,"'].forEach((method) => { it(`accepts '${method}' as a header value`, () => { - assert.isTrue(Utils.isHeaderValue(method)); + assert.ok(Utils.isHeaderValue(method)); }); }); it('rejects invalid header values', () => { - assert.isFalse(Utils.isHeaderValue(' with leading space')); - assert.isFalse(Utils.isHeaderValue('with trailing space ')); - assert.isFalse(Utils.isHeaderValue('with null (\0) char')); + assert.ok(!Utils.isHeaderValue(' with leading space')); + assert.ok(!Utils.isHeaderValue('with trailing space ')); + assert.ok(!Utils.isHeaderValue('with null (\0) char')); }); }); describe('isRequestMethodForbidden', () => { ['CONNECT', 'TRACE', 'TRACK'].forEach((method) => { it(`forbids '${method}'`, () => { - assert.isTrue(Utils.isRequestMethodForbidden(method)); - assert.isTrue(Utils.isRequestMethodForbidden(method.toLowerCase())); + assert.ok(Utils.isRequestMethodForbidden(method)); + assert.ok(Utils.isRequestMethodForbidden(method.toLowerCase())); }); }); it('accepts valid methods', () => { - assert.isFalse(Utils.isRequestMethodForbidden('MyMethod')); + assert.ok(!Utils.isRequestMethodForbidden('MyMethod')); }); }); describe('isRequestMethod', () => { ['get', 'CONNECT', 'MyMethod', '!#$%&\'*+-.^_`|~'].forEach((method) => { it(`accepts '${method}' as a method`, () => { - assert.isTrue(Utils.isRequestMethod(method)); + assert.ok(Utils.isRequestMethod(method)); }); }); it('rejects invalid methods', () => { - assert.isFalse(Utils.isRequestMethod('\\')); - assert.isFalse(Utils.isRequestMethod(';')); + assert.ok(!Utils.isRequestMethod('\\')); + assert.ok(!Utils.isRequestMethod(';')); }); }); @@ -153,15 +154,15 @@ describe('Utils', () => { describe('isRequestHeaderForbidden', () => { ['Content-Length', 'proxy-123', 'sec-234'].forEach((header) => { it(`forbids '${header}'`, () => { - assert.isTrue(Utils.isRequestHeaderForbidden(header)); - assert.isTrue(Utils.isRequestHeaderForbidden(header.toUpperCase())); - assert.isTrue(Utils.isRequestHeaderForbidden(header.toLowerCase())); + assert.ok(Utils.isRequestHeaderForbidden(header)); + assert.ok(Utils.isRequestHeaderForbidden(header.toUpperCase())); + assert.ok(Utils.isRequestHeaderForbidden(header.toLowerCase())); }); }); it('accepts valid headers', () => { - assert.isFalse(Utils.isRequestMethodForbidden('My-Header')); - assert.isFalse(Utils.isRequestMethodForbidden('My-Proxy-123')); + assert.ok(!Utils.isRequestMethodForbidden('My-Header')); + assert.ok(!Utils.isRequestMethodForbidden('My-Proxy-123')); }); }); diff --git a/test/XhrEventTargetTest.ts b/test/XhrEventTarget.test.ts similarity index 92% rename from test/XhrEventTargetTest.ts rename to test/XhrEventTarget.test.ts index 15a02e1..825b5c6 100644 --- a/test/XhrEventTargetTest.ts +++ b/test/XhrEventTarget.test.ts @@ -1,7 +1,8 @@ -import { assert } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; -import XhrEventTarget from '../src/XhrEventTarget'; -import { XHR_PROGRESS_EVENT_NAMES } from '../src/XhrProgressEventsNames'; +import XhrEventTarget from '../src/XhrEventTarget.ts'; +import { XHR_PROGRESS_EVENT_NAMES } from '../src/XhrProgressEventsNames.ts'; describe('EventTarget', () => { describe('addEventListener()', () => { @@ -144,14 +145,14 @@ describe('EventTarget', () => { let propertyHandlerCount = 0; eventTarget[`on${event.type}`] = function onEventListener(e) { propertyHandlerCount += 1; - assert.strictEqual(this, eventTarget as any); - assert.strictEqual(e, event as any, 'event parameter'); + assert.strictEqual(this, eventTarget); + assert.strictEqual(e, event, 'event parameter'); }; let listenerCount = 0; eventTarget.addEventListener(event.type, function propertyEventListener(e) { listenerCount += 1; assert.strictEqual(this, eventTarget); - assert.strictEqual(e, event as any, 'event parameter'); + assert.strictEqual(e, event, 'event parameter'); }); eventTarget.dispatchEvent(event); @@ -199,19 +200,19 @@ describe('EventTarget', () => { let callback2Called = false; function callback2() { callback2Called = true; - assert.isTrue(callback1Called, 'callback2 after callback1'); + assert.ok(callback1Called, 'callback2 after callback1'); } let callback3Called = false; function callback3() { callback3Called = true; - assert.isTrue(callback2Called, 'callback3 after callback2'); + assert.ok(callback2Called, 'callback3 after callback2'); } eventTarget.addEventListener(event.type, callback1); eventTarget.onerror = callback2; eventTarget.addEventListener(event.type, callback3, { once: true }); eventTarget.dispatchEvent({ type: 'error' }); - assert.isTrue(callback3Called, 'callback3 called'); + assert.ok(callback3Called, 'callback3 called'); }); it('should not call listeners added in dispatchEvent() listeners', () => { @@ -272,7 +273,7 @@ describe('EventTarget', () => { const context = new XhrEventTarget(); const eventTarget = new XhrEventTarget(context); eventTarget.onprogress = function listener() { - assert.strictEqual(this, context as any, 'custom context'); + assert.strictEqual(this, context, 'custom context'); }; eventTarget.dispatchEvent({ type: 'progress' }); }); @@ -280,12 +281,12 @@ describe('EventTarget', () => { it('hasListeners()', () => { const eventTarget = new XhrEventTarget(); - assert.isFalse(eventTarget.hasListeners()); + assert.ok(!eventTarget.hasListeners()); eventTarget.onerror = () => {}; - assert.isTrue(eventTarget.hasListeners()); + assert.ok(eventTarget.hasListeners()); eventTarget.onerror = null; - assert.isFalse(eventTarget.hasListeners()); + assert.ok(!eventTarget.hasListeners()); eventTarget.addEventListener('error', () => {}); - assert.isTrue(eventTarget.hasListeners()); + assert.ok(eventTarget.hasListeners()); }); }); diff --git a/test/tsconfig.json b/test/tsconfig.json index 7d4823d..105cf08 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,19 +1,9 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - // ts-node only has experimental support for ES modules for now - // https://stackoverflow.com/questions/59042716/trying-es6-style-import-gives-cannot-use-import-statement-outside-a-module - // https://github.com/mochajs/mocha-examples/tree/master/packages/typescript#es-modules - "module": "node16", - "moduleResolution": "node16", - "types": [ - "mocha", "node", ], - - // Not comptabile with the commonjs module options above - "verbatimModuleSyntax": false, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ }, "include": ["./**/*.ts"], } diff --git a/tsconfig.base.json b/tsconfig.base.json index 94ce24c..7f7eac2 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,24 +1,25 @@ { "compilerOptions": { /* Language and Environment */ - "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ "lib": [ /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // Restrict the lib to the minimal set of features that are needed. + // Restrict the lib to the minimal set of features that we need. "dom", "dom.iterable", - "es2020", + "es2021", ], /* Modules */ - "module": "es2020", /* Specify what module code is generated. */ + "module": "preserve", /* Specify what module code is generated. */ "moduleResolution": "bundler", /* Specify how TypeScript looks up a file from a given module specifier. */ "types": [ /* Specify type package names to be included without being referenced in a source file. */ - // Restrict the types to the minimal set of features that are needed. + // Restrict the types to the minimal set of features that we need. "node", ], + "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ /* Emit */ - "newLine": "lf", /* Set the newline character for emitting files. */ + "noEmit": true, /* Disable emitting files from a compilation. */ /* Interop Constraints */ "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */