diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e24ef2f6..7611fca7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,24 +1,24 @@ version: 2 updates: - - package-ecosystem: 'github-actions' - directory: '/' - target-branch: 'main' + - package-ecosystem: github-actions + directory: / + target-branch: main schedule: - interval: 'weekly' - - package-ecosystem: 'npm' - directory: '/' - target-branch: 'main' + interval: weekly + - package-ecosystem: npm + directory: / + target-branch: main schedule: - interval: 'weekly' + interval: weekly ignore: - - dependency-name: 'chai' + - dependency-name: chai update-types: - - 'version-update:semver-major' - - dependency-name: 'esbuild' + - version-update:semver-major + - dependency-name: esbuild update-types: - - 'version-update:semver-minor' - - package-ecosystem: 'gitsubmodule' - directory: '/' - target-branch: 'main' + - version-update:semver-minor + - package-ecosystem: gitsubmodule + directory: / + target-branch: main schedule: - interval: 'daily' + interval: daily diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 53280fd8..2900f7a0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -16,9 +16,9 @@ permissions: on: push: - branches: [main] + branches: [typescript] pull_request: - branches: [main] + branches: [typescript] schedule: - cron: 26 6 * * 5 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 9d344e61..923f9dbb 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -2,7 +2,9 @@ name: Test coverage on: push: - branches: [main] + branches: [main, typescript] + pull_request: + branches: [main, typescript] permissions: contents: read @@ -22,7 +24,7 @@ jobs: - name: Download dependencies run: npm ci - name: Generate coverage report - run: ./node_modules/.bin/jest --coverage + run: npm run coverage - name: Upload coverage to Quality uses: qltysh/qlty-action/coverage@v2 with: diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml index 48253487..ae6cbad8 100644 --- a/.github/workflows/deploy-pages.yml +++ b/.github/workflows/deploy-pages.yml @@ -4,11 +4,11 @@ name: Deploy Vite App to GitHub Pages on: push: branches: - - main + - typescript # Allow only one concurrent deployment, cancelling any previously running ones. concurrency: - group: 'pages' + group: pages cancel-in-progress: true permissions: {} @@ -28,9 +28,9 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v6 with: - node-version: 22 + node-version: lts/* # Cache npm dependencies to speed up future builds - cache: 'npm' + cache: npm cache-dependency-path: | package-lock.json browser/package-lock.json diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 26a801a5..073eacef 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,8 +14,8 @@ jobs: # Setup .npmrc file to publish to npm - uses: actions/setup-node@v6 with: - node-version: 'lts/*' - registry-url: 'https://registry.npmjs.org' + node-version: lts/* + registry-url: https://registry.npmjs.org - name: Update npm run: npm install -g npm@latest - run: npm ci diff --git a/.github/workflows/test-types.yml b/.github/workflows/test-types.yml deleted file mode 100644 index df04fb2e..00000000 --- a/.github/workflows/test-types.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Test TypeScript Definitions - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - test-types: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [22.x, lts/*] - - steps: - - uses: actions/checkout@v6 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v6 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Build the package - run: npm run build - - - name: Pack the package - run: npm pack - - - name: Create test directory - run: mkdir -p test-package - - - name: Install packed package - run: | - cd test-package - npm init -y - npm install ../hed-validator-*.tgz - npm install typescript @types/node tsx - - - name: Copy test files and tsconfig - run: | - cp types/test.ts test-package/ - cp types/tsconfig.json test-package/ - cp tsconfig.json test-package/tsconfig.base.json - cp scripts/runtime-test.template.ts test-package/runtime-test.ts - - - name: Update tsconfig paths - run: | - cd test-package - sed -i 's|"../tsconfig.json"|"./tsconfig.base.json"|' tsconfig.json - sed -i 's|"baseUrl": ".."|"baseUrl": "."|' tsconfig.json - - - name: Test TypeScript definitions - run: | - cd test-package - npx tsc --project tsconfig.json - - - name: Test runtime execution (basic smoke test) - run: | - cd test-package - npx tsx runtime-test.ts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4e8e793c..ac74ae7c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,20 +2,20 @@ name: Tests on: push: - branches: [main] + branches: [main, typescript] pull_request: - branches: [main] + branches: [main, typescript] permissions: contents: read jobs: - Build: + Main: runs-on: ubuntu-latest strategy: matrix: - node-version: [22.x, lts/*, 'node'] + node-version: [22.x, lts/*, node] steps: - name: Check out the code @@ -33,12 +33,12 @@ jobs: - name: Test with Node.js ${{ matrix.node-version }} run: npm test - JSON-spec-tests: + JSON: runs-on: ubuntu-latest strategy: matrix: - node-version: [22.x, lts/*, 'node'] + node-version: [22.x, lts/*, node] steps: - name: Check out the code @@ -56,12 +56,12 @@ jobs: - name: Run JSON spec tests with Node.js ${{ matrix.node-version }} run: npm run testSpecs - Browser-tests: + Browser: runs-on: ubuntu-latest strategy: matrix: - node-version: [22.x, lts/*, 'node'] + node-version: [22.x, lts/*, node] steps: - name: Check out the code diff --git a/babel.config.js b/babel.config.js index 67dc225e..49218f78 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,12 +1,14 @@ -module.exports = { +export default { presets: [ - ['@babel/preset-env', { - targets: { - node: '22' + [ + '@babel/preset-env', + { + targets: { + node: '22', + }, }, - modules: 'commonjs' - }] + ], + '@babel/preset-typescript', ], - plugins: [] + plugins: [], } - diff --git a/browser/src/bids/BidsWebAccessor.js b/browser/src/bids/BidsWebAccessor.js index c22c8f7c..9090f17c 100644 --- a/browser/src/bids/BidsWebAccessor.js +++ b/browser/src/bids/BidsWebAccessor.js @@ -1,6 +1,6 @@ -import { BidsFileAccessor } from '../../../src/bids/datasetParser.js' -import { buildSchemasFromVersion } from '../schema/init.js' -import { BidsHedIssue } from '../../../src/bids/types/issues.js' +import { BidsFileAccessor } from '../../../src/bids/datasetParser' +import { buildSchemasFromVersion } from '../schema/init' +import { BidsHedIssue } from '../../../src/bids/types/issues' /** * Build HED schemas from a dataset description for the browser environment. @@ -90,8 +90,7 @@ export class BidsWebAccessor extends BidsFileAccessor { * @private */ constructor(datasetRootDirectory, fileMap) { - super(datasetRootDirectory, fileMap) - this.schemaBuilder = buildBidsSchemas + super(datasetRootDirectory, fileMap, buildBidsSchemas) } async getFileContent(relativePath) { diff --git a/browser/src/bids/BidsWebAccessor.spec.js b/browser/src/bids/BidsWebAccessor.spec.js index 90af75e4..b7095aa5 100644 --- a/browser/src/bids/BidsWebAccessor.spec.js +++ b/browser/src/bids/BidsWebAccessor.spec.js @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { BidsWebAccessor } from './BidsWebAccessor.js' +import { BidsWebAccessor } from './BidsWebAccessor' // Mock File object for BidsWebAccessor tests const MockFile = class { diff --git a/browser/src/schema/init.js b/browser/src/schema/init.js index 92f7d66e..ec6ec277 100644 --- a/browser/src/schema/init.js +++ b/browser/src/schema/init.js @@ -7,11 +7,9 @@ import zip from 'lodash/zip' import { loadSchema } from './loader' -import { setParent } from '../../../src/utils/xml.js' - import SchemaParser from '../../../src/schema/parser' import PartneredSchemaMerger from '../../../src/schema/schemaMerger' -import { Schema, Schemas } from '../../../src/schema/containers' +import { Schema, HedSchemas } from '../../../src/schema/containers' import { IssueError } from '../../../src/issues/issues' import { splitStringTrimAndRemoveBlanks } from '../../../src/utils/string' import { SchemasSpec } from '../../../src/schema/specs' @@ -20,12 +18,10 @@ import { SchemasSpec } from '../../../src/schema/specs' * Build a single schema container object from an XML file. * * @param {Object} xmlData The schema's XML data - * @returns {Schema} The HED schema object. + * @returns {HedSchema} The HED schema object. */ const buildSchemaObject = function (xmlData) { - const rootElement = xmlData.HED - setParent(rootElement, null) - const schemaEntries = new SchemaParser(rootElement).parse() + const schemaEntries = new SchemaParser(xmlData.HED).parse() return new Schema(xmlData, schemaEntries) } @@ -33,7 +29,7 @@ const buildSchemaObject = function (xmlData) { * Build a single merged schema container object from one or more XML files. * * @param {Object[]} xmlData The schemas' XML data. - * @returns {Schema} The HED schema object. + * @returns {HedSchema} The HED schema object. */ const buildSchemaObjects = function (xmlData) { const schemas = xmlData.map((data) => buildSchemaObject(data)) @@ -48,7 +44,7 @@ const buildSchemaObjects = function (xmlData) { * Build a schema collection object from a schema specification. * * @param {SchemasSpec} schemaSpecs The description of which schemas to use. - * @returns {Promise} The schema container object and any issues found. + * @returns {Promise} The schema container object and any issues found. */ export async function buildSchemas(schemaSpecs) { const schemaPrefixes = Array.from(schemaSpecs.data.keys()) @@ -62,14 +58,14 @@ export async function buildSchemas(schemaSpecs) { ) const schemaObjects = schemaXmlData.map(buildSchemaObjects) const schemas = new Map(zip(schemaPrefixes, schemaObjects)) - return new Schemas(schemas) + return new HedSchemas(schemas) } /** * Build HED schemas from a version specification string. * * @param {string} hedVersionString The HED version specification string (can contain comma-separated versions). - * @returns {Promise} A Promise that resolves to the built schemas. + * @returns {Promise} A Promise that resolves to the built schemas. * @throws {IssueError} If the schema specification is invalid or schemas cannot be built. */ export async function buildSchemasFromVersion(hedVersionString) { diff --git a/browser/src/schema/loader.js b/browser/src/schema/loader.js index 0216d270..8d07adfc 100644 --- a/browser/src/schema/loader.js +++ b/browser/src/schema/loader.js @@ -2,8 +2,8 @@ /* Imports */ import { IssueError } from '../../../src/issues/issues' -import { parseSchemaXML } from '../../../src/utils/xml.js' -import { schemaData } from './vite-importer.js' +import parseSchemaXML from '../../../src/utils/xml.js' +import { schemaData } from './vite-importer' /** * Load schema XML data from a schema version or path description. diff --git a/browser/src/schema/loader.spec.js b/browser/src/schema/loader.spec.js index 7eaabc02..899b826b 100644 --- a/browser/src/schema/loader.spec.js +++ b/browser/src/schema/loader.spec.js @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest' -import { loadSchema } from './loader.js' -import { SchemaSpec } from '../../../src/schema/specs.js' +import { loadSchema } from './loader' +import { SchemaSpec } from '../../../src/schema/specs.ts' describe('Browser Schema Loader', () => { it('should return undefined when loading a bundled schema in test environment', async () => { diff --git a/browser/src/utils/files.js b/browser/src/utils/files.js index 11555e93..7e81b667 100644 --- a/browser/src/utils/files.js +++ b/browser/src/utils/files.js @@ -8,9 +8,9 @@ import { IssueError } from '../../../src/issues/issues' * @throws {IssueError} Always throws since local file reading is not supported in browsers. */ export async function readFile(fileName) { - IssueError.generateAndThrow('fileReadError', { - fileName: fileName, - message: 'Local file reading is not supported in browser environment' + IssueError.generateAndThrow('fileReadError', { + fileName: fileName, + message: 'Local file reading is not supported in browser environment', }) } @@ -26,7 +26,7 @@ export async function readHTTPSFile(url) { if (!response.ok) { IssueError.generateAndThrow('networkReadError', { url: url, - statusCode: response.status, + statusCode: response.status.toString(), statusText: response.statusText, }) } diff --git a/browser/src/validate_dataset.jsx b/browser/src/validate_dataset.jsx index 41f7052d..012e3b01 100644 --- a/browser/src/validate_dataset.jsx +++ b/browser/src/validate_dataset.jsx @@ -3,9 +3,9 @@ import { createRoot } from 'react-dom/client' import { FolderInput } from './components/FolderInput' import { ErrorDisplay } from './components/ErrorDisplay' import { BidsWebAccessor } from './bids/BidsWebAccessor.js' -import { BidsDataset } from '@hed-javascript-root/src/bids/types/dataset.js' -import { IssueError } from '@hed-javascript-root/src/issues/issues.js' -import { BidsHedIssue } from '@hed-javascript-root/src/bids/types/issues.js' +import { BidsDataset } from '@hed-javascript-root/src/bids/types/dataset' +import { IssueError } from '@hed-javascript-root/src/issues/issues' +import { BidsHedIssue } from '@hed-javascript-root/src/bids/types/issues' // --- Add TailwindCSS to the document head for immediate styling --- ;(() => { diff --git a/browser/src/validate_file.jsx b/browser/src/validate_file.jsx index c0f630b5..fe544ee9 100644 --- a/browser/src/validate_file.jsx +++ b/browser/src/validate_file.jsx @@ -10,8 +10,8 @@ import { buildBidsSchemas, } from '@hed-javascript-root/src/bids/index.js' import { buildSchemasFromVersion } from './schema/init.js' -import { generateIssue, IssueError } from '@hed-javascript-root/src/issues/issues.js' -import parseTSV from '@hed-javascript-root/src/bids/tsvParser.js' +import { generateIssue, IssueError } from '@hed-javascript-root/src/issues/issues' +import parseTSV from '@hed-javascript-root/src/bids/tsvParser' // --- Main Application Component --- diff --git a/index.js b/index.js index 2052ab87..869c5d7e 100644 --- a/index.js +++ b/index.js @@ -8,9 +8,9 @@ export { BidsJsonFile, BidsSidecar, BidsHedIssue, - buildBidsSchemas, BidsFileAccessor, BidsDirectoryAccessor, + buildBidsSchemas, } from './src/bids' export { IssueError, Issue } from './src/issues/issues' diff --git a/jest.config.js b/jest.config.js index d5fa2985..cb0192cb 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,19 +1,16 @@ -module.exports = { +export default { testEnvironment: 'node', transform: { - '\\.xml$': '/xml-transformer.js', + '\\.xml$': '/xml-transformer.cjs', '^.+\\.(js|jsx)$': 'babel-jest', + '^.+\\.(ts|tsx)$': 'babel-jest', }, transformIgnorePatterns: ['/node_modules/(?!unicode-name|semver)'], - testPathIgnorePatterns: ['node_modules/', '/types/test.ts', '/browser/'], + testPathIgnorePatterns: ['node_modules/', '/browser/'], moduleNameMapper: { '\\.(css|less|scss|sass)$': 'identity-obj-proxy', }, - collectCoverageFrom: [ - 'src/**/*.js', - '!src/**/*.spec.js', - '!src/**/*.test.js', - ], + collectCoverageFrom: ['src/**/*.(js|ts)', '!src/**/*.spec.js', '!src/**/*.test.js'], coveragePathIgnorePatterns: [ '/node_modules/', '/tests/', diff --git a/package-lock.json b/package-lock.json index 62217930..4499f9f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,17 +13,21 @@ "fast-xml-parser": "^5.2.5", "lodash": "^4.17.21", "pluralize": "^8.0.0", - "semver": "^7.6.0", + "semver": "^7.7.2", "unicode-name": "^1.0.2" }, "devDependencies": { "@babel/core": "^7.28.0", "@babel/preset-env": "^7.28.0", + "@babel/preset-typescript": "^7.27.1", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^9.18.0", "@jest/globals": "^30.0.0", + "@types/lodash": "^4.17.20", "@types/node": "^25.0.2", - "babel-jest": "^30.0.5", + "@types/pluralize": "^0.0.33", + "@types/semver": "^7.7.0", + "babel-jest": "^30.2.0", "chai": "^4.3.6", "esbuild": "^0.25.0", "esbuild-plugins-node-modules-polyfill": "^1.7.1", @@ -39,6 +43,7 @@ "prettier": "^3.2.5", "pretty-quick": "^4.0.0", "ts-jest": "^29.4.0", + "ts-node": "^10.9.2", "tsx": "^4.20.3", "typedoc": "^0.28.0", "typescript": "^5.4.5" @@ -49,8 +54,6 @@ }, "node_modules/@babel/code-frame": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { @@ -63,9 +66,7 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "version": "7.28.0", "dev": true, "license": "MIT", "engines": { @@ -73,21 +74,19 @@ } }, "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "version": "7.28.4", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", + "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -105,8 +104,6 @@ }, "node_modules/@babel/core/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, "license": "ISC", "bin": { @@ -114,14 +111,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.28.3", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -132,8 +127,6 @@ }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "dev": true, "license": "MIT", "dependencies": { @@ -145,8 +138,6 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "license": "MIT", "dependencies": { @@ -162,8 +153,6 @@ }, "node_modules/@babel/helper-compilation-targets/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, "license": "ISC", "bin": { @@ -172,8 +161,6 @@ }, "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", - "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", "dev": true, "license": "MIT", "dependencies": { @@ -194,8 +181,6 @@ }, "node_modules/@babel/helper-create-class-features-plugin/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, "license": "ISC", "bin": { @@ -204,8 +189,6 @@ }, "node_modules/@babel/helper-create-regexp-features-plugin": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", "dev": true, "license": "MIT", "dependencies": { @@ -222,8 +205,6 @@ }, "node_modules/@babel/helper-create-regexp-features-plugin/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, "license": "ISC", "bin": { @@ -232,8 +213,6 @@ }, "node_modules/@babel/helper-define-polyfill-provider": { "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", - "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", "dev": true, "license": "MIT", "dependencies": { @@ -249,8 +228,6 @@ }, "node_modules/@babel/helper-globals": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, "license": "MIT", "engines": { @@ -259,8 +236,6 @@ }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", "dev": true, "license": "MIT", "dependencies": { @@ -273,8 +248,6 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "license": "MIT", "dependencies": { @@ -287,8 +260,6 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, "license": "MIT", "dependencies": { @@ -305,8 +276,6 @@ }, "node_modules/@babel/helper-optimise-call-expression": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "dev": true, "license": "MIT", "dependencies": { @@ -318,8 +287,6 @@ }, "node_modules/@babel/helper-plugin-utils": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", "engines": { @@ -328,8 +295,6 @@ }, "node_modules/@babel/helper-remap-async-to-generator": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", "dev": true, "license": "MIT", "dependencies": { @@ -346,8 +311,6 @@ }, "node_modules/@babel/helper-replace-supers": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", "dev": true, "license": "MIT", "dependencies": { @@ -364,8 +327,6 @@ }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "dev": true, "license": "MIT", "dependencies": { @@ -378,8 +339,6 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -387,9 +346,7 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.27.1", "dev": true, "license": "MIT", "engines": { @@ -398,8 +355,6 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -408,8 +363,6 @@ }, "node_modules/@babel/helper-wrap-function": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", "dev": true, "license": "MIT", "dependencies": { @@ -423,8 +376,6 @@ }, "node_modules/@babel/helpers": { "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "license": "MIT", "dependencies": { @@ -436,13 +387,11 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "version": "7.28.4", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.28.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -452,14 +401,12 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", - "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", + "version": "7.27.1", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -470,8 +417,6 @@ }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", - "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", "dev": true, "license": "MIT", "dependencies": { @@ -486,8 +431,6 @@ }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", "dev": true, "license": "MIT", "dependencies": { @@ -502,8 +445,6 @@ }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", "dev": true, "license": "MIT", "dependencies": { @@ -520,8 +461,6 @@ }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", - "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", "dev": true, "license": "MIT", "dependencies": { @@ -537,8 +476,6 @@ }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, "license": "MIT", "engines": { @@ -550,8 +487,6 @@ }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, "license": "MIT", "dependencies": { @@ -563,8 +498,6 @@ }, "node_modules/@babel/plugin-syntax-bigint": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, "license": "MIT", "dependencies": { @@ -576,8 +509,6 @@ }, "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "license": "MIT", "dependencies": { @@ -589,8 +520,6 @@ }, "node_modules/@babel/plugin-syntax-class-static-block": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, "license": "MIT", "dependencies": { @@ -605,8 +534,6 @@ }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", "dev": true, "license": "MIT", "dependencies": { @@ -621,8 +548,6 @@ }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, "license": "MIT", "dependencies": { @@ -637,8 +562,6 @@ }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "license": "MIT", "dependencies": { @@ -650,8 +573,6 @@ }, "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "license": "MIT", "dependencies": { @@ -663,8 +584,6 @@ }, "node_modules/@babel/plugin-syntax-jsx": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", "dev": true, "license": "MIT", "dependencies": { @@ -679,8 +598,6 @@ }, "node_modules/@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "license": "MIT", "dependencies": { @@ -692,8 +609,6 @@ }, "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -705,8 +620,6 @@ }, "node_modules/@babel/plugin-syntax-numeric-separator": { "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "license": "MIT", "dependencies": { @@ -718,8 +631,6 @@ }, "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "license": "MIT", "dependencies": { @@ -731,8 +642,6 @@ }, "node_modules/@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "license": "MIT", "dependencies": { @@ -744,8 +653,6 @@ }, "node_modules/@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, "license": "MIT", "dependencies": { @@ -757,8 +664,6 @@ }, "node_modules/@babel/plugin-syntax-private-property-in-object": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, "license": "MIT", "dependencies": { @@ -773,8 +678,6 @@ }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "license": "MIT", "dependencies": { @@ -789,8 +692,6 @@ }, "node_modules/@babel/plugin-syntax-typescript": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -805,8 +706,6 @@ }, "node_modules/@babel/plugin-syntax-unicode-sets-regex": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, "license": "MIT", "dependencies": { @@ -822,8 +721,6 @@ }, "node_modules/@babel/plugin-transform-arrow-functions": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", "dev": true, "license": "MIT", "dependencies": { @@ -838,8 +735,6 @@ }, "node_modules/@babel/plugin-transform-async-generator-functions": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", - "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", "dev": true, "license": "MIT", "dependencies": { @@ -856,8 +751,6 @@ }, "node_modules/@babel/plugin-transform-async-to-generator": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", "dev": true, "license": "MIT", "dependencies": { @@ -874,8 +767,6 @@ }, "node_modules/@babel/plugin-transform-block-scoped-functions": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", "dev": true, "license": "MIT", "dependencies": { @@ -889,9 +780,7 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz", - "integrity": "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==", + "version": "7.28.0", "dev": true, "license": "MIT", "dependencies": { @@ -906,8 +795,6 @@ }, "node_modules/@babel/plugin-transform-class-properties": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", - "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", "dev": true, "license": "MIT", "dependencies": { @@ -923,8 +810,6 @@ }, "node_modules/@babel/plugin-transform-class-static-block": { "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", - "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", "dev": true, "license": "MIT", "dependencies": { @@ -939,9 +824,7 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", - "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", + "version": "7.28.3", "dev": true, "license": "MIT", "dependencies": { @@ -950,7 +833,7 @@ "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.4" + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -961,8 +844,6 @@ }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", "dev": true, "license": "MIT", "dependencies": { @@ -977,14 +858,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", + "version": "7.28.0", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -995,8 +874,6 @@ }, "node_modules/@babel/plugin-transform-dotall-regex": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", "dev": true, "license": "MIT", "dependencies": { @@ -1012,8 +889,6 @@ }, "node_modules/@babel/plugin-transform-duplicate-keys": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1028,8 +903,6 @@ }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1045,8 +918,6 @@ }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", - "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", "dev": true, "license": "MIT", "dependencies": { @@ -1061,8 +932,6 @@ }, "node_modules/@babel/plugin-transform-explicit-resource-management": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", - "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1077,9 +946,7 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz", - "integrity": "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==", + "version": "7.27.1", "dev": true, "license": "MIT", "dependencies": { @@ -1094,8 +961,6 @@ }, "node_modules/@babel/plugin-transform-export-namespace-from": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1110,8 +975,6 @@ }, "node_modules/@babel/plugin-transform-for-of": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", "dev": true, "license": "MIT", "dependencies": { @@ -1127,8 +990,6 @@ }, "node_modules/@babel/plugin-transform-function-name": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1145,8 +1006,6 @@ }, "node_modules/@babel/plugin-transform-json-strings": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", - "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1161,8 +1020,6 @@ }, "node_modules/@babel/plugin-transform-literals": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", "dev": true, "license": "MIT", "dependencies": { @@ -1176,9 +1033,7 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz", - "integrity": "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==", + "version": "7.27.1", "dev": true, "license": "MIT", "dependencies": { @@ -1193,8 +1048,6 @@ }, "node_modules/@babel/plugin-transform-member-expression-literals": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1209,8 +1062,6 @@ }, "node_modules/@babel/plugin-transform-modules-amd": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", "dev": true, "license": "MIT", "dependencies": { @@ -1226,8 +1077,6 @@ }, "node_modules/@babel/plugin-transform-modules-commonjs": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", "dev": true, "license": "MIT", "dependencies": { @@ -1242,16 +1091,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz", - "integrity": "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==", + "version": "7.27.1", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.28.3", + "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.5" + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1262,8 +1109,6 @@ }, "node_modules/@babel/plugin-transform-modules-umd": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", "dev": true, "license": "MIT", "dependencies": { @@ -1279,8 +1124,6 @@ }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", "dev": true, "license": "MIT", "dependencies": { @@ -1296,8 +1139,6 @@ }, "node_modules/@babel/plugin-transform-new-target": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1312,8 +1153,6 @@ }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", - "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", "dev": true, "license": "MIT", "dependencies": { @@ -1328,8 +1167,6 @@ }, "node_modules/@babel/plugin-transform-numeric-separator": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", - "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", "dev": true, "license": "MIT", "dependencies": { @@ -1343,9 +1180,7 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz", - "integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==", + "version": "7.28.0", "dev": true, "license": "MIT", "dependencies": { @@ -1353,7 +1188,7 @@ "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.4" + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1364,8 +1199,6 @@ }, "node_modules/@babel/plugin-transform-object-super": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", "dev": true, "license": "MIT", "dependencies": { @@ -1381,8 +1214,6 @@ }, "node_modules/@babel/plugin-transform-optional-catch-binding": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", - "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1396,9 +1227,7 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz", - "integrity": "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==", + "version": "7.27.1", "dev": true, "license": "MIT", "dependencies": { @@ -1414,8 +1243,6 @@ }, "node_modules/@babel/plugin-transform-parameters": { "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", "dev": true, "license": "MIT", "dependencies": { @@ -1430,8 +1257,6 @@ }, "node_modules/@babel/plugin-transform-private-methods": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", - "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", "dev": true, "license": "MIT", "dependencies": { @@ -1447,8 +1272,6 @@ }, "node_modules/@babel/plugin-transform-private-property-in-object": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", - "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1465,8 +1288,6 @@ }, "node_modules/@babel/plugin-transform-property-literals": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1480,9 +1301,7 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz", - "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==", + "version": "7.28.3", "dev": true, "license": "MIT", "dependencies": { @@ -1497,8 +1316,6 @@ }, "node_modules/@babel/plugin-transform-regexp-modifiers": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", - "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", "dev": true, "license": "MIT", "dependencies": { @@ -1514,8 +1331,6 @@ }, "node_modules/@babel/plugin-transform-reserved-words": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", "dev": true, "license": "MIT", "dependencies": { @@ -1530,8 +1345,6 @@ }, "node_modules/@babel/plugin-transform-shorthand-properties": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1546,8 +1359,6 @@ }, "node_modules/@babel/plugin-transform-spread": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1563,8 +1374,6 @@ }, "node_modules/@babel/plugin-transform-sticky-regex": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", "dev": true, "license": "MIT", "dependencies": { @@ -1579,8 +1388,6 @@ }, "node_modules/@babel/plugin-transform-template-literals": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", "dev": true, "license": "MIT", "dependencies": { @@ -1595,8 +1402,6 @@ }, "node_modules/@babel/plugin-transform-typeof-symbol": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", "dev": true, "license": "MIT", "dependencies": { @@ -1609,10 +1414,26 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", "dev": true, "license": "MIT", "dependencies": { @@ -1627,8 +1448,6 @@ }, "node_modules/@babel/plugin-transform-unicode-property-regex": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", - "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1644,8 +1463,6 @@ }, "node_modules/@babel/plugin-transform-unicode-regex": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", "dev": true, "license": "MIT", "dependencies": { @@ -1661,8 +1478,6 @@ }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", - "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", "dev": true, "license": "MIT", "dependencies": { @@ -1677,17 +1492,15 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.5.tgz", - "integrity": "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==", + "version": "7.28.3", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.5", + "@babel/compat-data": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", @@ -1700,42 +1513,42 @@ "@babel/plugin-transform-async-generator-functions": "^7.28.0", "@babel/plugin-transform-async-to-generator": "^7.27.1", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.5", + "@babel/plugin-transform-block-scoping": "^7.28.0", "@babel/plugin-transform-class-properties": "^7.27.1", "@babel/plugin-transform-class-static-block": "^7.28.3", - "@babel/plugin-transform-classes": "^7.28.4", + "@babel/plugin-transform-classes": "^7.28.3", "@babel/plugin-transform-computed-properties": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-dotall-regex": "^7.27.1", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.0", - "@babel/plugin-transform-exponentiation-operator": "^7.28.5", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.27.1", "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-modules-systemjs": "^7.28.5", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-numeric-separator": "^7.27.1", - "@babel/plugin-transform-object-rest-spread": "^7.28.4", + "@babel/plugin-transform-object-rest-spread": "^7.28.0", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.28.5", + "@babel/plugin-transform-optional-chaining": "^7.27.1", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.28.4", + "@babel/plugin-transform-regenerator": "^7.28.3", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", @@ -1763,8 +1576,6 @@ }, "node_modules/@babel/preset-env/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, "license": "ISC", "bin": { @@ -1773,8 +1584,6 @@ }, "node_modules/@babel/preset-modules": { "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "license": "MIT", "dependencies": { @@ -1786,10 +1595,26 @@ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/preset-typescript": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/template": { "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "license": "MIT", "dependencies": { @@ -1802,18 +1627,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.28.4", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", + "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/types": "^7.28.4", "debug": "^4.3.1" }, "engines": { @@ -1821,14 +1644,12 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.28.4", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1836,881 +1657,380 @@ }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, "license": "MIT" }, - "node_modules/@emnapi/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", - "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", - "dev": true, - "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "node_modules/@esbuild/linux-x64": { + "version": "0.25.11", "cpu": [ - "ppc64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "aix" + "linux" ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=18" + "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/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=18" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/config-array": { + "version": "0.21.0", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/config-helpers": { + "version": "0.4.0", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/core": { + "version": "0.16.0", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/js": { + "version": "9.38.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], + "node_modules/@eslint/object-schema": { + "version": "2.1.6", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], + "node_modules/@eslint/plugin-kit": { + "version": "0.4.0", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0", + "levn": "^0.4.1" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], + "node_modules/@gerrit0/mini-shiki": { + "version": "3.13.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@shikijs/engine-oniguruma": "^3.13.0", + "@shikijs/langs": "^3.13.0", + "@shikijs/themes": "^3.13.0", + "@shikijs/types": "^3.13.0", + "@shikijs/vscode-textmate": "^10.0.2" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], + "node_modules/@humanfs/core": { + "version": "0.19.1", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "node": ">=18.18.0" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], + "node_modules/@humanfs/node": { + "version": "0.16.6", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, "engines": { - "node": ">=18" + "node": ">=18.18.0" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], + "node_modules/@isaacs/cliui": { + "version": "8.0.2", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, "engines": { - "node": ">=18" + "node": ">=12" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "p-locate": "^4.1.0" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "p-try": "^2.0.0" }, - "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" + "node": ">=6" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", "dev": true, "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=8" } }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=8" } }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "node_modules/@jest/console": { + "version": "30.2.0", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.15" + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@gerrit0/mini-shiki": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.17.0.tgz", - "integrity": "sha512-Bpf6WuFar20ZXL6qU6VpVl4bVQfyyYiX+6O4xrns4nkU3Mr8paeupDbS1HENpcLOYj7pN4Rkd/yCaPA0vQwKww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/engine-oniguruma": "^3.17.0", - "@shikijs/langs": "^3.17.0", - "@shikijs/themes": "^3.17.0", - "@shikijs/types": "^3.17.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "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": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@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", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", - "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", - "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "node_modules/@jest/core": { + "version": "30.2.0", "dev": true, "license": "MIT", "dependencies": { @@ -2757,8 +2077,6 @@ }, "node_modules/@jest/diff-sequences": { "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", "dev": true, "license": "MIT", "engines": { @@ -2767,8 +2085,6 @@ }, "node_modules/@jest/environment": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", "dev": true, "license": "MIT", "dependencies": { @@ -2783,8 +2099,6 @@ }, "node_modules/@jest/expect": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", "dev": true, "license": "MIT", "dependencies": { @@ -2797,8 +2111,6 @@ }, "node_modules/@jest/expect-utils": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", "dev": true, "license": "MIT", "dependencies": { @@ -2810,8 +2122,6 @@ }, "node_modules/@jest/fake-timers": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", "dev": true, "license": "MIT", "dependencies": { @@ -2828,8 +2138,6 @@ }, "node_modules/@jest/get-type": { "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", "dev": true, "license": "MIT", "engines": { @@ -2838,8 +2146,6 @@ }, "node_modules/@jest/globals": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", - "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", "dev": true, "license": "MIT", "dependencies": { @@ -2854,8 +2160,6 @@ }, "node_modules/@jest/pattern": { "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", "dev": true, "license": "MIT", "dependencies": { @@ -2868,8 +2172,6 @@ }, "node_modules/@jest/reporters": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", - "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2911,8 +2213,6 @@ }, "node_modules/@jest/schemas": { "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", "dependencies": { @@ -2924,8 +2224,6 @@ }, "node_modules/@jest/snapshot-utils": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", - "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", "dev": true, "license": "MIT", "dependencies": { @@ -2940,8 +2238,6 @@ }, "node_modules/@jest/source-map": { "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", - "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", "dev": true, "license": "MIT", "dependencies": { @@ -2955,8 +2251,6 @@ }, "node_modules/@jest/test-result": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", - "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", "dev": true, "license": "MIT", "dependencies": { @@ -2971,8 +2265,6 @@ }, "node_modules/@jest/test-sequencer": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", - "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2987,8 +2279,6 @@ }, "node_modules/@jest/transform": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", - "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", "dev": true, "license": "MIT", "dependencies": { @@ -3014,8 +2304,6 @@ }, "node_modules/@jest/types": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", "dev": true, "license": "MIT", "dependencies": { @@ -3033,8 +2321,6 @@ }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "dev": true, "license": "MIT", "dependencies": { @@ -3044,8 +2330,6 @@ }, "node_modules/@jridgewell/remapping": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3055,8 +2339,6 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", "engines": { @@ -3065,15 +2347,11 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3083,28 +2361,11 @@ }, "node_modules/@jspm/core": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@jspm/core/-/core-2.1.0.tgz", - "integrity": "sha512-3sRl+pkyFY/kLmHl0cgHiFp2xEqErA8N3ECjMs7serSUBmoJ70lBa0PG5t0IM6WJgdZNyyI0R8YFfi5wM8+mzg==", "dev": true, "license": "Apache-2.0" }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, "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, "license": "MIT", "optional": true, @@ -3114,8 +2375,6 @@ }, "node_modules/@pkgr/core": { "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true, "license": "MIT", "engines": { @@ -3126,40 +2385,32 @@ } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.17.1.tgz", - "integrity": "sha512-fsXPy4va/4iblEGS+22nP5V08IwwBcM+8xHUzSON0QmHm29/AJRghA95w9VDnxuwp9wOdJxEhfPkKp6vqcsN+w==", + "version": "3.13.0", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/types": "3.17.1", + "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "node_modules/@shikijs/langs": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.17.1.tgz", - "integrity": "sha512-YTBVN+L2j7zBuOVjNZ2XiSNQEkm/7wZ1TSc5UO77GJPcg7Rk25WSscWA7y8pW7Bo25JIU0EWchUkq/UQjOJlJA==", + "version": "3.13.0", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/types": "3.17.1" + "@shikijs/types": "3.13.0" } }, "node_modules/@shikijs/themes": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.17.1.tgz", - "integrity": "sha512-aohwwqNUB5h2ATfgrqYRPl8vyazqCiQ2wIV4xq+UzaBRHpqLMGSemkasK+vIEpl0YaendoaKUsDfpwhCqyHIaQ==", + "version": "3.13.0", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/types": "3.17.1" + "@shikijs/types": "3.13.0" } }, "node_modules/@shikijs/types": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.17.1.tgz", - "integrity": "sha512-yUFLiCnZHHJ16KbVbt3B1EzBUadU3OVpq0PEyb301m5BbuFKApQYBzJGhrK48hH/tYWSjzwcj7BSmYbBc0zntQ==", + "version": "3.13.0", "dev": true, "license": "MIT", "dependencies": { @@ -3169,22 +2420,16 @@ }, "node_modules/@shikijs/vscode-textmate": { "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "dev": true, "license": "MIT" }, "node_modules/@sinclair/typebox": { "version": "0.34.38", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.38.tgz", - "integrity": "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==", "dev": true, "license": "MIT" }, "node_modules/@sinonjs/commons": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3193,29 +2438,34 @@ }, "node_modules/@sinonjs/fake-timers": { "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.1" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "node_modules/@tsconfig/node10": { + "version": "1.0.11", "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "dev": true, + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "license": "MIT", "dependencies": { @@ -3228,8 +2478,6 @@ }, "node_modules/@types/babel__generator": { "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "license": "MIT", "dependencies": { @@ -3238,8 +2486,6 @@ }, "node_modules/@types/babel__template": { "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "license": "MIT", "dependencies": { @@ -3249,8 +2495,6 @@ }, "node_modules/@types/babel__traverse": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3259,15 +2503,11 @@ }, "node_modules/@types/estree": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, "node_modules/@types/hast": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3276,15 +2516,11 @@ }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true, "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "license": "MIT", "dependencies": { @@ -3293,8 +2529,6 @@ }, "node_modules/@types/istanbul-reports": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3303,39 +2537,46 @@ }, "node_modules/@types/json-schema": { "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.20", "dev": true, "license": "MIT" }, "node_modules/@types/node": { - "version": "25.0.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.9.tgz", - "integrity": "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==", + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" } }, + "node_modules/@types/pluralize": { + "version": "0.0.33", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/semver": { + "version": "7.7.0", + "dev": true, + "license": "MIT" + }, "node_modules/@types/stack-utils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true, "license": "MIT" }, "node_modules/@types/unist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "dev": true, "license": "MIT" }, "node_modules/@types/yargs": { "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "license": "MIT", "dependencies": { @@ -3344,204 +2585,16 @@ }, "node_modules/@types/yargs-parser": { "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true, "license": "MIT" }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true, "license": "ISC" }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", "cpu": [ "x64" ], @@ -3554,8 +2607,6 @@ }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", "cpu": [ "x64" ], @@ -3566,69 +2617,8 @@ "linux" ] }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/acorn": { "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -3640,18 +2630,25 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "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": { @@ -3667,8 +2664,6 @@ }, "node_modules/ansi-escapes": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3683,8 +2678,6 @@ }, "node_modules/ansi-regex": { "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": { @@ -3696,8 +2689,6 @@ }, "node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { @@ -3712,8 +2703,6 @@ }, "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==", "dev": true, "license": "ISC", "dependencies": { @@ -3724,17 +2713,18 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, "license": "Python-2.0" }, "node_modules/assertion-error": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, "license": "MIT", "engines": { @@ -3743,8 +2733,6 @@ }, "node_modules/babel-jest": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", - "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", "dev": true, "license": "MIT", "dependencies": { @@ -3765,8 +2753,6 @@ }, "node_modules/babel-plugin-istanbul": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", - "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, "license": "BSD-3-Clause", "workspaces": [ @@ -3785,8 +2771,6 @@ }, "node_modules/babel-plugin-jest-hoist": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", - "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", "dev": true, "license": "MIT", "dependencies": { @@ -3798,8 +2782,6 @@ }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.14", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", - "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", "dev": true, "license": "MIT", "dependencies": { @@ -3813,8 +2795,6 @@ }, "node_modules/babel-plugin-polyfill-corejs2/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, "license": "ISC", "bin": { @@ -3823,8 +2803,6 @@ }, "node_modules/babel-plugin-polyfill-corejs3": { "version": "0.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", - "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", "dev": true, "license": "MIT", "dependencies": { @@ -3837,8 +2815,6 @@ }, "node_modules/babel-plugin-polyfill-regenerator": { "version": "0.6.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", - "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", "dev": true, "license": "MIT", "dependencies": { @@ -3850,8 +2826,6 @@ }, "node_modules/babel-preset-current-node-syntax": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", "dev": true, "license": "MIT", "dependencies": { @@ -3877,8 +2851,6 @@ }, "node_modules/babel-preset-jest": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz", - "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3894,15 +2866,11 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, "license": "MIT" }, "node_modules/brace-expansion": { "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -3912,8 +2880,6 @@ }, "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": { @@ -3925,8 +2891,6 @@ }, "node_modules/browserslist": { "version": "4.25.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", - "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", "dev": true, "funding": [ { @@ -3958,8 +2922,6 @@ }, "node_modules/bs-logger": { "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, "license": "MIT", "dependencies": { @@ -3971,8 +2933,6 @@ }, "node_modules/bser": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3981,15 +2941,11 @@ }, "node_modules/buffer-from": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, "license": "MIT" }, "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": { @@ -3998,8 +2954,6 @@ }, "node_modules/camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "license": "MIT", "engines": { @@ -4008,8 +2962,6 @@ }, "node_modules/caniuse-lite": { "version": "1.0.30001733", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001733.tgz", - "integrity": "sha512-e4QKw/O2Kavj2VQTKZWrwzkt3IxOmIlU6ajRb6LP64LHpBo1J67k2Hi4Vu/TgJWsNtynurfS0uK3MaUTCPfu5Q==", "dev": true, "funding": [ { @@ -4029,8 +2981,6 @@ }, "node_modules/chai": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", "dev": true, "license": "MIT", "dependencies": { @@ -4048,8 +2998,6 @@ }, "node_modules/chai/node_modules/type-detect": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, "license": "MIT", "engines": { @@ -4058,8 +3006,6 @@ }, "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": { @@ -4075,8 +3021,6 @@ }, "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, "license": "MIT", "engines": { @@ -4085,8 +3029,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -4098,8 +3040,6 @@ }, "node_modules/ci-info": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", "dev": true, "funding": [ { @@ -4114,15 +3054,11 @@ }, "node_modules/cjs-module-lexer": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", - "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", "dev": true, "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { @@ -4136,8 +3072,6 @@ }, "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": { @@ -4146,15 +3080,11 @@ }, "node_modules/cliui/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, "license": "MIT" }, "node_modules/cliui/node_modules/string-width": { "version": "4.2.3", - "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": { @@ -4168,8 +3098,6 @@ }, "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": { @@ -4181,8 +3109,6 @@ }, "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": { @@ -4199,8 +3125,6 @@ }, "node_modules/co": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, "license": "MIT", "engines": { @@ -4210,15 +3134,11 @@ }, "node_modules/collect-v8-coverage": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true, "license": "MIT" }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4230,36 +3150,26 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, "node_modules/confbox": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", - "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", "dev": true, "license": "MIT" }, "node_modules/convert-source-map": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, "node_modules/core-js-compat": { "version": "3.45.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.0.tgz", - "integrity": "sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==", "dev": true, "license": "MIT", "dependencies": { @@ -4271,9 +3181,7 @@ } }, "node_modules/core-js-pure": { - "version": "3.47.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.47.0.tgz", - "integrity": "sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==", + "version": "3.46.0", "hasInstallScript": true, "license": "MIT", "funding": { @@ -4281,10 +3189,13 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -4298,8 +3209,6 @@ }, "node_modules/debug": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4316,8 +3225,6 @@ }, "node_modules/dedent": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", - "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -4331,8 +3238,6 @@ }, "node_modules/deep-eql": { "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dev": true, "license": "MIT", "dependencies": { @@ -4344,15 +3249,11 @@ }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/deepmerge": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "license": "MIT", "engines": { @@ -4361,32 +3262,32 @@ }, "node_modules/detect-newline": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "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, "license": "MIT" }, "node_modules/electron-to-chromium": { "version": "1.5.199", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.199.tgz", - "integrity": "sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==", "dev": true, "license": "ISC" }, "node_modules/emittery": { "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "license": "MIT", "engines": { @@ -4398,15 +3299,11 @@ }, "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/entities": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -4418,8 +3315,6 @@ }, "node_modules/error-ex": { "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4427,9 +3322,7 @@ } }, "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "version": "0.25.11", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4440,57 +3333,52 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "@esbuild/aix-ppc64": "0.25.11", + "@esbuild/android-arm": "0.25.11", + "@esbuild/android-arm64": "0.25.11", + "@esbuild/android-x64": "0.25.11", + "@esbuild/darwin-arm64": "0.25.11", + "@esbuild/darwin-x64": "0.25.11", + "@esbuild/freebsd-arm64": "0.25.11", + "@esbuild/freebsd-x64": "0.25.11", + "@esbuild/linux-arm": "0.25.11", + "@esbuild/linux-arm64": "0.25.11", + "@esbuild/linux-ia32": "0.25.11", + "@esbuild/linux-loong64": "0.25.11", + "@esbuild/linux-mips64el": "0.25.11", + "@esbuild/linux-ppc64": "0.25.11", + "@esbuild/linux-riscv64": "0.25.11", + "@esbuild/linux-s390x": "0.25.11", + "@esbuild/linux-x64": "0.25.11", + "@esbuild/netbsd-arm64": "0.25.11", + "@esbuild/netbsd-x64": "0.25.11", + "@esbuild/openbsd-arm64": "0.25.11", + "@esbuild/openbsd-x64": "0.25.11", + "@esbuild/openharmony-arm64": "0.25.11", + "@esbuild/sunos-x64": "0.25.11", + "@esbuild/win32-arm64": "0.25.11", + "@esbuild/win32-ia32": "0.25.11", + "@esbuild/win32-x64": "0.25.11" } }, "node_modules/esbuild-plugins-node-modules-polyfill": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/esbuild-plugins-node-modules-polyfill/-/esbuild-plugins-node-modules-polyfill-1.8.0.tgz", - "integrity": "sha512-0fVd8qPbqxz/8wt9cnsoUWOR+gEZbsFGbepGUcr9eTvNDVPaNFSSJ9FfcXTPqKoDIhF3WjAn8MmSuGa7nS3+Pw==", + "version": "1.7.1", "dev": true, - "hasInstallScript": true, "license": "MIT", "dependencies": { "@jspm/core": "^2.1.0", - "local-pkg": "^1.1.2", + "local-pkg": "^1.1.1", "resolve.exports": "^2.0.3" }, "engines": { "node": ">=14.0.0" }, "peerDependencies": { - "esbuild": ">=0.14.0 <=0.27.x" + "esbuild": ">=0.14.0 <=0.25.x" } }, "node_modules/esbuild-runner": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/esbuild-runner/-/esbuild-runner-2.2.2.tgz", - "integrity": "sha512-fRFVXcmYVmSmtYm2mL8RlUASt2TDkGh3uRcvHFOKNr/T58VrfVeKD9uT9nlgxk96u0LS0ehS/GY7Da/bXWKkhw==", "dev": true, "license": "Apache License 2.0", "dependencies": { @@ -4506,8 +3394,6 @@ }, "node_modules/escalade": { "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": { @@ -4516,8 +3402,6 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { @@ -4528,24 +3412,23 @@ } }, "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "version": "9.37.0", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.4.0", + "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", - "@eslint/plugin-kit": "^0.4.1", + "@eslint/js": "9.37.0", + "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", @@ -4589,8 +3472,6 @@ }, "node_modules/eslint-config-prettier": { "version": "10.1.8", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", - "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -4604,14 +3485,12 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", - "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", + "version": "5.5.4", "dev": true, "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.1", - "synckit": "^0.11.12" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -4636,8 +3515,6 @@ }, "node_modules/eslint-scope": { "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4653,8 +3530,6 @@ }, "node_modules/eslint-visitor-keys": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4664,10 +3539,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "9.37.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, "node_modules/espree": { "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4684,8 +3568,6 @@ }, "node_modules/esprima": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, "license": "BSD-2-Clause", "bin": { @@ -4698,8 +3580,6 @@ }, "node_modules/esquery": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -4711,8 +3591,6 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4724,8 +3602,6 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -4734,8 +3610,6 @@ }, "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": { @@ -4744,8 +3618,6 @@ }, "node_modules/execa": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "license": "MIT", "dependencies": { @@ -4768,15 +3640,11 @@ }, "node_modules/execa/node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, "license": "ISC" }, "node_modules/exit-x": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", - "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", "dev": true, "license": "MIT", "engines": { @@ -4785,8 +3653,6 @@ }, "node_modules/expect": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", "dev": true, "license": "MIT", "dependencies": { @@ -4802,44 +3668,32 @@ } }, "node_modules/exsolve": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", - "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "version": "1.0.7", "dev": true, "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, "node_modules/fast-diff": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true, "license": "Apache-2.0" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, "node_modules/fast-xml-parser": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz", - "integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==", + "version": "5.3.0", "funding": [ { "type": "github", @@ -4856,8 +3710,6 @@ }, "node_modules/fb-watchman": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4866,8 +3718,6 @@ }, "node_modules/file-entry-cache": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4879,8 +3729,6 @@ }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { @@ -4892,8 +3740,6 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { @@ -4909,8 +3755,6 @@ }, "node_modules/flat-cache": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { @@ -4923,15 +3767,11 @@ }, "node_modules/flatted": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, "node_modules/foreground-child": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", "dependencies": { @@ -4945,25 +3785,8 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, "license": "MIT", "funding": { @@ -4972,8 +3795,6 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "license": "MIT", "engines": { @@ -4982,8 +3803,6 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", - "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": { @@ -4992,8 +3811,6 @@ }, "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, "license": "MIT", "engines": { @@ -5002,8 +3819,6 @@ }, "node_modules/get-package-type": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "license": "MIT", "engines": { @@ -5012,8 +3827,6 @@ }, "node_modules/get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "license": "MIT", "engines": { @@ -5025,8 +3838,6 @@ }, "node_modules/get-tsconfig": { "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5037,9 +3848,7 @@ } }, "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "version": "10.4.5", "dev": true, "license": "ISC", "dependencies": { @@ -5059,8 +3868,6 @@ }, "node_modules/glob-parent": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { @@ -5072,8 +3879,6 @@ }, "node_modules/glob/node_modules/brace-expansion": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5082,8 +3887,6 @@ }, "node_modules/glob/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": { @@ -5097,9 +3900,9 @@ } }, "node_modules/globals": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.0.0.tgz", - "integrity": "sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==", + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", + "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", "dev": true, "license": "MIT", "engines": { @@ -5111,15 +3914,11 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, "license": "ISC" }, "node_modules/handlebars": { "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5140,15 +3939,11 @@ }, "node_modules/harmony-reflect": { "version": "1.6.2", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", - "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", "dev": true, "license": "(Apache-2.0 OR MPL-1.1)" }, "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": { @@ -5157,8 +3952,6 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5170,15 +3963,11 @@ }, "node_modules/html-escaper": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, "license": "MIT" }, "node_modules/human-signals": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5187,8 +3976,6 @@ }, "node_modules/husky": { "version": "9.1.7", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", - "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, "license": "MIT", "bin": { @@ -5203,8 +3990,6 @@ }, "node_modules/identity-obj-proxy": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", "dev": true, "license": "MIT", "dependencies": { @@ -5216,8 +4001,6 @@ }, "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": { @@ -5226,8 +4009,6 @@ }, "node_modules/import-fresh": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5243,8 +4024,6 @@ }, "node_modules/import-local": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "license": "MIT", "dependencies": { @@ -5263,8 +4042,6 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { @@ -5273,15 +4050,11 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, "license": "MIT" }, "node_modules/is-core-module": { "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { @@ -5296,8 +4069,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { @@ -5306,8 +4077,6 @@ }, "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": { @@ -5316,8 +4085,6 @@ }, "node_modules/is-generator-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "license": "MIT", "engines": { @@ -5326,8 +4093,6 @@ }, "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": { @@ -5339,8 +4104,6 @@ }, "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": { @@ -5349,8 +4112,6 @@ }, "node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "license": "MIT", "engines": { @@ -5362,15 +4123,11 @@ }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -5379,8 +4136,6 @@ }, "node_modules/istanbul-lib-instrument": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5396,8 +4151,6 @@ }, "node_modules/istanbul-lib-report": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5411,8 +4164,6 @@ }, "node_modules/istanbul-lib-source-maps": { "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5426,8 +4177,6 @@ }, "node_modules/istanbul-reports": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5440,8 +4189,6 @@ }, "node_modules/jackspeak": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -5456,8 +4203,6 @@ }, "node_modules/jest": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", - "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", "dependencies": { @@ -5483,8 +4228,6 @@ }, "node_modules/jest-changed-files": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz", - "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5498,8 +4241,6 @@ }, "node_modules/jest-circus": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", - "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", "dev": true, "license": "MIT", "dependencies": { @@ -5530,8 +4271,6 @@ }, "node_modules/jest-cli": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", - "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", "dev": true, "license": "MIT", "dependencies": { @@ -5563,8 +4302,6 @@ }, "node_modules/jest-config": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", - "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", "dev": true, "license": "MIT", "dependencies": { @@ -5615,8 +4352,6 @@ }, "node_modules/jest-diff": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", "dev": true, "license": "MIT", "dependencies": { @@ -5631,8 +4366,6 @@ }, "node_modules/jest-docblock": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", - "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", "dev": true, "license": "MIT", "dependencies": { @@ -5644,8 +4377,6 @@ }, "node_modules/jest-each": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", - "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5661,8 +4392,6 @@ }, "node_modules/jest-environment-node": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", - "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", "dev": true, "license": "MIT", "dependencies": { @@ -5680,8 +4409,6 @@ }, "node_modules/jest-haste-map": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", - "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", "dev": true, "license": "MIT", "dependencies": { @@ -5705,8 +4432,6 @@ }, "node_modules/jest-leak-detector": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", - "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5719,8 +4444,6 @@ }, "node_modules/jest-matcher-utils": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", - "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", "dev": true, "license": "MIT", "dependencies": { @@ -5735,8 +4458,6 @@ }, "node_modules/jest-message-util": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", "dev": true, "license": "MIT", "dependencies": { @@ -5756,8 +4477,6 @@ }, "node_modules/jest-mock": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, "license": "MIT", "dependencies": { @@ -5771,8 +4490,6 @@ }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "license": "MIT", "engines": { @@ -5789,8 +4506,6 @@ }, "node_modules/jest-regex-util": { "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", "engines": { @@ -5799,8 +4514,6 @@ }, "node_modules/jest-resolve": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", - "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", "dev": true, "license": "MIT", "dependencies": { @@ -5819,8 +4532,6 @@ }, "node_modules/jest-resolve-dependencies": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz", - "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", "dev": true, "license": "MIT", "dependencies": { @@ -5833,8 +4544,6 @@ }, "node_modules/jest-runner": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", - "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5867,8 +4576,6 @@ }, "node_modules/jest-runner/node_modules/source-map-support": { "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "license": "MIT", "dependencies": { @@ -5878,8 +4585,6 @@ }, "node_modules/jest-runtime": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", - "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", "dev": true, "license": "MIT", "dependencies": { @@ -5912,8 +4617,6 @@ }, "node_modules/jest-snapshot": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", - "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", "dev": true, "license": "MIT", "dependencies": { @@ -5945,8 +4648,6 @@ }, "node_modules/jest-util": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "license": "MIT", "dependencies": { @@ -5963,8 +4664,6 @@ }, "node_modules/jest-util/node_modules/picomatch": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -5976,8 +4675,6 @@ }, "node_modules/jest-validate": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz", - "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -5989,2130 +4686,1412 @@ "pretty-format": "30.2.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz", - "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "jest-util": "30.2.0", - "string-length": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-worker/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "uc.micro": "^2.0.0" - } - }, - "node_modules/local-pkg": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", - "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", - "dev": true, - "license": "MIT", - "dependencies": { - "mlly": "^1.7.4", - "pkg-types": "^2.3.0", - "quansync": "^0.2.11" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=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, - "license": "ISC", - "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, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mlly": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", - "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.15.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.1" - } - }, - "node_modules/mlly/node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/mlly/node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" - } - }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "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/napi-postinstall": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", - "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", - "dev": true, - "license": "MIT", - "bin": { - "napi-postinstall": "lib/cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, - "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/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "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, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", "dev": true, "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/jest-watcher": { + "version": "30.2.0", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.2.0", + "string-length": "^4.0.2" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/jest-worker": { + "version": "30.2.0", "dev": true, "license": "MIT", "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" }, "engines": { - "node": ">= 0.8.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "has-flag": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" + "argparse": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/jsesc": { + "version": "3.1.0", "dev": true, "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, "engines": { "node": ">=6" } }, - "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==", + "node_modules/json-buffer": { + "version": "3.0.1", "dev": true, - "license": "BlueOak-1.0.0" + "license": "MIT" }, - "node_modules/parent-module": { + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { "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" + }, + "node_modules/json5": { + "version": "2.2.3", "dev": true, "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" + "bin": { + "json5": "lib/cli.js" }, "engines": { "node": ">=6" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/keyv": { + "version": "4.5.4", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "json-buffer": "3.0.1" } }, - "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==", + "node_modules/leven": { + "version": "3.1.0", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6" } }, - "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==", + "node_modules/levn": { + "version": "0.4.1", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "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, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.8.0" } }, - "node_modules/path-scurry/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, - "license": "ISC" - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "node_modules/lines-and-columns": { + "version": "1.2.4", "dev": true, "license": "MIT" }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "node_modules/linkify-it": { + "version": "5.0.0", "dev": true, "license": "MIT", - "engines": { - "node": "*" + "dependencies": { + "uc.micro": "^2.0.0" } }, - "node_modules/picocolors": { + "node_modules/local-pkg": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.0.1", + "quansync": "^0.2.8" + }, "engines": { - "node": ">=8.6" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/locate-path": { + "version": "6.0.0", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^4.0.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/lodash": { + "version": "4.17.21", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/loupe": { + "version": "2.3.7", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" + "get-func-name": "^2.0.1" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/lru-cache": { + "version": "5.1.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" + "yallist": "^3.0.2" } }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/lunr": { + "version": "2.3.9", + "dev": true, + "license": "MIT" + }, + "node_modules/make-dir": { + "version": "4.0.0", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/make-error": { + "version": "1.3.6", "dev": true, - "license": "MIT", + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" + "tmpl": "1.0.5" } }, - "node_modules/pkg-types": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", - "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "node_modules/markdown-it": { + "version": "14.1.0", "dev": true, "license": "MIT", "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" } }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "license": "MIT", - "engines": { - "node": ">=4" - } + "node_modules/mdurl": { + "version": "2.0.0", + "dev": true, + "license": "MIT" }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/merge-stream": { + "version": "2.0.0", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } + "license": "MIT" }, - "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "node_modules/micromatch": { + "version": "4.0.8", "dev": true, "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "node": ">=8.6" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", - "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "node_modules/mimic-fn": { + "version": "2.1.0", "dev": true, "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, "engines": { - "node": ">=6.0.0" + "node": ">=6" } }, - "node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "node_modules/minimatch": { + "version": "3.1.2", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "*" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/minimist": { + "version": "1.2.8", "dev": true, "license": "MIT", - "engines": { - "node": ">=10" - }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pretty-quick": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-4.2.2.tgz", - "integrity": "sha512-uAh96tBW1SsD34VhhDmWuEmqbpfYc/B3j++5MC/6b3Cb8Ow7NJsvKFhg0eoGu2xXX+o9RkahkTK6sUdd8E7g5w==", + "node_modules/minipass": { + "version": "7.1.2", "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.7", - "ignore": "^7.0.5", - "mri": "^1.2.0", - "picocolors": "^1.1.1", - "picomatch": "^4.0.2", - "tinyexec": "^0.3.2", - "tslib": "^2.8.1" - }, - "bin": { - "pretty-quick": "lib/cli.mjs" - }, + "license": "ISC", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://opencollective.com/pretty-quick" - }, - "peerDependencies": { - "prettier": "^3.0.0" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/pretty-quick/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "node_modules/mlly": { + "version": "1.7.4", "dev": true, "license": "MIT", - "engines": { - "node": ">= 4" + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" } }, - "node_modules/pretty-quick/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "license": "MIT" }, - "node_modules/pretty-quick/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", "dev": true, - "license": "0BSD" + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/mri": { + "version": "1.2.0", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.3", "dev": true, "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" } }, - "node_modules/pure-rand": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", - "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "node_modules/natural-compare": { + "version": "1.4.0", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], "license": "MIT" }, - "node_modules/quansync": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", - "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "node_modules/neo-async": { + "version": "2.6.2", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/antfu" - }, - { - "type": "individual", - "url": "https://github.com/sponsors/sxzz" - } - ], "license": "MIT" }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "node_modules/node-int64": { + "version": "0.4.0", "dev": true, "license": "MIT" }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "node_modules/node-releases": { + "version": "2.0.19", "dev": true, "license": "MIT" }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", "dev": true, "license": "MIT", "dependencies": { - "regenerate": "^1.4.2" + "path-key": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", "dev": true, "license": "MIT", "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "node_modules/p-locate": { + "version": "5.0.0", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "jsesc": "~3.0.2" + "p-limit": "^3.0.2" }, - "bin": { - "regjsparser": "bin/parser" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "node_modules/p-try": { + "version": "2.2.0", "dev": true, "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, "engines": { "node": ">=6" } }, - "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==", + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", "dev": true, "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "node_modules/parse-json": { + "version": "5.2.0", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "node_modules/path-exists": { + "version": "4.0.0", "dev": true, "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, "engines": { "node": ">=8" } }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/path-key": { + "version": "3.1.1", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/pathe": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": "*" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "node_modules/picocolors": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", "dev": true, "license": "MIT", + "engines": { + "node": ">=8.6" + }, "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "node_modules/pirates": { + "version": "4.0.7", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "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==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", "dev": true, "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, "engines": { "node": ">=8" } }, - "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==", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, "engines": { - "node": ">=14" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", "dev": true, "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, "engines": { "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/pkg-types": { + "version": "2.2.0", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/prelude-ls": { + "version": "1.2.1", "dev": true, "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "node_modules/prettier": { + "version": "3.6.2", "dev": true, - "license": "BSD-3-Clause" + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", "dev": true, "license": "MIT", "dependencies": { - "escape-string-regexp": "^2.0.0" + "fast-diff": "^1.1.2" }, "engines": { - "node": ">=10" + "node": ">=6.0.0" } }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/pretty-format": { + "version": "30.2.0", "dev": true, "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", "dev": true, "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/string-length/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==", + "node_modules/pretty-quick": { + "version": "4.2.2", "dev": true, "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.7", + "ignore": "^7.0.5", + "mri": "^1.2.0", + "picocolors": "^1.1.1", + "picomatch": "^4.0.2", + "tinyexec": "^0.3.2", + "tslib": "^2.8.1" + }, + "bin": { + "pretty-quick": "lib/cli.mjs" + }, "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "url": "https://opencollective.com/pretty-quick" + }, + "peerDependencies": { + "prettier": "^3.0.0" } }, - "node_modules/string-length/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==", + "node_modules/pretty-quick/node_modules/ignore": { + "version": "7.0.5", "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { - "node": ">=8" + "node": ">= 4" } }, - "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==", + "node_modules/pretty-quick/node_modules/picomatch": { + "version": "4.0.3", "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" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/pretty-quick/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/punycode": { + "version": "2.3.1", "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "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==", + "node_modules/punycode.js": { + "version": "2.3.1", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/string-width-cjs/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==", + "node_modules/pure-rand": { + "version": "7.0.1", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], "license": "MIT" }, - "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==", + "node_modules/quansync": { + "version": "0.2.10", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate": { + "version": "1.4.2", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "regenerate": "^1.4.2" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "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==", + "node_modules/regexpu-core": { + "version": "6.2.0", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" }, "engines": { - "node": ">=12" + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.0.2" }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "bin": { + "regjsparser": "bin/parser" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "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==", + "node_modules/require-directory": { + "version": "2.1.1", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "node_modules/resolve": { + "version": "1.22.10", "dev": true, "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "node_modules/resolve-cwd": { + "version": "3.0.0", "dev": true, "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/resolve-from": { + "version": "4.0.0", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/supports-preserve-symlinks-flag": { + "node_modules/resolve-pkg-maps": { "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" - }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/synckit": { - "version": "0.11.12", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", - "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "node_modules/resolve.exports": { + "version": "2.0.3", "dev": true, "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" - }, "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" + "node": ">=10" } }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, + "node_modules/semver": { + "version": "7.7.3", "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=8" - } - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" + "node": ">=10" + } }, - "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==", + "node_modules/shebang-command": { + "version": "2.0.0", "dev": true, "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=8.0" + "node": ">=8" } }, - "node_modules/ts-jest": { - "version": "29.4.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", - "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", + "node_modules/shebang-regex": { + "version": "3.0.0", "dev": true, "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.3", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } + "node": ">=8" } }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "node_modules/signal-exit": { + "version": "4.1.0", "dev": true, - "license": "(MIT OR CC0-1.0)", + "license": "ISC", "engines": { - "node": ">=16" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true, - "license": "0BSD" - }, - "node_modules/tsx": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", - "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "node_modules/slash": { + "version": "3.0.0", "dev": true, "license": "MIT", - "dependencies": { - "esbuild": "~0.27.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", - "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==", - "cpu": [ - "ppc64" - ], + "node_modules/source-map": { + "version": "0.6.1", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], + "license": "BSD-3-Clause", "engines": { - "node": ">=18" + "node": ">=0.10.0" } }, - "node_modules/tsx/node_modules/@esbuild/android-arm": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz", - "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==", - "cpu": [ - "arm" - ], + "node_modules/source-map-support": { + "version": "0.5.21", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/tsx/node_modules/@esbuild/android-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz", - "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==", - "cpu": [ - "arm64" - ], + "node_modules/sprintf-js": { + "version": "1.0.3", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } + "license": "BSD-3-Clause" }, - "node_modules/tsx/node_modules/@esbuild/android-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz", - "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==", - "cpu": [ - "x64" - ], + "node_modules/stack-utils": { + "version": "2.0.6", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, "engines": { - "node": ">=18" + "node": ">=10" } }, - "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz", - "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==", - "cpu": [ - "arm64" - ], + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/darwin-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz", - "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==", - "cpu": [ - "x64" - ], + "node_modules/string-length": { + "version": "4.0.2", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=18" + "node": ">=10" } }, - "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz", - "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==", - "cpu": [ - "arm64" - ], + "node_modules/string-length/node_modules/ansi-regex": { + "version": "5.0.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz", - "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==", - "cpu": [ - "x64" - ], + "node_modules/string-length/node_modules/strip-ansi": { + "version": "6.0.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/linux-arm": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz", - "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==", - "cpu": [ - "arm" - ], + "node_modules/string-width": { + "version": "5.1.2", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tsx/node_modules/@esbuild/linux-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz", - "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==", - "cpu": [ - "arm64" - ], + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/linux-ia32": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz", - "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==", - "cpu": [ - "ia32" - ], + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/linux-loong64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz", - "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==", - "cpu": [ - "loong64" - ], + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz", - "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==", - "cpu": [ - "mips64el" - ], + "node_modules/strip-ansi": { + "version": "7.1.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz", - "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==", - "cpu": [ - "ppc64" - ], + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz", - "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==", - "cpu": [ - "riscv64" - ], + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/linux-s390x": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz", - "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==", - "cpu": [ - "s390x" - ], + "node_modules/strip-bom": { + "version": "4.0.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/linux-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz", - "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==", - "cpu": [ - "x64" - ], + "node_modules/strip-final-newline": { + "version": "2.0.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=6" } }, - "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz", - "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==", - "cpu": [ - "arm64" - ], + "node_modules/strip-json-comments": { + "version": "3.1.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], "engines": { - "node": ">=18" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz", - "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==", - "cpu": [ - "x64" + "node_modules/strnum": { + "version": "2.1.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } ], + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz", - "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==", - "cpu": [ - "arm64" - ], + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz", - "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==", - "cpu": [ - "x64" - ], + "node_modules/synckit": { + "version": "0.11.11", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "@pkgr/core": "^0.2.9" + }, "engines": { - "node": ">=18" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" } }, - "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz", - "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==", - "cpu": [ - "arm64" - ], + "node_modules/test-exclude": { + "version": "6.0.0", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/tsx/node_modules/@esbuild/sunos-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz", - "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==", - "cpu": [ - "x64" - ], + "node_modules/tinyexec": { + "version": "0.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], + "dependencies": { + "is-number": "^7.0.0" + }, "engines": { - "node": ">=18" + "node": ">=8.0" } }, - "node_modules/tsx/node_modules/@esbuild/win32-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz", - "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==", - "cpu": [ - "arm64" - ], + "node_modules/ts-jest": { + "version": "29.4.5", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.3", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, "engines": { - "node": ">=18" + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } } }, - "node_modules/tsx/node_modules/@esbuild/win32-ia32": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz", - "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==", - "cpu": [ - "ia32" - ], + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=18" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tsx/node_modules/@esbuild/win32-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz", - "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==", - "cpu": [ - "x64" - ], + "node_modules/ts-node": { + "version": "10.9.2", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "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/tsx/node_modules/esbuild": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", - "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", + "node_modules/tslib": { + "version": "2.4.0", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.20.6", "dev": true, - "hasInstallScript": true, "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, "bin": { - "esbuild": "bin/esbuild" + "tsx": "dist/cli.mjs" }, "engines": { - "node": ">=18" + "node": ">=18.0.0" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.0", - "@esbuild/android-arm": "0.27.0", - "@esbuild/android-arm64": "0.27.0", - "@esbuild/android-x64": "0.27.0", - "@esbuild/darwin-arm64": "0.27.0", - "@esbuild/darwin-x64": "0.27.0", - "@esbuild/freebsd-arm64": "0.27.0", - "@esbuild/freebsd-x64": "0.27.0", - "@esbuild/linux-arm": "0.27.0", - "@esbuild/linux-arm64": "0.27.0", - "@esbuild/linux-ia32": "0.27.0", - "@esbuild/linux-loong64": "0.27.0", - "@esbuild/linux-mips64el": "0.27.0", - "@esbuild/linux-ppc64": "0.27.0", - "@esbuild/linux-riscv64": "0.27.0", - "@esbuild/linux-s390x": "0.27.0", - "@esbuild/linux-x64": "0.27.0", - "@esbuild/netbsd-arm64": "0.27.0", - "@esbuild/netbsd-x64": "0.27.0", - "@esbuild/openbsd-arm64": "0.27.0", - "@esbuild/openbsd-x64": "0.27.0", - "@esbuild/openharmony-arm64": "0.27.0", - "@esbuild/sunos-x64": "0.27.0", - "@esbuild/win32-arm64": "0.27.0", - "@esbuild/win32-ia32": "0.27.0", - "@esbuild/win32-x64": "0.27.0" + "fsevents": "~2.3.3" } }, "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": { @@ -8124,8 +6103,6 @@ }, "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, "license": "MIT", "engines": { @@ -8134,8 +6111,6 @@ }, "node_modules/type-fest": { "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -8146,13 +6121,11 @@ } }, "node_modules/typedoc": { - "version": "0.28.16", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.16.tgz", - "integrity": "sha512-x4xW77QC3i5DUFMBp0qjukOTnr/sSg+oEs86nB3LjDslvAmwe/PUGDWbe3GrIqt59oTqoXK5GRK9tAa0sYMiog==", + "version": "0.28.14", "dev": true, "license": "Apache-2.0", "dependencies": { - "@gerrit0/mini-shiki": "^3.17.0", + "@gerrit0/mini-shiki": "^3.12.0", "lunr": "^2.3.9", "markdown-it": "^14.1.0", "minimatch": "^9.0.5", @@ -8171,8 +6144,6 @@ }, "node_modules/typedoc/node_modules/brace-expansion": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8181,8 +6152,6 @@ }, "node_modules/typedoc/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": { @@ -8197,8 +6166,6 @@ }, "node_modules/typescript": { "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -8211,22 +6178,16 @@ }, "node_modules/uc.micro": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", "dev": true, "license": "MIT" }, "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "version": "1.6.1", "dev": true, "license": "MIT" }, "node_modules/uglify-js": { "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, "license": "BSD-2-Clause", "optional": true, @@ -8246,8 +6207,6 @@ }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, "license": "MIT", "engines": { @@ -8256,8 +6215,6 @@ }, "node_modules/unicode-match-property-ecmascript": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, "license": "MIT", "dependencies": { @@ -8270,8 +6227,6 @@ }, "node_modules/unicode-match-property-value-ecmascript": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "dev": true, "license": "MIT", "engines": { @@ -8280,8 +6235,6 @@ }, "node_modules/unicode-name": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-name/-/unicode-name-1.1.0.tgz", - "integrity": "sha512-2XBI32vZP6a1tJmMDSUBabiIZuY+1udwZUoS7i5BTSU2c4ELMy6YJrTPF9uvYOVMU4vTMlHH6HG/TsHjCEsazA==", "license": "MIT", "engines": { "node": ">=18.20" @@ -8289,8 +6242,6 @@ }, "node_modules/unicode-property-aliases-ecmascript": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, "license": "MIT", "engines": { @@ -8299,8 +6250,6 @@ }, "node_modules/unrs-resolver": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -8334,8 +6283,6 @@ }, "node_modules/update-browserslist-db": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -8365,18 +6312,19 @@ }, "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", + "dev": true, + "license": "MIT" + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "license": "ISC", "dependencies": { @@ -8390,8 +6338,6 @@ }, "node_modules/walker": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -8400,8 +6346,6 @@ }, "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": { @@ -8416,8 +6360,6 @@ }, "node_modules/word-wrap": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { @@ -8426,15 +6368,11 @@ }, "node_modules/wordwrap": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true, "license": "MIT" }, "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": { @@ -8452,8 +6390,6 @@ "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": { @@ -8470,8 +6406,6 @@ }, "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": { @@ -8480,15 +6414,11 @@ }, "node_modules/wrap-ansi-cjs/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, "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", - "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": { @@ -8502,8 +6432,6 @@ }, "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": { @@ -8515,8 +6443,6 @@ }, "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": { @@ -8528,8 +6454,6 @@ }, "node_modules/write-file-atomic": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "license": "ISC", "dependencies": { @@ -8542,8 +6466,6 @@ }, "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": { @@ -8552,15 +6474,11 @@ }, "node_modules/yallist": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" }, "node_modules/yaml": { "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", "dev": true, "license": "ISC", "bin": { @@ -8572,8 +6490,6 @@ }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { @@ -8591,8 +6507,6 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { @@ -8601,8 +6515,6 @@ }, "node_modules/yargs/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": { @@ -8611,15 +6523,11 @@ }, "node_modules/yargs/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, "license": "MIT" }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", - "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": { @@ -8633,8 +6541,6 @@ }, "node_modules/yargs/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": { @@ -8644,10 +6550,16 @@ "node": ">=8" } }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "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": { diff --git a/package.json b/package.json index 596dd954..78d00282 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,10 @@ "name": "hed-validator", "version": "4.1.4", "description": "A JavaScript validator for HED (Hierarchical Event Descriptor) strings.", + "type": "module", "main": "./dist/commonjs/index.js", - "types": "./types/index.d.ts", "exports": { ".": { - "types": "./types/index.d.ts", "import": "./dist/esm/index.js", "require": "./dist/commonjs/index.js" } @@ -24,10 +23,6 @@ "lint": "eslint ./src/**/*.js", "test": "jest --testPathPatterns='/tests/'", "testSpecs": "jest --testPathPatterns='/spec_tests/' --silent --noStackTrace", - "test-types": "npx tsx types/test.ts", - "test-types-local": "node scripts/test-types-local.js", - "test-types-local-windows": "powershell -ExecutionPolicy Bypass -File ./scripts/test-types-local-windows.ps1", - "test-types-local-unix": "sh ./scripts/test-types-local-unix.sh", "type-check": "tsc --noEmit", "coverage": "jest --coverage", "prepare": "husky", @@ -48,17 +43,21 @@ "fast-xml-parser": "^5.2.5", "lodash": "^4.17.21", "pluralize": "^8.0.0", - "semver": "^7.6.0", + "semver": "^7.7.2", "unicode-name": "^1.0.2" }, "devDependencies": { "@babel/core": "^7.28.0", "@babel/preset-env": "^7.28.0", + "@babel/preset-typescript": "^7.27.1", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^9.18.0", "@jest/globals": "^30.0.0", + "@types/lodash": "^4.17.20", "@types/node": "^25.0.2", - "babel-jest": "^30.0.5", + "@types/pluralize": "^0.0.33", + "@types/semver": "^7.7.0", + "babel-jest": "^30.2.0", "chai": "^4.3.6", "esbuild": "^0.25.0", "esbuild-plugins-node-modules-polyfill": "^1.7.1", @@ -74,6 +73,7 @@ "prettier": "^3.2.5", "pretty-quick": "^4.0.0", "ts-jest": "^29.4.0", + "ts-node": "^10.9.2", "tsx": "^4.20.3", "typedoc": "^0.28.0", "typescript": "^5.4.5" @@ -86,7 +86,6 @@ }, "files": [ "dist/*", - "types/*", "esbuild.mjs" ] } diff --git a/scripts/runtime-test.template.ts b/scripts/runtime-test.template.ts deleted file mode 100644 index 95408a47..00000000 --- a/scripts/runtime-test.template.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { getLocalSchemaVersions } from 'hed-validator' - -const versions = getLocalSchemaVersions() -console.log('✅ Available schema versions:', versions.length) -console.log('✅ First version:', versions[0]) - -// Type check -const firstVersion: string = versions[0] -console.log('✅ Type test passed') diff --git a/scripts/test-types-local-unix.sh b/scripts/test-types-local-unix.sh deleted file mode 100644 index 11cad91c..00000000 --- a/scripts/test-types-local-unix.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/sh - -# Local TypeScript definition testing script -echo "Building package..." -npm run build - -echo "Packing package..." -npm pack - -echo "Setting up test environment..." -mkdir -p test-types-local -cd test-types-local - -echo "Installing packed package..." -npm init -y -npm install ../hed-validator-*.tgz -npm install typescript @types/node tsx - -echo "Copying test files and tsconfig..." -cp ../types/test.ts ./ -cp ../types/tsconfig.json ./ -cp ../tsconfig.json ./tsconfig.base.json -cp ../scripts/runtime-test.template.ts ./runtime-test.ts - -echo "Updating tsconfig paths..." -sed -i 's|"../tsconfig.json"|"./tsconfig.base.json"|' tsconfig.json -sed -i 's|"baseUrl": ".."|"baseUrl": "."|' tsconfig.json - -echo "Testing TypeScript compilation..." -npx tsc --project tsconfig.json - -if [ $? -eq 0 ]; then - echo "✅ TypeScript compilation passed!" -else - echo "❌ TypeScript compilation failed!" - cd .. - rm -rf test-types-local - rm hed-validator-*.tgz - exit 1 -fi - -echo "Running runtime test..." -npx tsx runtime-test.ts - -if [ $? -eq 0 ]; then - echo "✅ Runtime test passed!" -else - echo "❌ Runtime test failed!" - cd .. - rm -rf test-types-local - rm hed-validator-*.tgz - exit 1 -fi - -echo "Cleaning up..." -cd .. -rm -rf test-types-local -rm hed-validator-*.tgz - -echo "✅ All TypeScript definition tests passed!" diff --git a/scripts/test-types-local-windows.ps1 b/scripts/test-types-local-windows.ps1 deleted file mode 100644 index 3e7ca542..00000000 --- a/scripts/test-types-local-windows.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -# Local TypeScript definition testing script for Windows -Write-Host "Building package..." -ForegroundColor Green -npm run build - -if ($LASTEXITCODE -ne 0) { - Write-Host "Build failed!" -ForegroundColor Red - exit 1 -} - -Write-Host "Packing package..." -ForegroundColor Green -npm pack - -if ($LASTEXITCODE -ne 0) { - Write-Host "Pack failed!" -ForegroundColor Red - exit 1 -} - -Write-Host "Setting up test environment..." -ForegroundColor Green -New-Item -ItemType Directory -Force -Path "test-types-local" | Out-Null -Set-Location "test-types-local" - -Write-Host "Installing packed package..." -ForegroundColor Green -npm init -y | Out-Null -$tarball = Get-ChildItem -Path ".." -Filter "hed-validator-*.tgz" | Select-Object -First 1 -if ($tarball) { - npm install "../$($tarball.Name)" typescript "@types/node" tsx | Out-Null -} else { - Write-Host "No tarball found!" -ForegroundColor Red - Set-Location ".." - exit 1 -} - -Write-Host "Copying test file..." -ForegroundColor Green -Copy-Item "../types/test.ts" "./test.ts" -Copy-Item "../types/tsconfig.json" "./tsconfig.json" -Copy-Item "../tsconfig.json" "./tsconfig.base.json" - -# Correctly modify the tsconfig.json for the test environment -$config = Get-Content -Path ./tsconfig.json -Raw | ConvertFrom-Json -$config.extends = "./tsconfig.base.json" -$config.compilerOptions.baseUrl = "." -$config | ConvertTo-Json -Depth 100 | Set-Content -Path ./tsconfig.json - -Write-Host "Testing TypeScript compilation..." -ForegroundColor Green -npx tsc --project tsconfig.json - -if ($LASTEXITCODE -eq 0) { - Write-Host "✅ TypeScript compilation passed!" -ForegroundColor Green -} else { - Write-Host "❌ TypeScript compilation failed!" -ForegroundColor Red - Set-Location ".." - Remove-Item -Recurse -Force "test-types-local" - Remove-Item -Force "hed-validator-*.tgz" - exit 1 -} - -Write-Host "Creating runtime test..." -ForegroundColor Green -Copy-Item "../scripts/runtime-test.template.ts" "runtime-test.ts" - -Write-Host "Running runtime test..." -ForegroundColor Green -npx tsx runtime-test.ts - -if ($LASTEXITCODE -eq 0) { - Write-Host "✅ Runtime test passed!" -ForegroundColor Green -} else { - Write-Host "❌ Runtime test failed!" -ForegroundColor Red - Set-Location ".." - Remove-Item -Recurse -Force "test-types-local" - Remove-Item -Force "hed-validator-*.tgz" - exit 1 -} - -Write-Host "Cleaning up..." -ForegroundColor Green -Set-Location ".." -Remove-Item -Recurse -Force "test-types-local" -Remove-Item -Force "hed-validator-*.tgz" - -Write-Host "✅ All TypeScript definition tests passed!" -ForegroundColor Green diff --git a/scripts/test-types-local.js b/scripts/test-types-local.js deleted file mode 100644 index 885f7112..00000000 --- a/scripts/test-types-local.js +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env node - -/** - * Cross-platform script runner for TypeScript definition testing - * Automatically detects the platform and runs the appropriate script - */ - -const { spawn } = require('node:child_process') -const path = require('node:path') - -function runScript() { - const isWindows = process.platform === 'win32' - - let command, args - - if (isWindows) { - // Windows: Run PowerShell script - command = 'powershell' - args = ['-ExecutionPolicy', 'Bypass', '-File', './scripts/test-types-local-windows.ps1'] - } else { - // Unix/Linux/macOS: Run bash script - command = 'sh' - args = ['./scripts/test-types-local-unix.sh'] - } - - console.log(`Running TypeScript definition tests on ${isWindows ? 'Windows' : 'Unix/Linux/macOS'}...`) - - const childProcess = spawn(command, args, { - stdio: 'inherit', - shell: true, - }) - - childProcess.on('error', (error) => { - console.error(`Failed to start script: ${error.message}`) - process.exit(1) - }) - - childProcess.on('exit', (code) => { - process.exit(code || 0) - }) -} - -// Run the script -runScript() diff --git a/spec_tests/jsonTests.spec.js b/spec_tests/jsonTests.spec.js index 8c2c0266..1a96f64a 100644 --- a/spec_tests/jsonTests.spec.js +++ b/spec_tests/jsonTests.spec.js @@ -1,18 +1,19 @@ +import * as fs from 'node:fs' +import path from 'node:path' + import chai from 'chai' const assert = chai.assert -import { beforeAll, describe, afterAll } from '@jest/globals' +import { beforeAll, describe, afterAll } from '@jest/globals' import { BidsHedIssue } from '../src/bids/types/issues' import { buildSchemas } from '../src/schema/init' import { SchemaSpec, SchemasSpec } from '../src/schema/specs' -import { Schemas } from '../src/schema/containers' -import path from 'node:path' +import { HedSchemas } from '../src/schema/containers' import { BidsSidecar, BidsTsvFile } from '../src/bids' import { generateIssue, IssueError } from '../src/issues/issues' import { DefinitionManager } from '../src/parser/definitionManager' import parseTSV from '../src/bids/tsvParser' import { shouldRun } from '../tests/testHelpers/testUtilities' -const fs = require('node:fs') const skipMap = new Map() const runAll = true @@ -331,7 +332,7 @@ describe('HED validation using JSON tests', () => { hedMap.set(prefix, schema) } } - return new Schemas(hedMap) + return new HedSchemas(hedMap) } beforeAll(async () => { diff --git a/src/bids/datasetParser.js b/src/bids/datasetParser.ts similarity index 60% rename from src/bids/datasetParser.js rename to src/bids/datasetParser.ts index 58d11ad6..9c9f2c8a 100644 --- a/src/bids/datasetParser.js +++ b/src/bids/datasetParser.ts @@ -12,10 +12,14 @@ import fsp from 'node:fs/promises' import path from 'node:path' -import { organizePaths } from '../utils/paths.js' -import { generateIssue } from '../issues/issues' -import { BidsHedIssue } from './types/issues' + +import { organizePaths } from '../utils/paths' +import { IssueError } from '../issues/issues' import { buildBidsSchemas } from './schema' +import { type BidsJsonFile } from './types/json' +import { type HedSchemas } from '../schema/containers' + +type SchemaBuilder = (datasetDescription: BidsJsonFile) => Promise /** * Base class for BIDS file accessors. @@ -23,74 +27,65 @@ import { buildBidsSchemas } from './schema' * This class provides a common interface for accessing files in a BIDS dataset, regardless of the underlying storage * mechanism. Subclasses must implement the `create` and `getFileContent` methods. */ -export class BidsFileAccessor { +export abstract class BidsFileAccessor { /** * The root directory of the dataset. - * @type {string} */ - datasetRootDirectory + readonly datasetRootDirectory: string /** * Map of relative file paths to file representations. - * @type {Map} */ - fileMap + fileMap: Map /** * Organized paths. - * @type {Map>} */ - organizedPaths + organizedPaths: Map> /** * The HED schema builder function. - * @type {function} */ - schemaBuilder + protected readonly schemaBuilder: SchemaBuilder /** * BIDS suffixes. - * @type {string[]} */ - static SUFFIXES = ['dataset_description', 'participants', '_events', '_beh', '_scans', '_sessions', 'samples'] + private static readonly SUFFIXES: string[] = [ + 'dataset_description', + 'participants', + '_events', + '_beh', + '_scans', + '_sessions', + 'samples', + ] /** * BIDS special directories. - * @type {string[]} */ - static SPECIAL_DIRS = ['phenotype', 'stimuli'] + private static readonly SPECIAL_DIRS: string[] = ['phenotype', 'stimuli'] /** * Constructs a BidsFileAccessor. * - * @param {string} datasetRootDirectory The root directory of the dataset. - * @param {Map} fileMap A map of relative file paths to file representations (e.g., `File` objects for web, full paths for Node.js). + * @param datasetRootDirectory The root directory of the dataset. + * @param fileMap A map of relative file paths to file representations (e.g., `File` objects for web, full paths for Node.js). + * @param schemaBuilder The HED schema builder function. */ - constructor(datasetRootDirectory, fileMap) { + protected constructor(datasetRootDirectory: string, fileMap: Map, schemaBuilder: SchemaBuilder) { if (typeof datasetRootDirectory !== 'string') { - throw new Error('BidsFileAccessor constructor requires a string for datasetRootDirectory.') + IssueError.generateAndThrowInternalError( + 'BidsFileAccessor constructor requires a string for datasetRootDirectory.', + ) } if (!(fileMap instanceof Map)) { // Ensure fileMap is a Map - throw new Error('BidsFileAccessor constructor requires a Map argument for fileMap.') + IssueError.generateAndThrowInternalError('BidsFileAccessor constructor requires a Map argument for fileMap.') } this.datasetRootDirectory = datasetRootDirectory this._initialize(fileMap) - this.schemaBuilder = null - } - - /** - * Factory method to create a BidsFileAccessor. - * - * This method must be implemented by subclasses to handle environment-specific setup. - * - * @param {string | object} datasetRootDirectory The root directory of the dataset or a file-like object. - * @returns {Promise} A Promise that resolves to a new BidsFileAccessor instance. - * @throws {Error} If the method is not implemented by a subclass. - */ - static async create(datasetRootDirectory) { - // Use datasetRootDirectory in the error message to satisfy the linter - throw new Error(`BidsFileAccessor.create for '${datasetRootDirectory}' must be implemented by a subclass.`) + this.schemaBuilder = schemaBuilder } /** @@ -98,16 +93,14 @@ export class BidsFileAccessor { * * This method filters the file map to include only BIDS-related files and organizes them by type and category. * - * @param {Map} fileMap A map of relative file paths to file representations. - * @private + * @param fileMap A map of relative file paths to file representations. */ - _initialize(fileMap) { + private _initialize(fileMap: Map) { const relativeFilePaths = Array.from(fileMap.keys()) const { candidates, organizedPaths } = organizePaths( relativeFilePaths, BidsFileAccessor.SUFFIXES, BidsFileAccessor.SPECIAL_DIRS, - ['json', 'tsv'], ) this.organizedPaths = organizedPaths @@ -123,19 +116,11 @@ export class BidsFileAccessor { * * This method must be implemented by subclasses to handle environment-specific file reading. * - * @param {string} relativePath The relative path to the file. - * @returns {Promise} A promise that resolves with the file content as a string, or null if the file cannot be read. - * @throws {Error} If the method is not implemented by a subclass. + * @param relativePath The relative path to the file. + * @returns A promise that resolves with the file content as a string, or null if the file cannot be read. + * @throws {IssueError} If the method is not implemented by a subclass. */ - async getFileContent(relativePath) { - if (!this.fileMap.has(relativePath)) { - return null - } - // Use relativePath in the error message to satisfy the linter - throw new Error( - `getFileContent for '${relativePath}': File found in map, but base class cannot determine how to read content. Subclass must implement.`, - ) - } + public abstract getFileContent(relativePath: string): Promise } /** @@ -161,23 +146,20 @@ export class BidsFileAccessor { * * main(); */ -export class BidsDirectoryAccessor extends BidsFileAccessor { +export class BidsDirectoryAccessor extends BidsFileAccessor { /** * Constructs a BidsDirectoryAccessor. * - * @param {string} datasetRootDirectory The absolute path to the dataset's root directory. - * @param {Map} fileMap A map of relative file paths to their absolute paths. - * @throws {BidsHedIssue} If the dataset root directory path is invalid. + * @param datasetRootDirectory The absolute path to the dataset's root directory. + * @param fileMap A map of relative file paths to their absolute paths. + * @throws {IssueError} If the dataset root directory path is invalid. */ - constructor(datasetRootDirectory, fileMap) { + private constructor(datasetRootDirectory: string, fileMap: Map) { if (typeof datasetRootDirectory !== 'string' || !datasetRootDirectory) { const message = `Bids validation requires a non-empty string for the dataset root directory but received: ${datasetRootDirectory}` - throw BidsHedIssue.fromHedIssue( - generateIssue('fileReadError', { filename: datasetRootDirectory, message: `${message}` }), - ) + IssueError.generateAndThrow('fileReadError', { filename: datasetRootDirectory, message: `${message}` }) } - super(datasetRootDirectory, fileMap) - this.schemaBuilder = buildBidsSchemas + super(datasetRootDirectory, fileMap, buildBidsSchemas) } /** @@ -185,13 +167,13 @@ export class BidsDirectoryAccessor extends BidsFileAccessor { * * This method recursively reads the specified directory and creates a file map. * - * @param {string} datasetRootDirectory The absolute path to the dataset's root directory. - * @returns {Promise} A Promise that resolves to a new BidsDirectoryAccessor instance. - * @throws {Error} If the dataset root directory path is empty. + * @param datasetRootDirectory The absolute path to the dataset's root directory. + * @returns A Promise that resolves to a new BidsDirectoryAccessor instance. + * @throws {IssueError} If the dataset root directory path is empty. */ - static async create(datasetRootDirectory) { + public static async create(datasetRootDirectory: string): Promise { if (typeof datasetRootDirectory !== 'string' || !datasetRootDirectory) { - throw new Error('Must have a non-empty dataset root directory path.') + IssueError.generateAndThrowInternalError('Must have a non-empty dataset root directory path.') } const resolvedDatasetRoot = path.resolve(datasetRootDirectory) const fileMap = new Map() @@ -202,10 +184,10 @@ export class BidsDirectoryAccessor extends BidsFileAccessor { /** * Asynchronously reads the content of a file from the file system. * - * @param {string} relativePath The relative path to the file within the dataset. - * @returns {Promise} A promise that resolves with the file content as a string, or null if the file is not found or an error occurs. + * @param relativePath The relative path to the file within the dataset. + * @returns A promise that resolves with the file content as a string, or null if the file is not found or an error occurs. */ - async getFileContent(relativePath) { + public async getFileContent(relativePath: string): Promise { const absolutePath = this.fileMap.get(relativePath) if (!absolutePath) { return null @@ -221,12 +203,11 @@ export class BidsDirectoryAccessor extends BidsFileAccessor { /** * Recursively reads directory contents and populates the file map. * - * @param {string} dir The directory to read. - * @param {string} baseDir The base directory to calculate relative paths from. - * @param {Map} fileMapRef The Map to populate with relativePath: absolutePath. - * @private + * @param dir The directory to read. + * @param baseDir The base directory to calculate relative paths from. + * @param fileMapRef The Map to populate with relativePath: absolutePath. */ - static async _readDirRecursive(dir, baseDir, fileMapRef) { + private static async _readDirRecursive(dir: string, baseDir: string, fileMapRef: Map): Promise { try { // Check if the current path is a directory before attempting to read it. // This handles cases where datasetRootDirectory itself might be a file or non-existent. diff --git a/src/bids/index.js b/src/bids/index.ts similarity index 83% rename from src/bids/index.js rename to src/bids/index.ts index bf55be84..b01575cf 100644 --- a/src/bids/index.js +++ b/src/bids/index.ts @@ -21,18 +21,7 @@ export { BidsJsonFile, BidsSidecar, BidsHedIssue, - buildBidsSchemas, BidsFileAccessor, BidsDirectoryAccessor, -} - -export default { - BidsDataset, - BidsTsvFile, - BidsJsonFile, - BidsSidecar, - BidsHedIssue, buildBidsSchemas, - BidsFileAccessor, - BidsDirectoryAccessor, } diff --git a/src/bids/schema.js b/src/bids/schema.ts similarity index 59% rename from src/bids/schema.js rename to src/bids/schema.ts index f9e6deb9..47633bd3 100644 --- a/src/bids/schema.js +++ b/src/bids/schema.ts @@ -1,19 +1,22 @@ /** * This module contains functions for building HED schemas for BIDS datasets. + * * @module bids/schema */ +import { type HedSchemas } from '../schema/containers' import { buildSchemas } from '../schema/init' import { SchemasSpec } from '../schema/specs' +import { type BidsJsonFile } from './types/json' /** * Build a HED schema collection based on the defined BIDS schemas. * - * @param {BidsJsonFile} datasetDescription The description of the BIDS dataset being validated. - * @returns {Promise} A Promise with the schema collection, or null if the specification is missing. + * @param datasetDescription The description of the BIDS dataset being validated. + * @returns A Promise with the schema collection, or null if the specification is missing. * @throws {IssueError} If the schema specification is invalid. */ -export async function buildBidsSchemas(datasetDescription) { +export async function buildBidsSchemas(datasetDescription: BidsJsonFile): Promise { if (datasetDescription?.jsonData?.HEDVersion) { const schemasSpec = SchemasSpec.parseVersionSpecs(datasetDescription.jsonData.HEDVersion) return buildSchemas(schemasSpec) diff --git a/src/bids/tsvParser.js b/src/bids/tsvParser.js deleted file mode 100644 index b2728983..00000000 --- a/src/bids/tsvParser.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * This module provides functions for parsing TSV files. - * - * @module bids/tsvParser - */ - -const stripBOM = (str) => str.replace(/^\uFEFF/, '') -const normalizeEOL = (str) => str.replace(/\r\n/g, '\n').replace(/\r/g, '\n') -const isContentfulRow = (row) => row && !/^\s*$/.test(row) - -/** - * Parse a TSV file - * - * @param {string} contents The contents of a TSV file. - * @returns {Map} The parsed contents of the TSV file. - */ -export function parseTSV(contents) { - const columns = new Map() - const rows = stripBOM(normalizeEOL(contents)) - .split('\n') - .filter(isContentfulRow) - .map((str) => str.split('\t').map((cell) => cell.trim())) - const headers = rows.length ? rows[0] : [] - - headers.forEach((x) => { - columns.set(x, []) - }) - for (let i = 1; i < rows.length; i++) { - for (let j = 0; j < headers.length; j++) { - columns.get(headers[j])?.push(rows[i][j]) - } - } - for (const [key, value] of columns) { - columns.set(key, value) - } - return columns -} -/** - * Convert parsed TSV file data from the old BIDS format to the new BIDS format. - * - * @param {{headers: string[], rows: string[][]}} oldParsedTsv Parsed TSV data using the old format - * @returns {Map} The parsed contents of the TSV file, using the new format. - */ -export function convertParsedTSVData(oldParsedTsv) { - const columns = new Map() - - oldParsedTsv.headers.forEach((x) => { - columns.set(x, []) - }) - for (let i = 1; i < oldParsedTsv.rows.length; i++) { - for (let j = 0; j < oldParsedTsv.headers.length; j++) { - columns.get(oldParsedTsv.headers[j])?.push(oldParsedTsv.rows[i][j]) - } - } - for (const [key, value] of columns) { - columns.set(key, value) - } - return columns -} - -export default parseTSV diff --git a/src/bids/tsvParser.ts b/src/bids/tsvParser.ts new file mode 100644 index 00000000..d4e0b1ff --- /dev/null +++ b/src/bids/tsvParser.ts @@ -0,0 +1,70 @@ +/** + * This module provides functions for parsing TSV files. + * + * @module bids/tsvParser + */ + +export type ParsedTSV = Map +export type OldParsedTSV = { + headers: string[] + rows: string[][] +} + +const stripBOM = (str: string) => str.replace(/^\uFEFF/, '') +const normalizeEOL = (str: string) => str.replace(/\r\n/g, '\n').replace(/\r/g, '\n') +const isContentfulRow = (row: string) => row && !/^\s*$/.test(row) + +/** + * Parse a TSV file. + * + * @param contents The contents of a TSV file. + * @returns The parsed contents of the TSV file. + */ +export function parseTSV(contents: string): ParsedTSV { + const rows = stripBOM(normalizeEOL(contents)) + .split('\n') + .filter(isContentfulRow) + .map((str) => str.split('\t').map((cell) => cell.trim())) + + if (rows.length === 0) { + return new Map() + } + + const headers = rows[0] + + return createTsvMap(headers, rows) +} + +/** + * Convert parsed TSV file data from the old BIDS format to the new BIDS format. + * + * @param oldParsedTsv Parsed TSV data using the old format + * @returns The parsed contents of the TSV file, using the new format. + */ +export function convertParsedTSVData(oldParsedTsv: OldParsedTSV): ParsedTSV { + return createTsvMap(oldParsedTsv.headers, oldParsedTsv.rows) +} + +/** + * Create a parsed TSV map. + * + * @param headers The list of headers. + * @param rows The grid of rows and cells. + * @returns The parsed contents of the TSV file. + */ +function createTsvMap(headers: string[], rows: string[][]): ParsedTSV { + const columns = new Map() + + for (const header of headers) { + columns.set(header, []) + } + for (let i = 1; i < rows.length; i++) { + for (let j = 0; j < headers.length; j++) { + columns.get(headers[j])?.push(rows[i][j]) + } + } + + return columns +} + +export default parseTSV diff --git a/src/bids/types/dataset.js b/src/bids/types/dataset.ts similarity index 74% rename from src/bids/types/dataset.js rename to src/bids/types/dataset.ts index c54fdf5f..ecad091d 100644 --- a/src/bids/types/dataset.js +++ b/src/bids/types/dataset.ts @@ -2,13 +2,20 @@ * This module contains the {@link BidsDataset} class, which represents a BIDS dataset for HED validation. * @module bids/types/dataset */ + +import path from 'node:path' + import { BidsFileAccessor } from '../datasetParser' import { BidsSidecar } from './json' import { BidsTsvFile } from './tsv' import { generateIssue, IssueError } from '../../issues/issues' import { getMergedSidecarData, organizedPathsGenerator } from '../../utils/paths' import { BidsHedIssue } from './issues' -import path from 'node:path' +import { type HedSchemas } from '../../schema/containers' + +type BidsFileAccessorConstructor = { + create(datasetRootDirectory: string | object): Promise +} /** * A BIDS dataset. @@ -42,47 +49,41 @@ import path from 'node:path' * * @property {Map} sidecarMap Map of BIDS sidecar files that contain HED annotations. * @property {string|null} datasetRootDirectory The dataset's root directory as an absolute path (Node.js context). - * @property {Schemas} hedSchemas The HED schemas used to validate this dataset. + * @property {HedSchemas} hedSchemas The HED schemas used to validate this dataset. * @property {BidsFileAccessor} fileAccessor The BIDS file accessor. */ export class BidsDataset { /** * Map of BIDS sidecar files that contain HED annotations. * The keys are relative paths and the values are BidsSidecar objects. - * @type {Map} */ - sidecarMap + public sidecarMap: Map /** * The dataset's root directory as an absolute path (Node.js context). - * @type {string|null} */ - datasetRootDirectory + public readonly datasetRootDirectory: string | null /** * The HED schemas used to validate this dataset. - * @type {Schemas} */ - hedSchemas + private hedSchemas: HedSchemas /** * The BIDS file accessor. - * @type {BidsFileAccessor} - * @public */ - fileAccessor + public fileAccessor: BidsFileAccessor /** * Constructor for a BIDS dataset. * - * @param {BidsFileAccessor} accessor An instance of BidsFileAccessor (or its subclasses). - * @throws {Error} If accessor is not an instance of BidsFileAccessor. - * @private + * @param accessor An instance of BidsFileAccessor (or its subclasses). + * @throws {IssueError} If accessor is not an instance of BidsFileAccessor. * @see BidsDataset.create */ - constructor(accessor) { + private constructor(accessor: BidsFileAccessor) { if (!(accessor instanceof BidsFileAccessor)) { - throw new Error('BidsDataset constructor requires an instance of BidsFileAccessor.\n') + IssueError.generateAndThrowInternalError('BidsDataset constructor requires an instance of BidsFileAccessor') } this.fileAccessor = accessor this.datasetRootDirectory = accessor.datasetRootDirectory // Set from fileAccessor @@ -97,34 +98,25 @@ export class BidsDataset { * Note: This method will fail to create a BidsDataset if a valid HED schema cannot be loaded or any of the * JSON sidecars cannot be loaded. It does not perform HED validation. * - * @param {string | object} rootOrFiles The root directory of the dataset or a file-like object. - * @param {function} fileAccessorClass The BidsFileAccessor class to use for accessing files. - * @returns {Promise<[BidsDataset|null, BidsHedIssue[]]>} A Promise that resolves to a two-element array containing the BidsDataset instance (or null if creation failed) and an array of issues. - * + * @param rootOrFiles The root directory of the dataset or a file-like object. + * @param fileAccessorClass The BidsFileAccessor class to use for accessing files. + * @returns A Promise that resolves to a two-element array containing the BidsDataset instance (or null if creation failed) and an array of issues. */ - static async create(rootOrFiles, fileAccessorClass) { + static async create( + rootOrFiles: string | object, + fileAccessorClass: BidsFileAccessorConstructor, + ): Promise<[BidsDataset | null, BidsHedIssue[]]> { let dataset = null - const issues = [] + const issues: BidsHedIssue[] = [] try { const accessor = await fileAccessorClass.create(rootOrFiles) dataset = new BidsDataset(accessor) const schemaIssues = await dataset.setHedSchemas() issues.push(...schemaIssues) - if (dataset.hedSchemas === null) { - return [null, issues] - } const sidecarIssues = await dataset.setSidecars() issues.push(...sidecarIssues) } catch (error) { - if (error instanceof IssueError) { - issues.push(error.issue) - } else { - issues.push({ - code: 'INTERNAL_ERROR', - message: `An unexpected error occurred while creating the BidsDataset: ${error.message}`, - location: typeof rootOrFiles === 'string' ? rootOrFiles : 'Uploaded files', - }) - } + issues.push(...BidsHedIssue.fromHedIssues(error, null)) } if (issues.length > 0) { dataset = null @@ -138,16 +130,16 @@ export class BidsDataset { * This method reads the `dataset_description.json` file, extracts the `HEDVersion` field, * and builds the HED schemas. The result is stored in {@link BidsDataset.hedSchemas}. * - * @returns {Promise} A promise that resolves to an array of issues encountered during schema loading. + * @returns A promise that resolves to an array of issues encountered during schema loading. * @throws {IssueError} If `dataset_description.json` is missing or contains an invalid HED specification. */ - async setHedSchemas() { + async setHedSchemas(): Promise { let description try { const descriptionContentString = await this.fileAccessor.getFileContent('dataset_description.json') if (descriptionContentString === null || typeof descriptionContentString === 'undefined') { - throw new IssueError(generateIssue('missingSchemaSpecification', { file: 'dataset_description.json' })) + IssueError.generateAndThrow('missingSchemaSpecification', { file: 'dataset_description.json' }) } description = { jsonData: JSON.parse(descriptionContentString), @@ -156,23 +148,19 @@ export class BidsDataset { if (e instanceof IssueError) { throw e } - throw new IssueError(generateIssue('missingSchemaSpecification', { file: 'dataset_description.json' })) + IssueError.generateAndThrow('missingSchemaSpecification', { file: 'dataset_description.json' }) } try { this.hedSchemas = await this.fileAccessor.schemaBuilder(description) if (this.hedSchemas === null) { - throw new IssueError( - generateIssue('invalidSchemaSpecification', { spec: description.jsonData?.HEDVersion || null }), - ) + IssueError.generateAndThrow('invalidSchemaSpecification', { spec: description.jsonData?.HEDVersion || null }) } } catch (e) { if (e instanceof IssueError) { throw e } - throw new IssueError( - generateIssue('invalidSchemaSpecification', { spec: description.jsonData?.HEDVersion || null }), - ) + IssueError.generateAndThrow('invalidSchemaSpecification', { spec: description.jsonData?.HEDVersion || null }) } return [] } @@ -185,9 +173,9 @@ export class BidsDataset { * * Note: This method does not validate the HED data within the sidecars; it only parses them. * - * @returns {Promise} A promise that resolves to an array of issues encountered during sidecar parsing. + * @returns A promise that resolves to an array of issues encountered during sidecar parsing. */ - async setSidecars() { + async setSidecars(): Promise { this.sidecarMap = new Map() const issues = [] const organizedPaths = this.fileAccessor.organizedPaths @@ -204,7 +192,7 @@ export class BidsDataset { const promise = this.fileAccessor .getFileContent(jsonPath) .then((jsonText) => { - const sidecarIssues = [] + const sidecarIssues: BidsHedIssue[] = [] if (jsonText === null) { const errorMessage = `Could not read JSON file: ${jsonPath}` sidecarIssues.push( @@ -266,9 +254,9 @@ export class BidsDataset { * Note: If any of the sidecars have errors (not just warnings), the TSV files will not be validated. * This is because a single error in a sidecar can result in errors on every line of a TSV file. * - * @returns {Promise} A promise that resolves to an array of issues found during validation. + * @returns A promise that resolves to an array of issues found during validation. */ - async validate() { + async validate(): Promise { const issues = this.validateSidecars() if (issues.some((issue) => issue.severity === 'error')) { return issues @@ -282,11 +270,10 @@ export class BidsDataset { * This method iterates through all JSON sidecars and validates the HED data within them. * Note: The data has already been parsed into BidsSidecar objects. * - * @returns {BidsHedIssue[]} An array of issues found during sidecar validation. - * @private + * @returns An array of issues found during sidecar validation. */ - validateSidecars() { - let issues = [] + private validateSidecars(): BidsHedIssue[] { + let issues: BidsHedIssue[] = [] for (const relativePath of organizedPathsGenerator(this.fileAccessor.organizedPaths, '.json')) { const sidecar = this.sidecarMap.get(relativePath) @@ -303,11 +290,10 @@ export class BidsDataset { * This method iterates through all `.tsv` files, merges them with the corresponding JSON sidecars, * and validates the HED data within them. * - * @returns {Promise} A promise that resolves to an array of issues found during TSV validation. - * @private + * @returns A promise that resolves to an array of issues found during TSV validation. */ - async validateTsvFiles() { - let issues = [] + private async validateTsvFiles(): Promise { + let issues: BidsHedIssue[] = [] for (const [category, catMap] of this.fileAccessor.organizedPaths) { const tsvPaths = catMap.get('tsv') || [] const jsonPaths = catMap.get('json') || [] @@ -324,13 +310,12 @@ export class BidsDataset { * This method reads the TSV file content, merges it with corresponding sidecar data, * creates a BidsTsvFile object, and validates the HED data within it. * - * @param {string} tsvPath The relative path to the TSV file. - * @param {string} category The BIDS category (e.g., 'sub-001/ses-001/func'). - * @param {string[]} jsonPaths Array of JSON sidecar paths for this category of tsv. - * @returns {Promise} A promise that resolves to an array of issues found during validation of this TSV file. - * @private + * @param tsvPath The relative path to the TSV file. + * @param category The BIDS category (e.g., 'sub-001/ses-001/func'). + * @param jsonPaths Array of JSON sidecar paths for this category of tsv. + * @returns A promise that resolves to an array of issues found during validation of this TSV file. */ - async _validateTsvFile(tsvPath, category, jsonPaths) { + private async _validateTsvFile(tsvPath: string, category: string, jsonPaths: string[]): Promise { // Read the TSV file content -- if none do not proceed. const parsedPath = path.parse(tsvPath) const tsvContents = await this.fileAccessor.getFileContent(tsvPath) @@ -361,13 +346,12 @@ export class BidsDataset { * For special directories (phenotype, stimuli), it looks for a direct JSON counterpart. * For other files, it uses the BIDS inheritance hierarchy to merge multiple sidecars. * - * @param {string} tsvPath The relative path to the TSV file. - * @param {string} category The BIDS category (e.g., 'sub-001/ses-001/func'). - * @param {string[]} jsonPaths Array of JSON sidecar paths for this category. - * @returns {object} The merged sidecar data object, or empty object if no applicable sidecars found. - * @private + * @param tsvPath The relative path to the TSV file. + * @param category The BIDS category (e.g., 'sub-001/ses-001/func'). + * @param jsonPaths Array of JSON sidecar paths for this category. + * @returns The merged sidecar data object, or empty object if no applicable sidecars found. */ - _getSidecarData(tsvPath, category, jsonPaths) { + private _getSidecarData(tsvPath: string, category: string, jsonPaths: string[]): Record { const parsedPath = path.parse(tsvPath) if (BidsFileAccessor.SPECIAL_DIRS.includes(category)) { @@ -378,7 +362,6 @@ export class BidsDataset { } return {} } - const mergedSidecarData = getMergedSidecarData(tsvPath, jsonPaths, this.sidecarMap) - return mergedSidecarData + return getMergedSidecarData(tsvPath, jsonPaths, this.sidecarMap) } } diff --git a/src/bids/types/file.js b/src/bids/types/file.js deleted file mode 100644 index e834d187..00000000 --- a/src/bids/types/file.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * This module contains the {@link BidsFile} class, which is the base class for BIDS files. - * - * @module bids/types/file - */ - -import { BidsHedIssue } from './issues' -import { generateIssue } from '../../issues/issues' - -/** - * A BIDS file. - */ -export class BidsFile { - /** - * The name of this file. - * @type {string} - */ - name - - /** - * The Object representing this file data. - * This is used to generate {@link BidsHedIssue} objects. - * @type {Object} - */ - file - - /** - * The validator class used to validate this file. - * @private - */ - _validatorClass - - constructor(name, file, validatorClass) { - this.name = name - this.file = file - this._validatorClass = validatorClass - } - - /** - * Validate this validator's tsv file. - * - * @param {Schemas} schemas - The HED schemas used to validate this file. - * @returns {BidsHedIssue[]} - Any issues found during validation of this TSV file. - */ - validate(schemas) { - if (!this.hasHedData) { - return [] - } - if (!schemas) { - return [ - BidsHedIssue.fromHedIssue( - generateIssue('missingSchemaSpecification', { - message: 'No valid HED schema specification was supplied.', - }), - this.file, - ), - ] - } - - try { - const validator = new this.validatorClass(this, schemas) - validator.validate() - return [...validator.errors, ...validator.warnings] - } catch (error) { - // The low-level parsing throws exceptions with the issue encapsulated. - return BidsHedIssue.fromHedIssues(error, this.file) - } - } - - /** - * Determine whether this file has any HED data. - * - * @returns {boolean} - */ - get hasHedData() { - return false - } - - /** - * The validator class used to validate this file. - * - * @returns {function} The validator class used to validate this file. - */ - get validatorClass() { - return this._validatorClass - } -} diff --git a/src/bids/types/file.ts b/src/bids/types/file.ts new file mode 100644 index 00000000..473f054a --- /dev/null +++ b/src/bids/types/file.ts @@ -0,0 +1,106 @@ +/** + * This module contains the {@link BidsFile} class, which is the base class for BIDS files. + * + * @module bids/types/file + */ + +import { BidsHedIssue } from './issues' +import { type BidsValidator } from '../validator/validator' +import { generateIssue } from '../../issues/issues' +import { type HedSchemas } from '../../schema/containers' + +type BidsValidatorConstructor = { + new (file: BidsFile, schemas: HedSchemas): BidsValidator +} + +/** + * A BIDS file. + */ +export abstract class BidsFile { + /** + * The name of this file. + */ + public readonly name: string + + /** + * The Object representing this file data. + * This is used to generate {@link BidsHedIssue} objects. + */ + public readonly file: any + + /** + * The validator class used to validate this file. + */ + readonly #validatorClass: BidsValidatorConstructor + + /** + * Constructor. + * + * @param name The name of this file. + * @param file The Object representing this file data. + * @param validatorClass The validator class used to validate this file. + */ + protected constructor(name: string, file: any, validatorClass: BidsValidatorConstructor) { + this.name = name + this.file = file + this.#validatorClass = validatorClass + } + + /** + * Override of {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString | Object.prototype.toString}. + * + * @returns The file name. + */ + public toString(): string { + return this.name + } + + /** + * Validate this validator's file. + * + * @param schemas The HED schemas used to validate this file. + * @returns Any issues found during validation of this TSV file. + */ + public validate(schemas: HedSchemas): BidsHedIssue[] { + if (!this.hasHedData) { + return [] + } + if (!schemas) { + return [ + BidsHedIssue.fromHedIssue( + generateIssue('missingSchemaSpecification', { + message: 'No valid HED schema specification was supplied.', + }), + this.file, + ), + ] + } + + try { + const validator = new this.validatorClass(this, schemas) + validator.validate() + return [...validator.errors, ...validator.warnings] + } catch (error) { + // The low-level parsing throws exceptions with the issue encapsulated. + return BidsHedIssue.fromHedIssues(error, this.file) + } + } + + /** + * Determine whether this file has any HED data. + * + * @returns Whether this file has any HED data. + */ + public get hasHedData(): boolean { + return false + } + + /** + * The validator class used to validate this file. + * + * @returns The validator class used to validate this file. + */ + public get validatorClass(): BidsValidatorConstructor { + return this.#validatorClass + } +} diff --git a/src/bids/types/issues.js b/src/bids/types/issues.ts similarity index 53% rename from src/bids/types/issues.js rename to src/bids/types/issues.ts index 6f6ca9ff..d4e3bd12 100644 --- a/src/bids/types/issues.js +++ b/src/bids/types/issues.ts @@ -2,11 +2,11 @@ * Provides a wrapper for HED validation issues that is compatible with the BIDS validator. * @module bids/types/issues */ -import { generateIssue, IssueError } from '../../issues/issues.js' -/** - * @typedef {import('../../issues/issues.js').Issue} Issue - */ +import { generateIssue, type Issue, IssueError, type IssueParameters } from '../../issues/issues' +import { type IssueLevel } from '../../issues/data' + +type BidsIssueCode = 'HED_ERROR' | 'HED_WARNING' | 'INTERNAL_ERROR' /** * A wrapper for a HED validation issue that is compatible with the BIDS validator. @@ -17,64 +17,60 @@ import { generateIssue, IssueError } from '../../issues/issues.js' export class BidsHedIssue { /** * The file associated with this issue. - * @type {object} */ - file + public readonly file: any /** * The underlying HED issue object. - * @type {Issue} */ - hedIssue + public readonly hedIssue: Issue /** * The BIDS-compliant issue code. - * @type {string} */ - code + public readonly code: BidsIssueCode /** * The HED-specific issue code. - * @type {string} */ - subCode + public readonly subCode: string /** * The severity of the issue (e.g., 'error' or 'warning'). - * @type {string} */ - severity + public readonly severity: IssueLevel /** * The human-readable issue message. - * @type {string} */ - issueMessage + public issueMessage: string /** * The line number where the issue occurred. - * @type {number} */ - line + public readonly line: string /** * The path to the file where the issue occurred. - * @type {string} */ - location + public readonly location: string /** * Constructs a BidsHedIssue object. * - * @param {Issue} hedIssue The HED issue object to be wrapped. - * @param {object} file The file object associated with this issue. + * @deprecated Direct use of this constructor is not recommended. Use {@link BidsHedIssue.fromHedIssues}. + * + * @param hedIssue The HED issue object to be wrapped. + * @param file The file object associated with this issue. */ - constructor(hedIssue, file) { + public constructor(hedIssue: Issue, file: any) { this.hedIssue = hedIssue this.file = file // BIDS fields - if (hedIssue.level === 'warning') { + if (hedIssue.internalCode === 'internalError') { + this.code = 'INTERNAL_ERROR' + } else if (hedIssue.level === 'warning') { this.code = 'HED_WARNING' } else { this.code = 'HED_ERROR' @@ -86,14 +82,23 @@ export class BidsHedIssue { this.location = file?.path } + /** + * Override of {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString | Object.prototype.toString}. + * + * @returns The issue message. + */ + public toString(): string { + return this.issueMessage + } + /** * Transforms a list of issues into a Map, keyed by severity level. * - * @param {BidsHedIssue[]} issues A list of BIDS HED issues. - * @returns {Map} A map where keys are severity levels and values are arrays of issues. + * @param issues A list of BIDS HED issues. + * @returns A map where keys are severity levels and values are arrays of issues. */ - static splitErrors(issues) { - const issueMap = new Map() + public static splitErrors(issues: BidsHedIssue[]): Map { + const issueMap = new Map() for (const issue of issues) { if (!issueMap.has(issue.severity)) { issueMap.set(issue.severity, []) @@ -106,11 +111,11 @@ export class BidsHedIssue { /** * Categorizes a list of issues by their subCode values. * - * @param {BidsHedIssue[]} issues A list of BIDS HED issues. - * @returns {Map} A map where keys are HED issue codes and values are arrays of issues. + * @param issues A list of BIDS HED issues. + * @returns A map where keys are HED issue codes and values are arrays of issues. */ - static categorizeByCode(issues) { - const codeMap = new Map() + public static categorizeByCode(issues: BidsHedIssue[]): Map { + const codeMap = new Map() for (const issue of issues) { if (!codeMap.has(issue.subCode)) { codeMap.set(issue.subCode, []) @@ -123,12 +128,12 @@ export class BidsHedIssue { /** * Reduces a list of issues to one of each subCode that occurred in the incoming list, summarizing the occurrences. * - * @param {BidsHedIssue[]} issues A list of BIDS HED issues. - * @returns {BidsHedIssue[]} A new list of issues with one issue of each type. + * @param issues A list of BIDS HED issues. + * @returns A new list of issues with one issue of each type. */ - static reduceIssues(issues) { + public static reduceIssues(issues: BidsHedIssue[]): BidsHedIssue[] { const categorizedIssues = BidsHedIssue.categorizeByCode(issues) - const reducedIssues = [] + const reducedIssues: BidsHedIssue[] = [] for (const issueList of categorizedIssues.values()) { if (issueList.length === 0) { continue @@ -154,12 +159,16 @@ export class BidsHedIssue { * If `limitErrors` is true, the output will be reduced to one issue of each subCode type in the list. * The message of each "representative" issue will be updated to summarize the number of occurrences and files. * - * @param {BidsHedIssue[]} issues A list of BIDS HED issues. - * @param {boolean} checkWarnings Whether to include warnings in the output. - * @param {boolean} limitErrors Whether to reduce the list of issues to one of each type. - * @returns {BidsHedIssue[]} The processed list of issues. + * @param issues A list of BIDS HED issues. + * @param checkWarnings Whether to include warnings in the output. + * @param limitErrors Whether to reduce the list of issues to one of each type. + * @returns The processed list of issues. */ - static processIssues(issues, checkWarnings = false, limitErrors = false) { + public static processIssues( + issues: BidsHedIssue[], + checkWarnings: boolean = false, + limitErrors: boolean = false, + ): BidsHedIssue[] { const issueMap = BidsHedIssue.splitErrors(issues) const errorIssues = issueMap.get('error') ?? [] const warningIssues = issueMap.get('warning') ?? [] @@ -179,18 +188,22 @@ export class BidsHedIssue { /** * Converts one or more HED issues into BIDS-compatible issues. * - * @param {Error|Issue[]} hedIssues One or more HED-format issues. - * @param {object} file A BIDS-format file object used to generate {@link BidsHedIssue} objects. - * @param {object?} extraParameters Any extra parameters to inject into the {@link Issue} objects. - * @returns {BidsHedIssue[]} An array of BIDS-compatible issues. + * @param hedIssues One or more HED-format issues. + * @param file A BIDS-format file object used to generate {@link BidsHedIssue} objects. + * @param extraParameters Any extra parameters to inject into the {@link Issue} objects. + * @returns An array of BIDS-compatible issues. */ - static fromHedIssues(hedIssues, file, extraParameters) { - if (hedIssues.length === 0) { - return [] - } else if (hedIssues instanceof IssueError) { + public static fromHedIssues( + hedIssues: Error | Issue[], + file: object, + extraParameters: IssueParameters = {}, + ): BidsHedIssue[] { + if (hedIssues instanceof IssueError) { return [BidsHedIssue.fromHedIssue(hedIssues.issue, file, extraParameters)] } else if (hedIssues instanceof Error) { return [new BidsHedIssue(generateIssue('internalError', { message: hedIssues.message }), file ?? null)] + } else if (hedIssues.length === 0) { + return [] } else { return hedIssues.map((hedIssue) => BidsHedIssue.fromHedIssue(hedIssue, file, extraParameters)) } @@ -199,31 +212,29 @@ export class BidsHedIssue { /** * Converts a single HED issue into a BIDS-compatible issue. * - * @param {Issue} hedIssue A HED-format issue. - * @param {object} file A BIDS-format file object used to generate a {@link BidsHedIssue} object. - * @param {object?} extraParameters Any extra parameters to inject into the {@link Issue} object. - * @returns {BidsHedIssue} The BIDS-compatible issue. - */ - static fromHedIssue(hedIssue, file, extraParameters) { - Object.assign(hedIssue.parameters, extraParameters) - hedIssue.generateMessage() + * @param hedIssue A HED-format issue. + * @param file A BIDS-format file object used to generate a {@link BidsHedIssue} object. + * @param extraParameters Any extra parameters to inject into the {@link Issue} object. + * @returns The BIDS-compatible issue. + */ + public static fromHedIssue(hedIssue: Issue, file: object, extraParameters: IssueParameters = {}): BidsHedIssue { + hedIssue.addParameters(extraParameters) return new BidsHedIssue(hedIssue, file) } /** * Transforms a list of mixed-format issues into BIDS-compatible issues. * - * @param {Array} issues A list of mixed-format issues. - * @param {object} file A BIDS-format file object used to generate {@link BidsHedIssue} objects. - * @returns {BidsHedIssue[]} An array of BIDS-compatible issues. + * @param issues A list of mixed-format issues. + * @param file A BIDS-format file object used to generate {@link BidsHedIssue} objects. + * @returns An array of BIDS-compatible issues. */ - static transformToBids(issues, file = null) { + public static transformToBids(issues: Array, file: object = null): BidsHedIssue[] { return issues.map((issue) => { if (issue instanceof BidsHedIssue) { return issue } else if (issue instanceof IssueError) { - const issueFile = issue.issue.file || file - return BidsHedIssue.fromHedIssue(issue.issue, issueFile, issue.issue.parameters) + return BidsHedIssue.fromHedIssue(issue.issue, file) } else if (issue instanceof Error) { return new BidsHedIssue(generateIssue('internalError', { message: issue.message }), file) } else { @@ -231,4 +242,18 @@ export class BidsHedIssue { } }) } + + /** + * Add new parameters to the underlying HED issues of a list of BIDS issues and regenerate the issue messages. + * + * @param issues A list of BIDS-compatible issues. + * @param parameters The parameters to add. + */ + public static addIssueParameters(issues: BidsHedIssue[], parameters: IssueParameters): void { + for (const issue of issues) { + const hedIssue = issue.hedIssue + hedIssue.addParameters(parameters) + issue.issueMessage = hedIssue.message + } + } } diff --git a/src/bids/types/json.js b/src/bids/types/json.ts similarity index 68% rename from src/bids/types/json.js rename to src/bids/types/json.ts index c78d7ae6..1ee73594 100644 --- a/src/bids/types/json.js +++ b/src/bids/types/json.ts @@ -9,31 +9,34 @@ import { parseHedString } from '../../parser/parser' import ParsedHedString from '../../parser/parsedHedString' import { BidsFile } from './file' import BidsHedSidecarValidator from '../validator/sidecarValidator' -import { IssueError, updateIssueParameters } from '../../issues/issues' +import { IssueError, addIssueParameters, type Issue } from '../../issues/issues' import { DefinitionManager, Definition } from '../../parser/definitionManager' +import { type HedSchemas } from '../../schema/containers' +import { type ParsedHedColumnSplice } from '../../parser/parsedHedColumnSplice' const ILLEGAL_SIDECAR_KEYS = new Set(['hed', 'n/a']) +type BidsSidecarHedEntry = { HED: unknown } + /** * A BIDS JSON file. */ export class BidsJsonFile extends BidsFile { /** * This file's JSON data. - * @type {Object} */ - jsonData + public readonly jsonData: Record /** * Constructor for a BIDS JSON file. * * Note: This class is used for non-sidecars such as dataset_description.json and does not have a validation method. * - * @param {string} name The name of the JSON file. - * @param {Object} file The file object representing this file. - * @param {Object} jsonData The JSON data for this file. + * @param name The name of the JSON file. + * @param file The file object representing this file. + * @param jsonData The JSON data for this file. */ - constructor(name, file, jsonData) { + public constructor(name: string, file: object, jsonData: Record) { super(name, file, BidsHedSidecarValidator) this.jsonData = jsonData } @@ -51,62 +54,59 @@ export class BidsJsonFile extends BidsFile { */ export class BidsSidecar extends BidsJsonFile { /** - * The extracted keys for this sidecar (string --> BidsSidecarKey) - * @type {Map} + * The extracted keys for this sidecar. */ - sidecarKeys + sidecarKeys: Map /** - * The extracted HED data for this sidecar (string --> string | Object: string, string - * @type {Map} + * The extracted HED data for this sidecar. */ - hedData + hedData: Map> /** - * The parsed HED data for this sidecar (string --> ParsedHedString | Map: string --> ParsedHedString). - * @type {Map} + * The parsed HED data for this sidecar. */ - parsedHedData + parsedHedData: Map> /** * The extracted HED value strings. - * @type {string[]} */ - hedValueStrings + hedValueStrings: string[] /** * The extracted HED categorical strings. - * @type {string[]} */ - hedCategoricalStrings + hedCategoricalStrings: string[] /** - * The mapping of column splice references (string --> Set of string). - * @type {Map} + * The mapping of column splice references. */ - columnSpliceMapping + columnSpliceMapping: Map> /** * The set of column splice references. - * @type {Set} */ - columnSpliceReferences + columnSpliceReferences: Set /** * The object that manages definitions. - * @type {DefinitionManager} */ - definitions + definitions: DefinitionManager /** * Constructor for a BIDS sidecar. Used for files like events.json, participants.json, etc. * - * @param {string} name The name of the sidecar file. - * @param {Object} file The file object representing this file. - * @param {Object} sidecarData The raw JSON data. - * @param {DefinitionManager} defManager The external definitions to use. - */ - constructor(name, file, sidecarData = {}, defManager = null) { + * @param name The name of the sidecar file. + * @param file The file object representing this file. + * @param sidecarData The raw JSON data. + * @param defManager The external definitions to use. + */ + public constructor( + name: string, + file: object, + sidecarData: Record = {}, + defManager: DefinitionManager | null = null, + ) { super(name, file, sidecarData) this.columnSpliceMapping = new Map() this.columnSpliceReferences = new Set() @@ -120,7 +120,7 @@ export class BidsSidecar extends BidsJsonFile { * * @returns {boolean} */ - get hasHedData() { + public get hasHedData(): boolean { return this.sidecarKeys.size > 0 } @@ -131,11 +131,11 @@ export class BidsSidecar extends BidsJsonFile { * * The parsed strings are placed into {@link parsedHedData}. * - * @param {Schemas} hedSchemas - The HED schema collection. - * @param {boolean} fullValidation - True if full validation should be performed. - * @returns {Array} [Issue[], Issue[]] Any errors and warnings found + * @param hedSchemas The HED schema collection. + * @param fullValidation True if full validation should be performed. + * @returns Any errors and warnings found. */ - parseSidecarKeys(hedSchemas, fullValidation = false) { + parseSidecarKeys(hedSchemas: HedSchemas, fullValidation: boolean = false): [Issue[], Issue[]] { this.parsedHedData = new Map() const errors = [] const warnings = [] @@ -145,8 +145,8 @@ export class BidsSidecar extends BidsJsonFile { fullValidation && !this.columnSpliceReferences.has(name), ) const updateParams = { sidecarKey: name, filePath: this.file?.path } - updateIssueParameters(errorIssues, updateParams) - updateIssueParameters(warningIssues, updateParams) + addIssueParameters(errorIssues, updateParams) + addIssueParameters(warningIssues, updateParams) errors.push(...errorIssues) warnings.push(...warningIssues) if (sidecarKey.isValueKey) { @@ -161,10 +161,9 @@ export class BidsSidecar extends BidsJsonFile { /** * Set the definition manager for this sidecar. - * @param defManager - * @private + * @param defManager The definition manager. */ - _setDefinitions(defManager) { + private _setDefinitions(defManager: DefinitionManager | null): void { if (defManager instanceof DefinitionManager) { this.definitions = defManager } else if (defManager == null) { @@ -179,12 +178,11 @@ export class BidsSidecar extends BidsJsonFile { /** * Create the sidecar key map from the JSON. - * @private */ - _filterHedStrings() { + private _filterHedStrings(): void { this.sidecarKeys = new Map( Object.entries(this.jsonData) - .map(([key, value]) => { + .map(([key, value]): [string, BidsSidecarKey] | null => { const trimmedKey = key.trim() const lowerKey = trimmedKey.toLowerCase() if (ILLEGAL_SIDECAR_KEYS.has(lowerKey)) { @@ -208,13 +206,12 @@ export class BidsSidecar extends BidsJsonFile { /** * Verify that a column has no deeply nested "HED" keys. * - * @param {string} key An object key. - * @param {*} value An object value. - * @param {string|null} topKey The top-level key, if any. + * @param key An object key. + * @param value An object value. + * @param topKey The top-level key, if any. * @throws {IssueError} If an invalid "HED" key is found. - * @private */ - _verifyKeyHasNoDeepHed(key, value, topKey = null) { + private _verifyKeyHasNoDeepHed(key: string, value: unknown, topKey: string | null = null): void { if (key.toUpperCase() === 'HED') { IssueError.generateAndThrow('illegalSidecarHedDeepKey', { key: topKey, @@ -233,19 +230,17 @@ export class BidsSidecar extends BidsJsonFile { /** * Determine whether a sidecar value has HED data. * - * @param {Object} sidecarValue A BIDS sidecar value. - * @returns {boolean} Whether the sidecar value has HED data. - * @private + * @param sidecarValue A BIDS sidecar value. + * @returns Whether the sidecar value has HED data. */ - static _sidecarValueHasHed(sidecarValue) { + private static _sidecarValueHasHed(sidecarValue: unknown): sidecarValue is BidsSidecarHedEntry { return sidecarValue !== null && typeof sidecarValue === 'object' && 'HED' in sidecarValue } /** * Categorize the column strings into value strings and categorical strings - * @private */ - _categorizeHedStrings() { + private _categorizeHedStrings(): void { this.hedValueStrings = [] this.hedCategoricalStrings = [] this.hedData = new Map() @@ -262,10 +257,8 @@ export class BidsSidecar extends BidsJsonFile { /** * Generate a mapping of an individual BIDS sidecar's curly brace references. - * - * @private */ - _generateSidecarColumnSpliceMap() { + private _generateSidecarColumnSpliceMap(): void { this.columnSpliceMapping = new Map() this.columnSpliceReferences = new Set() @@ -282,11 +275,10 @@ export class BidsSidecar extends BidsJsonFile { /** * - * @param {BidsSidecarKey} sidecarKey - The column to be checked for column splices. - * @param {ParsedHedString} hedData - The parsed HED string to check for column splices. - * @private + * @param sidecarKey The column to be checked for column splices. + * @param hedData The parsed HED string to check for column splices. */ - _parseValueSplice(sidecarKey, hedData) { + private _parseValueSplice(sidecarKey: string, hedData: ParsedHedString): void { if (hedData.columnSplices.length > 0) { const keyReferences = this._processColumnSplices(new Set(), hedData.columnSplices) this.columnSpliceMapping.set(sidecarKey, keyReferences) @@ -295,11 +287,10 @@ export class BidsSidecar extends BidsJsonFile { /** * - * @param {BidsSidecarKey} sidecarKey - The column to be checked for column splices. - * @param {Map} hedData - A Map with columnValue --> parsed HED string for a sidecar key to check for column splices. - * @private + * @param sidecarKey The column to be checked for column splices. + * @param hedData A Map with columnValue --> parsed HED string for a sidecar key to check for column splices. */ - _parseCategorySplice(sidecarKey, hedData) { + private _parseCategorySplice(sidecarKey: string, hedData: Map): void { let keyReferences = null for (const valueString of hedData.values()) { if (valueString?.columnSplices.length > 0) { @@ -313,12 +304,15 @@ export class BidsSidecar extends BidsJsonFile { /** * Add a list of columnSplices to a key set. - * @param {Set|null} keyReferences - * @param {ParsedHedColumnSplice[]} columnSplices - * @returns {Set} - * @private - */ - _processColumnSplices(keyReferences, columnSplices) { + * + * @param keyReferences + * @param columnSplices + * @returns + */ + private _processColumnSplices( + keyReferences: Set | null, + columnSplices: ParsedHedColumnSplice[], + ): Set { keyReferences ??= new Set() for (const columnSplice of columnSplices) { keyReferences.add(columnSplice.originalTag) @@ -331,54 +325,47 @@ export class BidsSidecar extends BidsJsonFile { export class BidsSidecarKey { /** * The name of this key -- usually corresponds to a column name in a TSV file. - * @type {string} */ - name + readonly name: string /** * The unparsed category mapping. - * @type {Map} */ - categoryMap + readonly categoryMap: Map /** * The parsed category mapping. - * @type {Map} */ - parsedCategoryMap + parsedCategoryMap: Map /** * The unparsed value string. - * @type {string} */ - valueString + readonly valueString: string /** * The parsed value string. - * @type {ParsedHedString} */ - parsedValueString + parsedValueString: ParsedHedString /** * Weak reference to the sidecar. - * @type {BidsSidecar} */ - sidecar + readonly sidecar: BidsSidecar /** * Indication of whether this key is for definitions. - * @type {Boolean} */ - hasDefinitions + hasDefinitions: boolean /** * Constructor for BidsSidecarKey. * - * @param {string} key The name of this key. - * @param {string|Object} data The data for this key. - * @param {BidsSidecar} sidecar The parent sidecar. + * @param key The name of this key. + * @param data The data for this key. + * @param sidecar The parent sidecar. */ - constructor(key, data, sidecar) { + public constructor(key: string, data: unknown, sidecar: BidsSidecar) { this.name = key this.hasDefinitions = false // May reset to true when definitions for this key are checked this.sidecar = sidecar @@ -396,11 +383,11 @@ export class BidsSidecarKey { * * ###Note: This sets the parsedHedData as a side effect. * - * @param {Schemas} hedSchemas The HED schema collection. - * @param {boolean} fullValidation True if full validation should be performed. - * @returns {Array} - [Issue[], Issues[]] Errors and warnings that result from parsing. + * @param hedSchemas The HED schema collection. + * @param fullValidation True if full validation should be performed. + * @returns Errors and warnings that result from parsing. */ - parseHed(hedSchemas, fullValidation = false) { + public parseHed(hedSchemas: HedSchemas, fullValidation: boolean = false): [Issue[], Issue[]] { if (this.isValueKey) { return this._parseValueString(hedSchemas, fullValidation) } @@ -413,12 +400,11 @@ export class BidsSidecarKey { * ### Note: * The value strings cannot contain definitions. * - * @param {Schemas} hedSchemas - The HED schemas to use. - * @param {boolean} fullValidation - True if full validation should be performed. - * @returns {Array} - [Issue[], Issue[]] - Errors due for the value. - * @private + * @param hedSchemas The HED schemas to use. + * @param fullValidation True if full validation should be performed. + * @returns Errors due for the value. */ - _parseValueString(hedSchemas, fullValidation) { + private _parseValueString(hedSchemas: HedSchemas, fullValidation: boolean): [Issue[], Issue[]] { const [parsedString, errorIssues, warningIssues] = parseHedString( this.valueString, hedSchemas, @@ -432,12 +418,11 @@ export class BidsSidecarKey { /** * Parse the categorical values associated with this key. - * @param {Schemas} hedSchemas - The HED schemas used to check against. - * @param {boolean} fullValidation - True if full validation should be performed. - * @returns {Array} - Array[Issue[], Issue[]] A list of error issues and warning issues. - * @private + * @param hedSchemas The HED schemas used to check against. + * @param fullValidation True if full validation should be performed. + * @returns A list of error issues and warning issues. */ - _parseCategory(hedSchemas, fullValidation) { + private _parseCategory(hedSchemas: HedSchemas, fullValidation: boolean): [Issue[], Issue[]] { this.parsedCategoryMap = new Map() const errors = [] const warnings = [] @@ -469,11 +454,10 @@ export class BidsSidecarKey { /** * Check for definitions in the HED string. - * @param {ParsedHedString} parsedString - The string to check for definitions. - * @returns {Issue[]} - Errors that occur. - * @private + * @param parsedString The string to check for definitions. + * @returns Errors that occur. */ - _checkDefinitions(parsedString) { + private _checkDefinitions(parsedString: ParsedHedString): Issue[] { const errors = [] for (const group of parsedString.tagGroups) { if (!group.isDefinitionGroup) { @@ -492,9 +476,8 @@ export class BidsSidecarKey { /** * Whether this key is a value key. - * @returns {boolean} */ - get isValueKey() { + public get isValueKey(): boolean { return Boolean(this.valueString) } } diff --git a/src/bids/types/tsv.js b/src/bids/types/tsv.js deleted file mode 100644 index 89ac1a6e..00000000 --- a/src/bids/types/tsv.js +++ /dev/null @@ -1,164 +0,0 @@ -/** - * This module contains classes for representing BIDS TSV files and their components. - * - * @module bids/types/tsv - */ -import isPlainObject from 'lodash/isPlainObject' - -import { BidsFile } from './file' -import { convertParsedTSVData, parseTSV } from '../tsvParser' -import { BidsSidecar } from './json' -import BidsHedTsvValidator from '../validator/tsvValidator' -import { IssueError } from '../../issues/issues' - -/** - * A BIDS TSV file. - */ -export class BidsTsvFile extends BidsFile { - /** - * This file's parsed TSV data. - * @type {Map} - */ - parsedTsv - - /** - * HED strings in the "HED" column of the TSV data. - * @type {string[]} - */ - hedColumnHedStrings - - /** - * The pseudo-sidecar object representing the merged sidecar data. - * @type {BidsSidecar} - */ - mergedSidecar - - constructor(name, file, tsvData, mergedDictionary = {}, defManager = null) { - super(name, file, BidsHedTsvValidator) - - if (typeof tsvData === 'string') { - this.parsedTsv = parseTSV(tsvData) - } else if (tsvData instanceof Map) { - this.parsedTsv = new Map(tsvData) - } else if (isPlainObject(tsvData)) { - this.parsedTsv = convertParsedTSVData(tsvData) - } else { - const msg = 'The tsvData was not a string, Map or plain object, so a BidsTsvFile could not be created.' - IssueError.generateAndThrow('internalError', { message: msg, filePath: file.path }) - } - - this.mergedSidecar = new BidsSidecar(name, this.file, mergedDictionary, defManager) - this._parseHedColumn() - } - - /** - * Parse the HED column from the TSV data. - * @private - */ - _parseHedColumn() { - const hedColumn = this.parsedTsv.get('HED') - if (hedColumn === undefined) { - this.hedColumnHedStrings = [] - } else { - this.hedColumnHedStrings = hedColumn.map((hedCell) => (hedCell && hedCell !== 'n/a' ? hedCell : '')) - } - } - - /** - * Determine whether this file has any HED data. - * - * @returns {boolean} - */ - get hasHedData() { - return this.parsedTsv.has('HED') || this.mergedSidecar.hasHedData - } - - /** - * Whether this TSV file is a timeline file. - * - * @returns {boolean} - */ - get isTimelineFile() { - return this.parsedTsv.has('onset') - } -} - -/** - * An element in a BIDS TSV file. - */ -export class BidsTsvElement { - /** - * The string representation of this row - * @type {string} - */ - hedString - - /** - * The ParsedHedString representation of this row - * @type {import('../../parser/parsedHedString.js').ParsedHedString} - */ - parsedHedString - - /** - * The file this row belongs to (usually just the path). - * @type {Object} - */ - file - - /** - * The onset represented by this row or a NaN. - * @type {Number} - */ - onset - - /** - * The line number(s) (including the header) represented by this row. - * @type {string} - */ - tsvLine - - constructor(hedString, tsvFile, onset, tsvLine) { - this.hedString = hedString - this.parsedHedString = null - this.file = tsvFile.file - this.fileName = tsvFile.name - this.onset = onset - this.tsvLine = tsvLine - } - - /** - * Override of {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString | Object.prototype.toString}. - * - * @returns {string} The string representation of this element. - */ - toString() { - const onsetString = this.onset ? ` with onset=${this.onset.toString()}` : '' - return this.hedString + ` in TSV file "${this.fileName}" at line(s) ${this.tsvLine}` + onsetString - } - - /** - * Create a string list of a list of BidsTsvElement objects. - * @param {BidsTsvElement[]} elements - A list of elements to construct line numbers from. - * @returns {string} - A string with the list of line numbers for error messages. - */ - static getTsvLines(elements) { - return elements.map((element) => element.tsvLine).join(',') - } -} - -/** - * A row in a BIDS TSV file. - */ -export class BidsTsvRow extends BidsTsvElement { - /** - * The map of column name to value for this row. - * @type {Map} - */ - rowCells - - constructor(hedString, tsvFile, tsvLine, rowCells) { - const onset = rowCells.has('onset') ? rowCells.get('onset') : undefined - super(hedString, tsvFile, onset, tsvLine.toString()) - this.rowCells = rowCells - } -} diff --git a/src/bids/types/tsv.ts b/src/bids/types/tsv.ts new file mode 100644 index 00000000..ffc02ec0 --- /dev/null +++ b/src/bids/types/tsv.ts @@ -0,0 +1,207 @@ +/** + * This module contains classes for representing BIDS TSV files and their components. + * + * @module bids/types/tsv + */ +import isPlainObject from 'lodash/isPlainObject' + +import { BidsFile } from './file' +import { convertParsedTSVData, parseTSV, type OldParsedTSV, type ParsedTSV } from '../tsvParser' +import { BidsSidecar } from './json' +import BidsHedTsvValidator from '../validator/tsvValidator' +import { IssueError } from '../../issues/issues' +import { type DefinitionManager } from '../../parser/definitionManager' +import type ParsedHedString from '../../parser/parsedHedString' + +/** + * A BIDS TSV file. + */ +export class BidsTsvFile extends BidsFile { + /** + * This file's parsed TSV data. + */ + public readonly parsedTsv: ParsedTSV + + /** + * HED strings in the "HED" column of the TSV data. + */ + public readonly hedColumnHedStrings: string[] + + /** + * The pseudo-sidecar object representing the merged sidecar data. + */ + public readonly mergedSidecar: BidsSidecar + + /** + * Constructor. + * + * @param name The name of this file. + * @param file The Object representing this file data. + * @param tsvData This file's TSV data. + * @param mergedDictionary This file's merged JSON dictionary. + * @param defManager This file's definition manager. + */ + constructor( + name: string, + file: any, + tsvData: string | ParsedTSV | OldParsedTSV, + mergedDictionary: Record = {}, + defManager: DefinitionManager = null, + ) { + super(name, file, BidsHedTsvValidator) + + this.parsedTsv = this._parseTsv(tsvData, file) + this.hedColumnHedStrings = this._parseHedColumn() + this.mergedSidecar = new BidsSidecar(name, this.file, mergedDictionary, defManager) + } + + /** + * Parse the TSV file. + * + * @param tsvData This file's TSV data. + * @param file The Object representing this file data. + * @returns The parsed TSV data. + */ + private _parseTsv(tsvData: string | ParsedTSV | OldParsedTSV, file: any): ParsedTSV { + if (typeof tsvData === 'string') { + return parseTSV(tsvData) + } else if (tsvData instanceof Map) { + return new Map(tsvData) + } else if (isPlainObject(tsvData)) { + return convertParsedTSVData(tsvData) + } else { + const msg = 'The tsvData was not a string, Map or plain object, so a BidsTsvFile could not be created.' + IssueError.generateAndThrow('internalError', { message: msg, filePath: file.path }) + } + } + + /** + * Parse the HED column from the TSV data. + * + * @returns The parsed HED column. + */ + private _parseHedColumn(): string[] { + const hedColumn = this.parsedTsv.get('HED') + if (hedColumn === undefined) { + return [] + } else { + return hedColumn.map((hedCell) => (hedCell && hedCell !== 'n/a' ? hedCell : '')) + } + } + + /** + * Determine whether this file has any HED data. + * + * @returns Whether this file has any HED data. + */ + public get hasHedData(): boolean { + return this.parsedTsv.has('HED') || this.mergedSidecar.hasHedData + } + + /** + * Whether this TSV file is a timeline file. + * + * @returns Whether this TSV file is a timeline file. + */ + public get isTimelineFile(): boolean { + return this.parsedTsv.has('onset') + } +} + +/** + * An element in a BIDS TSV file. + */ +export class BidsTsvElement { + /** + * The string representation of this element. + */ + public readonly hedString: string + + /** + * The ParsedHedString representation of this element. + */ + public parsedHedString: ParsedHedString | null + + /** + * The file this element belongs to (usually just the path). + */ + public readonly file: any + + /** + * The name of the file this element belongs to (usually just the path). + */ + public readonly fileName: string + + /** + * The onset represented by this element or a NaN. + * + * @todo This probably should be a number. Move numeric conversion to this file? + */ + public readonly onset: string + + /** + * The line number(s) (including the header) represented by this element. + */ + public readonly tsvLine: string + + /** + * Constructor. + * + * @param hedString The string representation of this element. + * @param tsvFile The file this element belongs to (usually just the path). + * @param onset The onset represented by this element or a NaN. + * @param tsvLine The line number(s) (including the header) represented by this element. + */ + constructor(hedString: string, tsvFile: BidsTsvFile, onset: string, tsvLine: string) { + this.hedString = hedString + this.parsedHedString = null + this.file = tsvFile.file + this.fileName = tsvFile.name + this.onset = onset + this.tsvLine = tsvLine + } + + /** + * Override of {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString | Object.prototype.toString}. + * + * @returns The string representation of this element. + */ + public toString(): string { + const onsetString = this.onset ? ` with onset=${this.onset}` : '' + return this.hedString + ` in TSV file "${this.fileName}" at line(s) ${this.tsvLine}` + onsetString + } + + /** + * Create a string list of a list of BidsTsvElement objects. + * + * @param elements A list of elements to construct line numbers from. + * @returns A string with the list of line numbers for error messages. + */ + public static getTsvLines(elements: BidsTsvElement[]): string { + return elements.map((element) => element.tsvLine).join(',') + } +} + +/** + * A row in a BIDS TSV file. + */ +export class BidsTsvRow extends BidsTsvElement { + /** + * The map of column name to value for this row. + */ + public readonly rowCells: Map + + /** + * Constructor. + * + * @param hedString The string representation of this row. + * @param tsvFile The file this row belongs to (usually just the path). + * @param tsvLine The line number (including the header) represented by this row. + * @param rowCells The map of column name to value for this row. + */ + constructor(hedString: string, tsvFile: BidsTsvFile, tsvLine: number, rowCells: Map) { + const onset = rowCells.get('onset') ?? '' + super(hedString, tsvFile, onset, tsvLine.toString()) + this.rowCells = rowCells + } +} diff --git a/src/bids/validator/sidecarValidator.js b/src/bids/validator/sidecarValidator.ts similarity index 72% rename from src/bids/validator/sidecarValidator.js rename to src/bids/validator/sidecarValidator.ts index 25a7f393..12efda69 100644 --- a/src/bids/validator/sidecarValidator.js +++ b/src/bids/validator/sidecarValidator.ts @@ -2,11 +2,13 @@ * @module bids/validator/sidecarValidator */ +import { BidsValidator } from './validator' import { BidsHedIssue } from '../types/issues' +import { type BidsSidecar } from '../types/json' import ParsedHedString from '../../parser/parsedHedString' -import { generateIssue, IssueError, updateIssueParameters } from '../../issues/issues' -import { getCharacterCount } from '../../utils/string.js' -import { BidsValidator } from './validator' +import { generateIssue, IssueError } from '../../issues/issues' +import { type HedSchemas } from '../../schema/containers' +import { getCharacterCount } from '../../utils/string' /** * Validator for HED data in BIDS JSON sidecars. @@ -14,26 +16,24 @@ import { BidsValidator } from './validator' export class BidsHedSidecarValidator extends BidsValidator { /** * The BIDS sidecar being validated. - * @type {BidsSidecar} */ - sidecar + private readonly sidecar: BidsSidecar /** * Constructor for the BidsHedSidecarValidator. * - * @param {BidsSidecar} sidecar - The BIDS sidecar being validated. - * @param {Schemas} hedSchemas - The schemas used for the sidecar validation. + * @param sidecar The BIDS sidecar being validated. + * @param hedSchemas The schemas used for the sidecar validation. */ - constructor(sidecar, hedSchemas) { + public constructor(sidecar: BidsSidecar, hedSchemas: HedSchemas) { super(hedSchemas) this.sidecar = sidecar } /** * Validate a BIDS JSON sidecar file. Errors and warnings are stored. - * */ - validate() { + public validate(): void { // Allow schema to be set a validation time -- this is checked by the superclass of BIDS file const [errorIssues, warningIssues] = this.sidecar.parseSidecarKeys(this.hedSchemas, false) this.errors.push(...BidsHedIssue.fromHedIssues(errorIssues, this.sidecar.file)) @@ -56,10 +56,10 @@ export class BidsHedSidecarValidator extends BidsValidator { /** * Validate this sidecar's HED strings. * - * @returns {BidsHedIssue[]} All issues found. + * @returns All issues found. */ - _validateStrings() { - const issues = [] + private _validateStrings(): BidsHedIssue[] { + const issues: BidsHedIssue[] = [] for (const [sidecarKeyName, hedData] of this.sidecar.parsedHedData) { if (hedData instanceof ParsedHedString) { @@ -83,28 +83,26 @@ export class BidsHedSidecarValidator extends BidsValidator { /** * Check definitions and placeholders for a string associated with a sidecar key. * - * @param {string} sidecarKeyName - The name of the sidecar key associated with string to be checked. - * @param {ParsedHedString} hedString - The parsed string to be checked. - * @returns {BidsHedIssue[]} - Issues associated with the check. - * @private + * @param sidecarKeyName The name of the sidecar key associated with string to be checked. + * @param hedString The parsed string to be checked. + * @returns Issues associated with the check. */ - _checkDetails(sidecarKeyName, hedString) { + private _checkDetails(sidecarKeyName: string, hedString: ParsedHedString): BidsHedIssue[] { const issues = this._checkDefs(sidecarKeyName, hedString, true) issues.push(...this._checkPlaceholders(sidecarKeyName, hedString)) - updateIssueParameters(issues, { sidecarKey: sidecarKeyName, filePath: this.sidecar?.file?.path }) + BidsHedIssue.addIssueParameters(issues, { sidecarKey: sidecarKeyName, filePath: this.sidecar?.file?.path }) return issues } /** * Validate the Def and Def-expand usage against the sidecar definitions. * - * @param {string} sidecarKeyName - Name of the sidecar key for this HED string - * @param {ParsedHedString} hedString - The parsed HED string object associated with this key. - * @param {boolean} placeholdersAllowed - If true, placeholders are allowed here. - * @returns {BidsHedIssue[]} - Issues encountered such as missing definitions or improper Def-expand values. - * @private + * @param sidecarKeyName Name of the sidecar key for this HED string + * @param hedString The parsed HED string object associated with this key. + * @param placeholdersAllowed If true, placeholders are allowed here. + * @returns Issues encountered such as missing definitions or improper Def-expand values. */ - _checkDefs(sidecarKeyName, hedString, placeholdersAllowed) { + private _checkDefs(sidecarKeyName: string, hedString: ParsedHedString, placeholdersAllowed: boolean): BidsHedIssue[] { let issues = this.sidecar.definitions.validateDefs(hedString, this.hedSchemas, placeholdersAllowed) if (issues.length > 0) { return BidsHedIssue.fromHedIssues(issues, this.sidecar.file, { sidecarKey: sidecarKeyName }) @@ -113,7 +111,14 @@ export class BidsHedSidecarValidator extends BidsValidator { return BidsHedIssue.fromHedIssues(issues, this.sidecar.file, { sidecarKey: sidecarKeyName }) } - _checkPlaceholders(sidecarKeyName, hedString) { + /** + * Validate the placeholders. + * + * @param sidecarKeyName Name of the sidecar key for this HED string + * @param hedString The parsed HED string object associated with this key. + * @returns Issues encountered relating to invalid placeholders. + */ + private _checkPlaceholders(sidecarKeyName: string, hedString: ParsedHedString): BidsHedIssue[] { const numberPlaceholders = getCharacterCount(hedString.hedString, '#') const sidecarKey = this.sidecar.sidecarKeys.get(sidecarKeyName) if (!sidecarKey.valueString && !sidecarKey.hasDefinitions && numberPlaceholders > 0) { @@ -145,10 +150,10 @@ export class BidsHedSidecarValidator extends BidsValidator { /** * Validate this sidecar's curly braces -- checking recursion and missing columns. * - * @returns {BidsHedIssue[]} All issues found. + * @returns All issues found. */ - _validateCurlyBraces() { - const issues = [] + private _validateCurlyBraces(): BidsHedIssue[] { + const issues: BidsHedIssue[] = [] const references = this.sidecar.columnSpliceMapping for (const [key, referredKeys] of references) { diff --git a/src/bids/validator/tsvValidator.js b/src/bids/validator/tsvValidator.ts similarity index 76% rename from src/bids/validator/tsvValidator.js rename to src/bids/validator/tsvValidator.ts index 31e5a237..f27d5802 100644 --- a/src/bids/validator/tsvValidator.js +++ b/src/bids/validator/tsvValidator.ts @@ -2,7 +2,7 @@ * @module bids/validator/tsvValidator */ import { BidsHedIssue } from '../types/issues' -import { BidsTsvElement, BidsTsvRow } from '../types/tsv' +import { BidsTsvElement, type BidsTsvFile, BidsTsvRow } from '../types/tsv' import { BidsValidator } from './validator' import { parseHedString, parseStandaloneString } from '../../parser/parser' import ParsedHedString from '../../parser/parsedHedString' @@ -10,6 +10,7 @@ import { generateIssue } from '../../issues/issues' import { ReservedChecker } from '../../parser/reservedChecker' import { cleanupEmpties, getTagListString } from '../../parser/parseUtils' import { EventManager } from '../../parser/eventManager' +import { type HedSchemas } from '../../schema/containers' /** * Validator for HED data in BIDS TSV files. @@ -17,32 +18,30 @@ import { EventManager } from '../../parser/eventManager' export class BidsHedTsvValidator extends BidsValidator { /** * The BIDS TSV file being validated. - * @type {BidsTsvFile} */ - tsvFile + private readonly tsvFile: BidsTsvFile /** * The singleton instance of the checker for reserved requirements. - * @type {ReservedChecker} */ - reserved + private readonly reserved: ReservedChecker /** * Constructor. * - * @param {BidsTsvFile} tsvFile - The BIDS TSV file being validated. - * @param {Schemas} hedSchemas - The HED schemas used to validate the tsv file. + * @param tsvFile The BIDS TSV file being validated. + * @param hedSchemas The HED schemas used to validate the tsv file. */ - constructor(tsvFile, hedSchemas) { + public constructor(tsvFile: BidsTsvFile, hedSchemas: HedSchemas) { super(hedSchemas) this.tsvFile = tsvFile this.reserved = ReservedChecker.getInstance() } /** - * Validate a BIDS TSV file. This method returns the complete issue list for convenience. + * Validate a BIDS TSV file. */ - validate() { + public validate(): void { // Validate the BIDS sidecar if it exists and return if there are errors if (this.tsvFile.mergedSidecar) { const issues = this.tsvFile.mergedSidecar.validate(this.hedSchemas) @@ -67,7 +66,7 @@ export class BidsHedTsvValidator extends BidsValidator { if (this.errors.length > 0) { return } - this.validateDataset(bidsEvents) + this._validateDataset(bidsEvents) if (this.errors.length === 0 && this.tsvFile.mergedSidecar?.hasHedData) { this._checkMissingHedWarning() this._checkMissingValueWarnings() @@ -76,9 +75,8 @@ export class BidsHedTsvValidator extends BidsValidator { /** * Check for a warning if the HED column is used as a splice but no HED column exists. - * @private */ - _checkMissingHedWarning() { + private _checkMissingHedWarning(): void { // Check for HED column used as splice but no HED column if (this.tsvFile.mergedSidecar.columnSpliceReferences.has('HED') && !this.tsvFile.parsedTsv.has('HED')) { this.warnings.push(BidsHedIssue.fromHedIssue(generateIssue('hedUsedAsSpliceButNoTsvHed', {}), this.tsvFile.file)) @@ -87,9 +85,8 @@ export class BidsHedTsvValidator extends BidsValidator { /** * Check for categorical column value in tsv but not in sidecar. - * @private */ - _checkMissingValueWarnings() { + private _checkMissingValueWarnings(): void { for (const columnName of this.tsvFile.parsedTsv.keys()) { const sidecarColumn = this.tsvFile.mergedSidecar?.sidecarKeys.get(columnName) if (!sidecarColumn || sidecarColumn.isValueKey) { @@ -113,10 +110,8 @@ export class BidsHedTsvValidator extends BidsValidator { /** * Validate this TSV file's HED column. - * - * @private */ - _validateHedColumn() { + private _validateHedColumn(): void { if (this.tsvFile.hedColumnHedStrings.length > 0) { this.tsvFile.hedColumnHedStrings.flatMap((hedString, rowIndexMinusTwo) => this._validateHedColumnString(hedString, rowIndexMinusTwo + 2), @@ -127,15 +122,14 @@ export class BidsHedTsvValidator extends BidsValidator { /** * Validate a string in this TSV file's HED column. * - * @param {string} hedString - The string to be validated. - * @param {number} rowIndex - The index of this row in the TSV file. - * @private + * @param hedString The string to be validated. + * @param rowIndex The index of this row in the TSV file. */ - _validateHedColumnString(hedString, rowIndex) { + private _validateHedColumnString(hedString: string, rowIndex: number) { if (!hedString) { return } - const [parsedString, errorIssues, warningIssues] = parseStandaloneString( + const [, errorIssues, warningIssues] = parseStandaloneString( hedString, this.hedSchemas, this.tsvFile.mergedSidecar.definitions, @@ -147,8 +141,10 @@ export class BidsHedTsvValidator extends BidsValidator { /** * Validate the HED data in a combined event TSV file/sidecar BIDS data collection. + * + * @param elements The elements representing the tsv file. */ - validateDataset(elements) { + private _validateDataset(elements: BidsTsvElement[]): void { // Final top-tag detection cannot be done until the strings are fully assembled and finalized. this._checkNoTopTags(elements) if (this.errors.length > 0) { @@ -166,10 +162,9 @@ export class BidsHedTsvValidator extends BidsValidator { /** * Check the temporal relationships among events. * - * @param {BidsTsvElement[]} elements - The elements representing the tsv file. - * @private + * @param elements The elements representing the tsv file. */ - _validateTemporal(elements) { + private _validateTemporal(elements: BidsTsvElement[]): void { // Check basic temporal conflicts such as Offset before Onset, or temporal tags with same def at same time. const eventManager = new EventManager() const [eventList, temporalIssues] = eventManager.parseEvents(elements) @@ -191,11 +186,9 @@ export class BidsHedTsvValidator extends BidsValidator { * Duplicate onsets are relatively rare and duplicates for single rows are checked when a ParsedHedString is * constructed. * - * @param {BidsTsvElement[]} elements - The elements representing the tsv file. - * @returns {BidsHedIssue[]} - Errors in temporal relationships among events. - * @private + * @param elements The elements representing the tsv file. */ - _checkDuplicatesAcrossRows(elements) { + private _checkDuplicatesAcrossRows(elements: BidsTsvElement[]): void { const duplicateMap = this._getOnsetMap(elements) for (const elementList of duplicateMap.values()) { if (elementList.length === 1) { @@ -213,11 +206,10 @@ export class BidsHedTsvValidator extends BidsValidator { /** * Get map of onsets to BidsTsvElements. * - * @param {BidsTsvElement[]} elements - The elements representing the tsv file. - * @returns {Map} - Map of onset value to a list of elements with that onset. - * @private + * @param elements The elements representing the tsv file. + * @returns Map of onset value to a list of elements with that onset. */ - _getOnsetMap(elements) { + private _getOnsetMap(elements: BidsTsvElement[]): Map { const onsetMap = new Map() for (const element of elements) { if (!element.hedString) { @@ -235,10 +227,9 @@ export class BidsHedTsvValidator extends BidsValidator { /** * Top group tag requirements may not be satisfied until all splices have been done. * - * @param {BidsTsvElement[]} elements - The elements to be checked. - * @private + * @param elements The elements to be checked. */ - _checkNoTopTags(elements) { + private _checkNoTopTags(elements: BidsTsvElement[]): void { for (const element of elements) { const topTags = element.parsedHedString ? element.parsedHedString.topLevelTags : [] const badTags = topTags.filter((tag) => ReservedChecker.hasTopLevelTagGroupAttribute(tag)) @@ -257,9 +248,9 @@ export class BidsHedTsvValidator extends BidsValidator { /** * Verify that this non-temporal file does not contain any temporal tags. * - * @param {BidsTsvElement[]} elements - The elements representing a tsv file (with HED string parsed). + * @param elements The elements representing a tsv file (with HED string parsed). */ - _checkNoTime(elements) { + private _checkNoTime(elements: BidsTsvElement[]): void { for (const element of elements) { if (element.parsedHedString.tags.some((tag) => this.reserved.timelineTags.has(tag.schemaTag.name))) { this.errors.push( @@ -277,28 +268,26 @@ export class BidsHedTsvValidator extends BidsValidator { * Class that performs basic parsing and splicing. */ export class BidsHedTsvParser { - static nullSet = new Set([null, undefined, '', 'n/a']) - static braceRegEx = /\{([^{}]*?)\}/g + private static readonly nullSet = new Set([null, undefined, '', 'n/a']) + private static readonly braceRegEx = /\{([^{}]*?)\}/g /** * The BIDS TSV file being parsed. - * @type {BidsTsvFile} */ - tsvFile + private readonly tsvFile: BidsTsvFile /** * The HED schema collection being parsed against. - * @type {Schemas} */ - hedSchemas + private readonly hedSchemas: HedSchemas /** * Constructor. * - * @param {BidsTsvFile} tsvFile The BIDS TSV file being parsed. - * @param {Schemas} hedSchemas The HED schema collection being parsed against. + * @param tsvFile The BIDS TSV file being parsed. + * @param hedSchemas The HED schema collection being parsed against. */ - constructor(tsvFile, hedSchemas) { + constructor(tsvFile: BidsTsvFile, hedSchemas: HedSchemas) { this.tsvFile = tsvFile this.hedSchemas = hedSchemas } @@ -306,9 +295,9 @@ export class BidsHedTsvParser { /** * Combine the BIDS sidecar HED data into a BIDS TSV file's HED data. * - * @returns {Array} - Returns a two-element array [BidsTsvElement[], BidsHedIssue[], BidsHedIssue[]]. + * @returns The TSV rows, the list of errors, and the list of warnings. */ - parse() { + public parse(): [BidsTsvElement[], BidsHedIssue[], BidsHedIssue[]] { const tsvHedRows = this._generateHedRows() const tsvElements = this._parseHedRows(tsvHedRows) const [errors, warnings] = this._parseElementStrings(tsvElements) @@ -318,17 +307,17 @@ export class BidsHedTsvParser { /** * Parse element HED strings. * - * @param {BidsTsvElement[]} elements - The objects representing tsv rows with their parsed HEd strings. - * @returns {Array} - [BidsHedIssue[], BidsHedIssue[]] The errors and warnings resulting in creating the parsed HED strings. + * @param elements The objects representing tsv rows with their parsed HEd strings. + * @returns The errors and warnings resulting in creating the parsed HED strings. */ - _parseElementStrings(elements) { + private _parseElementStrings(elements: BidsTsvElement[]): [BidsHedIssue[], BidsHedIssue[]] { if (elements.length === 0) { return [[], []] } // Add the parsed HED strings to the elements and quite if there are serious errors - const errors = [] - const warnings = [] + const errors: BidsHedIssue[] = [] + const warnings: BidsHedIssue[] = [] for (const element of elements) { const [parsedHedString, errorIssues, warningIssues] = parseHedString( element.hedString, @@ -347,15 +336,14 @@ export class BidsHedTsvParser { /** * Generate a list of rows with column-to-value mappings. * - * @returns {Array} A list of single-row column-to-value mappings. - * @private + * @returns A list of single-row column-to-value mappings. */ - _generateHedRows() { + private _generateHedRows(): Map[] { const tsvHedColumns = Array.from(this.tsvFile.parsedTsv.entries()).filter( ([header]) => this.tsvFile.mergedSidecar.hedData.has(header) || header === 'HED' || header === 'onset', ) - const tsvHedRows = [] + const tsvHedRows: Map[] = [] for (const [header, data] of tsvHedColumns) { data.forEach((value, index) => { tsvHedRows[index] ??= new Map() @@ -368,12 +356,11 @@ export class BidsHedTsvParser { /** * Parse the rows in the TSV file into HED strings. * - * @param {Map[]} tsvHedRows - A list of single-row column-to-value mappings. - * @returns {BidsTsvRow[]} - A list of row-based parsed HED strings. - * @private + * @param tsvHedRows A list of single-row column-to-value mappings. + * @returns A list of row-based parsed HED strings. */ - _parseHedRows(tsvHedRows) { - const hedRows = [] + private _parseHedRows(tsvHedRows: Map[]): BidsTsvRow[] { + const hedRows: BidsTsvRow[] = [] tsvHedRows.forEach((row, index) => { const hedRow = this._parseHedRow(row, index + 2) if (hedRow !== null) { @@ -386,15 +373,14 @@ export class BidsHedTsvParser { /** * Parse a row in a TSV file into a BIDS row. * - * @param {Map} rowCells - The column-to-value mapping for a single row. - * @param {number} tsvLine - The index of this row in the TSV file. - * @returns {BidsTsvRow} - A parsed HED string. - * @private + * @param rowCells The column-to-value mapping for a single row. + * @param tsvLine The index of this row in the TSV file. + * @returns A parsed HED string. */ - _parseHedRow(rowCells, tsvLine) { - const hedStringParts = [] + private _parseHedRow(rowCells: Map, tsvLine: number): BidsTsvRow { + const hedStringParts: string[] = [] const columnMap = this._getColumnMapping(rowCells) - this.spliceValues(columnMap) + this._spliceValues(columnMap) for (const [columnName, columnValue] of rowCells.entries()) { // If a splice, it can't be used in an assembled HED string. @@ -418,12 +404,11 @@ export class BidsHedTsvParser { /** * Generate a mapping from tsv columns to strings (may have splices in the strings) * - * @param {Map} rowCells - The column-to-value mapping for a single row. - * @returns {Map} - A mapping of column names to their corresponding parsed sidecar strings. - * @private + * @param rowCells The column-to-value mapping for a single row. + * @returns A mapping of column names to their corresponding parsed sidecar strings. */ - _getColumnMapping(rowCells) { - const columnMap = new Map() + private _getColumnMapping(rowCells: Map): Map { + const columnMap = new Map() if (rowCells.has('HED')) { columnMap.set('HED', rowCells.get('HED')) @@ -459,14 +444,11 @@ export class BidsHedTsvParser { /** * Update the map to splice-in the values for columns that have splices. * - * @param {Map} columnMap - Map of column name to HED string for a row. + * @param columnMap Map of column name to HED string for a row. * * Note: Updates the map in place. */ - spliceValues(columnMap) { - if (!(this.tsvFile.mergedSidecar?.columnSpliceMapping?.size > 0)) { - return - } + private _spliceValues(columnMap: Map) { // Only iterate over the column names that have splices for (const column of this.tsvFile.mergedSidecar.columnSpliceMapping.keys()) { // if (!columnMap.has(column)) { @@ -483,12 +465,11 @@ export class BidsHedTsvParser { /** * Replace a HED string containing slices with a resolved version for the column value in a row. * - * @param {string} unspliced - A HED string possibly with unresolved splices. - * @param {Map} columnMap - The map of column name to HED string for a row. - * @returns {string} - The fully resolved HED string with no splices. - * @private + * @param unspliced A HED string possibly with unresolved splices. + * @param columnMap The map of column name to HED string for a row. + * @returns The fully resolved HED string with no splices. */ - _replaceSplices(unspliced, columnMap) { + private _replaceSplices(unspliced: string, columnMap: Map): string { const result = unspliced.replace(BidsHedTsvParser.braceRegEx, (match, content) => { // Resolve the replacement value const resolved = columnMap.has(content) ? columnMap.get(content) : '' diff --git a/src/bids/validator/validator.js b/src/bids/validator/validator.js deleted file mode 100644 index 95c4ccae..00000000 --- a/src/bids/validator/validator.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Validator base class for HED data in BIDS TSV files. - * @abstract bids/validator/validator - */ -export class BidsValidator { - /** - * The HED schema collection being validated against. - * @type {Schemas} - */ - hedSchemas - - /** - * The issues found during validation. - * @type {BidsHedIssue[]} - */ - errors - - /** - * The warnings found during validation. - * @type {BidsHedIssue[]} - */ - warnings - - /** - * Constructor. - * - * @param {Schemas} hedSchemas - The HED schemas used for validation. - */ - constructor(hedSchemas) { - this.hedSchemas = hedSchemas // Will be set when the file is validated - this.errors = [] - this.warnings = [] - } - - /** - * Validate a BIDS file. Overridden by particular types of BIDS files. - * @abstract - */ - validate() {} -} diff --git a/src/bids/validator/validator.ts b/src/bids/validator/validator.ts new file mode 100644 index 00000000..76da58d4 --- /dev/null +++ b/src/bids/validator/validator.ts @@ -0,0 +1,43 @@ +/** + * Validator base class for HED data in BIDS TSV files. + * @module bids/validator/validator + */ + +import { BidsHedIssue } from '../types/issues' +import { type HedSchemas } from '../../schema/containers' + +/** + * Validator base class for HED data in BIDS TSV files. + */ +export abstract class BidsValidator { + /** + * The HED schema collection being validated against. + */ + hedSchemas: HedSchemas + + /** + * The errors found during validation. + */ + errors: BidsHedIssue[] + + /** + * The warnings found during validation. + */ + warnings: BidsHedIssue[] + + /** + * Constructor. + * + * @param hedSchemas The HED schemas used for validation. + */ + protected constructor(hedSchemas: HedSchemas) { + this.hedSchemas = hedSchemas + this.errors = [] + this.warnings = [] + } + + /** + * Validate a BIDS file. Overridden by particular types of BIDS files. + */ + public abstract validate(): void +} diff --git a/src/issues/data.js b/src/issues/data.js deleted file mode 100644 index 44a410f1..00000000 --- a/src/issues/data.js +++ /dev/null @@ -1,507 +0,0 @@ -/** This module contains the templates for the issues. - * @module issues/data - */ - -import { stringTemplate } from '../utils/string' - -export default { - // Syntax issues - parentheses: { - hedCode: 'PARENTHESES_MISMATCH', - level: 'error', - message: stringTemplate`Number of opening and closing parentheses are unequal. ${'opening'} opening parentheses. ${'closing'} closing parentheses.`, - }, - unopenedParenthesis: { - hedCode: 'PARENTHESES_MISMATCH', - level: 'error', - message: stringTemplate`Closing parenthesis at index ${'index'} of string "${'string'}" does not have a corresponding opening parenthesis.`, - }, - unclosedParenthesis: { - hedCode: 'PARENTHESES_MISMATCH', - level: 'error', - message: stringTemplate`Opening parenthesis at index ${'index'} of string "${'string'}" does not have a corresponding closing parenthesis.`, - }, - commaMissing: { - hedCode: 'COMMA_MISSING', - level: 'error', - message: stringTemplate`Comma missing at position ${'index'} of string "${'string'}". ${'msg'}`, - }, - deprecatedTag: { - hedCode: 'ELEMENT_DEPRECATED', - level: 'warning', - message: stringTemplate`Tags "${'tags'} in "${'string'} are deprecated. Please see tag description for instructions on replacement.".`, - }, - duplicateTag: { - hedCode: 'TAG_EXPRESSION_REPEATED', - level: 'error', - message: stringTemplate`Duplicate tags - "${'tags'} in "${'string'}".`, - }, - extendedTag: { - hedCode: 'TAG_EXTENDED', - level: 'warning', - message: stringTemplate`Tag extensions found for ${'tags'} in "${'string'}".`, - }, - invalidCharacter: { - hedCode: 'CHARACTER_INVALID', - level: 'error', - message: stringTemplate`Invalid character "${'character'}" at index ${'index'} of string "${'string'}".`, - }, - // Common semantic validation issues - invalidTag: { - hedCode: 'TAG_INVALID', - level: 'error', - message: stringTemplate`Invalid tag - "${'tag'}". ${'msg'}`, - }, - extraSlash: { - hedCode: 'TAG_INVALID', - level: 'error', - message: stringTemplate`Tag has extra slash at index ${'index'} of string "${'string'}".`, - }, - extraBlank: { - hedCode: 'TAG_INVALID', - level: 'error', - message: stringTemplate`Tag has extra blank at index ${'index'} of string "${'string'}".`, - }, - extraCommaOrInvalid: { - hedCode: 'TAG_INVALID', - level: 'error', - message: stringTemplate`Either "${'previousTag'}" contains a comma when it should not or "${'tag'}" is not a valid tag.`, - }, - invalidTagPrefix: { - hedCode: 'TAG_NAMESPACE_PREFIX_INVALID', - level: 'error', - message: stringTemplate`Either tag prefix at index ${'index'} contains non-alphabetic characters or does not have an associated schema. ${'msg'}`, - }, - multipleUniqueTags: { - hedCode: 'TAG_NOT_UNIQUE', - level: 'error', - message: stringTemplate`Multiple unique "${'tag'}" tags in "${'string'}".`, - }, - childRequired: { - hedCode: 'TAG_REQUIRES_CHILD', - level: 'error', - message: stringTemplate`Descendant tag required - "${'tag'}". ${'msg'}`, - }, - valueRequired: { - hedCode: 'TAG_REQUIRES_CHILD', - level: 'error', - message: stringTemplate`Tag "${'tag'}" requires a value.`, - }, - childForbidden: { - hedCode: 'TAG_INVALID', - level: 'error', - message: stringTemplate`Child tag or value not allowed - "${'tag'}".`, - }, - requiredPrefixMissing: { - hedCode: 'REQUIRED_TAG_MISSING', - level: 'warning', - message: stringTemplate`Tag with prefix "${'tagPrefix'}" is required.`, - }, - unitClassInvalidUnit: { - hedCode: 'UNITS_INVALID', - level: 'error', - message: stringTemplate`Invalid unit - "${'tag'}".`, - }, - invalidValue: { - hedCode: 'VALUE_INVALID', - level: 'error', - message: stringTemplate`Invalid placeholder value for tag "${'tag'}". ${'msg'}`, - }, - invalidPlaceholderContext: { - hedCode: 'PLACEHOLDER_INVALID', - level: 'error', - message: stringTemplate`"${'string'}" has "#" placeholders, which are not allowed in this context.`, - }, - invalidSidecarPlaceholder: { - hedCode: 'PLACEHOLDER_INVALID', - level: 'error', - message: stringTemplate`"${'string'}" of sidecar key "${'sidecarKey'}" has an invalid # placeholder.`, - }, - // HED 3-specific validation issues - invalidPlaceholder: { - hedCode: 'PLACEHOLDER_INVALID', - level: 'error', - message: stringTemplate`Invalid # placeholder - "${'tag'}".`, - }, - missingPlaceholder: { - hedCode: 'PLACEHOLDER_INVALID', - level: 'error', - message: stringTemplate`HED value string "${'string'}" is missing a required # placeholder for sidecar key "${'sidecarKey'}".`, - }, - extraPlaceholder: { - hedCode: 'PLACEHOLDER_INVALID', - level: 'error', - message: stringTemplate`HED value string "${'string'}" has too many placeholders in sidecar key "${'sidecarKey'}".`, - }, - invalidPlaceholderInDefinition: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`Invalid placeholder or missing placeholder in definition - "${'definition'}". ${'msg'}`, - }, - invalidDefinition: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`Invalid definition - "${'definition'}". ${'msg'}`, - }, - invalidDefinitionManager: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`Invalid definition manager "${'defManager'}", usually because invalid external definitions were provided to a sidecar or tsv.`, - }, - nestedDefinition: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`Illegal nested definition in tag group for definition "${'definition'}".`, - }, - missingDefinitionForDef: { - hedCode: 'DEF_INVALID', - level: 'error', - message: stringTemplate`Def tag found for definition name "${'definition'}" does not correspond to an existing definition.`, - }, - missingDefinitionForDefExpand: { - hedCode: 'DEF_EXPAND_INVALID', - level: 'error', - message: stringTemplate`Def-expand tag found for definition name "${'definition'}" does not correspond to an existing definition.`, - }, - invalidDefinitionGroupStructure: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`The Definition or Def-expand tag "${'tag'} in "${'tagGroup'}" has multiple top tags or more than one top group.`, - }, - invalidDefinitionForbidden: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`The Definition or Def-expand tag "${'tag'} in "${'tagGroup'}" has other definition-related tags in subgroups.`, - }, - defExpandContentsInvalid: { - hedCode: 'DEF_EXPAND_INVALID', - level: 'error', - message: stringTemplate`Def-expand contents "${'contents'}" disagree with evaluated definition "${'contentsDef'}".`, - }, - duplicateDefinition: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`Definition "${'definition'}" is declared multiple times. This instance's tag group is "${'tagGroup'}".`, - }, - conflictingDefinitions: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`Definition "${'definition1'}" and "${'definition2'}' conflict.`, - }, - duplicateDefinitionNames: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`Definition "${'definition1'}" and "${'definition2'}" have same name but are not equivalent.`, - }, - multipleTagGroupsInDefinition: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`Multiple inner tag groups found in definition "${'definition'}".`, - }, - illegalDefinitionGroupTag: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`Illegal tag "${'tag'}" in tag group for definition "${'definition'}".`, - }, - illegalDefinitionContext: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`Definition "${'definition'}" was found in string "${'string'}" in a context where definitions are not allowed.`, - }, - illegalInExclusiveContext: { - hedCode: 'TAG_INVALID', - level: 'error', - message: stringTemplate`"${'tag'}" can only appear in groups with other definitions but "${'string'}" has other types of groups or tags.`, - }, - inactiveOnset: { - hedCode: 'TEMPORAL_TAG_ERROR', - level: 'error', - message: stringTemplate`${'tag'} found for inactive onset with definition name and value "${'definition'}".`, - }, - temporalWithoutInnerGroup: { - hedCode: 'TEMPORAL_TAG_ERROR', - level: 'error', - message: stringTemplate`${'tag'} found without an included inner top-level tag group. This instance's tag group is "${'tagGroup'}".`, - }, - temporalWithWrongNumberDefs: { - hedCode: 'TEMPORAL_TAG_ERROR', - level: 'error', - message: stringTemplate`${'tag'} found in tag group "${'tagGroup'}" with the wrong number of Def tags and Def-expand groups.`, - }, - temporalWithoutDefinition: { - hedCode: 'TEMPORAL_TAG_ERROR', - level: 'error', - message: stringTemplate`${'tag'} found in tag group "${'tagGroup'}" without an included definition.`, - }, - extraTagsInTemporal: { - hedCode: 'TEMPORAL_TAG_ERROR', - level: 'error', - message: stringTemplate`Extra top-level tags or tag groups found in onset, inset, or offset group "${'tagGroup'}" with definition "${'definition'}".`, - }, - temporalTagInNonTemporalContext: { - hedCode: 'TEMPORAL_TAG_ERROR', - level: 'error', - message: stringTemplate`HED event string "${'string'}" has temporal tags on line(s) [${'tsvline'}] in a tsv file without an onset time.`, - }, - duplicateTemporal: { - hedCode: 'TEMPORAL_TAG_ERROR', - level: 'error', - message: stringTemplate`HED event string "${'string'}" has onset/offset/inset tags with duplicated definition "${'definition'}".`, - }, - multipleTemporalTags: { - hedCode: 'TEMPORAL_TAG_ERROR', - level: 'error', - message: stringTemplate`HED event string "${'string'}" has multiple temporal tags ${'tags'} in the same group.`, - }, - multipleRequiresDefTags: { - hedCode: 'TEMPORAL_TAG_ERROR', - level: 'error', - message: stringTemplate`HED event string "${'string'}" has multiple temporal tags ${'tags'} that require a definition in the same group.`, - }, - simultaneousDuplicateEvents: { - hedCode: 'TEMPORAL_TAG_ERROR', - level: 'error', - message: stringTemplate`Temporal tag group "${'tagGroup1'}" at ${'onset1'} line ${'tsvLine1'} is simultaneous with "${'tagGroup2'}" at ${'onset2'} line ${'tsvLine2'}.`, - }, - missingTagGroup: { - hedCode: 'TAG_GROUP_ERROR', - level: 'error', - message: stringTemplate`Tag(s) "${'tag'}" must appear in a tag group.`, - }, - invalidTagGroup: { - hedCode: 'TAG_GROUP_ERROR', - level: 'error', - message: stringTemplate`"${'tagGroup'}" has invalid group tags or invalid number of subgroups.`, - }, - forbiddenSubgroupTags: { - hedCode: 'TAG_GROUP_ERROR', - level: 'error', - message: stringTemplate`Tag "${'tag'}" in "${'string'}" cannot have tags "${'tagList'}" in a subgroup.`, - }, - invalidTopLevelTagGroupTag: { - hedCode: 'TAG_GROUP_ERROR', - level: 'error', - message: stringTemplate`Tag "${'tag'}" is only allowed inside of a top-level tag group, but is not at top level in "${'string'}".`, - }, - invalidGroupTags: { - hedCode: 'TAG_GROUP_ERROR', - level: 'error', - message: stringTemplate`Tags "${'tags'}" in "${'string'}" cannot be in a subgroups together.`, - }, - invalidGroupTopTags: { - hedCode: 'TAG_GROUP_ERROR', - level: 'error', - message: stringTemplate`Tags "${'tags'}" cannot be at the same level in group "${'string'}".`, - }, - tooManyGroupTopTags: { - hedCode: 'TAG_GROUP_ERROR', - level: 'error', - message: stringTemplate`Group "${'string'}" has too many or too few tags or Def-expand groups at the top level.`, - }, - multipleTopLevelTagGroupTags: { - hedCode: 'TAG_GROUP_ERROR', - level: 'error', - message: stringTemplate`Tag "${'tag'}" found in top-level tag group where "${'otherTag'}" was already defined.`, - }, - invalidNumberOfSubgroups: { - hedCode: 'TAG_GROUP_ERROR', - level: 'error', - message: stringTemplate`The tag "${'tag'} is in a "${'string'} with too many or too few subgroups.`, - }, - invalidTopLevelTag: { - hedCode: 'TAG_GROUP_ERROR', - level: 'error', - message: stringTemplate`Tag(s) "${'tag'}" should be in a top group in "${'string'}".`, - }, - invalidGroupTag: { - hedCode: 'TAG_GROUP_ERROR', - level: 'error', - message: stringTemplate`Tag(s) "${'tag'}" should be in a group in "${'string'}".`, - }, - // Tag conversion issues - invalidParentNode: { - hedCode: 'TAG_EXTENSION_INVALID', - level: 'error', - message: stringTemplate`"${'tag'}" does not have "${'parentTag'}" as its parent in the schema. ${'msg'}`, - }, - invalidExtension: { - hedCode: 'TAG_EXTENSION_INVALID', - level: 'error', - message: stringTemplate`"${'tag'}" appears as an extension of "${'parentTag'}". ${'msg'}`, - }, - emptyTagFound: { - hedCode: 'TAG_EMPTY', - level: 'error', - message: stringTemplate`Empty tag at index ${'index'} cannot be converted.`, - }, - invalidTagString: { - hedCode: 'TAG_INVALID', - level: 'error', - message: stringTemplate`Tag string is null or undefined.`, - }, - duplicateTagsInSchema: { - hedCode: 'SCHEMA_DUPLICATE_NODE', - level: 'error', - message: stringTemplate`Source HED schema is invalid as it contains duplicate tags.`, - }, - // Curly brace issues - unopenedCurlyBrace: { - hedCode: 'SIDECAR_BRACES_INVALID', - level: 'error', - message: stringTemplate`Closing curly brace at index ${'index'} of string "${'string'}" does not have a corresponding opening curly brace. ${'msg'}`, - }, - unclosedCurlyBrace: { - hedCode: 'SIDECAR_BRACES_INVALID', - level: 'error', - message: stringTemplate`Opening curly brace at index ${'index'} of string "${'string'}" does not have a corresponding closing curly brace. ${'msg'}`, - }, - nestedCurlyBrace: { - hedCode: 'SIDECAR_BRACES_INVALID', - level: 'error', - message: stringTemplate`Opening curly brace at index ${'index'} of string "${'string'}" when curly brace expression is already open.`, - }, - emptyCurlyBrace: { - hedCode: 'SIDECAR_BRACES_INVALID', - level: 'error', - message: stringTemplate`Curly brace expression of string "${'string'}" is empty. ${'msg'}`, - }, - curlyBracesInDefinition: { - hedCode: 'DEFINITION_INVALID', - level: 'error', - message: stringTemplate`Illegal curly brace expression "${'sidecarKey'}" found in HED string containing definition "${'definition'}".`, - }, - curlyBracesInHedColumn: { - hedCode: 'CHARACTER_INVALID', - level: 'error', - message: stringTemplate`Curly brace expression "${'string'}" found in the HED column of a TSV file.`, - }, - curlyBracesNotAllowed: { - hedCode: 'CHARACTER_INVALID', - level: 'error', - message: stringTemplate`Curly brace expression not allowed in "${'string'}".`, - }, - recursiveCurlyBraces: { - hedCode: 'SIDECAR_BRACES_INVALID', - level: 'error', - message: stringTemplate`Sidecar key "${'sidecarKey'}", which has curly braces, is illegally referred to by a string using curly braces.`, - }, - recursiveCurlyBracesWithKey: { - hedCode: 'SIDECAR_BRACES_INVALID', - level: 'error', - message: stringTemplate`Sidecar key "${'sidecarKey'}", which has curly braces, is referred to by sidecar key "${'referrer'}", which also has curly braces.`, - }, - undefinedCurlyBraces: { - hedCode: 'SIDECAR_BRACES_INVALID', - level: 'error', - message: stringTemplate`Sidecar key "${'sidecarKey'}", used in curly braces, is not mapped to a defined column.`, - }, - // Schema issues - invalidSchemaSpecification: { - hedCode: 'SCHEMA_LOAD_FAILED', - level: 'error', - message: stringTemplate`The supplied HED schema specification is invalid. Specification: ${'spec'}.`, - }, - missingSchemaSpecification: { - hedCode: 'SCHEMA_LOAD_FAILED', - level: 'error', - message: stringTemplate`No valid HED schema specification was supplied.`, - }, - bundledSchemaLoadFailed: { - hedCode: 'SCHEMA_LOAD_FAILED', - level: 'error', - message: stringTemplate`Could not load HED schema for spec "${'spec'}" from bundled copy - "${'error'}".`, - }, - localSchemaLoadFailed: { - hedCode: 'SCHEMA_LOAD_FAILED', - level: 'error', - message: stringTemplate`Could not load HED schema from path "${'path'}" - "${'error'}".`, - }, - remoteSchemaLoadFailed: { - hedCode: 'SCHEMA_LOAD_FAILED', - level: 'error', - message: stringTemplate`Could not load HED schema "${'spec'}" from remote repository - "${'error'}".`, - }, - unmatchedBaseSchema: { - hedCode: 'TAG_NAMESPACE_PREFIX_INVALID', - level: 'error', - message: stringTemplate`Tag "${'tag'}" is declared to use a base schema in the dataset's schema listing, but no such schema was defined.`, - }, - unmatchedLibrarySchema: { - hedCode: 'TAG_NAMESPACE_PREFIX_INVALID', - level: 'error', - message: stringTemplate`Tag "${'tag'}" is declared to use a library schema nicknamed "${'library'}" in the dataset's schema listing, but no such schema was found.`, - }, - differentWithStandard: { - hedCode: 'SCHEMA_LOAD_FAILED', - level: 'error', - message: stringTemplate`Could not merge lazy partnered schemas with different "withStandard" values: "${'first'}" and "${'second'}".`, - }, - lazyPartneredSchemasShareTag: { - hedCode: 'SCHEMA_LOAD_FAILED', - level: 'error', - message: stringTemplate`Lazy partnered schemas are incompatible because they share the short tag "${'tag'}". These schemas require different prefixes.`, - }, - deprecatedStandardSchemaVersion: { - hedCode: 'VERSION_DEPRECATED', - level: 'error', - message: stringTemplate`HED standard schema version ${'version'} is deprecated. Please upgrade to a newer version.`, - }, - // BIDS issues - sidecarKeyMissing: { - hedCode: 'SIDECAR_KEY_MISSING', - level: 'warning', - message: stringTemplate`Values "${'values'}" appear for sidecar key "${'sidecarKey'}" of file "${'file'}", but were not defined in any associated sidecar.`, - }, - hedUsedAsSpliceButNoTsvHed: { - hedCode: 'SIDECAR_KEY_MISSING', - level: 'warning', - message: stringTemplate`Key "{HED}" was referenced in sidecar for file "${'file'}", but this file does not have a HED column.`, - }, - illegalSidecarHedType: { - hedCode: 'SIDECAR_INVALID', - level: 'error', - message: stringTemplate`The HED data for sidecar key "${'sidecarKey'}" of file "${'filePath'}" is not either a key-value dictionary or a string.`, - }, - illegalSidecarHedKey: { - hedCode: 'SIDECAR_INVALID', - level: 'error', - message: stringTemplate`The string 'HED' or 'n/a' was illegally used as a top-level sidecar key.`, - }, - illegalSidecarData: { - hedCode: 'SIDECAR_INVALID', - level: 'error', - message: stringTemplate`The data associated with sidecar key "${'sidecarKey'}" cannot be parsed.`, - }, - illegalSidecarHedCategoricalValue: { - hedCode: 'SIDECAR_INVALID', - level: 'error', - message: stringTemplate`The string 'HED' or 'n/a' was illegally used as a sidecar categorical value for sidecar key "${'sidecarKey'}" in sidecar "${'filePath'}".`, - }, - illegalSidecarHedDeepKey: { - hedCode: 'SIDECAR_INVALID', - level: 'error', - message: stringTemplate`An illegal "HED" appeared as a key below level 2 in a sidecar entry with top-level key "${'sidecarKey'}".`, - }, - // Internal errors - internalError: { - hedCode: 'INTERNAL_ERROR', - level: 'error', - message: stringTemplate`Internal error - message: "${'message'}".`, - }, - networkReadError: { - hedCode: 'INTERNAL_ERROR', - level: 'error', - message: stringTemplate`I/O error when reading from network - server responded to URL "${'url'}" with HTTP status code ${'statusCode'} ${'statusText'}.`, - }, - fileReadError: { - hedCode: 'INTERNAL_ERROR', - level: 'error', - message: stringTemplate`I/O error when reading file or directory "${'fileName'}" - message: "${'message'}".`, - }, - genericError: { - hedCode: 'INTERNAL_ERROR', - level: 'error', - message: (parameters) => - `Unknown HED error "${parameters.internalCode}" - parameters: "${JSON.stringify(parameters)}".`, - }, -} diff --git a/src/issues/data.ts b/src/issues/data.ts new file mode 100644 index 00000000..70ec7a6b --- /dev/null +++ b/src/issues/data.ts @@ -0,0 +1,518 @@ +/** + * This module contains the templates for the issues. + * @module issues/data + */ + +import { issueMessageTemplate, type IssueMessageTemplateString } from '../utils/string' + +export type IssueLevel = 'error' | 'warning' + +type IssueType = { + hedCode: string + level: IssueLevel + message: IssueMessageTemplateString +} + +const issueData: Record = { + // Syntax issues + parentheses: { + hedCode: 'PARENTHESES_MISMATCH', + level: 'error', + message: issueMessageTemplate`Number of opening and closing parentheses are unequal. ${'opening'} opening parentheses. ${'closing'} closing parentheses.`, + }, + unopenedParenthesis: { + hedCode: 'PARENTHESES_MISMATCH', + level: 'error', + message: issueMessageTemplate`Closing parenthesis at index ${'index'} of string "${'string'}" does not have a corresponding opening parenthesis.`, + }, + unclosedParenthesis: { + hedCode: 'PARENTHESES_MISMATCH', + level: 'error', + message: issueMessageTemplate`Opening parenthesis at index ${'index'} of string "${'string'}" does not have a corresponding closing parenthesis.`, + }, + commaMissing: { + hedCode: 'COMMA_MISSING', + level: 'error', + message: issueMessageTemplate`Comma missing at position ${'index'} of string "${'string'}". ${'msg'}`, + }, + deprecatedTag: { + hedCode: 'ELEMENT_DEPRECATED', + level: 'warning', + message: issueMessageTemplate`Tags "${'tags'} in "${'string'} are deprecated. Please see tag description for instructions on replacement.".`, + }, + duplicateTag: { + hedCode: 'TAG_EXPRESSION_REPEATED', + level: 'error', + message: issueMessageTemplate`Duplicate tags - "${'tags'} in "${'string'}".`, + }, + extendedTag: { + hedCode: 'TAG_EXTENDED', + level: 'warning', + message: issueMessageTemplate`Tag extensions found for ${'tags'} in "${'string'}".`, + }, + invalidCharacter: { + hedCode: 'CHARACTER_INVALID', + level: 'error', + message: issueMessageTemplate`Invalid character "${'character'}" at index ${'index'} of string "${'string'}".`, + }, + // Common semantic validation issues + invalidTag: { + hedCode: 'TAG_INVALID', + level: 'error', + message: issueMessageTemplate`Invalid tag - "${'tag'}". ${'msg'}`, + }, + extraSlash: { + hedCode: 'TAG_INVALID', + level: 'error', + message: issueMessageTemplate`Tag has extra slash at index ${'index'} of string "${'string'}".`, + }, + extraBlank: { + hedCode: 'TAG_INVALID', + level: 'error', + message: issueMessageTemplate`Tag has extra blank at index ${'index'} of string "${'string'}".`, + }, + extraCommaOrInvalid: { + hedCode: 'TAG_INVALID', + level: 'error', + message: issueMessageTemplate`Either "${'previousTag'}" contains a comma when it should not or "${'tag'}" is not a valid tag.`, + }, + invalidTagPrefix: { + hedCode: 'TAG_NAMESPACE_PREFIX_INVALID', + level: 'error', + message: issueMessageTemplate`Either tag prefix at index ${'index'} contains non-alphabetic characters or does not have an associated schema. ${'msg'}`, + }, + multipleUniqueTags: { + hedCode: 'TAG_NOT_UNIQUE', + level: 'error', + message: issueMessageTemplate`Multiple unique "${'tag'}" tags in "${'string'}".`, + }, + childRequired: { + hedCode: 'TAG_REQUIRES_CHILD', + level: 'error', + message: issueMessageTemplate`Descendant tag required - "${'tag'}". ${'msg'}`, + }, + valueRequired: { + hedCode: 'TAG_REQUIRES_CHILD', + level: 'error', + message: issueMessageTemplate`Tag "${'tag'}" requires a value.`, + }, + childForbidden: { + hedCode: 'TAG_INVALID', + level: 'error', + message: issueMessageTemplate`Child tag or value not allowed - "${'tag'}".`, + }, + requiredPrefixMissing: { + hedCode: 'REQUIRED_TAG_MISSING', + level: 'warning', + message: issueMessageTemplate`Tag with prefix "${'tagPrefix'}" is required.`, + }, + unitClassInvalidUnit: { + hedCode: 'UNITS_INVALID', + level: 'error', + message: issueMessageTemplate`Invalid unit - "${'tag'}".`, + }, + invalidValue: { + hedCode: 'VALUE_INVALID', + level: 'error', + message: issueMessageTemplate`Invalid placeholder value for tag "${'tag'}". ${'msg'}`, + }, + invalidPlaceholderContext: { + hedCode: 'PLACEHOLDER_INVALID', + level: 'error', + message: issueMessageTemplate`"${'string'}" has "#" placeholders, which are not allowed in this context.`, + }, + invalidSidecarPlaceholder: { + hedCode: 'PLACEHOLDER_INVALID', + level: 'error', + message: issueMessageTemplate`"${'string'}" of sidecar key "${'sidecarKey'}" has an invalid # placeholder.`, + }, + // HED 3-specific validation issues + invalidPlaceholder: { + hedCode: 'PLACEHOLDER_INVALID', + level: 'error', + message: issueMessageTemplate`Invalid # placeholder - "${'tag'}".`, + }, + missingPlaceholder: { + hedCode: 'PLACEHOLDER_INVALID', + level: 'error', + message: issueMessageTemplate`HED value string "${'string'}" is missing a required # placeholder for sidecar key "${'sidecarKey'}".`, + }, + extraPlaceholder: { + hedCode: 'PLACEHOLDER_INVALID', + level: 'error', + message: issueMessageTemplate`HED value string "${'string'}" has too many placeholders in sidecar key "${'sidecarKey'}".`, + }, + invalidPlaceholderInDefinition: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`Invalid placeholder or missing placeholder in definition - "${'definition'}". ${'msg'}`, + }, + invalidDefinition: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`Invalid definition - "${'definition'}". ${'msg'}`, + }, + invalidDefinitionManager: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`Invalid definition manager "${'defManager'}", usually because invalid external definitions were provided to a sidecar or tsv.`, + }, + nestedDefinition: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`Illegal nested definition in tag group for definition "${'definition'}".`, + }, + missingDefinitionForDef: { + hedCode: 'DEF_INVALID', + level: 'error', + message: issueMessageTemplate`Def tag found for definition name "${'definition'}" does not correspond to an existing definition.`, + }, + missingDefinitionForDefExpand: { + hedCode: 'DEF_EXPAND_INVALID', + level: 'error', + message: issueMessageTemplate`Def-expand tag found for definition name "${'definition'}" does not correspond to an existing definition.`, + }, + invalidDefinitionGroupStructure: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`The Definition or Def-expand tag "${'tag'} in "${'tagGroup'}" has multiple top tags or more than one top group.`, + }, + invalidDefinitionForbidden: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`The Definition or Def-expand tag "${'tag'} in "${'tagGroup'}" has other definition-related tags in subgroups.`, + }, + defExpandContentsInvalid: { + hedCode: 'DEF_EXPAND_INVALID', + level: 'error', + message: issueMessageTemplate`Def-expand contents "${'contents'}" disagree with evaluated definition "${'contentsDef'}".`, + }, + duplicateDefinition: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`Definition "${'definition'}" is declared multiple times. This instance's tag group is "${'tagGroup'}".`, + }, + conflictingDefinitions: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`Definition "${'definition1'}" and "${'definition2'}' conflict.`, + }, + duplicateDefinitionNames: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`Definition "${'definition1'}" and "${'definition2'}" have same name but are not equivalent.`, + }, + multipleTagGroupsInDefinition: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`Multiple inner tag groups found in definition "${'definition'}".`, + }, + illegalDefinitionGroupTag: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`Illegal tag "${'tag'}" in tag group for definition "${'definition'}".`, + }, + illegalDefinitionContext: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`Definition "${'definition'}" was found in string "${'string'}" in a context where definitions are not allowed.`, + }, + illegalInExclusiveContext: { + hedCode: 'TAG_INVALID', + level: 'error', + message: issueMessageTemplate`"${'tag'}" can only appear in groups with other definitions but "${'string'}" has other types of groups or tags.`, + }, + inactiveOnset: { + hedCode: 'TEMPORAL_TAG_ERROR', + level: 'error', + message: issueMessageTemplate`${'tag'} found for inactive onset with definition name and value "${'definition'}".`, + }, + temporalWithoutInnerGroup: { + hedCode: 'TEMPORAL_TAG_ERROR', + level: 'error', + message: issueMessageTemplate`${'tag'} found without an included inner top-level tag group. This instance's tag group is "${'tagGroup'}".`, + }, + temporalWithWrongNumberDefs: { + hedCode: 'TEMPORAL_TAG_ERROR', + level: 'error', + message: issueMessageTemplate`${'tag'} found in tag group "${'tagGroup'}" with the wrong number of Def tags and Def-expand groups.`, + }, + temporalWithoutDefinition: { + hedCode: 'TEMPORAL_TAG_ERROR', + level: 'error', + message: issueMessageTemplate`${'tag'} found in tag group "${'tagGroup'}" without an included definition.`, + }, + extraTagsInTemporal: { + hedCode: 'TEMPORAL_TAG_ERROR', + level: 'error', + message: issueMessageTemplate`Extra top-level tags or tag groups found in onset, inset, or offset group "${'tagGroup'}" with definition "${'definition'}".`, + }, + temporalTagInNonTemporalContext: { + hedCode: 'TEMPORAL_TAG_ERROR', + level: 'error', + message: issueMessageTemplate`HED event string "${'string'}" has temporal tags on line(s) [${'tsvline'}] in a tsv file without an onset time.`, + }, + duplicateTemporal: { + hedCode: 'TEMPORAL_TAG_ERROR', + level: 'error', + message: issueMessageTemplate`HED event string "${'string'}" has onset/offset/inset tags with duplicated definition "${'definition'}".`, + }, + multipleTemporalTags: { + hedCode: 'TEMPORAL_TAG_ERROR', + level: 'error', + message: issueMessageTemplate`HED event string "${'string'}" has multiple temporal tags ${'tags'} in the same group.`, + }, + multipleRequiresDefTags: { + hedCode: 'TEMPORAL_TAG_ERROR', + level: 'error', + message: issueMessageTemplate`HED event string "${'string'}" has multiple temporal tags ${'tags'} that require a definition in the same group.`, + }, + simultaneousDuplicateEvents: { + hedCode: 'TEMPORAL_TAG_ERROR', + level: 'error', + message: issueMessageTemplate`Temporal tag group "${'tagGroup1'}" at ${'onset1'} line ${'tsvLine1'} is simultaneous with "${'tagGroup2'}" at ${'onset2'} line ${'tsvLine2'}.`, + }, + missingTagGroup: { + hedCode: 'TAG_GROUP_ERROR', + level: 'error', + message: issueMessageTemplate`Tag(s) "${'tag'}" must appear in a tag group.`, + }, + invalidTagGroup: { + hedCode: 'TAG_GROUP_ERROR', + level: 'error', + message: issueMessageTemplate`"${'tagGroup'}" has invalid group tags or invalid number of subgroups.`, + }, + forbiddenSubgroupTags: { + hedCode: 'TAG_GROUP_ERROR', + level: 'error', + message: issueMessageTemplate`Tag "${'tag'}" in "${'string'}" cannot have tags "${'tagList'}" in a subgroup.`, + }, + invalidTopLevelTagGroupTag: { + hedCode: 'TAG_GROUP_ERROR', + level: 'error', + message: issueMessageTemplate`Tag "${'tag'}" is only allowed inside of a top-level tag group, but is not at top level in "${'string'}".`, + }, + invalidGroupTags: { + hedCode: 'TAG_GROUP_ERROR', + level: 'error', + message: issueMessageTemplate`Tags "${'tags'}" in "${'string'}" cannot be in a subgroups together.`, + }, + invalidGroupTopTags: { + hedCode: 'TAG_GROUP_ERROR', + level: 'error', + message: issueMessageTemplate`Tags "${'tags'}" cannot be at the same level in group "${'string'}".`, + }, + tooManyGroupTopTags: { + hedCode: 'TAG_GROUP_ERROR', + level: 'error', + message: issueMessageTemplate`Group "${'string'}" has too many or too few tags or Def-expand groups at the top level.`, + }, + multipleTopLevelTagGroupTags: { + hedCode: 'TAG_GROUP_ERROR', + level: 'error', + message: issueMessageTemplate`Tag "${'tag'}" found in top-level tag group where "${'otherTag'}" was already defined.`, + }, + invalidNumberOfSubgroups: { + hedCode: 'TAG_GROUP_ERROR', + level: 'error', + message: issueMessageTemplate`The tag "${'tag'} is in a "${'string'} with too many or too few subgroups.`, + }, + invalidTopLevelTag: { + hedCode: 'TAG_GROUP_ERROR', + level: 'error', + message: issueMessageTemplate`Tag(s) "${'tag'}" should be in a top group in "${'string'}".`, + }, + invalidGroupTag: { + hedCode: 'TAG_GROUP_ERROR', + level: 'error', + message: issueMessageTemplate`Tag(s) "${'tag'}" should be in a group in "${'string'}".`, + }, + // Tag conversion issues + invalidParentNode: { + hedCode: 'TAG_EXTENSION_INVALID', + level: 'error', + message: issueMessageTemplate`"${'tag'}" does not have "${'parentTag'}" as its parent in the schema. ${'msg'}`, + }, + invalidExtension: { + hedCode: 'TAG_EXTENSION_INVALID', + level: 'error', + message: issueMessageTemplate`"${'tag'}" appears as an extension of "${'parentTag'}". ${'msg'}`, + }, + emptyTagFound: { + hedCode: 'TAG_EMPTY', + level: 'error', + message: issueMessageTemplate`Empty tag at index ${'index'} cannot be converted.`, + }, + invalidTagString: { + hedCode: 'TAG_INVALID', + level: 'error', + message: issueMessageTemplate`Tag string is null or undefined.`, + }, + duplicateTagsInSchema: { + hedCode: 'SCHEMA_DUPLICATE_NODE', + level: 'error', + message: issueMessageTemplate`Source HED schema is invalid as it contains duplicate tags.`, + }, + // Curly brace issues + unopenedCurlyBrace: { + hedCode: 'SIDECAR_BRACES_INVALID', + level: 'error', + message: issueMessageTemplate`Closing curly brace at index ${'index'} of string "${'string'}" does not have a corresponding opening curly brace. ${'msg'}`, + }, + unclosedCurlyBrace: { + hedCode: 'SIDECAR_BRACES_INVALID', + level: 'error', + message: issueMessageTemplate`Opening curly brace at index ${'index'} of string "${'string'}" does not have a corresponding closing curly brace. ${'msg'}`, + }, + nestedCurlyBrace: { + hedCode: 'SIDECAR_BRACES_INVALID', + level: 'error', + message: issueMessageTemplate`Opening curly brace at index ${'index'} of string "${'string'}" when curly brace expression is already open.`, + }, + emptyCurlyBrace: { + hedCode: 'SIDECAR_BRACES_INVALID', + level: 'error', + message: issueMessageTemplate`Curly brace expression of string "${'string'}" is empty. ${'msg'}`, + }, + curlyBracesInDefinition: { + hedCode: 'DEFINITION_INVALID', + level: 'error', + message: issueMessageTemplate`Illegal curly brace expression "${'sidecarKey'}" found in HED string containing definition "${'definition'}".`, + }, + curlyBracesInHedColumn: { + hedCode: 'CHARACTER_INVALID', + level: 'error', + message: issueMessageTemplate`Curly brace expression "${'string'}" found in the HED column of a TSV file.`, + }, + curlyBracesNotAllowed: { + hedCode: 'CHARACTER_INVALID', + level: 'error', + message: issueMessageTemplate`Curly brace expression not allowed in "${'string'}".`, + }, + recursiveCurlyBraces: { + hedCode: 'SIDECAR_BRACES_INVALID', + level: 'error', + message: issueMessageTemplate`Sidecar key "${'sidecarKey'}", which has curly braces, is illegally referred to by a string using curly braces.`, + }, + recursiveCurlyBracesWithKey: { + hedCode: 'SIDECAR_BRACES_INVALID', + level: 'error', + message: issueMessageTemplate`Sidecar key "${'sidecarKey'}", which has curly braces, is referred to by sidecar key "${'referrer'}", which also has curly braces.`, + }, + undefinedCurlyBraces: { + hedCode: 'SIDECAR_BRACES_INVALID', + level: 'error', + message: issueMessageTemplate`Sidecar key "${'sidecarKey'}", used in curly braces, is not mapped to a defined column.`, + }, + // Schema issues + invalidSchemaSpecification: { + hedCode: 'SCHEMA_LOAD_FAILED', + level: 'error', + message: issueMessageTemplate`The supplied HED schema specification is invalid. Specification: ${'spec'}.`, + }, + missingSchemaSpecification: { + hedCode: 'SCHEMA_LOAD_FAILED', + level: 'error', + message: issueMessageTemplate`No valid HED schema specification was supplied.`, + }, + bundledSchemaLoadFailed: { + hedCode: 'SCHEMA_LOAD_FAILED', + level: 'error', + message: issueMessageTemplate`Could not load HED schema for spec "${'spec'}" from bundled copy - "${'error'}".`, + }, + localSchemaLoadFailed: { + hedCode: 'SCHEMA_LOAD_FAILED', + level: 'error', + message: issueMessageTemplate`Could not load HED schema from path "${'path'}" - "${'error'}".`, + }, + remoteSchemaLoadFailed: { + hedCode: 'SCHEMA_LOAD_FAILED', + level: 'error', + message: issueMessageTemplate`Could not load HED schema "${'spec'}" from remote repository - "${'error'}".`, + }, + unmatchedBaseSchema: { + hedCode: 'TAG_NAMESPACE_PREFIX_INVALID', + level: 'error', + message: issueMessageTemplate`Tag "${'tag'}" is declared to use a base schema in the dataset's schema listing, but no such schema was defined.`, + }, + unmatchedLibrarySchema: { + hedCode: 'TAG_NAMESPACE_PREFIX_INVALID', + level: 'error', + message: issueMessageTemplate`Tag "${'tag'}" is declared to use a library schema nicknamed "${'library'}" in the dataset's schema listing, but no such schema was found.`, + }, + differentWithStandard: { + hedCode: 'SCHEMA_LOAD_FAILED', + level: 'error', + message: issueMessageTemplate`Could not merge lazy partnered schemas with different "withStandard" values: "${'first'}" and "${'second'}".`, + }, + lazyPartneredSchemasShareTag: { + hedCode: 'SCHEMA_LOAD_FAILED', + level: 'error', + message: issueMessageTemplate`Lazy partnered schemas are incompatible because they share the short tag "${'tag'}". These schemas require different prefixes.`, + }, + deprecatedStandardSchemaVersion: { + hedCode: 'VERSION_DEPRECATED', + level: 'error', + message: issueMessageTemplate`HED standard schema version ${'version'} is deprecated. Please upgrade to a newer version.`, + }, + // BIDS issues + sidecarKeyMissing: { + hedCode: 'SIDECAR_KEY_MISSING', + level: 'warning', + message: issueMessageTemplate`Values "${'values'}" appear for sidecar key "${'sidecarKey'}" of file "${'file'}", but were not defined in any associated sidecar.`, + }, + hedUsedAsSpliceButNoTsvHed: { + hedCode: 'SIDECAR_KEY_MISSING', + level: 'warning', + message: issueMessageTemplate`Key "{HED}" was referenced in sidecar for file "${'file'}", but this file does not have a HED column.`, + }, + illegalSidecarHedType: { + hedCode: 'SIDECAR_INVALID', + level: 'error', + message: issueMessageTemplate`The HED data for sidecar key "${'sidecarKey'}" of file "${'filePath'}" is not either a key-value dictionary or a string.`, + }, + illegalSidecarHedKey: { + hedCode: 'SIDECAR_INVALID', + level: 'error', + message: issueMessageTemplate`The string 'HED' or 'n/a' was illegally used as a top-level sidecar key.`, + }, + illegalSidecarData: { + hedCode: 'SIDECAR_INVALID', + level: 'error', + message: issueMessageTemplate`The data associated with sidecar key "${'sidecarKey'}" cannot be parsed.`, + }, + illegalSidecarHedCategoricalValue: { + hedCode: 'SIDECAR_INVALID', + level: 'error', + message: issueMessageTemplate`The string 'HED' or 'n/a' was illegally used as a sidecar categorical value for sidecar key "${'sidecarKey'}" in sidecar "${'filePath'}".`, + }, + illegalSidecarHedDeepKey: { + hedCode: 'SIDECAR_INVALID', + level: 'error', + message: issueMessageTemplate`An illegal "HED" appeared as a key below level 2 in a sidecar entry with top-level key "${'sidecarKey'}".`, + }, + // Internal errors + internalError: { + hedCode: 'INTERNAL_ERROR', + level: 'error', + message: issueMessageTemplate`Internal error - message: "${'message'}".`, + }, + networkReadError: { + hedCode: 'INTERNAL_ERROR', + level: 'error', + message: issueMessageTemplate`I/O error when reading from network - server responded to URL "${'url'}" with HTTP status code ${'statusCode'} ${'statusText'}.`, + }, + fileReadError: { + hedCode: 'INTERNAL_ERROR', + level: 'error', + message: issueMessageTemplate`I/O error when reading file or directory "${'fileName'}" - message: "${'message'}".`, + }, + genericError: { + hedCode: 'INTERNAL_ERROR', + level: 'error', + message: (parameters, ...bounds) => + `Unknown HED error "${parameters.internalCode}" - parameters: "${JSON.stringify(parameters)}".`, + }, +} + +export default issueData diff --git a/src/issues/issues.js b/src/issues/issues.js deleted file mode 100644 index 6d7e3899..00000000 --- a/src/issues/issues.js +++ /dev/null @@ -1,252 +0,0 @@ -/** This module holds the issue classes. - * @module issues/issues - */ -import mapValues from 'lodash/mapValues' - -import issueData from './data' - -export class IssueError extends Error { - /** - * The associated HED issue. - * @type {import('./issues.js').Issue} - */ - issue - - /** - * Constructor. - * - * @param {Issue} issue The associated HED issue. - * @param {...*} params Extra parameters (to be forwarded to the {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error | Error} constructor). - */ - constructor(issue, ...params) { - // Pass remaining arguments (including vendor specific ones) to parent constructor - super(issue.message, ...params) - - // Maintains proper stack trace for where our error was thrown (only available on V8) - if (Error.captureStackTrace) { - Error.captureStackTrace(this, IssueError) - } - - this.name = 'IssueError' - this.issue = issue - - Object.setPrototypeOf(this, IssueError.prototype) - } - - /** - * Generate a new {@link Issue} object and immediately throw it as an {@link IssueError}. - * - * @param {string} internalCode The internal error code. - * @param {Object?} parameters The error string parameters. - * @throws {IssueError} Corresponding to the generated {@link Issue}. - */ - static generateAndThrow(internalCode, parameters = {}) { - throw new IssueError(generateIssue(internalCode, parameters)) - } - - /** - * Generate a new {@link Issue} object for an internal error and immediately throw it as an {@link IssueError}. - * - * @param {string} message A message describing the internal error. - * @throws {IssueError} Corresponding to the generated internal error {@link Issue}. - */ - static generateAndThrowInternalError(message = 'Unknown internal error') { - IssueError.generateAndThrow('internalError', { message: message }) - } -} - -/** - * A HED validation error or warning. - */ -export class Issue { - static SPECIAL_PARAMETERS = new Map([ - ['sidecarKey', 'Sidecar key'], - ['tsvLine', 'TSV line'], - ['hedString', 'HED string'], - ['filePath', 'File path'], - ]) - - /** - * The internal error code. - * @type {string} - */ - internalCode - - /** - * The HED 3 error code. - * @type {string} - */ - hedCode - - /** - * The issue level (error or warning). - * @type {string} - */ - level - - /** - * The detailed error message. - * @type {string} - */ - message - - /** - * The parameters to the error message template. Object with string and map parameters. - * @type {Object} - */ - parameters - - /** - * Constructor. - * - * @param {string} internalCode The internal error code. - * @param {string} hedCode The HED 3 error code. - * @param {string} level The issue level (error or warning). - * @param {Object} parameters The error string parameters. - */ - constructor(internalCode, hedCode, level, parameters) { - this.internalCode = internalCode - this.hedCode = hedCode - this.level = level - this.parameters = parameters - this.generateMessage() - } - - /** - * Override of {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString | Object.prototype.toString}. - * - * @returns {string} This issue's message. - */ - toString() { - return this.message - } - - /** - * (Re-)generate the issue message. - */ - generateMessage() { - this._stringifyParameters() - const baseMessage = this._parseMessageTemplate() - const specialParameterMessages = this._parseSpecialParameters() - const hedSpecLink = this._generateHedSpecificationLink() - - this.message = `${this.level.toUpperCase()}: [${this.hedCode}] ${baseMessage} ${specialParameterMessages} (${hedSpecLink}.)` - } - - /** - * Convert all parameters except the substring bounds (an integer array) to their string forms. - * @private - */ - _stringifyParameters() { - this.parameters = mapValues(this.parameters, (value, key) => (key === 'bounds' ? value : String(value))) - } - - /** - * Find and parse the appropriate message template. - * - * @returns {string} The parsed base message. - * @private - */ - _parseMessageTemplate() { - const bounds = this.parameters.bounds ?? [] - const issueCodeData = issueData[this.internalCode] - if (issueCodeData === undefined) { - const messageTemplate = issueData.genericError.message - const tempParameters = { - internalCode: this.internalCode, - parameters: JSON.stringify(this.parameters), - } - return messageTemplate(tempParameters) - } - const messageTemplate = issueCodeData.message - return messageTemplate(...bounds, this.parameters) - } - - /** - * Parse "special" parameters. - * - * @returns {string} The parsed special parameters. - * @private - */ - _parseSpecialParameters() { - const specialParameterMessages = [] - for (const [parameterName, parameterHeader] of Issue.SPECIAL_PARAMETERS) { - if (this.parameters[parameterName]) { - specialParameterMessages.push(`${parameterHeader}: "${this.parameters[parameterName]}".`) - } - } - return specialParameterMessages.join(' ') - } - - /** - * Generate a link to the appropriate section in the HED specification. - * - * @returns {string} A link to the HED specification - * @private - */ - _generateHedSpecificationLink() { - const hedCodeAnchor = this.hedCode.toLowerCase().replace(/_/g, '-') - return `For more information on this HED ${this.level}, see https://hed-specification.readthedocs.io/en/latest/Appendix_B.html#${hedCodeAnchor}` - } -} - -/** - * Generate a new issue object. - * - * @param {string} internalCode The internal error code. - * @param {Object} parameters The error string parameters. - * @returns {Issue} An object representing the issue. - */ -export const generateIssue = function (internalCode, parameters = {}) { - const issueCodeData = issueData[internalCode] ?? issueData.genericError - const { hedCode, level } = issueCodeData - if (issueCodeData === issueData.genericError) { - parameters.internalCode = internalCode - internalCode = 'genericError' - parameters.parameters = 'Issue parameters: ' + JSON.stringify(parameters) - } - - return new Issue(internalCode, hedCode, level, parameters) -} - -/** - * Update the parameters of a list of issues. - * - * @param {IssueError[] | Issue[]} issues The list of issues (different types can be intermixed). - * @param {Object} parameters The parameters to add. - */ -export const updateIssueParameters = function (issues, parameters) { - for (const thisIssue of issues) { - if (thisIssue instanceof IssueError) { - _updateIssueParameters(thisIssue.issue, parameters) - } else if (thisIssue instanceof Issue) { - _updateIssueParameters(thisIssue, parameters) - } - } -} - -/** - * Update the parameters for an Issue. - * - * Note: the issue is modified in place. - * - * @param {Issue} issue The issue to be updated. - * @param {Object} parameters The parameters to add. - */ -const _updateIssueParameters = function (issue, parameters) { - if (!issue.parameters) { - issue.parameters = {} - } - - let changed = false - for (const [key, value] of Object.entries(parameters)) { - if (!Object.hasOwn(issue.parameters, key)) { - issue.parameters[key] = value - changed = true - } - } - - if (changed) { - issue.generateMessage() - } -} diff --git a/src/issues/issues.ts b/src/issues/issues.ts new file mode 100644 index 00000000..e8cada20 --- /dev/null +++ b/src/issues/issues.ts @@ -0,0 +1,298 @@ +/** + * This module holds the issue classes. + * @module issues/issues + */ + +import issueData, { type IssueLevel } from './data' +import { isNumberPair } from '../utils/array' + +export type IssueParameters = Record +type IssueSavedParameters = Record + +export class IssueError extends Error { + /** + * The associated HED issue. + */ + public readonly issue: Issue + + /** + * Constructor. + * + * @param issue The associated HED issue. + * @param params Extra parameters (to be forwarded to the {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error | Error} constructor). + */ + constructor(issue: Issue, ...params: any[]) { + // Pass remaining arguments (including vendor specific ones) to parent constructor + super(issue.message, ...params) + + // Maintains proper stack trace for where our error was thrown (only available on V8) + if (Error.captureStackTrace) { + Error.captureStackTrace(this, IssueError) + } + + this.name = 'IssueError' + this.issue = issue + + Object.setPrototypeOf(this, IssueError.prototype) + } + + /** + * Generate a new {@link Issue} object and immediately throw it as an {@link IssueError}. + * + * @param internalCode The internal error code. + * @param parameters The error string parameters. + * @throws {IssueError} Corresponding to the generated {@link Issue}. + */ + public static generateAndThrow(internalCode: string, parameters: IssueParameters = {}) { + throw new IssueError(generateIssue(internalCode, parameters)) + } + + /** + * Generate a new {@link Issue} object for an internal error and immediately throw it as an {@link IssueError}. + * + * @param message A message describing the internal error. The message should not end with any punctuation. + * @throws {IssueError} Corresponding to the generated internal error {@link Issue}. + */ + public static generateAndThrowInternalError(message: string = 'Unknown internal error') { + IssueError.generateAndThrow('internalError', { message: message }) + } +} + +/** + * A HED validation error or warning. + */ +export class Issue { + private static readonly SPECIAL_PARAMETERS = new Map([ + ['sidecarKey', 'Sidecar key'], + ['tsvLine', 'TSV line'], + ['hedString', 'HED string'], + ['filePath', 'File path'], + ]) + + /** + * The internal error code. + */ + public readonly internalCode: string + + /** + * The HED 3 error code. + */ + public readonly hedCode: string + + /** + * The issue level (error or warning). + */ + public readonly level: IssueLevel + + /** + * The bounds of this issue. + */ + _bounds: [number, number] | undefined + + /** + * The parameters to the error message template. + */ + _parameters: IssueSavedParameters + + /** + * Constructor. + * + * @param internalCode The internal error code. + * @param hedCode The HED 3 error code. + * @param level The issue level (error or warning). + * @param parameters The error string parameters. + */ + constructor(internalCode: string, hedCode: string, level: IssueLevel, parameters: IssueParameters) { + this.internalCode = internalCode + this.hedCode = hedCode + this.level = level + this.parameters = parameters + } + + /** + * Override of {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString | Object.prototype.toString}. + * + * @returns This issue's message. + */ + public toString(): string { + return this.message + } + + /** + * Generate the detailed error message. + * + * @returns This issue's message. + */ + public get message(): string { + const baseMessage = this._parseMessageTemplate() + const specialParameterMessages = this._parseSpecialParameters() + const hedSpecLink = this._generateHedSpecificationLink() + + return `${this.level.toUpperCase()}: [${this.hedCode}] ${baseMessage} ${specialParameterMessages} (${hedSpecLink}.)` + } + + /** + * Get the issue parameters. + * + * @returns The issue parameters. + */ + public get parameters(): IssueSavedParameters { + return this._parameters + } + + /** + * Get the issue's bounds. + * + * @returns The issue bounds within the parent string. + */ + public get bounds(): [number, number] | undefined { + return this._bounds + } + + /** + * Validate and set the issue's bounds. + * + * @param value The issue bounds within the parent string. + */ + private set bounds(value: unknown) { + if (this._bounds) { + return + } + if (isNumberPair(value)) { + this._bounds = value + } else { + IssueError.generateAndThrowInternalError('Bounds must be a numeric pair') + } + } + + /** + * Set new parameters by converting all parameters except the substring bounds (an integer array) to their string forms. + * + * @param parameters The new issue parameters. + */ + public set parameters(parameters: IssueParameters) { + this._parameters = {} + this.addParameters(parameters) + } + + /** + * Determine whether this issue has a given parameter. + * + * @param name The parameter name to check. + * @returns Whether this issue already has a parameter by that name. + */ + public hasParameter(name: string): boolean { + if (name === 'bounds') { + return this._bounds !== undefined + } + return Object.hasOwn(this._parameters, name) + } + + /** + * Add a new parameter value. + * + * @param name The parameter name. + * @param value The new parameter value. + */ + public addParameter(name: string, value: unknown): void { + if (this.hasParameter(name)) { + return + } + if (name === 'bounds') { + this.bounds = value + return + } + this._parameters[name] = String(value) + } + + /** + * Add multiple parameters for this Issue. + * + * @param parameters The new values of the parameters. + */ + public addParameters(parameters: IssueParameters): void { + for (const [key, value] of Object.entries(parameters)) { + this.addParameter(key, value) + } + } + + /** + * Find and parse the appropriate message template. + * + * @returns The parsed base message. + */ + private _parseMessageTemplate(): string { + const issueCodeData = issueData[this.internalCode] + const bounds = this._bounds ?? [] + if (issueCodeData === undefined) { + const messageTemplate = issueData.genericError.message + const tempParameters = { + internalCode: this.internalCode, + parameters: JSON.stringify(this.parameters), + } + return messageTemplate(tempParameters) + } + const messageTemplate = issueCodeData.message + return messageTemplate(this.parameters, ...bounds) + } + + /** + * Parse "special" parameters. + * + * @returns The parsed special parameters. + */ + private _parseSpecialParameters(): string { + const specialParameterMessages = [] + for (const [parameterName, parameterHeader] of Issue.SPECIAL_PARAMETERS) { + if (this.parameters[parameterName]) { + specialParameterMessages.push(`${parameterHeader}: "${this.parameters[parameterName]}".`) + } + } + return specialParameterMessages.join(' ') + } + + /** + * Generate a link to the appropriate section in the HED specification. + * + * @returns A link to the HED specification + */ + private _generateHedSpecificationLink(): string { + const hedCodeAnchor = this.hedCode.toLowerCase().replace(/_/g, '-') + return `For more information on this HED ${this.level}, see https://hed-specification.readthedocs.io/en/latest/Appendix_B.html#${hedCodeAnchor}` + } +} + +/** + * Generate a new issue object. + * + * @param internalCode The internal error code. + * @param parameters The error string parameters. + * @returns An object representing the issue. + */ +export function generateIssue(internalCode: string, parameters: IssueParameters = {}): Issue { + const issueCodeData = issueData[internalCode] ?? issueData.genericError + const { hedCode, level } = issueCodeData + if (issueCodeData === issueData.genericError) { + parameters.internalCode = internalCode + internalCode = 'genericError' + parameters.parameters = 'Issue parameters: ' + JSON.stringify(parameters) + } + + return new Issue(internalCode, hedCode, level, parameters) +} + +/** + * Update the parameters of a list of issues. + * + * @param issues The list of issues (different types can be intermixed). + * @param parameters The parameters to add. + */ +export function addIssueParameters(issues: Array, parameters: IssueParameters) { + for (const thisIssue of issues) { + if (thisIssue instanceof IssueError) { + thisIssue.issue.addParameters(parameters) + } else if (thisIssue instanceof Issue) { + thisIssue.addParameters(parameters) + } + } +} diff --git a/src/parser/definitionManager.js b/src/parser/definitionManager.js index dab49644..f33d5ec4 100644 --- a/src/parser/definitionManager.js +++ b/src/parser/definitionManager.js @@ -55,7 +55,7 @@ export class Definition { /** * Return the evaluated definition contents and any issues. * @param {ParsedHedTag} tag - The parsed HED tag whose details should be checked. - * @param {Schemas} hedSchema - The HED schemas used to validate against. + * @param {HedSchemas} hedSchema - The HED schemas used to validate against. * @param {boolean} placeholderAllowed - If true then placeholder is allowed in the def tag. * @returns {Array} - Returns [string, Issue[], Issue[]] containing the evaluated normalized definition string and any issues in the evaluation, */ @@ -113,7 +113,7 @@ export class Definition { * Create a list of Definition objects from a list of strings. * * @param {string} hedString - A string representing a definition. - * @param {Schemas} hedSchemas - The HED schemas to use in creation. + * @param {HedSchemas} hedSchemas - The HED schemas to use in creation. * @returns {Array} - Returns [Definition, Issue[], Issue[]] with the definition and any issues. */ static createDefinition(hedString, hedSchemas) { @@ -227,7 +227,7 @@ export class DefinitionManager { /** * Check the Def tags in a HED string for missing or incorrectly used Def tags. * @param {ParsedHedString} hedString - A parsed HED string to be checked. - * @param {Schemas} hedSchemas - Schemas to validate against. + * @param {HedSchemas} hedSchemas - Schemas to validate against. * @param {boolean} placeholderAllowed - If true then placeholder is allowed in the def tag. * @returns {Issue[]} - If there is no matching definition or definition applied incorrectly. */ @@ -246,7 +246,7 @@ export class DefinitionManager { /** * Check the Def tags in a HED string for missing or incorrectly used Def-expand tags. * @param {ParsedHedString} hedString - A parsed HED string to be checked. - * @param {Schemas} hedSchemas - Schemas to validate against. + * @param {HedSchemas} hedSchemas - Schemas to validate against. * @param {boolean} placeholderAllowed - If true then placeholder is allowed in the def tag. * @returns {Issue[]} - If there is no matching definition or definition applied incorrectly. */ @@ -266,7 +266,7 @@ export class DefinitionManager { /** * Evaluate the definition based on a parsed HED tag. * @param {ParsedHedTag} tag - The tag to evaluate against the definitions. - * @param {Schemas} hedSchemas - The schemas to be used to assist in the evaluation. + * @param {HedSchemas} hedSchemas - The schemas to be used to assist in the evaluation. * @param {boolean} placeholderAllowed - If true then placeholder is allowed in the def tag. * @returns {Array} - Returns [string, Issue[]] with definition contents for this tag and any issues. * @@ -285,7 +285,7 @@ export class DefinitionManager { /** * Recursively check for Def-expand groups in this group. * @param {ParsedHedGroup} topGroup - a top group in a HED string to be evaluated for Def-expand groups. - * @param {Schemas} hedSchemas - The HED schemas to used in the check. + * @param {HedSchemas} hedSchemas - The HED schemas to used in the check. * @param {boolean} placeholderAllowed - If true then placeholder is allowed in the def tag. * @returns {Issue[]} * @private @@ -345,7 +345,7 @@ export class DefinitionManager { * Create a list of Definition objects from a list of strings. * * @param {string[]} defStrings - A list of string definitions. - * @param {Schemas} hedSchemas - The HED schemas to use in creation. + * @param {HedSchemas} hedSchemas - The HED schemas to use in creation. * @returns {Array} - Returns [Definition[], Issue[]] with a definition list and any issues found. */ static createDefinitions(defStrings, hedSchemas) { diff --git a/src/parser/eventManager.js b/src/parser/eventManager.js index 8584bac5..f4bc9c30 100644 --- a/src/parser/eventManager.js +++ b/src/parser/eventManager.js @@ -2,7 +2,7 @@ * @module parser/eventManager */ import { generateIssue } from '../issues/issues' -import { BidsHedIssue } from '../bids/types/issues.js' +import { BidsHedIssue } from '../bids/types/issues' export class Event { /** diff --git a/src/parser/parsedHedTag.js b/src/parser/parsedHedTag.js index 63c1fa8c..3e118196 100644 --- a/src/parser/parsedHedTag.js +++ b/src/parser/parsedHedTag.js @@ -28,7 +28,7 @@ export default class ParsedHedTag extends ParsedHedSubstring { /** * The HED schema this tag belongs to. - * @type {Schema} + * @type {HedSchema} */ schema @@ -73,7 +73,7 @@ export default class ParsedHedTag extends ParsedHedSubstring { * Constructor. * * @param {TagSpec} tagSpec The token for this tag. - * @param {Schemas} hedSchemas The collection of HED schemas. + * @param {HedSchemas} hedSchemas The collection of HED schemas. * @param {string} hedString The original HED string. * @throws {IssueError} If tag conversion or parsing fails. */ @@ -86,7 +86,7 @@ export default class ParsedHedTag extends ParsedHedSubstring { /** * Convert this tag to its various forms * - * @param {Schemas} hedSchemas The collection of HED schemas. + * @param {HedSchemas} hedSchemas The collection of HED schemas. * @param {string} hedString The original HED string. * @param {TagSpec} tagSpec The token for this tag. * @throws {IssueError} If tag conversion or parsing fails. diff --git a/src/parser/parser.js b/src/parser/parser.js index 67867a51..3af8ef21 100644 --- a/src/parser/parser.js +++ b/src/parser/parser.js @@ -19,7 +19,7 @@ class HedStringParser { /** * The collection of HED schemas. - * @type {Schemas} + * @type {HedSchemas} */ hedSchemas @@ -39,7 +39,7 @@ class HedStringParser { * Constructor. * * @param {string|ParsedHedString} hedString - The HED string to be parsed. - * @param {Schemas} hedSchemas - The collection of HED schemas. + * @param {HedSchemas} hedSchemas - The collection of HED schemas. * @param {boolean} definitionsAllowed - True if definitions are allowed * @param {boolean} placeholdersAllowed - True if placeholders are allowed */ @@ -172,7 +172,7 @@ class HedStringParser { * Parse a list of HED strings. * * @param {string[]|ParsedHedString[]} hedStrings A list of HED strings. - * @param {Schemas} hedSchemas The collection of HED schemas. + * @param {HedSchemas} hedSchemas The collection of HED schemas. * @param {boolean} definitionsAllowed - True if definitions are allowed * @param {boolean} placeholdersAllowed - True if placeholders are allowed * @returns {Array} - [ParsedHedString[], Issue[], Issue[]] representing the parsed HED strings and any errors and warnings. @@ -206,7 +206,7 @@ class HedStringParser { * ###Note: now separates errors and warnings for easier handling. * * @param {string|ParsedHedString} hedString A (possibly already parsed) HED string. - * @param {Schemas} hedSchemas - The collection of HED schemas. + * @param {HedSchemas} hedSchemas - The collection of HED schemas. * @param {boolean} definitionsAllowed - True if definitions are allowed. * @param {boolean} placeholdersAllowed - True if placeholders are allowed. * @param {boolean} fullValidation - True if full validation is required. @@ -220,7 +220,7 @@ export function parseHedString(hedString, hedSchemas, definitionsAllowed, placeh * Parse a HED string in a standalone context. * * @param {string|ParsedHedString} hedString - A (possibly already parsed) HED string. - * @param {Schemas} hedSchemas - The collection of HED schemas. + * @param {HedSchemas} hedSchemas - The collection of HED schemas. * @param {DefinitionManager|null} defManager - The definition manager to use for parsing definitions. * @returns {Array} - [ParsedHedString, Issue[], Issue[]] representing the parsed HED string and any issues found. */ @@ -234,7 +234,7 @@ export function parseStandaloneString(hedString, hedSchemas, defManager = null) * ###Note: now separates errors and warnings for easier handling. * * @param {string[]|ParsedHedString[]} hedStrings - A list of HED strings. - * @param {Schemas} hedSchemas - The collection of HED schemas. + * @param {HedSchemas} hedSchemas - The collection of HED schemas. * @param {boolean} definitionsAllowed - True if definitions are allowed * @param {boolean} placeholdersAllowed - True if placeholders are allowed * @param {boolean} fullValidation - True if full validation is required. diff --git a/src/parser/splitter.js b/src/parser/splitter.js index ec1f90bb..702ee830 100644 --- a/src/parser/splitter.js +++ b/src/parser/splitter.js @@ -17,7 +17,7 @@ export default class HedStringSplitter { /** * The collection of HED schemas. - * @type {Schemas} + * @type {HedSchemas} */ hedSchemas @@ -31,7 +31,7 @@ export default class HedStringSplitter { * Constructor. * * @param {string} hedString The HED string to be split and parsed. - * @param {Schemas} hedSchemas The collection of HED schemas. + * @param {HedSchemas} hedSchemas The collection of HED schemas. */ constructor(hedString, hedSchemas) { this.hedString = hedString @@ -69,7 +69,7 @@ export default class HedStringSplitter { _createParsedTags(tagSpecs, groupSpecs) { // Create tags from specifications this.issues = [] - const parsedTags = recursiveMap((tagSpec) => this._createParsedTag(tagSpec), tagSpecs) + const parsedTags = recursiveMap(tagSpecs, (tagSpec) => this._createParsedTag(tagSpec)) // Create groups from the parsed tags const parsedTagsWithGroups = this._createParsedGroups(parsedTags, groupSpecs.children) diff --git a/src/parser/tagConverter.js b/src/parser/tagConverter.js index 5f6025ee..3e3c6c43 100644 --- a/src/parser/tagConverter.js +++ b/src/parser/tagConverter.js @@ -6,8 +6,8 @@ import { getTagSlashIndices } from '../utils/hedStrings' import { ReservedChecker } from './reservedChecker' /** - * @typedef {import('../schema/entries.js').SchemaTag} SchemaTag - * @typedef {import('../schema/containers.js').Schemas} Schemas + * @typedef {import('../schema/entries.ts').SchemaTag} SchemaTag + * @typedef {import('../schema/containers.ts').HedSchemas} Schemas * @typedef {import('./tokenizer.js').TagSpec} TagSpec */ @@ -41,7 +41,7 @@ export default class TagConverter { /** * A HED schema collection. - * @type {Schemas} + * @type {HedSchemas} */ hedSchemas @@ -67,7 +67,7 @@ export default class TagConverter { * Constructor. * * @param {TagSpec} tagSpec The tag specification to convert. - * @param {Schemas} hedSchemas The HED schema collection. + * @param {HedSchemas} hedSchemas The HED schema collection. */ constructor(tagSpec, hedSchemas) { this.hedSchemas = hedSchemas diff --git a/src/schema/config.js b/src/schema/config.ts similarity index 55% rename from src/schema/config.js rename to src/schema/config.ts index e377cf24..01db3cf4 100644 --- a/src/schema/config.js +++ b/src/schema/config.ts @@ -7,7 +7,7 @@ // The actual loading mechanism is handled by the schema loader, // which may use an application-provided loader for browser environments // or a Node.js fs-based loader for server-side/test environments. -export const localSchemaNames = [ +const _localSchemaNames = [ 'HED8.0.0', 'HED8.1.0', 'HED8.2.0', @@ -20,23 +20,36 @@ export const localSchemaNames = [ 'HED_score_2.1.0', // Add other bundled schema base names here if needed ] +/** + * This list defines the base names of HED XML schema files that are considered "bundled" with the library. + * The actual loading mechanism is handled by the schema loader, which may use an application-provided loader + * for browser environments or a Node.js fs-based loader for server-side/test environments. + */ +export const localSchemaNames = Object.freeze(_localSchemaNames) -let schemaMap +let _localSchemaMap // @ts-ignore __VITE_ENV__ is defined by Vite in browser builds if (typeof __VITE_ENV__ !== 'undefined' && __VITE_ENV__) { // In the browser, this map is not used. The loader uses import.meta.glob. - schemaMap = new Map() + _localSchemaMap = new Map() } else { // For Node.js, pre-load the schemas. - schemaMap = new Map( + _localSchemaMap = new Map( localSchemaNames.map((localSchema) => [localSchema, require(`../data/schemas/${localSchema}.xml`)]), ) } -export const localSchemaMap = schemaMap +/** + * A mapping from the bundled schema names to their XML data (as strings). + */ +export const localSchemaMap = Object.freeze(_localSchemaMap) -export const getLocalSchemaVersions = function () { - // Return a copy of the local schema names to avoid external modifications +/** + * Return a copy of the bundled schema names without the "HED" prefix. + * + * @returns The list of unprefixed bundled schema names. + */ +export function getLocalSchemaVersions(): string[] { return localSchemaNames.map((name) => name.replace(/^HED_?/, '')) } diff --git a/src/schema/containers.js b/src/schema/containers.js deleted file mode 100644 index aaeaead8..00000000 --- a/src/schema/containers.js +++ /dev/null @@ -1,149 +0,0 @@ -/** This module holds the schema container classes. - * @module schema/containers - */ -import lt from 'semver/functions/lt' - -import { IssueError } from '../issues/issues' - -/** - * An imported HED 3 schema. - */ -export class Schema { - /** - * The HED schema version. - * @type {string} - */ - version - - /** - * The HED library schema name. - * @type {string} - */ - library - - /** - * This schema's prefix in the active schema set. - * @type {string} - */ - prefix - - /** - * The collection of schema entries. - * @type {SchemaEntries} - */ - entries - - /** - * The standard HED schema version this schema is linked to. - * @type {string} - */ - withStandard - - /** - * Constructor. - * - * @param {Object} xmlData The schema XML data. - * @param {SchemaEntries} entries A collection of schema entries. - */ - constructor(xmlData, entries) { - const rootElement = xmlData.HED - this.version = rootElement?.$?.version - this.library = rootElement?.$?.library ?? '' - - if (!this.library && this.version && lt(this.version, '8.0.0')) { - IssueError.generateAndThrow('deprecatedStandardSchemaVersion', { - version: this.version, - }) - } - - if (!this.library) { - this.withStandard = this.version - } else { - this.withStandard = xmlData.HED?.$?.withStandard - } - this.entries = entries - } -} - -/** - * An imported lazy partnered HED 3 schema. - */ -export class PartneredSchema extends Schema { - /** - * The actual HED 3 schemas underlying this partnered schema. - * @type {Schema[]} - */ - actualSchemas - - /** - * Constructor. - * - * @param {Schema[]} actualSchemas The actual HED 3 schemas underlying this partnered schema. - */ - constructor(actualSchemas) { - if (actualSchemas.length === 0) { - IssueError.generateAndThrowInternalError('A partnered schema set must contain at least one schema.') - } - super({}, actualSchemas[0].entries) - this.actualSchemas = actualSchemas - this.withStandard = actualSchemas[0].withStandard - this.library = undefined - } -} - -/** - * The collection of active HED schemas. - */ -export class Schemas { - /** - * The imported HED schemas. - * - * The empty string key ("") corresponds to the schema with no prefix, - * while other keys correspond to the respective prefixes. - * - * @type {Map} - */ - schemas - - /** - * Constructor. - * @param {Schema|Map} schemas The imported HED schemas. - */ - constructor(schemas) { - if (schemas instanceof Map) { - this.schemas = schemas - } else if (schemas instanceof Schema) { - this.schemas = new Map([['', schemas]]) - } else { - IssueError.generateAndThrowInternalError('Invalid type passed to Schemas constructor') - } - if (this.schemas) { - this._addPrefixesToSchemas() - } - } - - _addPrefixesToSchemas() { - for (const [prefix, schema] of this.schemas) { - schema.prefix = prefix - } - } - - /** - * Return the schema with the given prefix. - * - * @param {string} schemaName A prefix in the schema set. - * @returns {Schema} The schema object corresponding to that prefix. - */ - getSchema(schemaName) { - return this.schemas?.get(schemaName) - } - - /** - * The base schema, i.e. the schema with no prefix, if one is defined. - * - * @returns {Schema} - */ - get baseSchema() { - return this.getSchema('') - } -} diff --git a/src/schema/containers.ts b/src/schema/containers.ts new file mode 100644 index 00000000..4dcc1eeb --- /dev/null +++ b/src/schema/containers.ts @@ -0,0 +1,153 @@ +/** This module holds the schema container classes. + * @module schema/containers + */ +import lt from 'semver/functions/lt' + +import { IssueError } from '../issues/issues' +import { type SchemaEntries } from './entries' +import { type HedSchemaXMLObject } from './xmlType' + +export class HedSchema { + /** + * The collection of schema entries. + */ + readonly entries: SchemaEntries + + /** + * The standard HED schema version this schema is linked to. + */ + readonly withStandard: string + + /** + * This schema's prefix in the active schema set. + */ + prefix: string + + /** + * Constructor. + * + * @param entries A collection of schema entries. + * @param withStandard The standard HED schema version this schema is linked to. + */ + constructor(entries: SchemaEntries, withStandard: string) { + this.entries = entries + this.withStandard = withStandard + } +} + +/** + * An imported HED 3 schema. + */ +export class PrimarySchema extends HedSchema { + /** + * The HED schema version. + */ + readonly version: string + + /** + * The HED library schema name. + */ + readonly library: string + + /** + * Constructor. + * + * @param xmlData The schema XML data. + * @param entries A collection of schema entries. + */ + constructor(xmlData: HedSchemaXMLObject, entries: SchemaEntries) { + let withStandard + const rootElement = xmlData.HED + const library = rootElement.$.library ?? '' + const version = rootElement.$.version + + if (!library) { + withStandard = version + } else { + withStandard = xmlData.HED.$.withStandard + } + + super(entries, withStandard) + + if (!library && version && lt(version, '8.0.0')) { + IssueError.generateAndThrow('deprecatedStandardSchemaVersion', { + version, + }) + } + + this.library = library + this.version = version + } +} + +/** + * An imported lazy partnered HED 3 schema. + */ +export class PartneredSchema extends HedSchema { + /** + * The actual HED 3 schemas underlying this partnered schema. + */ + readonly actualSchemas: HedSchema[] + + /** + * Constructor. + * + * @param actualSchemas The actual HED 3 schemas underlying this partnered schema. + */ + constructor(actualSchemas: HedSchema[]) { + if (actualSchemas.length === 0) { + IssueError.generateAndThrowInternalError('A partnered schema set must contain at least one schema.') + } + super(actualSchemas[0].entries, actualSchemas[0].withStandard) + this.actualSchemas = actualSchemas + } +} + +/** + * The collection of active HED schemas. + */ +export class HedSchemas { + /** + * The imported HED schemas. + * + * The empty string key ("") corresponds to the schema with no prefix, + * while other keys correspond to the respective prefixes. + */ + readonly schemas: Map + + /** + * Constructor. + * @param schemas The imported HED schemas. + */ + constructor(schemas: Map | HedSchema) { + if (schemas instanceof Map) { + this.schemas = schemas + } else { + this.schemas = new Map([['', schemas]]) + } + this.#addPrefixesToSchemas() + } + + #addPrefixesToSchemas(): void { + for (const [prefix, schema] of this.schemas) { + schema.prefix = prefix + } + } + + /** + * Return the schema with the given prefix. + * + * @param schemaName A prefix in the schema set. + * @returns The schema object corresponding to that prefix. + */ + public getSchema(schemaName: string): HedSchema | undefined { + return this.schemas?.get(schemaName) + } + + /** + * The base schema, i.e. the schema with no prefix, if one is defined. + */ + public get baseSchema(): HedSchema | undefined { + return this.getSchema('') + } +} diff --git a/src/schema/entries.js b/src/schema/entries.js deleted file mode 100644 index b7839a64..00000000 --- a/src/schema/entries.js +++ /dev/null @@ -1,877 +0,0 @@ -/** This module holds the schema entity classes. - * @module schema/entries - */ - -import pluralize from 'pluralize' -pluralize.addUncountableRule('hertz') - -import { IssueError } from '../issues/issues' -import Memoizer from '../utils/memoizer' - -/** - * SchemaEntries class - */ -export class SchemaEntries extends Memoizer { - /** - * The schema's properties. - * @type {SchemaEntryManager} - */ - properties - - /** - * The schema's attributes. - * @type {SchemaEntryManager} - */ - attributes - - /** - * The schema's value classes. - * @type {SchemaEntryManager} - */ - valueClasses - - /** - * The schema's unit classes. - * @type {SchemaEntryManager} - */ - unitClasses - - /** - * The schema's unit modifiers. - * @type {SchemaEntryManager} - */ - unitModifiers - - /** - * The schema's tags. - * @type {SchemaEntryManager} - */ - tags - - /** - * Constructor. - * @param {SchemaParser} schemaParser A constructed schema parser. - */ - constructor(schemaParser) { - super() - this.properties = new SchemaEntryManager(schemaParser.properties) - this.attributes = new SchemaEntryManager(schemaParser.attributes) - this.valueClasses = schemaParser.valueClasses - this.unitClasses = schemaParser.unitClasses - this.unitModifiers = schemaParser.unitModifiers - this.tags = schemaParser.tags - } -} - -/** - * A manager of {@link SchemaEntry} objects. - * - * @template T - */ -export class SchemaEntryManager extends Memoizer { - /** - * The definitions managed by this entry manager. - * @type {Map} - */ - _definitions - - /** - * Constructor. - * - * @param {Map} definitions A map of schema entry definitions. - */ - constructor(definitions) { - super() - this._definitions = definitions - } - - /** - * Iterator over the entry manager's entries. - * - * @template T - * @returns {IterableIterator} - [string, T] - */ - [Symbol.iterator]() { - return this._definitions.entries() - } - - /** - * Iterator over the entry manager's keys. - * - * @returns {IterableIterator} - [string] - */ - keys() { - return this._definitions.keys() - } - - /** - * Iterator over the entry manager's keys. - * - * @returns {IterableIterator} - [T] - */ - values() { - return this._definitions.values() - } - - /** - * Determine whether the entry with the given name exists. - * - * @param {string} name The name of the entry. - * @return {boolean} Whether the entry exists. - */ - hasEntry(name) { - return this._definitions.has(name) - } - - /** - * Get the entry with the given name. - * - * @param {string} name - The name of the entry to retrieve. - * @returns {T} - The entry with that name. - */ - getEntry(name) { - return this._definitions.get(name) - } - - /** - * Get a collection of entries with the given boolean attribute. - * - * @param {string} booleanAttributeName - The name of boolean attribute to filter on. - * @returns {Map} - string->T representing a collection of entries with that attribute. - */ - getEntriesWithBooleanAttribute(booleanAttributeName) { - return this._memoize(booleanAttributeName, () => { - return this.filter(([, v]) => { - return v.hasBooleanAttribute(booleanAttributeName) - }) - }) - } - - /** - * Filter the map underlying this manager. - * - * @param {function} fn - ([string, T]): boolean specifying the filtering function. - * @returns {Map} - string->T representing a collection of entries with that attribute. - */ - filter(fn) { - return SchemaEntryManager._filterDefinitionMap(this._definitions, fn) - } - - /** - * Filter a definition map. - * - * @template T - * @param {Map} definitionMap The definition map. - * @param {function} fn - ([string, T]):boolean specifying the filtering function. - * @returns {Map} - string->T representing the filtered definitions. - * @protected - */ - static _filterDefinitionMap(definitionMap, fn) { - const pairArray = Array.from(definitionMap.entries()) - return new Map(pairArray.filter((entry) => fn(entry))) - } - - /** - * The number of entries in this collection. - * - * @returns {number} The number of entries in this collection. - */ - get length() { - return this._definitions.size - } -} - -/** - * SchemaEntry class - */ -export class SchemaEntry extends Memoizer { - /** - * The name of this schema entry. - * @type {string} - */ - _name - - constructor(name) { - super() - this._name = name - } - - /** - * The name of this schema entry. - * @returns {string} - */ - get name() { - return this._name - } - - /** - * Whether this schema entry has this attribute (by name). - * - * This method is a stub to be overridden in {@link SchemaEntryWithAttributes}. - * - * @param {string} attributeName The attribute to check for. - * @returns {boolean} Whether this schema entry has this attribute. - */ - // eslint-disable-next-line no-unused-vars - hasBooleanAttribute(attributeName) { - return false - } -} - -// TODO: Switch back to class constant once upstream bug is fixed. -const categoryProperty = 'categoryProperty' -const typeProperty = 'typeProperty' -const roleProperty = 'roleProperty' - -/** - * A schema property. - */ -export class SchemaProperty extends SchemaEntry { - /** - * The type of the property. - * @type {string} - */ - _propertyType - - constructor(name, propertyType) { - super(name) - this._propertyType = propertyType - } - - /** - * Whether this property describes a schema category. - * @returns {boolean} - */ - get isCategoryProperty() { - return this._propertyType === categoryProperty - } - - /** - * Whether this property describes a data type. - * @returns {boolean} - */ - get isTypeProperty() { - return this._propertyType === typeProperty - } - - /** - * Whether this property describes a role. - * @returns {boolean} - */ - get isRoleProperty() { - return this._propertyType === roleProperty - } -} - -// Pseudo-properties - -// TODO: Switch back to class constant once upstream bug is fixed. -export const nodeProperty = new SchemaProperty('nodeProperty', categoryProperty) -export const schemaAttributeProperty = new SchemaProperty('schemaAttributeProperty', categoryProperty) -const stringProperty = new SchemaProperty('stringProperty', typeProperty) - -/** - * A schema attribute. - */ -export class SchemaAttribute extends SchemaEntry { - /** - * The categories of elements this schema attribute applies to. - * @type {Set} - */ - _categoryProperties - - /** - * The data type of this schema attribute. - * @type {SchemaProperty} - */ - _typeProperty - - /** - * The set of role properties for this schema attribute. - * @type {Set} - */ - _roleProperties - - /** - * Constructor. - * - * @param {string} name The name of the schema attribute. - * @param {SchemaProperty[]} properties The properties assigned to this schema attribute. - */ - constructor(name, properties) { - super(name, new Set(), new Map()) - - // Parse properties - const categoryProperties = properties.filter((property) => property?.isCategoryProperty) - this._categoryProperties = categoryProperties.length === 0 ? new Set([nodeProperty]) : new Set(categoryProperties) - const typeProperties = properties.filter((property) => property?.isTypeProperty) - this._typeProperty = typeProperties.length === 0 ? stringProperty : typeProperties[0] - this._roleProperties = new Set(properties.filter((property) => property?.isRoleProperty)) - } - - /** - * The categories of elements this schema attribute applies to. - * @returns {Set|SchemaProperty|undefined} - */ - get categoryProperty() { - switch (this._categoryProperties.size) { - case 0: - return undefined - case 1: - return Array.from(this._categoryProperties)[0] - default: - return this._categoryProperties - } - } - - /** - * The data type property of this schema attribute. - * @returns {SchemaProperty} - */ - get typeProperty() { - return this._typeProperty - } - - /** - * The set of role properties for this schema attribute. - * @returns {Set} - */ - get roleProperties() { - return new Set(this._roleProperties) - } -} - -/** - * SchemaEntryWithAttributes class - */ -export class SchemaEntryWithAttributes extends SchemaEntry { - /** - * The set of boolean attributes this schema entry has. - * @type {Set} - */ - booleanAttributes - - /** - * The collection of value attributes this schema entry has. - * @type {Map} - */ - valueAttributes - - /** - * The set of boolean attribute names this schema entry has. - * @type {Set} - */ - booleanAttributeNames - - /** - * The collection of value attribute names this schema entry has. - * @type {Map} - */ - valueAttributeNames - - constructor(name, booleanAttributes, valueAttributes) { - super(name) - this.booleanAttributes = booleanAttributes - this.valueAttributes = valueAttributes - this._parseAttributeNames() - } - - /** - * Create aliases of the attribute collections keyed on their names. - * - * @private - */ - _parseAttributeNames() { - this.booleanAttributeNames = new Set() - for (const attribute of this.booleanAttributes) { - this.booleanAttributeNames.add(attribute.name) - } - this.valueAttributeNames = new Map() - for (const [attributeName, value] of this.valueAttributes) { - this.valueAttributeNames.set(attributeName.name, value) - } - } - - /** - * Whether this schema entry has this attribute (by name). - * @param {string} attributeName The attribute to check for. - * @returns {boolean} Whether this schema entry has this attribute. - */ - hasAttribute(attributeName) { - return this.booleanAttributeNames.has(attributeName) || this.valueAttributeNames.has(attributeName) - } - - /** - * Whether this schema entry has this boolean attribute (by name). - * @param {string} attributeName The attribute to check for. - * @returns {boolean} Whether this schema entry has this attribute. - */ - hasBooleanAttribute(attributeName) { - return this.booleanAttributeNames.has(attributeName) - } - - /** - * Retrieve the value of a value attribute (by name) on this schema entry. - * @param {string} attributeName The attribute whose value should be returned. - * @param {boolean} alwaysReturnArray Whether to return a singleton array instead of a scalar value. - * @returns {*} The value of the attribute. - */ - getAttributeValue(attributeName, alwaysReturnArray = false) { - return SchemaEntryWithAttributes._getMapArrayValue(this.valueAttributeNames, attributeName, alwaysReturnArray) - } - - /** - * Return a map value, with a scalar being returned in lieu of a singleton array if alwaysReturnArray is false. - * - * @template K,V - * @param {Map} map The map to search. - * @param {K} key A key in the map. - * @param {boolean} alwaysReturnArray Whether to return a singleton array instead of a scalar value. - * @returns {V|V[]} The value for the key in the passed map. - * @private - */ - static _getMapArrayValue(map, key, alwaysReturnArray) { - const value = map.get(key) - if (!alwaysReturnArray && Array.isArray(value) && value.length === 1) { - return value[0] - } else { - return value - } - } -} - -/** - * SchemaUnit class - */ -export class SchemaUnit extends SchemaEntryWithAttributes { - /** - * The legal derivatives of this unit. - * @type {string[]} - */ - _derivativeUnits - - /** - * Constructor. - * - * @param {string} name The name of the unit. - * @param {Set} booleanAttributes This unit's boolean attributes. - * @param {Map} valueAttributes This unit's key-value attributes. - * @param {SchemaEntryManager} unitModifiers The collection of unit modifiers. - */ - constructor(name, booleanAttributes, valueAttributes, unitModifiers) { - super(name, booleanAttributes, valueAttributes) - - this._derivativeUnits = [name] - if (!this.isSIUnit) { - this._pushPluralUnit() - return - } - if (this.isUnitSymbol) { - const SIUnitSymbolModifiers = unitModifiers.getEntriesWithBooleanAttribute('SIUnitSymbolModifier') - for (const modifierName of SIUnitSymbolModifiers.keys()) { - this._derivativeUnits.push(modifierName + name) - } - } else { - const SIUnitModifiers = unitModifiers.getEntriesWithBooleanAttribute('SIUnitModifier') - const pluralUnit = this._pushPluralUnit() - for (const modifierName of SIUnitModifiers.keys()) { - this._derivativeUnits.push(modifierName + name, modifierName + pluralUnit) - } - } - } - - _pushPluralUnit() { - if (!this.isUnitSymbol) { - const pluralUnit = pluralize.plural(this._name) - this._derivativeUnits.push(pluralUnit) - return pluralUnit - } - return null - } - - *derivativeUnits() { - for (const unit of this._derivativeUnits) { - yield unit - } - } - - get isPrefixUnit() { - return this.hasAttribute('unitPrefix') - } - - get isSIUnit() { - return this.hasAttribute('SIUnit') - } - - get isUnitSymbol() { - return this.hasAttribute('unitSymbol') - } - - /** - * Determine if a value has this unit. - * - * @param {string} value -- either the whole value or the part after a blank (if not a prefix unit) - * @returns {boolean} Whether the value has these units. - */ - validateUnit(value) { - if (value == null || value === '') { - return false - } - if (this.isPrefixUnit) { - return value.startsWith(this.name) - } - - for (const dUnit of this.derivativeUnits()) { - if (value === dUnit) { - return true - } - } - return false - } -} - -/** - * SchemaUnitClass class - */ -export class SchemaUnitClass extends SchemaEntryWithAttributes { - /** - * The units for this unit class. - * @type {Map} - */ - _units - - /** - * Constructor. - * - * @param {string} name The name of this unit class. - * @param {Set} booleanAttributes The boolean attributes for this unit class. - * @param {Map} valueAttributes The value attributes for this unit class. - * @param {Map} units The units for this unit class. - */ - constructor(name, booleanAttributes, valueAttributes, units) { - super(name, booleanAttributes, valueAttributes) - this._units = units - } - - /** - * Get the units for this unit class. - * @returns {Map} - */ - get units() { - return new Map(this._units) - } - - /** - * Get the default unit for this unit class. - * @returns {SchemaUnit} - */ - get defaultUnit() { - return this._units.get(this.getAttributeValue('defaultUnits')) - } - - /** - * Extracts the Unit class and remainder - * @returns {Array} [SchemaUnit, string, string] containing unit class, unit string, and value string - */ - extractUnit(value) { - let actualUnit = null // The Unit class of the value - let actualValueString = null // The actual value part of the value - let actualUnitString = null - let lastPart = null - let firstPart = null - const index = value.indexOf(' ') - if (index !== -1) { - lastPart = value.slice(index + 1) - firstPart = value.slice(0, index) - } else { - // no blank -- there are no units - return [null, null, value] - } - actualValueString = firstPart - actualUnitString = lastPart - for (const unit of this._units.values()) { - if (!unit.isPrefixUnit && unit.validateUnit(lastPart)) { - // Checking if it is non-prefixed unit - actualValueString = firstPart - actualUnitString = lastPart - actualUnit = unit - break - } else if (!unit.isPrefixUnit) { - continue - } - if (unit.validateUnit(firstPart)) { - actualUnit = unit - actualValueString = value.substring(unit.name.length + 1) - actualUnitString = unit.name - break - } - // If it got here, can only be a prefix Unit - } - return [actualUnit, actualUnitString, actualValueString] - } -} - -/** - * SchemaUnitModifier class - */ -export class SchemaUnitModifier extends SchemaEntryWithAttributes { - constructor(name, booleanAttributes, valueAttributes) { - super(name, booleanAttributes, valueAttributes) - } -} - -/** - * SchemaValueClass class - */ -export class SchemaValueClass extends SchemaEntryWithAttributes { - /** - * The character class-based regular expression. - * @type {RegExp} - * @private - */ - _charClassRegex - /** - * The "word form"-based regular expression. - * @type {RegExp} - * @private - */ - _wordRegex - - /** - * Constructor. - * - * @param {string} name The name of this value class. - * @param {Set} booleanAttributes The boolean attributes for this value class. - * @param {Map} valueAttributes The value attributes for this value class. - * @param {RegExp} charClassRegex The character class-based regular expression for this value class. - * @param {RegExp} wordRegex The "word form"-based regular expression for this value class. - */ - - constructor(name, booleanAttributes, valueAttributes, charClassRegex, wordRegex) { - super(name, booleanAttributes, valueAttributes) - this._charClassRegex = charClassRegex - this._wordRegex = wordRegex - } - - /** - * Determine if a value is valid according to this value class. - * - * @param {string} value A HED value. - * @returns {boolean} Whether the value conforms to this value class. - */ - validateValue(value) { - return this._wordRegex.test(value) && this._charClassRegex.test(value) - } -} - -/** - * A tag in a HED schema. - */ -export class SchemaTag extends SchemaEntryWithAttributes { - /** - * This tag's parent tag. - * @type {SchemaTag} - * @private - */ - _parent - - /** - * This tag's unit classes. - * @type {SchemaUnitClass[]} - * @private - */ - _unitClasses - - /** - * This tag's value-classes - * @type {SchemaValueClass[]} - * @private - */ - _valueClasses - - /** - * This tag's value-taking child. - * @type {SchemaValueTag} - * @private - */ - _valueTag - - /** - * Constructor. - * - * @param {string} name The name of this tag. - * @param {Set} booleanAttributes The boolean attributes for this tag. - * @param {Map} valueAttributes The value attributes for this tag. - * @param {SchemaUnitClass[]} unitClasses The unit classes for this tag. - * @param {SchemaValueClass[]} valueClasses The value classes for this tag. - */ - constructor(name, booleanAttributes, valueAttributes, unitClasses, valueClasses) { - super(name, booleanAttributes, valueAttributes) - this._unitClasses = unitClasses ?? [] - this._valueClasses = valueClasses ?? [] - } - - /** - * This tag's unit classes. - * @type {SchemaUnitClass[]} - */ - get unitClasses() { - return this._unitClasses.slice() // The slice prevents modification - } - - /** - * Whether this tag has any unit classes. - * @returns {boolean} - */ - get hasUnitClasses() { - return this._unitClasses.length !== 0 - } - - /** - * This tag's value classes. - * @type {SchemaValueClass[]} - */ - get valueClasses() { - return this._valueClasses.slice() - } - - /** - * This tag's value-taking child tag. - * @returns {SchemaValueTag} - */ - get valueTag() { - return this._valueTag - } - - /** - * Set the tag's value-taking child tag. - * @param {SchemaValueTag} newValueTag The new value-taking child tag. - */ - set valueTag(newValueTag) { - if (!this._isPrivateFieldSet(this._valueTag, 'value tag')) { - this._valueTag = newValueTag - } - } - - /** - * This tag's parent tag. - * @type {SchemaTag} - */ - get parent() { - return this._parent - } - - /** - * Set the tag's parent tag. - * @param {SchemaTag} newParent The new parent tag. - */ - set parent(newParent) { - if (!this._isPrivateFieldSet(this._parent, 'parent')) { - this._parent = newParent - } - } - - /** - * Throw an error if a private field is already set. - * - * @param {*} field The field being set. - * @param {string} fieldName The name of the field (for error reporting). - * @return {boolean} Whether the field is set (never returns true). - * @throws {IssueError} If the field is already set. - * @private - */ - _isPrivateFieldSet(field, fieldName) { - if (field !== undefined) { - IssueError.generateAndThrowInternalError( - `Attempted to set ${fieldName} for schema tag ${this.longName} when it already has one.`, - ) - } - return false - } - - /** - * Return all of this tag's ancestors. - * @returns {Array} - */ - get ancestors() { - return this._memoize('ancestors', () => { - if (this.parent) { - return [this.parent, ...this.parent.ancestors] - } - return [] - }) - } - - /** - * This tag's long name. - * @returns {string} - */ - get longName() { - const nameParts = this.ancestors.map((parentTag) => parentTag.name) - nameParts.reverse().push(this.name) - return nameParts.join('/') - } - - /** - * Extend this tag's short name. - * - * @param {string} extension The extension. - * @returns {string} The extended short string. - */ - extend(extension) { - if (extension) { - return this.name + '/' + extension - } else { - return this.name - } - } - - /** - * Extend this tag's long name. - * - * @param {string} extension The extension. - * @returns {string} The extended long string. - */ - longExtend(extension) { - if (extension) { - return this.longName + '/' + extension - } else { - return this.longName - } - } -} - -/** - * A value-taking tag in a HED schema. - */ -export class SchemaValueTag extends SchemaTag { - /** - * This tag's long name. - * @returns {string} - */ - get longName() { - const nameParts = this.ancestors.map((parentTag) => parentTag.name) - nameParts.reverse().push('#') - return nameParts.join('/') - } - - /** - * Extend this tag's short name. - * - * @param {string} extension The extension. - * @returns {string} The extended short string. - */ - extend(extension) { - return this.parent.extend(extension) - } - - /** - * Extend this tag's long name. - * - * @param {string} extension The extension. - * @returns {string} The extended long string. - */ - longExtend(extension) { - return this.parent.longExtend(extension) - } -} diff --git a/src/schema/entries.ts b/src/schema/entries.ts new file mode 100644 index 00000000..e0d4a679 --- /dev/null +++ b/src/schema/entries.ts @@ -0,0 +1,743 @@ +/** This module holds the schema entity classes. + * @module schema/entries + */ +import pluralize from 'pluralize' +pluralize.addUncountableRule('hertz') + +import { IssueError } from '../issues/issues' +import type SchemaParser from './parser' + +/** + * SchemaEntries class + */ +export class SchemaEntries { + /** + * The schema's properties. + */ + readonly properties: SchemaEntryManager + + /** + * The schema's attributes. + */ + readonly attributes: SchemaEntryManager + + /** + * The schema's value classes. + */ + readonly valueClasses: SchemaEntryManager + + /** + * The schema's unit classes. + */ + readonly unitClasses: SchemaEntryManager + + /** + * The schema's unit modifiers. + */ + readonly unitModifiers: SchemaEntryManager + + /** + * The schema's tags. + */ + tags: SchemaEntryManager + + /** + * Constructor. + * @param schemaParser A constructed schema parser. + */ + constructor(schemaParser: SchemaParser) { + this.properties = new SchemaEntryManager(schemaParser.properties) + this.attributes = new SchemaEntryManager(schemaParser.attributes) + this.valueClasses = schemaParser.valueClasses + this.unitClasses = schemaParser.unitClasses + this.unitModifiers = schemaParser.unitModifiers + this.tags = schemaParser.tags + } +} + +/** + * A manager of {@link SchemaEntry} objects. + */ +export class SchemaEntryManager { + /** + * The definitions managed by this entry manager. + */ + private readonly _definitions: Map + + /** + * Constructor. + * + * @param definitions A map of schema entry definitions. + */ + constructor(definitions: Map) { + this._definitions = definitions + } + + /** + * Return a copy of the managed definition map. + */ + public get definitions(): Map { + return new Map(this._definitions) + } + + /** + * Iterator over the entry manager's entries. + */ + public [Symbol.iterator](): MapIterator<[string, T]> { + return this._definitions.entries() + } + + /** + * Iterator over the entry manager's keys. + */ + public keys(): MapIterator { + return this._definitions.keys() + } + + /** + * Iterator over the entry manager's keys. + */ + public values(): MapIterator { + return this._definitions.values() + } + + /** + * Determine whether the entry with the given name exists. + * + * @param name The name of the entry. + * @return Whether the entry exists. + */ + public hasEntry(name: string): boolean { + return this._definitions.has(name) + } + + /** + * Get the entry with the given name. + * + * @param name The name of the entry to retrieve. + * @returns The entry with that name. + */ + public getEntry(name: string): T { + return this._definitions.get(name) + } + + /** + * Get a collection of entries with the given boolean attribute. + * + * @param booleanAttributeName The name of boolean attribute to filter on. + * @returns A subset of the managed collection with the given boolean attribute. + */ + public getEntriesWithBooleanAttribute(booleanAttributeName: string): Map { + return this.filter(([, v]) => { + return v.hasBooleanAttribute(booleanAttributeName) + }) + } + + /** + * Filter the map underlying this manager. + * + * @param fn The filtering function. + * @returns A subset of the managed collection satisfying the filter. + */ + public filter(fn: (entry: [string, T]) => boolean): Map { + const pairArray = Array.from(this._definitions.entries()) + return new Map(pairArray.filter((entry) => fn(entry))) + } + + /** + * The number of entries in this collection. + * + * @returns The number of entries in this collection. + */ + public get length(): number { + return this._definitions.size + } +} + +/** + * SchemaEntry class + */ +export class SchemaEntry { + /** + * The name of this schema entry. + */ + private readonly _name: string + + constructor(name: string) { + this._name = name + } + + /** + * The name of this schema entry. + */ + public get name(): string { + return this._name + } + + /** + * Whether this schema entry has this attribute (by name). + * + * This method is a stub to be overridden in {@link SchemaEntryWithAttributes}. + * + * @param attributeName The attribute to check for. + * @returns Whether this schema entry has this attribute. + */ + // eslint-disable-next-line no-unused-vars + public hasBooleanAttribute(attributeName: string): boolean { + return false + } +} + +/** + * A schema property. + */ +export class SchemaProperty extends SchemaEntry {} + +/** + * A schema attribute. + */ +export class SchemaAttribute extends SchemaEntry { + /** + * The properties assigned to this schema attribute. + */ + readonly _properties: Set + + /** + * Constructor. + * + * @param name The name of the schema attribute. + * @param properties The properties assigned to this schema attribute. + */ + constructor(name: string, properties: Set) { + super(name) + this._properties = properties + } + + /** + * The collection of properties for this schema attribute. + */ + public get properties(): Set { + return new Set(this._properties) + } +} + +/** + * SchemaEntryWithAttributes class + */ +export class SchemaEntryWithAttributes extends SchemaEntry { + /** + * The set of boolean attributes this schema entry has. + */ + readonly booleanAttributes: Set + + /** + * The collection of value attributes this schema entry has. + */ + readonly valueAttributes: Map + + /** + * The set of boolean attribute names this schema entry has. + */ + readonly booleanAttributeNames: Set + + /** + * The collection of value attribute names this schema entry has. + */ + readonly valueAttributeNames: Map + + constructor(name: string, booleanAttributes: Set, valueAttributes: Map) { + super(name) + this.booleanAttributes = booleanAttributes + this.valueAttributes = valueAttributes + this.booleanAttributeNames = new Set() + for (const attribute of this.booleanAttributes) { + this.booleanAttributeNames.add(attribute.name) + } + this.valueAttributeNames = new Map() + for (const [attributeName, value] of this.valueAttributes) { + this.valueAttributeNames.set(attributeName.name, value) + } + } + + /** + * Whether this schema entry has this attribute (by name). + * @param attributeName The attribute to check for. + * @returns Whether this schema entry has this attribute. + */ + public hasAttribute(attributeName: string): boolean { + return this.booleanAttributeNames.has(attributeName) || this.valueAttributeNames.has(attributeName) + } + + /** + * Whether this schema entry has this boolean attribute (by name). + * @param attributeName The attribute to check for. + * @returns Whether this schema entry has this attribute. + */ + public hasBooleanAttribute(attributeName: string): boolean { + return this.booleanAttributeNames.has(attributeName) + } + + /** + * Retrieve a single value of a value attribute (by name) on this schema entry, throwing an error if more than one value exists. + * @param attributeName The attribute whose value should be returned. + * @returns The value of the attribute. + * @throws {IssueError} If the attribute has more than one value. + */ + public getSingleAttributeValue(attributeName: string): string | undefined { + const attributeValues = this.valueAttributeNames.get(attributeName) + if (attributeValues === undefined) { + return undefined + } else if (attributeValues.length > 1) { + IssueError.generateAndThrowInternalError( + `More than one value exists for attribute ${attributeName}, when only one value was expected.`, + ) + } + return attributeValues[0] + } + + /** + * Retrieve all values of a value attribute (by name) on this schema entry. + * @param attributeName The attribute whose value should be returned. + * @returns The values of the attribute. + */ + public getAttributeValues(attributeName: string): string[] | undefined { + return this.valueAttributeNames.get(attributeName) + } +} + +/** + * SchemaUnit class + */ +export class SchemaUnit extends SchemaEntryWithAttributes { + /** + * The legal derivatives of this unit. + */ + private readonly _derivativeUnits: string[] + + /** + * Constructor. + * + * @param name The name of the unit. + * @param booleanAttributes This unit's boolean attributes. + * @param valueAttributes This unit's key-value attributes. + * @param unitModifiers The collection of unit modifiers. + */ + constructor( + name: string, + booleanAttributes: Set, + valueAttributes: Map, + unitModifiers: SchemaEntryManager, + ) { + super(name, booleanAttributes, valueAttributes) + + this._derivativeUnits = [name] + if (!this.isSIUnit) { + this._pushPluralUnit() + return + } + if (this.isUnitSymbol) { + const SIUnitSymbolModifiers = unitModifiers.getEntriesWithBooleanAttribute('SIUnitSymbolModifier') + for (const modifierName of SIUnitSymbolModifiers.keys()) { + this._derivativeUnits.push(modifierName + name) + } + } else { + const SIUnitModifiers = unitModifiers.getEntriesWithBooleanAttribute('SIUnitModifier') + const pluralUnit = this._pushPluralUnit() + for (const modifierName of SIUnitModifiers.keys()) { + this._derivativeUnits.push(modifierName + name, modifierName + pluralUnit) + } + } + } + + private _pushPluralUnit(): string | null { + if (!this.isUnitSymbol) { + const pluralUnit = pluralize.plural(this.name) + this._derivativeUnits.push(pluralUnit) + return pluralUnit + } + return null + } + + public *derivativeUnits(): Generator { + for (const unit of this._derivativeUnits) { + yield unit + } + } + + public get isPrefixUnit(): boolean { + return this.hasAttribute('unitPrefix') + } + + public get isSIUnit(): boolean { + return this.hasAttribute('SIUnit') + } + + public get isUnitSymbol(): boolean { + return this.hasAttribute('unitSymbol') + } + + /** + * Determine if a value has this unit. + * + * @param value Either the whole value or the part after a blank (if not a prefix unit) + * @returns Whether the value has these units. + */ + public validateUnit(value: string): boolean { + if (value == null || value === '') { + return false + } + if (this.isPrefixUnit) { + return value.startsWith(this.name) + } + + for (const dUnit of this.derivativeUnits()) { + if (value === dUnit) { + return true + } + } + return false + } +} + +/** + * SchemaUnitClass class + */ +export class SchemaUnitClass extends SchemaEntryWithAttributes { + /** + * The units for this unit class. + * @type {Map} + */ + private readonly _units: Map + + /** + * Constructor. + * + * @param name The name of this unit class. + * @param booleanAttributes The boolean attributes for this unit class. + * @param valueAttributes The value attributes for this unit class. + * @param units The units for this unit class. + */ + constructor( + name: string, + booleanAttributes: Set, + valueAttributes: Map, + units: Map, + ) { + super(name, booleanAttributes, valueAttributes) + this._units = units + } + + /** + * Get the units for this unit class. + */ + public get units(): Map { + return new Map(this._units) + } + + /** + * Get the default unit for this unit class. + */ + public get defaultUnit(): SchemaUnit | undefined { + return this._units.get(this.getSingleAttributeValue('defaultUnits')) + } + + /** + * Extracts the Unit class and remainder + * @returns Unit class, unit string, and value string + */ + public extractUnit(value: string): [SchemaUnit | null, string | null, string] { + let actualUnit = null // The Unit class of the value + let actualValueString = null // The actual value part of the value + let actualUnitString = null + let lastPart = null + let firstPart = null + const index = value.indexOf(' ') + if (index !== -1) { + lastPart = value.slice(index + 1) + firstPart = value.slice(0, index) + } else { + // no blank -- there are no units + return [null, null, value] + } + actualValueString = firstPart + actualUnitString = lastPart + for (const unit of this._units.values()) { + if (!unit.isPrefixUnit && unit.validateUnit(lastPart)) { + // Checking if it is non-prefixed unit + actualValueString = firstPart + actualUnitString = lastPart + actualUnit = unit + break + } else if (!unit.isPrefixUnit) { + continue + } + if (unit.validateUnit(firstPart)) { + actualUnit = unit + actualValueString = value.substring(unit.name.length + 1) + actualUnitString = unit.name + break + } + // If it got here, can only be a prefix Unit + } + return [actualUnit, actualUnitString, actualValueString] + } +} + +/** + * SchemaUnitModifier class + */ +export class SchemaUnitModifier extends SchemaEntryWithAttributes {} + +/** + * SchemaValueClass class + */ +export class SchemaValueClass extends SchemaEntryWithAttributes { + /** + * The character class-based regular expression. + */ + private readonly _charClassRegex: RegExp + /** + * The "word form"-based regular expression. + */ + private readonly _wordRegex: RegExp + + /** + * Constructor. + * + * @param name The name of this value class. + * @param booleanAttributes The boolean attributes for this value class. + * @param valueAttributes The value attributes for this value class. + * @param charClassRegex The character class-based regular expression for this value class. + * @param wordRegex The "word form"-based regular expression for this value class. + */ + + constructor( + name: string, + booleanAttributes: Set, + valueAttributes: Map, + charClassRegex: RegExp, + wordRegex: RegExp, + ) { + super(name, booleanAttributes, valueAttributes) + this._charClassRegex = charClassRegex + this._wordRegex = wordRegex + } + + /** + * Determine if a value is valid according to this value class. + * + * @param value A HED value. + * @returns Whether the value conforms to this value class. + */ + public validateValue(value: string): boolean { + return this._wordRegex.test(value) && this._charClassRegex.test(value) + } +} + +/** + * A tag in a HED schema. + */ +export class SchemaTag extends SchemaEntryWithAttributes { + /** + * This tag's parent tag. + */ + private _parent: SchemaTag + + /** + * This tag's unit classes. + */ + private readonly _unitClasses: SchemaUnitClass[] + + /** + * This tag's value-classes + */ + private readonly _valueClasses: SchemaValueClass[] + + /** + * This tag's value-taking child. + */ + private _valueTag: SchemaValueTag + + /** + * This tag's ancestor tags. + */ + #ancestors: SchemaTag[] + + /** + * Constructor. + * + * @param name The name of this tag. + * @param booleanAttributes The boolean attributes for this tag. + * @param valueAttributes The value attributes for this tag. + * @param unitClasses The unit classes for this tag. + * @param valueClasses The value classes for this tag. + */ + constructor( + name: string, + booleanAttributes: Set, + valueAttributes: Map, + unitClasses: SchemaUnitClass[], + valueClasses: SchemaValueClass[], + ) { + super(name, booleanAttributes, valueAttributes) + this._unitClasses = unitClasses ?? [] + this._valueClasses = valueClasses ?? [] + } + + /** + * This tag's unit classes. + */ + public get unitClasses(): SchemaUnitClass[] { + return this._unitClasses.slice() // The slice prevents modification + } + + /** + * Whether this tag has any unit classes. + */ + public get hasUnitClasses(): boolean { + return this._unitClasses.length !== 0 + } + + /** + * This tag's value classes. + */ + public get valueClasses(): SchemaValueClass[] { + return this._valueClasses.slice() + } + + /** + * This tag's value-taking child tag. + */ + public get valueTag(): SchemaValueTag { + return this._valueTag + } + + /** + * Set the tag's value-taking child tag. + * @param newValueTag The new value-taking child tag. + */ + public set valueTag(newValueTag: SchemaValueTag) { + if (!this._isPrivateFieldSet(this._valueTag, 'value tag')) { + this._valueTag = newValueTag + } + } + + /** + * This tag's parent tag. + */ + public get parent(): SchemaTag { + return this._parent + } + + /** + * Set the tag's parent tag. + * @param newParent The new parent tag. + */ + public set parent(newParent: SchemaTag) { + if (!this._isPrivateFieldSet(this._parent, 'parent')) { + this._parent = newParent + } + } + + /** + * Throw an error if a private field is already set. + * + * @param field The field being set. + * @param fieldName The name of the field (for error reporting). + * @return Whether the field is set (never returns true). + * @throws {IssueError} If the field is already set. + * @private + */ + private _isPrivateFieldSet(field: any, fieldName: string): boolean { + if (field !== undefined) { + IssueError.generateAndThrowInternalError( + `Attempted to set ${fieldName} for schema tag ${this.longName} when it already has one.`, + ) + } + return false + } + + /** + * Return all of this tag's ancestors. + */ + public get ancestors(): SchemaTag[] { + if (this.#ancestors !== undefined) { + return this.#ancestors + } + this.#ancestors = this.parent ? [this.parent, ...this.parent.ancestors] : [] + return this.#ancestors + } + + /** + * This tag's long name. + */ + public get longName(): string { + const nameParts = this.ancestors.map((parentTag) => parentTag.name) + nameParts.reverse() + nameParts.push(this.name) + return nameParts.join('/') + } + + /** + * Extend this tag's short name. + * + * @param extension The extension. + * @returns The extended short string. + */ + public extend(extension: string): string { + if (extension) { + return this.name + '/' + extension + } else { + return this.name + } + } + + /** + * Extend this tag's long name. + * + * @param extension The extension. + * @returns The extended long string. + */ + public longExtend(extension: string): string { + if (extension) { + return this.longName + '/' + extension + } else { + return this.longName + } + } +} + +/** + * A value-taking tag in a HED schema. + */ +export class SchemaValueTag extends SchemaTag { + /** + * This tag's long name. + */ + public get longName(): string { + const nameParts = this.ancestors.map((parentTag) => parentTag.name) + nameParts.reverse() + nameParts.push('#') + return nameParts.join('/') + } + + /** + * Extend this tag's short name. + * + * @param extension The extension. + * @returns The extended short string. + */ + public extend(extension: string): string { + return this.parent.extend(extension) + } + + /** + * Extend this tag's long name. + * + * @param extension The extension. + * @returns The extended long string. + */ + public longExtend(extension: string): string { + return this.parent.longExtend(extension) + } +} diff --git a/src/schema/init.js b/src/schema/init.ts similarity index 62% rename from src/schema/init.js rename to src/schema/init.ts index 83fc916e..c7ba0cc0 100644 --- a/src/schema/init.js +++ b/src/schema/init.ts @@ -4,50 +4,21 @@ import zip from 'lodash/zip' import loadSchema from './loader' -import { setParent } from '../utils/xml' - import SchemaParser from './parser' import PartneredSchemaMerger from './schemaMerger' -import { Schema, Schemas } from './containers' +import { HedSchema, PrimarySchema, HedSchemas } from './containers' import { IssueError } from '../issues/issues' import { splitStringTrimAndRemoveBlanks } from '../utils/string' import { SchemasSpec } from './specs' - -/** - * Build a single schema container object from an XML file. - * - * @param {Object} xmlData The schema's XML data - * @returns {Schema} The HED schema object. - */ -const buildSchemaObject = function (xmlData) { - const rootElement = xmlData.HED - setParent(rootElement, null) - const schemaEntries = new SchemaParser(rootElement).parse() - return new Schema(xmlData, schemaEntries) -} - -/** - * Build a single merged schema container object from one or more XML files. - * - * @param {Object[]} xmlData The schemas' XML data. - * @returns {Schema} The HED schema object. - */ -const buildSchemaObjects = function (xmlData) { - const schemas = xmlData.map((data) => buildSchemaObject(data)) - if (schemas.length === 1) { - return schemas[0] - } - const partneredSchemaMerger = new PartneredSchemaMerger(schemas) - return partneredSchemaMerger.mergeSchemas() -} +import { type HedSchemaXMLObject } from './xmlType' /** * Build a schema collection object from a schema specification. * - * @param {SchemasSpec} schemaSpecs The description of which schemas to use. - * @returns {Promise} The schema container object and any issues found. + * @param schemaSpecs The description of which schemas to use. + * @returns The schema container object and any issues found. */ -export async function buildSchemas(schemaSpecs) { +export async function buildSchemas(schemaSpecs: SchemasSpec): Promise { const schemaPrefixes = Array.from(schemaSpecs.data.keys()) /* Data format example: * [[xmlData, ...], [xmlData, xmlData, ...], ...] */ @@ -59,19 +30,45 @@ export async function buildSchemas(schemaSpecs) { ) const schemaObjects = schemaXmlData.map(buildSchemaObjects) const schemas = new Map(zip(schemaPrefixes, schemaObjects)) - return new Schemas(schemas) + return new HedSchemas(schemas) } /** * Build HED schemas from a version specification string. * - * @param {string} hedVersionString The HED version specification string (can contain comma-separated versions). - * @returns {Promise} A Promise that resolves to the built schemas. + * @param hedVersionString The HED version specification string (can contain comma-separated versions). + * @returns A Promise that resolves to the built schemas. * @throws {IssueError} If the schema specification is invalid or schemas cannot be built. */ -export async function buildSchemasFromVersion(hedVersionString) { +export async function buildSchemasFromVersion(hedVersionString?: string): Promise { hedVersionString ??= '' const hedVersionSpecStrings = splitStringTrimAndRemoveBlanks(hedVersionString, ',') const hedVersionSpecs = SchemasSpec.parseVersionSpecs(hedVersionSpecStrings) return buildSchemas(hedVersionSpecs) } + +/** + * Build a single merged schema container object from one or more XML files. + * + * @param xmlData The schemas' XML data. + * @returns The HED schema object. + */ +function buildSchemaObjects(xmlData: HedSchemaXMLObject[]): HedSchema { + const schemas = xmlData.map((data) => buildSchemaObject(data)) + if (schemas.length === 1) { + return schemas[0] + } + const partneredSchemaMerger = new PartneredSchemaMerger(schemas) + return partneredSchemaMerger.mergeSchemas() +} + +/** + * Build a single schema container object from an XML file. + * + * @param xmlData The schema's XML data + * @returns The HED schema object. + */ +function buildSchemaObject(xmlData: HedSchemaXMLObject): PrimarySchema { + const schemaEntries = new SchemaParser(xmlData.HED).parse() + return new PrimarySchema(xmlData, schemaEntries) +} diff --git a/src/schema/loader.js b/src/schema/loader.ts similarity index 61% rename from src/schema/loader.js rename to src/schema/loader.ts index 3a621afc..ca59945f 100644 --- a/src/schema/loader.js +++ b/src/schema/loader.ts @@ -2,21 +2,22 @@ * @module schema/loader * */ -/* Imports */ -import * as files from '../utils/files' -import { IssueError } from '../issues/issues' -import { parseSchemaXML } from '../utils/xml' - +// Imports import { localSchemaMap, localSchemaNames } from './config' // Changed from localSchemaList +import { type SchemaSpec } from './specs' +import { type HedSchemaXMLObject } from './xmlType' +import { IssueError, type IssueParameters } from '../issues/issues' +import * as files from '../utils/files' +import parseSchemaXML from '../utils/xml' /** * Load schema XML data from a schema version or path description. * - * @param {SchemaSpec} schemaDef The description of which schema to use. - * @returns {Promise} The schema XML data. + * @param schemaDef The description of which schema to use. + * @returns The schema XML data. * @throws {IssueError} If the schema could not be loaded. */ -export default async function loadSchema(schemaDef = null) { +export default async function loadSchema(schemaDef: SchemaSpec): Promise { const xmlData = await loadPromise(schemaDef) if (xmlData === null) { IssueError.generateAndThrow('invalidSchemaSpecification', { spec: JSON.stringify(schemaDef) }) @@ -27,14 +28,12 @@ export default async function loadSchema(schemaDef = null) { /** * Choose the schema Promise from a schema version or path description. * - * @param {SchemaSpec} schemaDef The description of which schema to use. - * @returns {Promise} The schema XML data. + * @param schemaDef The description of which schema to use. + * @returns The schema XML data. * @throws {IssueError} If the schema could not be loaded. */ -async function loadPromise(schemaDef) { - if (schemaDef === null) { - return null - } else if (schemaDef.localPath) { +async function loadPromise(schemaDef: SchemaSpec): Promise { + if (schemaDef.localPath) { return loadLocalSchema(schemaDef.localPath) } else if (localSchemaNames.includes(schemaDef.localName)) { // Changed condition @@ -47,12 +46,12 @@ async function loadPromise(schemaDef) { /** * Load schema XML data from the HED GitHub repository. * - * @param {SchemaSpec} schemaDef The standard schema version to load. - * @returns {Promise} The schema XML data. + * @param schemaDef The standard schema version to load. + * @returns The schema XML data. * @throws {IssueError} If the schema could not be loaded. */ -function loadRemoteSchema(schemaDef) { - let url +async function loadRemoteSchema(schemaDef: SchemaSpec): Promise { + let url: string if (schemaDef.library) { url = `https://raw.githubusercontent.com/hed-standard/hed-schemas/refs/heads/main/library_schemas/${schemaDef.library}/hedxml/HED_${schemaDef.library}_${schemaDef.version}.xml` } else { @@ -64,22 +63,22 @@ function loadRemoteSchema(schemaDef) { /** * Load schema XML data from a local file. * - * @param {string} path The path to the schema XML data. - * @returns {Promise} The schema XML data. + * @param path The path to the schema XML data. + * @returns The schema XML data. * @throws {IssueError} If the schema could not be loaded. */ -function loadLocalSchema(path) { +async function loadLocalSchema(path: string): Promise { return loadSchemaFile(files.readFile(path), 'localSchemaLoadFailed', { path: path }) } /** * Load schema XML data from a bundled file. * - * @param {SchemaSpec} schemaDef The description of which schema to use. - * @returns {Promise} The schema XML data. + * @param schemaDef The description of which schema to use. + * @returns The schema XML data. * @throws {IssueError} If the schema could not be loaded. */ -async function loadBundledSchema(schemaDef) { +async function loadBundledSchema(schemaDef: SchemaSpec): Promise { try { return parseSchemaXML(localSchemaMap.get(schemaDef.localName)) } catch (error) { @@ -91,13 +90,17 @@ async function loadBundledSchema(schemaDef) { /** * Actually load the schema XML file. * - * @param {Promise} xmlDataPromise The Promise containing the unparsed XML data. - * @param {string} issueCode The issue code. - * @param {Object} issueArgs The issue arguments passed from the calling function. - * @returns {Promise} The parsed schema XML data. + * @param xmlDataPromise The Promise containing the unparsed XML data. + * @param issueCode The issue code. + * @param issueArgs The issue arguments passed from the calling function. + * @returns The parsed schema XML data. * @throws {IssueError} If the schema could not be loaded. */ -async function loadSchemaFile(xmlDataPromise, issueCode, issueArgs) { +async function loadSchemaFile( + xmlDataPromise: Promise, + issueCode: string, + issueArgs: IssueParameters, +): Promise { try { const data = await xmlDataPromise return parseSchemaXML(data) diff --git a/src/schema/parser.js b/src/schema/parser.ts similarity index 54% rename from src/schema/parser.js rename to src/schema/parser.ts index 14825a0e..83fc5f4a 100644 --- a/src/schema/parser.js +++ b/src/schema/parser.ts @@ -1,7 +1,6 @@ /** This module holds the classes for populating a schema from XML. * @module schema/parser */ -import Set from 'core-js-pure/actual/set' import flattenDeep from 'lodash/flattenDeep' import zip from 'lodash/zip' import semver from 'semver' @@ -20,57 +19,70 @@ import { } from './entries' import { IssueError } from '../issues/issues' -import classRegex from '../data/json/classRegex.json' +import { type DefinitionElement, type HedSchemaRootElement, type NamedElement, type NodeElement } from './xmlType' -const lc = (str) => str.toLowerCase() +interface ClassRegex { + char_regex: { + [key: string]: string + } + class_chars: { + [key: string]: string[] + } + class_words: { + [key: string]: string + } +} + +import * as _classRegex from '../data/json/classRegex.json' +const classRegex: ClassRegex = _classRegex + +const lc = (str: string) => str.toLowerCase() export default class SchemaParser { /** * The root XML element. - * @type {Object} */ - rootElement + rootElement: HedSchemaRootElement - /** - * @type {Map} - */ - properties + properties: Map - /** - * @type {Map} - */ - attributes + attributes: Map /** * The schema's value classes. - * @type {SchemaEntryManager} */ - valueClasses + valueClasses: SchemaEntryManager /** * The schema's unit classes. - * @type {SchemaEntryManager} */ - unitClasses + unitClasses: SchemaEntryManager /** * The schema's unit modifiers. - * @type {SchemaEntryManager} */ - unitModifiers + unitModifiers: SchemaEntryManager /** * The schema's tags. - * @type {SchemaEntryManager} */ - tags + tags: SchemaEntryManager + + /** + * Version-specific definitions. + */ + private readonly _versionDefinitions: { + typeProperties: Set + categoryProperties: Set + roleProperties: Set + } /** * Constructor. * * @param {Object} rootElement The root XML element. */ - constructor(rootElement) { + constructor(rootElement: HedSchemaRootElement) { this.rootElement = rootElement this._versionDefinitions = { typeProperties: new Set(['boolProperty']), @@ -87,12 +99,12 @@ export default class SchemaParser { } } - parse() { + public parse(): SchemaEntries { this.populateDictionaries() return new SchemaEntries(this) } - populateDictionaries() { + private populateDictionaries(): void { this.parseProperties() this.parseAttributes() this.parseUnitModifiers() @@ -101,20 +113,18 @@ export default class SchemaParser { this.parseTags() } - getAllChildTags(parentElement, excludeTakeValueTags = true) { + private getAllChildTags(parentElement: NodeElement, excludeTakeValueTags = true): NodeElement[] { if (excludeTakeValueTags && SchemaParser.getElementTagName(parentElement) === '#') { return [] } - const childTags = [] - if (parentElement.$parent) { - childTags.push(parentElement) - } + const childTags = [parentElement] const tagElementChildren = parentElement.node ?? [] - childTags.push(...flattenDeep(tagElementChildren.map((child) => this.getAllChildTags(child, excludeTakeValueTags)))) - return childTags + return childTags.concat( + flattenDeep(tagElementChildren.map((child) => this.getAllChildTags(child, excludeTakeValueTags))), + ) } - static getParentTagName(tagElement) { + private static getParentTagName(tagElement: NodeElement): string { const parentTagElement = tagElement.$parent if (parentTagElement?.$parent) { return SchemaParser.getElementTagName(parentTagElement) @@ -126,68 +136,50 @@ export default class SchemaParser { /** * Extract the name of an XML element. * - * @param {Object} element An XML element. - * @returns {string} The name of the element. + * @param element An XML element. + * @returns The name of the element. */ - static getElementTagName(element) { + private static getElementTagName(element: NamedElement): string { return element.name._ } /** * Retrieve all the tags in the schema. * - * @returns {Map} The tag names and XML elements. + * @returns The tag names and XML elements. */ - getAllTags() { + private getAllTags(): Map { const nodeRoot = this.rootElement.schema - const tagElements = this.getAllChildTags(nodeRoot, false) + const tagElements = [] + const tagElementChildren = nodeRoot.node + tagElements.push(...flattenDeep(tagElementChildren.map((child) => this.getAllChildTags(child, false)))) const tags = tagElements.map((element) => SchemaParser.getElementTagName(element)) return new Map(zip(tagElements, tags)) } - // Rewrite starts here. - - parseProperties() { - const propertyDefinitions = this._getDefinitionElements('property') - this.properties = new Map() + private parseProperties(): void { + const propertyDefinitions = this.rootElement.propertyDefinitions.propertyDefinition + this.properties = new Map() for (const definition of propertyDefinitions) { const propertyName = SchemaParser.getElementTagName(definition) - if (this._versionDefinitions.categoryProperties?.has(propertyName)) { - this.properties.set( - propertyName, - // TODO: Switch back to class constant once upstream bug is fixed. - new SchemaProperty(propertyName, 'categoryProperty'), - ) - } else if (this._versionDefinitions.typeProperties?.has(propertyName)) { - this.properties.set( - propertyName, - // TODO: Switch back to class constant once upstream bug is fixed. - new SchemaProperty(propertyName, 'typeProperty'), - ) - } else if (this._versionDefinitions.roleProperties?.has(propertyName)) { - this.properties.set( - propertyName, - // TODO: Switch back to class constant once upstream bug is fixed. - new SchemaProperty(propertyName, 'roleProperty'), - ) - } + this.properties.set(propertyName, new SchemaProperty(propertyName)) } this._addCustomProperties() } - parseAttributes() { - const attributeDefinitions = this._getDefinitionElements('schemaAttribute') - this.attributes = new Map() + private parseAttributes(): void { + const attributeDefinitions = this.rootElement.schemaAttributeDefinitions.schemaAttributeDefinition + this.attributes = new Map() for (const definition of attributeDefinitions) { const attributeName = SchemaParser.getElementTagName(definition) const propertyElements = definition.property ?? [] const properties = propertyElements.map((element) => this.properties.get(SchemaParser.getElementTagName(element))) - this.attributes.set(attributeName, new SchemaAttribute(attributeName, properties)) + this.attributes.set(attributeName, new SchemaAttribute(attributeName, new Set(properties))) } this._addCustomAttributes() } - _getValueClassChars(name) { + private _getValueClassChars(name: string): RegExp { let classChars if (Array.isArray(classRegex.class_chars[name]) && classRegex.class_chars[name].length > 0) { classChars = @@ -198,12 +190,13 @@ export default class SchemaParser { return new RegExp(classChars) } - parseValueClasses() { - const valueClasses = new Map() - const [booleanAttributeDefinitions, valueAttributeDefinitions] = this._parseDefinitions('valueClass') + private parseValueClasses(): void { + const valueClasses = new Map() + const [booleanAttributeDefinitions, valueAttributeDefinitions] = this._parseDefinitions( + this.rootElement.valueClassDefinitions.valueClassDefinition, + ) for (const [name, valueAttributes] of valueAttributeDefinitions) { const booleanAttributes = booleanAttributeDefinitions.get(name) - //valueClasses.set(name, new SchemaValueClass(name, booleanAttributes, valueAttributes)) const charRegex = this._getValueClassChars(name) const wordRegex = new RegExp(classRegex.class_words[name] ?? '^.+$') valueClasses.set(name, new SchemaValueClass(name, booleanAttributes, valueAttributes, charRegex, wordRegex)) @@ -211,9 +204,11 @@ export default class SchemaParser { this.valueClasses = new SchemaEntryManager(valueClasses) } - parseUnitModifiers() { - const unitModifiers = new Map() - const [booleanAttributeDefinitions, valueAttributeDefinitions] = this._parseDefinitions('unitModifier') + private parseUnitModifiers(): void { + const unitModifiers = new Map() + const [booleanAttributeDefinitions, valueAttributeDefinitions] = this._parseDefinitions( + this.rootElement.unitModifierDefinitions.unitModifierDefinition, + ) for (const [name, valueAttributes] of valueAttributeDefinitions) { const booleanAttributes = booleanAttributeDefinitions.get(name) unitModifiers.set(name, new SchemaUnitModifier(name, booleanAttributes, valueAttributes)) @@ -221,9 +216,11 @@ export default class SchemaParser { this.unitModifiers = new SchemaEntryManager(unitModifiers) } - parseUnitClasses() { - const unitClasses = new Map() - const [booleanAttributeDefinitions, valueAttributeDefinitions] = this._parseDefinitions('unitClass') + private parseUnitClasses(): void { + const unitClasses = new Map() + const [booleanAttributeDefinitions, valueAttributeDefinitions] = this._parseDefinitions( + this.rootElement.unitClassDefinitions.unitClassDefinition, + ) const unitClassUnits = this.parseUnits() for (const [name, valueAttributes] of valueAttributeDefinitions) { @@ -233,13 +230,13 @@ export default class SchemaParser { this.unitClasses = new SchemaEntryManager(unitClasses) } - parseUnits() { - const unitClassUnits = new Map() - const unitClassElements = this._getDefinitionElements('unitClass') + private parseUnits(): Map> { + const unitClassUnits = new Map>() + const unitClassElements = this.rootElement.unitClassDefinitions.unitClassDefinition const unitModifiers = this.unitModifiers for (const element of unitClassElements) { const elementName = SchemaParser.getElementTagName(element) - const units = new Map() + const units = new Map() unitClassUnits.set(elementName, units) if (element.unit === undefined) { continue @@ -261,21 +258,23 @@ export default class SchemaParser { /** * Parse the schema's tags. */ - parseTags() { + private parseTags(): void { const tags = this.getAllTags() const shortTags = this._getShortTags(tags) const [booleanAttributeDefinitions, valueAttributeDefinitions] = this._parseAttributeElements( tags.keys(), - (element) => shortTags.get(element), + (element: NodeElement) => shortTags.get(element), ) const tagUnitClassDefinitions = this._processTagUnitClasses(shortTags, valueAttributeDefinitions) + const tagValueClassDefinitions = this._processTagValueClasses(shortTags, valueAttributeDefinitions) this._processRecursiveAttributes(shortTags, booleanAttributeDefinitions) const tagEntries = this._createSchemaTags( booleanAttributeDefinitions, valueAttributeDefinitions, tagUnitClassDefinitions, + tagValueClassDefinitions, ) this._injectTagFields(tags, shortTags, tagEntries) @@ -286,12 +285,11 @@ export default class SchemaParser { /** * Generate the map from tag elements to shortened tag names. * - * @param {Map} tags The map from tag elements to tag strings. - * @returns {Map} The map from tag elements to shortened tag names. - * @private + * @param tags The map from tag elements to tag strings. + * @returns The map from tag elements to shortened tag names. */ - _getShortTags(tags) { - const shortTags = new Map() + private _getShortTags(tags: Map): Map { + const shortTags = new Map() for (const tagElement of tags.keys()) { const shortKey = SchemaParser.getElementTagName(tagElement) === '#' @@ -305,14 +303,16 @@ export default class SchemaParser { /** * Process unit classes in tags. * - * @param {Map} shortTags The map from tag elements to shortened tag names. - * @param {Map>} valueAttributeDefinitions The map from shortened tag names to their value schema attributes. - * @returns {Map} The map from shortened tag names to their unit classes. - * @private + * @param shortTags The map from tag elements to shortened tag names. + * @param valueAttributeDefinitions The map from shortened tag names to their value schema attributes. + * @returns The map from shortened tag names to their unit classes. */ - _processTagUnitClasses(shortTags, valueAttributeDefinitions) { + private _processTagUnitClasses( + shortTags: Map, + valueAttributeDefinitions: Map>, + ): Map { const tagUnitClassAttribute = this.attributes.get('unitClass') - const tagUnitClassDefinitions = new Map() + const tagUnitClassDefinitions = new Map() for (const tagName of shortTags.values()) { const valueAttributes = valueAttributeDefinitions.get(tagName) @@ -330,14 +330,47 @@ export default class SchemaParser { return tagUnitClassDefinitions } + /** + * Process value classes in tags. + * + * @param shortTags The map from tag elements to shortened tag names. + * @param valueAttributeDefinitions The map from shortened tag names to their value schema attributes. + * @returns The map from shortened tag names to their value classes. + */ + private _processTagValueClasses( + shortTags: Map, + valueAttributeDefinitions: Map>, + ): Map { + const tagValueClassAttribute = this.attributes.get('valueClass') + const tagValueClassDefinitions = new Map() + + for (const tagName of shortTags.values()) { + const valueAttributes = valueAttributeDefinitions.get(tagName) + if (valueAttributes.has(tagValueClassAttribute)) { + tagValueClassDefinitions.set( + tagName, + valueAttributes.get(tagValueClassAttribute).map((valueClassName) => { + return this.valueClasses.getEntry(valueClassName) + }), + ) + // TODO: Uncomment once value validation uses value classes. + // valueAttributes.delete(tagValueClassAttribute) + } + } + + return tagValueClassDefinitions + } + /** * Process recursive schema attributes. * - * @param {Map} shortTags The map from tag elements to shortened tag names. - * @param {Map>} booleanAttributeDefinitions The map from shortened tag names to their boolean schema attributes. Passed by reference. - * @private + * @param shortTags The map from tag elements to shortened tag names. + * @param booleanAttributeDefinitions The map from shortened tag names to their boolean schema attributes. Passed by reference. */ - _processRecursiveAttributes(shortTags, booleanAttributeDefinitions) { + private _processRecursiveAttributes( + shortTags: Map, + booleanAttributeDefinitions: Map>, + ): void { const recursiveAttributeMap = this._generateRecursiveAttributeMap(shortTags, booleanAttributeDefinitions) for (const [tagElement, recursiveAttributes] of recursiveAttributeMap) { @@ -352,13 +385,15 @@ export default class SchemaParser { /** * Generate a map from tags to their recursive attributes. * - * @param {Map} shortTags The map from tag elements to shortened tag names. - * @param {Map>} booleanAttributeDefinitions The map from shortened tag names to their boolean schema attributes. Passed by reference. - * @private + * @param shortTags The map from tag elements to shortened tag names. + * @param booleanAttributeDefinitions The map from shortened tag names to their boolean schema attributes. Passed by reference. */ - _generateRecursiveAttributeMap(shortTags, booleanAttributeDefinitions) { + private _generateRecursiveAttributeMap( + shortTags: Map, + booleanAttributeDefinitions: Map>, + ): Map> { const recursiveAttributes = this._getRecursiveAttributes() - const recursiveAttributeMap = new Map() + const recursiveAttributeMap = new Map>() for (const [tagElement, tagName] of shortTags) { recursiveAttributeMap.set(tagElement, booleanAttributeDefinitions.get(tagName)?.intersection(recursiveAttributes)) @@ -367,17 +402,17 @@ export default class SchemaParser { return recursiveAttributeMap } - _getRecursiveAttributes() { + private _getRecursiveAttributes(): Set { const attributeArray = Array.from(this.attributes.values()) let filteredAttributeArray if (semver.lt(this.rootElement.$.version, '8.3.0')) { filteredAttributeArray = attributeArray.filter((attribute) => - attribute.roleProperties.has(this.properties.get('isInheritedProperty')), + attribute.properties.has(this.properties.get('isInheritedProperty')), ) } else { filteredAttributeArray = attributeArray.filter( - (attribute) => !attribute.roleProperties.has(this.properties.get('annotationProperty')), + (attribute) => !attribute.properties.has(this.properties.get('annotationProperty')), ) } @@ -387,15 +422,20 @@ export default class SchemaParser { /** * Create the {@link SchemaTag} objects. * - * @param {Map>} booleanAttributeDefinitions The map from shortened tag names to their boolean schema attributes. - * @param {Map>} valueAttributeDefinitions The map from shortened tag names to their value schema attributes. - * @param {Map} tagUnitClassDefinitions The map from shortened tag names to their unit classes. - * @returns {Map} The map from lowercase shortened tag names to their tag objects. - * @private + * @param booleanAttributeDefinitions The map from shortened tag names to their boolean schema attributes. + * @param valueAttributeDefinitions The map from shortened tag names to their value schema attributes. + * @param tagUnitClassDefinitions The map from shortened tag names to their unit classes. + * @param tagValueClassDefinitions The map from shortened tag names to their value classes. + * @returns The map from lowercase shortened tag names to their tag objects. */ - _createSchemaTags(booleanAttributeDefinitions, valueAttributeDefinitions, tagUnitClassDefinitions) { + private _createSchemaTags( + booleanAttributeDefinitions: Map>, + valueAttributeDefinitions: Map>, + tagUnitClassDefinitions: Map, + tagValueClassDefinitions: Map, + ): Map { const tagTakesValueAttribute = this.attributes.get('takesValue') - const tagEntries = new Map() + const tagEntries = new Map() for (const [name, valueAttributes] of valueAttributeDefinitions) { if (tagEntries.has(name)) { @@ -404,11 +444,15 @@ export default class SchemaParser { const booleanAttributes = booleanAttributeDefinitions.get(name) const unitClasses = tagUnitClassDefinitions.get(name) + const valueClasses = tagValueClassDefinitions.get(name) if (booleanAttributes.has(tagTakesValueAttribute)) { - tagEntries.set(lc(name), new SchemaValueTag(name, booleanAttributes, valueAttributes, unitClasses)) + tagEntries.set( + lc(name), + new SchemaValueTag(name, booleanAttributes, valueAttributes, unitClasses, valueClasses), + ) } else { - tagEntries.set(lc(name), new SchemaTag(name, booleanAttributes, valueAttributes, unitClasses)) + tagEntries.set(lc(name), new SchemaTag(name, booleanAttributes, valueAttributes, unitClasses, valueClasses)) } } @@ -418,12 +462,15 @@ export default class SchemaParser { /** * Inject special tag fields into the {@link SchemaTag} objects. * - * @param {Map} tags The map from tag elements to tag strings. - * @param {Map} shortTags The map from tag elements to shortened tag names. - * @param {Map} tagEntries The map from shortened tag names to tag objects. - * @private + * @param tags The map from tag elements to tag strings. + * @param shortTags The map from tag elements to shortened tag names. + * @param tagEntries The map from shortened tag names to tag objects. */ - _injectTagFields(tags, shortTags, tagEntries) { + private _injectTagFields( + tags: Map, + shortTags: Map, + tagEntries: Map, + ): void { for (const tagElement of tags.keys()) { const tagName = shortTags.get(tagElement) const parentTagName = shortTags.get(tagElement.$parent) @@ -438,20 +485,18 @@ export default class SchemaParser { } } - _parseDefinitions(category) { - const definitionElements = this._getDefinitionElements(category) + private _parseDefinitions( + definitionElements: Iterable, + ): [Map>, Map>] { return this._parseAttributeElements(definitionElements, SchemaParser.getElementTagName) } - _getDefinitionElements(category) { - const categoryTagName = category + 'Definition' - const categoryParentTagName = categoryTagName + 's' - return this.rootElement[categoryParentTagName][categoryTagName] - } - - _parseAttributeElements(elements, namer) { - const booleanAttributeDefinitions = new Map() - const valueAttributeDefinitions = new Map() + private _parseAttributeElements( + elements: Iterable, + namer: (element: NamedElement) => string, + ): [Map>, Map>] { + const booleanAttributeDefinitions = new Map>() + const valueAttributeDefinitions = new Map>() for (const element of elements) { const [booleanAttributes, valueAttributes] = this._parseAttributeElement(element) @@ -464,9 +509,9 @@ export default class SchemaParser { return [booleanAttributeDefinitions, valueAttributeDefinitions] } - _parseAttributeElement(element) { - const booleanAttributes = new Set() - const valueAttributes = new Map() + private _parseAttributeElement(element: DefinitionElement): [Set, Map] { + const booleanAttributes = new Set() + const valueAttributes = new Map() const tagAttributes = element.attribute ?? [] @@ -476,28 +521,28 @@ export default class SchemaParser { booleanAttributes.add(this.attributes.get(attributeName)) continue } - const values = tagAttribute.value.map((value) => value._) + const values = tagAttribute.value.map((value) => value._.toString()) valueAttributes.set(this.attributes.get(attributeName), values) } return [booleanAttributes, valueAttributes] } - _addCustomAttributes() { + private _addCustomAttributes(): void { const isInheritedProperty = this.properties.get('isInheritedProperty') const extensionAllowedAttribute = this.attributes.get('extensionAllowed') if (this.rootElement.$.library === undefined && semver.lt(this.rootElement.$.version, '8.2.0')) { - extensionAllowedAttribute._roleProperties.add(isInheritedProperty) + extensionAllowedAttribute._properties.add(isInheritedProperty) } const inLibraryAttribute = this.attributes.get('inLibrary') if (inLibraryAttribute && semver.lt(this.rootElement.$.version, '8.3.0')) { - inLibraryAttribute._roleProperties.add(isInheritedProperty) + inLibraryAttribute._properties.add(isInheritedProperty) } } - _addCustomProperties() { + private _addCustomProperties(): void { if (this.rootElement.$.library === undefined && semver.lt(this.rootElement.$.version, '8.2.0')) { - const recursiveProperty = new SchemaProperty('isInheritedProperty', 'roleProperty') + const recursiveProperty = new SchemaProperty('isInheritedProperty') this.properties.set('isInheritedProperty', recursiveProperty) } } diff --git a/src/schema/schemaMerger.js b/src/schema/schemaMerger.ts similarity index 60% rename from src/schema/schemaMerger.js rename to src/schema/schemaMerger.ts index 2be95fc7..b10a4bab 100644 --- a/src/schema/schemaMerger.js +++ b/src/schema/schemaMerger.ts @@ -2,44 +2,46 @@ * @module schema/schemaMerger */ import { IssueError } from '../issues/issues' -import { SchemaTag, SchemaValueTag } from './entries' -import { PartneredSchema } from './containers' +import { type SchemaAttribute, SchemaEntryManager, SchemaTag, SchemaValueTag } from './entries' +import { type HedSchema, PartneredSchema } from './containers' export default class PartneredSchemaMerger { /** * The sources of data to be merged. - * @type {Schema[]} */ - sourceSchemas + sourceSchemas: HedSchema[] /** * The current source of data to be merged. - * @type {Schema} */ - currentSource + currentSource: HedSchema /** * The destination of data to be merged. - * @type {PartneredSchema} */ - destination + destination: PartneredSchema + + /** + * The tag definitions of the partnered schema + */ + destinationTagDefinitions: Map /** * Constructor. * - * @param {Schema[]} sourceSchemas The sources of data to be merged. + * @param sourceSchemas The sources of data to be merged. */ - constructor(sourceSchemas) { + constructor(sourceSchemas: HedSchema[]) { this.sourceSchemas = sourceSchemas this.destination = new PartneredSchema(sourceSchemas) + this.destinationTagDefinitions = this.destination.entries.tags.definitions this._validate() } /** * Pre-validate the partnered schemas. - * @private */ - _validate() { + private _validate(): void { for (const schema of this.sourceSchemas.slice(1)) { if (schema.withStandard !== this.destination.withStandard) { IssueError.generateAndThrow('differentWithStandard', { @@ -53,9 +55,9 @@ export default class PartneredSchemaMerger { /** * Merge the lazy partnered schemas. * - * @returns {PartneredSchema} The merged partnered schema. + * @returns The merged partnered schema. */ - mergeSchemas() { + public mergeSchemas(): PartneredSchema { for (const additionalSchema of this.sourceSchemas.slice(1)) { this.currentSource = additionalSchema this._mergeData() @@ -65,60 +67,47 @@ export default class PartneredSchemaMerger { /** * The source schema's tag collection. - * - * @return {SchemaEntryManager} */ - get sourceTags() { + get sourceTags(): SchemaEntryManager { return this.currentSource.entries.tags } - /** - * The destination schema's tag collection. - * - * @returns {SchemaEntryManager} - */ - get destinationTags() { - return this.destination.entries.tags - } - /** * Merge two lazy partnered schemas. - * @private */ - _mergeData() { + private _mergeData(): void { this._mergeTags() } /** * Merge the tags from two lazy partnered schemas. - * @private */ - _mergeTags() { + private _mergeTags(): void { for (const tag of this.sourceTags.values()) { this._mergeTag(tag) } + this.destination.entries.tags = new SchemaEntryManager(this.destinationTagDefinitions) } /** * Merge a tag from one schema to another. * - * @param {SchemaTag} tag The tag to copy. - * @private + * @param tag The tag to copy. */ - _mergeTag(tag) { - if (!tag.getAttributeValue('inLibrary')) { + private _mergeTag(tag: SchemaTag): void { + if (!tag.getAttributeValues('inLibrary')) { return } const shortName = tag.name - if (this.destinationTags.hasEntry(shortName.toLowerCase())) { + if (this.destinationTagDefinitions.has(shortName.toLowerCase())) { IssueError.generateAndThrow('lazyPartneredSchemasShareTag', { tag: shortName }) } - const rootedTagShortName = tag.getAttributeValue('rooted') + const rootedTagShortName = tag.getSingleAttributeValue('rooted') if (rootedTagShortName) { const parentTag = tag.parent - if (parentTag?.name?.toLowerCase() !== rootedTagShortName?.toLowerCase()) { + if (parentTag?.name?.toLowerCase() !== rootedTagShortName.toLowerCase()) { IssueError.generateAndThrowInternalError(`Node ${shortName} is improperly rooted.`) } } @@ -129,12 +118,11 @@ export default class PartneredSchemaMerger { /** * Copy a tag from one schema to another. * - * @param {SchemaTag} tag The tag to copy. - * @private + * @param tag The tag to copy. */ - _copyTagToSchema(tag) { - const booleanAttributes = new Set() - const valueAttributes = new Map() + private _copyTagToSchema(tag: SchemaTag): void { + const booleanAttributes = new Set() + const valueAttributes = new Map() for (const attribute of tag.booleanAttributes) { booleanAttributes.add(this.destination.entries.attributes.getEntry(attribute.name) ?? attribute) @@ -143,20 +131,20 @@ export default class PartneredSchemaMerger { valueAttributes.set(this.destination.entries.attributes.getEntry(key.name) ?? key, value) } - /** - * @type {SchemaUnitClass[]} - */ const unitClasses = tag.unitClasses.map( (unitClass) => this.destination.entries.unitClasses.getEntry(unitClass.name) ?? unitClass, ) + const valueClasses = tag.valueClasses.map( + (valueClass) => this.destination.entries.valueClasses.getEntry(valueClass.name) ?? valueClass, + ) let newTag if (tag instanceof SchemaValueTag) { - newTag = new SchemaValueTag(tag.name, booleanAttributes, valueAttributes, unitClasses) + newTag = new SchemaValueTag(tag.name, booleanAttributes, valueAttributes, unitClasses, valueClasses) } else { - newTag = new SchemaTag(tag.name, booleanAttributes, valueAttributes, unitClasses) + newTag = new SchemaTag(tag.name, booleanAttributes, valueAttributes, unitClasses, valueClasses) } - const destinationParentTag = this.destinationTags.getEntry(tag.parent?.name?.toLowerCase()) + const destinationParentTag = this.destinationTagDefinitions.get(tag.parent?.name?.toLowerCase()) if (destinationParentTag) { newTag.parent = destinationParentTag if (newTag instanceof SchemaValueTag) { @@ -164,6 +152,6 @@ export default class PartneredSchemaMerger { } } - this.destinationTags._definitions.set(newTag.name.toLowerCase(), newTag) + this.destinationTagDefinitions.set(newTag.name.toLowerCase(), newTag) } } diff --git a/src/schema/specs.js b/src/schema/specs.ts similarity index 57% rename from src/schema/specs.js rename to src/schema/specs.ts index e83e5d6c..1c1ff6b1 100644 --- a/src/schema/specs.js +++ b/src/schema/specs.ts @@ -12,37 +12,33 @@ import { IssueError } from '../issues/issues' export class SchemaSpec { /** * The prefix of this schema. - * @type {string} */ - prefix + readonly prefix: string /** * The version of this schema. - * @type {string} */ - version + readonly version: string /** * The library name of this schema. - * @type {string} */ - library + readonly library: string /** * The local path for this schema. - * @type {string} */ - localPath + readonly localPath: string /** * Constructor. * - * @param {string} prefix The prefix of this schema. - * @param {string} version The version of this schema. - * @param {string?} library The library name of this schema. - * @param {string?} localPath The local path for this schema. + * @param prefix The prefix of this schema. + * @param version The version of this schema. + * @param library The library name of this schema. + * @param localPath The local path for this schema. */ - constructor(prefix, version, library = '', localPath = '') { + constructor(prefix: string, version: string, library = '', localPath = '') { this.prefix = prefix this.version = version this.library = library @@ -51,10 +47,8 @@ export class SchemaSpec { /** * Compute the name for the bundled copy of this schema. - * - * @returns {string} */ - get localName() { + public get localName(): string { if (!this.library) { return 'HED' + this.version } else { @@ -65,12 +59,11 @@ export class SchemaSpec { /** * Parse a single schema specification string into a SchemaSpec object. * - * @param {string} versionSpec A schema version specification string (e.g., "nickname:library_version"). - * @returns {SchemaSpec} A schema specification object with parsed nickname, library, and version. + * @param versionSpec A schema version specification string (e.g., "nickname:library_version"). + * @returns A schema specification object with parsed nickname, library, and version. * @throws {IssueError} If the schema specification format is invalid. - * @public */ - static parseVersionSpec(versionSpec) { + public static parseVersionSpec(versionSpec: string): SchemaSpec { const [nickname, schema] = SchemaSpec._splitPrefixAndSchema(versionSpec) const [library, version] = SchemaSpec._splitLibraryAndVersion(schema, versionSpec) return new SchemaSpec(nickname, version, library) @@ -79,25 +72,23 @@ export class SchemaSpec { /** * Split a schema version string into prefix (nickname) and schema parts using colon delimiter. * - * @param {string} prefixSchemaSpec The schema version string to split. - * @returns {string[]} An array with [nickname, schema] where nickname may be empty string. + * @param prefixSchemaSpec The schema version string to split. + * @returns An array with [nickname, schema] where nickname may be empty string. * @throws {IssueError} If the schema specification format is invalid. - * @private */ - static _splitPrefixAndSchema(prefixSchemaSpec) { + private static _splitPrefixAndSchema(prefixSchemaSpec: string): [string, string] { return SchemaSpec._splitVersionSegments(prefixSchemaSpec, prefixSchemaSpec, ':') } /** * Split a schema string into library and version parts using underscore delimiter. * - * @param {string} libraryVersionSpec The schema string to split (library_version format). - * @param {string} originalVersion The original version string for error reporting. - * @returns {string[]} An array with [library, version] where library may be empty string. + * @param libraryVersionSpec The schema string to split (library_version format). + * @param originalVersion The original version string for error reporting. + * @returns An array with [library, version] where library may be empty string. * @throws {IssueError} If the schema specification format is invalid or version is not valid semver. - * @private */ - static _splitLibraryAndVersion(libraryVersionSpec, originalVersion) { + private static _splitLibraryAndVersion(libraryVersionSpec: string, originalVersion: string): [string, string] { const [library, version] = SchemaSpec._splitVersionSegments(libraryVersionSpec, originalVersion, '_') if (!semver.valid(version)) { IssueError.generateAndThrow('invalidSchemaSpecification', { spec: originalVersion }) @@ -108,15 +99,18 @@ export class SchemaSpec { /** * Split a version string into two segments using the specified delimiter. * - * @param {string} versionSpec The version string to split. - * @param {string} originalVersion The original version string for error reporting. - * @param {string} splitCharacter The character to use as delimiter (':' or '_'). - * @returns {string[]} An array with [firstSegment, secondSegment] where firstSegment may be empty string. + * @param versionSpec The version string to split. + * @param originalVersion The original version string for error reporting. + * @param splitCharacter The character to use as delimiter (':' or '_'). + * @returns An array with [firstSegment, secondSegment] where firstSegment may be empty string. * @throws {IssueError} If the schema specification format is invalid or contains non-alphabetic characters in first segment. - * @private */ - static _splitVersionSegments(versionSpec, originalVersion, splitCharacter) { - const alphabeticRegExp = new RegExp('^[a-zA-Z]+$') + private static _splitVersionSegments( + versionSpec: string, + originalVersion: string, + splitCharacter: string, + ): [string, string] { + const alphabeticRegExp = /^[a-zA-Z]+$/ const versionSplit = versionSpec.split(splitCharacter) const secondSegment = versionSplit.pop() const firstSegment = versionSplit.pop() @@ -136,39 +130,41 @@ export class SchemaSpec { export class SchemasSpec { /** * The specification mapping data. - * @type {Map} */ - data + readonly #data: Map /** * Constructor. */ constructor() { - this.data = new Map() + this.#data = new Map() } /** - * Iterator over the specifications. - * - * @yields {Iterator} - [string, SchemaSpec[]] + * The specification mapping data. */ - *[Symbol.iterator]() { - for (const [prefix, schemaSpecs] of this.data.entries()) { - yield [prefix, schemaSpecs] - } + public get data(): Map { + return new Map(this.#data) + } + + /** + * Iterate over the schema specifications. + */ + *[Symbol.iterator](): MapIterator<[string, SchemaSpec[]]> { + yield* this.#data.entries() } /** * Add a schema to this specification. * - * @param {SchemaSpec} schemaSpec A schema specification. - * @returns {SchemasSpec| map} This object. + * @param schemaSpec A schema specification. + * @returns This object. */ - addSchemaSpec(schemaSpec) { - if (this.data.has(schemaSpec.prefix)) { - this.data.get(schemaSpec.prefix).push(schemaSpec) + public addSchemaSpec(schemaSpec: SchemaSpec): this { + if (this.#data.has(schemaSpec.prefix)) { + this.#data.get(schemaSpec.prefix).push(schemaSpec) } else { - this.data.set(schemaSpec.prefix, [schemaSpec]) + this.#data.set(schemaSpec.prefix, [schemaSpec]) } return this } @@ -176,12 +172,11 @@ export class SchemasSpec { /** * Parse a HED version specification into a schemas specification object. * - * @param {string|string[]} versionSpecs The HED version specification, can be a single version string or array of version strings. - * @returns {SchemasSpec} A schemas specification object containing parsed schema specifications. + * @param versionSpecs The HED version specification, can be a single version string or array of version strings. + * @returns A schemas specification object containing parsed schema specifications. * @throws {IssueError} If any schema specification is invalid. - * @public */ - static parseVersionSpecs(versionSpecs) { + public static parseVersionSpecs(versionSpecs: string | string[]): SchemasSpec { const schemasSpec = new SchemasSpec() const processVersion = castArray(versionSpecs) if (processVersion.length === 0) { diff --git a/src/schema/xmlType.ts b/src/schema/xmlType.ts new file mode 100644 index 00000000..b5de6569 --- /dev/null +++ b/src/schema/xmlType.ts @@ -0,0 +1,31 @@ +export type NamedElement = { name: { _: string } } +export type AttributeValue = string | number +export type AttributeElement = NamedElement & { value?: { _: AttributeValue }[] } +export type DefinitionElement = NamedElement & { attribute?: AttributeElement[] } +export type NodeElement = DefinitionElement & { node?: NodeElement[]; $parent?: NodeElement | null } +type UnitClassElement = DefinitionElement & { unit: DefinitionElement[] } +type SchemaAttributeElement = NamedElement & { property: AttributeElement[] } + +export type HedSchemaRootElement = { + $: { version: string; library?: string; withStandard?: string } + schema: { node: NodeElement[] } + unitClassDefinitions: { + unitClassDefinition: UnitClassElement[] + } + unitModifierDefinitions: { + unitModifierDefinition: DefinitionElement[] + } + valueClassDefinitions: { + valueClassDefinition: DefinitionElement[] + } + schemaAttributeDefinitions: { + schemaAttributeDefinition: SchemaAttributeElement[] + } + propertyDefinitions: { + propertyDefinition: NamedElement[] + } +} + +export type HedSchemaXMLObject = { + HED: HedSchemaRootElement +} diff --git a/src/utils/array.js b/src/utils/array.js deleted file mode 100644 index 67aab8dd..00000000 --- a/src/utils/array.js +++ /dev/null @@ -1,19 +0,0 @@ -/** This module holds the sidecar validator class. - * @module - */ - -/** - * Apply a function recursively to an array. - * - * @template T,U - * @param {function(T): U} fn The function to apply. - * @param {T[]} array The array to map. - * @returns {U[]} The mapped array. - */ -export function recursiveMap(fn, array) { - if (Array.isArray(array)) { - return array.map((element) => recursiveMap(fn, element)) - } else { - return fn(array) - } -} diff --git a/src/utils/array.ts b/src/utils/array.ts new file mode 100644 index 00000000..ddb567b1 --- /dev/null +++ b/src/utils/array.ts @@ -0,0 +1,41 @@ +/** + * Array utility functions. + * @module + */ + +type NestedArray = T | NestedArray[] + +/** + * Apply a function recursively to an array. + * + * @param array The array to map. + * @param fn The function to apply. + * @returns The mapped array. + */ +export function recursiveMap(array: NestedArray, fn: (element: T) => U): NestedArray { + if (Array.isArray(array)) { + return array.map((element) => recursiveMap(element, fn)) + } else { + return fn(array) + } +} + +/** + * Generate an iterator over the pairwise combinations of an array. + * + * @param array The array to combine. + * @returns A generator which iterates over the list of combinations as tuples. + */ +export function* iteratePairwiseCombinations(array: T[]): Generator<[T, T]> { + const pairs = array.flatMap((first, index) => array.slice(index + 1).map((second): [T, T] => [first, second])) + yield* pairs +} + +/** + * Type guard for an ordered pair of numbers (e.g. bounds). + * + * @param value A possible ordered pair of numbers. + */ +export function isNumberPair(value: unknown): value is [number, number] { + return Array.isArray(value) && value.length === 2 && value.every((bound) => typeof bound === 'number') +} diff --git a/src/utils/files.js b/src/utils/files.ts similarity index 65% rename from src/utils/files.js rename to src/utils/files.ts index fc14e94c..6547b143 100644 --- a/src/utils/files.js +++ b/src/utils/files.ts @@ -1,4 +1,5 @@ -/** This module holds asynchronous functions for reading files. +/** + * This module holds asynchronous functions for reading files. * @module */ import { readFile as readFilePromise } from 'node:fs/promises' @@ -8,11 +9,11 @@ import { IssueError } from '../issues/issues' /** * Read a local file. * - * @param {string} fileName The file path. - * @returns {Promise} A promise with the file contents. + * @param fileName The file path. + * @returns A promise with the file contents. * @throws {IssueError} If the file read failed or if called in a browser environment. */ -export async function readFile(fileName) { +export async function readFile(fileName: string): Promise { try { const stringBuffer = await readFilePromise(fileName, 'utf8') return stringBuffer.toString() @@ -24,16 +25,16 @@ export async function readFile(fileName) { /** * Read a remote file using HTTPS. * - * @param {string} url The remote URL. - * @returns {Promise} A promise with the file contents. + * @param url The remote URL. + * @returns A promise with the file contents. * @throws {IssueError} If the network read failed. */ -export async function readHTTPSFile(url) { +export async function readHTTPSFile(url: string): Promise { const response = await fetch(url) if (!response.ok) { IssueError.generateAndThrow('networkReadError', { url: url, - statusCode: response.status, + statusCode: response.status.toString(), statusText: response.statusText, }) } diff --git a/src/utils/hedStrings.js b/src/utils/hedStrings.ts similarity index 59% rename from src/utils/hedStrings.js rename to src/utils/hedStrings.ts index f48673f3..908e07d3 100644 --- a/src/utils/hedStrings.js +++ b/src/utils/hedStrings.ts @@ -1,7 +1,12 @@ +/** + * HED string-related utility functions. + * @module + */ + /** * Get the indices of all slashes in a HED tag. */ -export const getTagSlashIndices = function (tag) { +export function getTagSlashIndices(tag: string): number[] { const indices = [] let i = -1 while ((i = tag.indexOf('/', i + 1)) >= 0) { diff --git a/src/utils/memoizer.js b/src/utils/memoizer.js deleted file mode 100644 index ba7db9d4..00000000 --- a/src/utils/memoizer.js +++ /dev/null @@ -1,44 +0,0 @@ -/** Memoizer class. **/ - -import { IssueError } from '../issues/issues' - -/** - * Superclass for property memoization until we can get away with private fields. - */ -export default class Memoizer { - /** - * Map containing memoized properties (string --> any). - * - * @type {Map} - * @private - */ - _memoizedProperties - - /** - * Constructor. - */ - constructor() { - this._memoizedProperties = new Map() - } - - /** - * Memoize a property. - * - * @template T - * @param {string} propertyName The property name. - * @param {function() : T} valueComputer A function to compute the property's value. - * @returns {T} The computed value. - * @protected - */ - _memoize(propertyName, valueComputer) { - if (!propertyName) { - IssueError.generateAndThrowInternalError('Invalid property name in Memoizer subclass.') - } - if (this._memoizedProperties.has(propertyName)) { - return this._memoizedProperties.get(propertyName) - } - const value = valueComputer() - this._memoizedProperties.set(propertyName, value) - return value - } -} diff --git a/src/utils/paths.js b/src/utils/paths.js deleted file mode 100644 index d7d5ab18..00000000 --- a/src/utils/paths.js +++ /dev/null @@ -1,421 +0,0 @@ -/** - * This module provides utility functions for working with BIDS paths. - * @module - */ - -import path from 'node:path' - -/** - * Checks if one path is a subpath of another. - * - * This function normalizes the input paths before comparison. Normalization includes: - * - Converting null or undefined to an empty string. - * - Replacing a single dot ('.') with an empty string. - * - Removing leading './'. - * - Removing trailing '/'. - * - * A path is considered a subpath of itself. - * If the normalized parent path is an empty string (e.g., from '.', './', or '/'), - * any non-empty child path is considered a subpath. - * - * @param {string|null|undefined} potentialChild The path to check if it's a subpath. - * @param {string|null|undefined} potentialParent The path to check if it's a parent. - * @returns {boolean} True if potentialChild is a subpath of potentialParent, false otherwise. - */ -export function isSubpath(potentialChild, potentialParent) { - // Normalize paths for consistent comparison - const normalizePath = (rawPath) => { - // Handle null/undefined gracefully, and ensure '.' normalizes to an empty string - // similar to how './' and '/' are effectively treated. - let p = String(rawPath == null ? '' : rawPath) - - if (p === '.') { - return '' // Explicitly normalize '.' to an empty string - } - if (p.startsWith('./')) { - p = p.substring(2) - } - if (p.endsWith('/')) { - p = p.slice(0, -1) - } - return p - } - - const normChild = normalizePath(potentialChild) - const normParent = normalizePath(potentialParent) - - if (normChild === normParent) { - // A path is considered a subpath of itself - return true - } - - // If the parent path normalizes to an empty string (e.g., from '.', './', or '/'), - // then any non-empty child path is considered a subpath. - // (The case where normChild is also empty is covered by normChild === normParent) - if (normParent === '') { - return true - } - - // Check if the child path starts with the parent path followed by a directory separator - return normChild.startsWith(normParent + '/') -} - -/** - * Organizes a list of relative file paths based on BIDS naming conventions. - * - * This function filters and categorizes file paths into a structured object. It identifies files - * based on whether they are in special directories (like 'phenotype'), are top-level files, or - * are located within subject-specific directories ('sub-xxx'). - * - * Files are categorized by matching their filename against a list of suffixes (e.g., 'events', - * 'participants') or by their presence in a special directory. Only files with '.tsv' or '.json' - * extensions are considered. - * - * @param {string[]} relativeFilePaths - A list of relative file paths to organize. - * @param {string[]} suffixes - A list of filename suffixes to categorize by (e.g., 'events'). - * @param {string[]} specialDirs - A list of special directory names (e.g., 'phenotype'). - * @returns {{candidates: string[], organizedPaths: Map>}} - * An object containing two properties: - * - `candidates`: A list of all file paths that were successfully categorized. - * - `organizedPaths`: A Map where keys are the suffixes and special directories. - * Each value is a Map with 'tsv' and 'json' properties, containing the corresponding - * file paths. Keys will be present even if no files are found for them. - */ -export function organizePaths(relativeFilePaths, suffixes, specialDirs) { - const candidates = [] - // Use helper function to initialize organizedPaths - const organizedPaths = _initializeOrganizedPaths([...suffixes, ...specialDirs]) - - for (const relativePath of relativeFilePaths) { - // Basic validation and extension check - if (typeof relativePath !== 'string' || (!relativePath.endsWith('.tsv') && !relativePath.endsWith('.json'))) { - continue - } - - const pathParts = relativePath.split('/') - const basename = pathParts[pathParts.length - 1] - const firstComponent = pathParts[0] - - const ext = relativePath.endsWith('.tsv') ? 'tsv' : 'json' - - let categorized = false - - // Rule 1: Check if the file is in a special directory. - if (specialDirs.includes(firstComponent)) { - organizedPaths.get(firstComponent).get(ext).push(relativePath) - categorized = true - } else { - // Rule 2: Check if it's a top-level file or in a subject directory. - const isToplevel = pathParts.length === 1 - const inSubDir = firstComponent.startsWith('sub-') - - if (isToplevel || inSubDir) { - // Rule 3: Either it is the suffix or the suffix starts with an underscore and matches the end of the filename. - const filenameNoExt = basename.substring(0, basename.lastIndexOf('.')) - for (const suffix of suffixes) { - let match = false - if (filenameNoExt === suffix) { - match = true - } else if (suffix.startsWith('_') && filenameNoExt.endsWith(suffix)) { - match = true - } - if (match) { - organizedPaths.get(suffix).get(ext).push(relativePath) - categorized = true - break // Stop after first suffix match. - } - } - } - } - - // If the file was categorized, add it to the candidates list. - if (categorized) { - candidates.push(relativePath) - } - } - - return { candidates, organizedPaths } -} - -/** - * Updates the entity dictionary with a new entity. - * - * @param {object} nameDict The dictionary of BIDS filename parts. - * @param {string} entity The entity string to parse and add. - * @private - */ -function _updateEntity(nameDict, entity) { - const parts = entity.split('-') - if (parts.length === 2 && parts[0] && parts[1]) { - nameDict.entities[parts[0]] = parts[1] - } else { - nameDict.bad.push(entity) - } -} - -/** - * Split a filename into BIDS-relevant components. - * - * This is a JavaScript implementation of the Python code provided by the user. - * - * @param {string} filePath Path to be parsed. - * @returns {{basename: string, suffix: string, prefix: string, ext: string, bad: string[], entities: Record}} An object containing the parts of the BIDS filename. - */ -export function parseBidsFilename(filePath) { - const nameDict = { - basename: '', - suffix: null, - prefix: null, - ext: '', - bad: [], - entities: {}, - } - - const strippedPath = filePath.trim() - const lastSlash = strippedPath.lastIndexOf('/') - const filename = lastSlash === -1 ? strippedPath : strippedPath.substring(lastSlash + 1) - - // Simplified extension parsing - const firstDot = filename.indexOf('.') - let basename = filename - if (firstDot !== -1) { - nameDict.ext = filename.substring(firstDot) - basename = filename.substring(0, firstDot) - } - nameDict.basename = basename - - if (!basename) { - return nameDict - } - - const lastUnderscore = basename.lastIndexOf('_') - - // Case: No underscore in filename - if (lastUnderscore === -1) { - const entityCount = (basename.match(/-/g) || []).length - if (entityCount > 1) { - nameDict.bad.push(basename) - } else if (entityCount === 1) { - _updateEntity(nameDict, basename) - } else { - nameDict.suffix = basename - } - return nameDict - } - - // Case: Underscore present - const rest = basename.substring(0, lastUnderscore) - let suffix = basename.substring(lastUnderscore + 1) - - if (suffix.includes('-') && (suffix.match(/-/g) || []).length === 1) { - _updateEntity(nameDict, suffix) - suffix = null // it was an entity, not a suffix - } - nameDict.suffix = suffix - - const entityPieces = rest.split('_') - if (entityPieces.length > 0 && !entityPieces[0].includes('-')) { - nameDict.prefix = entityPieces.shift() - } - - for (const entity of entityPieces) { - _updateEntity(nameDict, entity) - } - - return nameDict -} - -/** - * Get the directory part of a path. - * @param {string} path The path. - * @returns {string} The directory part of the path. - * @private - */ -export function getDir(path) { - const lastSlash = path.lastIndexOf('/') - return lastSlash === -1 ? '' : path.substring(0, lastSlash) -} - -/** - * Filter a list of JSON sidecar paths to find candidates for a given TSV file. - * @param {string[]} jsonList A list of relative paths of JSON sidecars. - * @param {string} tsvDir The directory of the TSV file. - * @param {object} tsvParsed The parsed BIDS filename of the TSV file. - * @returns {string[]} A list of candidate JSON sidecar paths. - * @private - */ -export function _getCandidates(jsonList, tsvDir, tsvParsed) { - return jsonList.filter((jsonPath) => { - const jsonDir = path.dirname(jsonPath) - - // Sidecar must be in the tsv file's directory hierarchy. - if (!isSubpath(tsvDir, jsonDir)) { - return false - } - - const jsonParsed = parseBidsFilename(jsonPath) - - // Suffix must match if json has a suffix. An events.json sidecar can apply to any events.tsv file. - if (jsonParsed.suffix && tsvParsed.suffix !== jsonParsed.suffix) { - return false - } - - // All entities in json must be in tsv and have same value - for (const [key, value] of Object.entries(jsonParsed.entities)) { - if (tsvParsed.entities[key] !== value) { - return false - } - } - - return true - }) -} - -/** - * Sort a list of candidate sidecar paths from least to most specific. - * - * The sorting is done based on path depth and number of entities. - * - * @param {string[]} candidates A list of candidate JSON sidecar paths. - * @private - */ -export function _sortCandidates(candidates) { - candidates.sort((a, b) => { - const aDir = path.dirname(a) - const bDir = path.dirname(b) - if (aDir.length !== bDir.length) { - return aDir.length - bDir.length - } - const aParsed = parseBidsFilename(a) - const bParsed = parseBidsFilename(b) - return Object.keys(aParsed.entities).length - Object.keys(bParsed.entities).length - }) -} - -/** - * Get the merged sidecar for a given TSV file. - * - * This function implements the BIDS inheritance principle for sidecar files. - * It finds all applicable sidecars for a given TSV file, sorts them by specificity, - * checks for conflicts, and then merges them. - * - * Note: This function assumes that the sidecars are already parsed and stored in a Map. - * - * Note: This function should not be called for files in directories with special association rules such as 'phenotype'. - * - * @param {string} tsvPath The path to the TSV file. - * @param {string[]} jsonList A list of relative paths of JSON sidecars. - * @param {Map} sidecarMap A map of sidecars. - * @returns {object} The merged sidecar data. - * @throws {Error} If a BIDS inheritance conflict is detected. - */ -export function getMergedSidecarData(tsvPath, jsonList, sidecarMap) { - const tsvDir = path.dirname(tsvPath) - const tsvParsed = parseBidsFilename(tsvPath) - - // 1. Filter to find applicable sidecars - const candidates = _getCandidates(jsonList, tsvDir, tsvParsed) - - // 2. Sort applicable sidecars from least to most specific. - _sortCandidates(candidates) - // 3. Check for conflicts - const groupedByDir = candidates.reduce((acc, xpath) => { - const dir = path.dirname(xpath) - if (!acc.has(dir)) { - acc.set(dir, []) - } - acc.get(dir).push(xpath) - return acc - }, new Map()) - - for (const [dir, sidecarsInDir] of groupedByDir.entries()) { - if (sidecarsInDir.length > 1) { - _testSameDir(dir, sidecarsInDir) - } - } - - // 4. Merge - let merged = {} - for (const path of candidates) { - const sidecar = sidecarMap.get(path) - if (sidecar && sidecar.jsonData) { - merged = { ...merged, ...sidecar.jsonData } - } - } - - return merged -} - -/** - * Tests that sidecar files in the same directory do not have conflicting inheritance relationships. - * - * In BIDS inheritance, sidecar files must be hierarchically related - one must be a - * subset of another in terms of entities. This function validates that no two sidecars - * in the same directory have conflicting inheritance relationships (i.e., neither is - * a subset of the other, or both are subsets of each other). - * - * @param {string} dir - The directory path being tested - * @param {string[]} sidecarsInDir - Array of sidecar filenames in the directory - * @throws {Error} Throws an error if any two sidecars are hierarchically related - * @private - */ -const _testSameDir = (dir, sidecarsInDir) => { - for (let i = 0; i < sidecarsInDir.length; i++) { - for (let j = i + 1; j < sidecarsInDir.length; j++) { - const aParsed = parseBidsFilename(sidecarsInDir[i]) - const bParsed = parseBidsFilename(sidecarsInDir[j]) - const aEntities = Object.keys(aParsed.entities) - const bEntities = Object.keys(bParsed.entities) - const aIsSubset = aEntities.every((k) => bEntities.includes(k) && aParsed.entities[k] === bParsed.entities[k]) - const bIsSubset = bEntities.every((k) => aEntities.includes(k) && bParsed.entities[k] === aParsed.entities[k]) - if (aIsSubset === bIsSubset) { - throw new Error( - `BIDS inheritance conflict in directory '${dir}': sidecars '${sidecarsInDir[i]}' and '${sidecarsInDir[j]}' are not hierarchically related.`, - ) - } - } - } -} - -/** - * A generator function that yields the paths of a given file extension from a BIDS-style organized path mapping. - * - * @param {Map>} organizedPaths A BIDS-style organized path mapping. - * @param {string} targetExtension The file extension to search for (e.g., '.json'). - * @yields {string} The paths of the given file extension. - */ -export function* organizedPathsGenerator(organizedPaths, targetExtension) { - if (!organizedPaths) { - return - } - const extKey = targetExtension.startsWith('.') ? targetExtension.slice(1) : targetExtension - for (const innerMap of organizedPaths.values()) { - const pathArray = innerMap.get(extKey) - if (Array.isArray(pathArray)) { - for (const path of pathArray) { - yield path - } - } - } -} - -/** - * Initialize the organized paths map. - * - * @param {string[]} keys The keys to initialize the map with. - * @returns {Map>} The initialized map. - * @private - */ -const _initializeOrganizedPaths = (keys) => { - const map = new Map() - for (const key of keys) { - map.set( - key, - new Map([ - ['json', []], - ['tsv', []], - ]), - ) - } - return map -} diff --git a/src/utils/paths.ts b/src/utils/paths.ts new file mode 100644 index 00000000..93d58737 --- /dev/null +++ b/src/utils/paths.ts @@ -0,0 +1,543 @@ +/** + * This module provides utility functions for working with BIDS paths. + * @module + */ + +import path from 'node:path' + +import zip from 'lodash/zip' + +import { BidsSidecar } from '../bids/types/json' +import { IssueError } from '../issues/issues' +import { iteratePairwiseCombinations } from './array' + +/** + * A parsed BIDS file name. + */ +class ParsedBidsFilename { + basename: string + suffix: string | null + prefix: string | null + ext: string + bad: string[] + entities: Record + + constructor() { + this.basename = '' + this.suffix = null + this.prefix = null + this.ext = '' + this.bad = [] + this.entities = {} + } + + /** + * Whether this file name is equivalent to another one. + * + * @param other Another parsed BIDS file name. + * @returns Whether or not the two names are equivalent. + */ + public equals(other: ParsedBidsFilename): boolean { + return this._isSubset(other) && other._isSubset(this) + } + + /** + * Whether this file name is a subset of another one. + * + * @param other Another parsed BIDS file name. + * @returns Whether or not this file name is a subset of the other one. + */ + private _isSubset(other: ParsedBidsFilename): boolean { + const ourEntities = Object.keys(this.entities) + return ourEntities.every((key) => Object.hasOwn(other.entities, key) && this.entities[key] === other.entities[key]) + } +} + +/** + * A Map where keys are the suffixes and special directories. + * Each value is a Map with 'tsv' and 'json' properties, containing the corresponding + * file paths. Keys will be present even if no files are found for them. + */ +export type OrganizedBidsPaths = Map> + +/** + * A collection of categorized BIDS file paths. + */ +class OrganizedBidsCandidates { + /** + * A list of all file paths that were successfully categorized. + */ + candidates: string[] + /** + * A Map where keys are the suffixes and special directories. + * Each value is a Map with 'tsv' and 'json' properties, containing the corresponding + * file paths. Keys will be present even if no files are found for them. + */ + organizedPaths: OrganizedBidsPaths + + /** + * Initialize the organized paths map. + * + * @param keys The keys to initialize the map with. + * @returns The initialized map. + */ + constructor(keys: string[]) { + this.candidates = [] + this.organizedPaths = new Map>() + for (const key of keys) { + this.organizedPaths.set( + key, + new Map([ + ['json', []], + ['tsv', []], + ]), + ) + } + } + + /** + * Add a candidate. + * + * @param relativePath The candidate's relative path. + * @param suffix The candidate's suffix. + * @param ext The candidate's extension. + */ + public addCandidate(relativePath: string, suffix: string, ext: string): void { + this.candidates.push(relativePath) + this.organizedPaths.get(suffix).get(ext).push(relativePath) + } +} + +class BidsPathOrganizer { + /** + * A list of relative file paths to organize. + */ + private readonly relativeFilePaths: string[] + /** + * A list of filename suffixes to categorize by (e.g., 'events'). + */ + private readonly suffixes: string[] + /** + * A list of special directory names (e.g., 'phenotype'). + */ + private readonly specialDirs: string[] + /** + * The relative file paths organized according to BIDS naming conventions. + */ + private readonly candidates: OrganizedBidsCandidates + /** + * Whether the candidates have already been organized. + * @private + */ + #alreadyOrganized: boolean + + /** + * Build the organizer. + * + * @param relativeFilePaths A list of relative file paths to organize. + * @param suffixes A list of filename suffixes to categorize by (e.g., 'events'). + * @param specialDirs A list of special directory names (e.g., 'phenotype'). + */ + constructor(relativeFilePaths: string[], suffixes: string[], specialDirs: string[]) { + this.relativeFilePaths = relativeFilePaths + this.suffixes = suffixes + this.specialDirs = specialDirs + this.candidates = new OrganizedBidsCandidates([...suffixes, ...specialDirs]) + this.#alreadyOrganized = false + } + + /** + * Organizes a list of relative file paths based on BIDS naming conventions. + * + * This function filters and categorizes file paths into a structured object. It identifies files + * based on whether they are in special directories (like 'phenotype'), are top-level files, or + * are located within subject-specific directories ('sub-xxx'). + * + * Files are categorized by matching their filename against a list of suffixes (e.g., 'events', + * 'participants') or by their presence in a special directory. Only files with '.tsv' or '.json' + * extensions are considered. + * + * @returns The relative file paths organized according to BIDS naming conventions. + */ + public organizePaths(): OrganizedBidsCandidates { + if (this.#alreadyOrganized) { + return this.candidates + } + + for (const relativePath of this.relativeFilePaths) { + // Basic validation and extension check + if (typeof relativePath !== 'string' || (!relativePath.endsWith('.tsv') && !relativePath.endsWith('.json'))) { + continue + } + this._organizePath(relativePath) + } + + this.#alreadyOrganized = true + + return this.candidates + } + + /** + * Helper function for organizing an individual path. + * + * @param relativePath A relative file path to organize. + */ + private _organizePath(relativePath: string): void { + const pathParts = relativePath.split('/') + const firstComponent = pathParts[0] + const ext = relativePath.endsWith('.tsv') ? 'tsv' : 'json' + + // Rule 1: Check if the file is in a special directory. + if (this.specialDirs.includes(firstComponent)) { + this.candidates.addCandidate(relativePath, firstComponent, ext) + return + } + + // Rule 2: Check if it's a top-level file or in a subject directory. + const isToplevel = pathParts.length === 1 + const inSubDir = firstComponent.startsWith('sub-') + if (!isToplevel && !inSubDir) { + return + } + + // Rule 3: Either it is the suffix or the suffix starts with an underscore and matches the end of the filename. + const basename = pathParts[pathParts.length - 1] + const filenameNoExt = basename.substring(0, basename.lastIndexOf('.')) + const matchingSuffix = this.suffixes.find( + (suffix) => filenameNoExt === suffix || (suffix.startsWith('_') && filenameNoExt.endsWith(suffix)), + ) + if (matchingSuffix) { + this.candidates.addCandidate(relativePath, matchingSuffix, ext) + } + } +} + +/** + * Organizes a list of relative file paths based on BIDS naming conventions. + * + * This function filters and categorizes file paths into a structured object. It identifies files + * based on whether they are in special directories (like 'phenotype'), are top-level files, or + * are located within subject-specific directories ('sub-xxx'). + * + * Files are categorized by matching their filename against a list of suffixes (e.g., 'events', + * 'participants') or by their presence in a special directory. Only files with '.tsv' or '.json' + * extensions are considered. + * + * @param relativeFilePaths A list of relative file paths to organize. + * @param suffixes A list of filename suffixes to categorize by (e.g., 'events'). + * @param specialDirs A list of special directory names (e.g., 'phenotype'). + * @returns The relative file paths organized according to BIDS naming conventions. + */ +export function organizePaths( + relativeFilePaths: string[], + suffixes: string[], + specialDirs: string[], +): OrganizedBidsCandidates { + const organizer = new BidsPathOrganizer(relativeFilePaths, suffixes, specialDirs) + return organizer.organizePaths() +} + +/** + * Checks if one path is a subpath of another. + * + * This function normalizes the input paths before comparison. Normalization includes: + * - Converting null or undefined to an empty string. + * - Replacing a single dot ('.') with an empty string. + * - Removing leading './'. + * - Removing trailing '/'. + * + * A path is considered a subpath of itself. + * If the normalized parent path is an empty string (e.g., from '.', './', or '/'), + * any non-empty child path is considered a subpath. + * + * @param potentialChild The path to check if it's a subpath. + * @param potentialParent The path to check if it's a parent. + * @returns True if potentialChild is a subpath of potentialParent, false otherwise. + */ +export function isSubpath( + potentialChild: string | null | undefined, + potentialParent: string | null | undefined, +): boolean { + // Normalize paths for consistent comparison + const normalizePath = (rawPath: string | null | undefined) => { + // Handle null/undefined gracefully, and ensure '.' normalizes to an empty string + // similar to how './' and '/' are effectively treated. + let p = rawPath ?? '' + + if (p === '.') { + return '' // Explicitly normalize '.' to an empty string + } + if (p.startsWith('./')) { + p = p.substring(2) + } + if (p.endsWith('/')) { + p = p.slice(0, -1) + } + return p + } + + const normChild = normalizePath(potentialChild) + const normParent = normalizePath(potentialParent) + + if (normChild === normParent) { + // A path is considered a subpath of itself + return true + } + + // If the parent path normalizes to an empty string (e.g., from '.', './', or '/'), + // then any non-empty child path is considered a subpath. + // (The case where normChild is also empty is covered by normChild === normParent) + if (normParent === '') { + return true + } + + // Check if the child path starts with the parent path followed by a directory separator + return normChild.startsWith(normParent + '/') +} + +/** + * Updates the entity dictionary with a new entity. + * + * @param nameDict The dictionary of BIDS filename parts. + * @param entity The entity string to parse and add. + */ +function _updateEntity(nameDict: ParsedBidsFilename, entity: string): void { + const parts = entity.split('-') + if (parts.length === 2 && parts[0] && parts[1]) { + nameDict.entities[parts[0]] = parts[1] + } else { + nameDict.bad.push(entity) + } +} + +/** + * Split a filename into BIDS-relevant components. + * + * @param filePath Path to be parsed. + * @returns An object containing the parts of the BIDS filename. + */ +export function parseBidsFilename(filePath: string): ParsedBidsFilename { + const nameDict = new ParsedBidsFilename() + + const strippedPath = filePath.trim() + const lastSlash = strippedPath.lastIndexOf('/') + const filename = lastSlash === -1 ? strippedPath : strippedPath.substring(lastSlash + 1) + + // Simplified extension parsing + const firstDot = filename.indexOf('.') + let basename = filename + if (firstDot !== -1) { + nameDict.ext = filename.substring(firstDot) + basename = filename.substring(0, firstDot) + } + nameDict.basename = basename + + if (!basename) { + return nameDict + } + + const lastUnderscore = basename.lastIndexOf('_') + + // Case: No underscore in filename + if (lastUnderscore === -1) { + const entityCount = (basename.match(/-/g) || []).length + if (entityCount > 1) { + nameDict.bad.push(basename) + } else if (entityCount === 1) { + _updateEntity(nameDict, basename) + } else { + nameDict.suffix = basename + } + return nameDict + } + + // Case: Underscore present + const rest = basename.substring(0, lastUnderscore) + let suffix = basename.substring(lastUnderscore + 1) + + if (suffix.includes('-') && (suffix.match(/-/g) || []).length === 1) { + _updateEntity(nameDict, suffix) + suffix = null // it was an entity, not a suffix + } + nameDict.suffix = suffix + + const entityPieces = rest.split('_') + if (entityPieces.length > 0 && !entityPieces[0].includes('-')) { + nameDict.prefix = entityPieces.shift() + } + + for (const entity of entityPieces) { + _updateEntity(nameDict, entity) + } + + return nameDict +} + +/** + * Get the directory part of a path. + * + * @param path The path. + * @returns The directory part of the path. + */ +export function getDir(path: string): string { + const lastSlash = path.lastIndexOf('/') + return lastSlash === -1 ? '' : path.substring(0, lastSlash) +} + +/** + * Filter a list of JSON sidecar paths to find candidates for a given TSV file. + * + * @param jsonList A list of relative paths of JSON sidecars. + * @param tsvDir The directory of the TSV file. + * @param tsvParsed The parsed BIDS filename of the TSV file. + * @returns A list of candidate JSON sidecar paths. + */ +export function _getCandidates(jsonList: string[], tsvDir: string, tsvParsed: ParsedBidsFilename): string[] { + return jsonList.filter((jsonPath) => { + const jsonDir = path.dirname(jsonPath) + + // Sidecar must be in the tsv file's directory hierarchy. + if (!isSubpath(tsvDir, jsonDir)) { + return false + } + + const jsonParsed = parseBidsFilename(jsonPath) + + // Suffix must match if json has a suffix. An events.json sidecar can apply to any events.tsv file. + if (jsonParsed.suffix && tsvParsed.suffix !== jsonParsed.suffix) { + return false + } + + // All entities in json must be in tsv and have same value + for (const [key, value] of Object.entries(jsonParsed.entities)) { + if (tsvParsed.entities[key] !== value) { + return false + } + } + + return true + }) +} + +/** + * Sort a list of candidate sidecar paths from least to most specific. + * + * The sorting is done based on path depth and number of entities. + * + * @param candidates A list of candidate JSON sidecar paths. + */ +export function _sortCandidates(candidates: string[]): void { + candidates.sort((a, b) => { + const aDir = path.dirname(a) + const bDir = path.dirname(b) + if (aDir.length !== bDir.length) { + return aDir.length - bDir.length + } + const aParsed = parseBidsFilename(a) + const bParsed = parseBidsFilename(b) + return Object.keys(aParsed.entities).length - Object.keys(bParsed.entities).length + }) +} + +/** + * Get the merged sidecar for a given TSV file. + * + * This function implements the BIDS inheritance principle for sidecar files. + * It finds all applicable sidecars for a given TSV file, sorts them by specificity, + * checks for conflicts, and then merges them. + * + * Note: This function assumes that the sidecars are already parsed and stored in a Map. + * + * Note: This function should not be called for files in directories with special association rules such as 'phenotype'. + * + * @param tsvPath The path to the TSV file. + * @param jsonList A list of relative paths of JSON sidecars. + * @param sidecarMap A map of sidecars. + * @returns The merged sidecar data. + * @throws {IssueError} If a BIDS inheritance conflict is detected. + */ +export function getMergedSidecarData( + tsvPath: string, + jsonList: string[], + sidecarMap: Map, +): Record { + const tsvDir = path.dirname(tsvPath) + const tsvParsed = parseBidsFilename(tsvPath) + + // 1. Filter to find applicable sidecars + const candidates = _getCandidates(jsonList, tsvDir, tsvParsed) + + // 2. Sort applicable sidecars from least to most specific. + _sortCandidates(candidates) + // 3. Check for conflicts + const groupedByDir = candidates.reduce((acc, xpath) => { + const dir = path.dirname(xpath) + if (!acc.has(dir)) { + acc.set(dir, []) + } + acc.get(dir).push(xpath) + return acc + }, new Map()) + + for (const [dir, sidecarsInDir] of groupedByDir.entries()) { + if (sidecarsInDir.length > 1) { + _testSameDir(dir, sidecarsInDir) + } + } + + // 4. Merge + let merged = {} + for (const path of candidates) { + const sidecar = sidecarMap.get(path) + if (sidecar?.jsonData) { + merged = { ...merged, ...sidecar.jsonData } + } + } + + return merged +} + +/** + * Tests that sidecar files in the same directory do not have conflicting inheritance relationships. + * + * In BIDS inheritance, sidecar files must be hierarchically related - one must be a + * subset of another in terms of entities. This function validates that no two sidecars + * in the same directory have conflicting inheritance relationships (i.e., neither is + * a subset of the other, or both are subsets of each other). + * + * @param dir The directory path being tested + * @param sidecarsInDir Array of sidecar filenames in the directory + * @throws {IssueError} Throws an error if any two sidecars are hierarchically related + */ +function _testSameDir(dir: string, sidecarsInDir: string[]): void { + const parsedBidsFileNames = sidecarsInDir.map((path) => parseBidsFilename(path)) + const iterator = iteratePairwiseCombinations(zip(sidecarsInDir, parsedBidsFileNames)) + for (const [[firstName, firstParsed], [secondName, secondParsed]] of iterator) { + if (firstParsed.equals(secondParsed)) { + IssueError.generateAndThrowInternalError( + `BIDS inheritance conflict in directory '${dir}': sidecars '${firstName}' and '${secondName}' are not hierarchically related.`, + ) + } + } +} + +/** + * A generator function that yields the paths of a given file extension from a BIDS-style organized path mapping. + * + * @param organizedPaths A BIDS-style organized path mapping. + * @param targetExtension The file extension to search for (e.g., '.json'). + * @returns A generator for the paths of the given file extension. + */ +export function* organizedPathsGenerator( + organizedPaths: OrganizedBidsPaths, + targetExtension: string, +): Generator { + if (!organizedPaths) { + return + } + const extKey = targetExtension.startsWith('.') ? targetExtension.slice(1) : targetExtension + for (const innerMap of organizedPaths.values()) { + const pathArray = innerMap.get(extKey) ?? [] + yield* pathArray + } +} diff --git a/src/utils/string.js b/src/utils/string.js deleted file mode 100644 index b8ba6a2b..00000000 --- a/src/utils/string.js +++ /dev/null @@ -1,49 +0,0 @@ -/** String-related utility functions - * @module utils/string - * */ - -/** - * Get number of instances of a character in a string. - * - * @param {string} string The string to search. - * @param {string} characterToCount The character to search for. - * @returns {number} The number of instances of the character in the string. - */ -export function getCharacterCount(string, characterToCount) { - return string.split(characterToCount).length - 1 -} - -/** - * Split a string on a given delimiter, trim the substrings, and remove any blank substrings from the returned array. - * - * @param {string} string The string to split. - * @param {string} delimiter The delimiter on which to split. - * @returns {string[]} The split string with blanks removed and the remaining entries trimmed. - */ -export function splitStringTrimAndRemoveBlanks(string, delimiter = ',') { - return string - .split(delimiter) - .map((item) => item.trim()) - .filter(Boolean) -} - -/** - * Parse a template literal string. - * - * Copied from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals. - * - * @param {string[]} strings The literal parts of the template string. - * @param {(number|string)} keys The keys of the closure arguments. - * @returns {function(...[*]): string} A closure to fill the string template. - */ -export function stringTemplate(strings, ...keys) { - return function (...values) { - const dict = values[values.length - 1] ?? {} - const result = [strings[0]] - keys.forEach((key, i) => { - const value = Number.isInteger(key) ? values[key] : dict[key] - result.push(value, strings[i + 1]) - }) - return result.join('') - } -} diff --git a/src/utils/string.ts b/src/utils/string.ts new file mode 100644 index 00000000..a2458d97 --- /dev/null +++ b/src/utils/string.ts @@ -0,0 +1,59 @@ +/** + * String-related utility functions + * @module utils/string + */ + +/** + * Get number of instances of a character in a string. + * + * @param string The string to search. + * @param characterToCount The character to search for. + * @returns The number of instances of the character in the string. + */ +export function getCharacterCount(string: string, characterToCount: string): number { + return string.split(characterToCount).length - 1 +} + +/** + * Split a string on a given delimiter, trim the substrings, and remove any blank substrings from the returned array. + * + * @param string The string to split. + * @param delimiter The delimiter on which to split. + * @returns The split string with blanks removed and the remaining entries trimmed. + */ +export function splitStringTrimAndRemoveBlanks(string: string, delimiter: string = ','): string[] { + return string + .split(delimiter) + .map((item) => item.trim()) + .filter(Boolean) +} + +export type IssueMessageTemplateString = ( + parameterValues: Record, + start?: number, + end?: number, +) => string + +/** + * Parse a template literal string. + * + * Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals. + * + * @param strings The literal parts of the template string. + * @param parameterKeys The keys of the closure arguments. + * @returns A closure to fill the string template. + */ +export function issueMessageTemplate( + strings: TemplateStringsArray, + ...parameterKeys: Array +): IssueMessageTemplateString { + return function (parameterValues: Record, start?: number, end?: number) { + const bounds = [start, end] + const result = [strings[0]] + parameterKeys.forEach((key, i) => { + const value = typeof key === 'number' ? bounds[key]?.toString() : parameterValues[key] + result.push(value, strings[i + 1]) + }) + return result.join('') + } +} diff --git a/src/utils/xml.js b/src/utils/xml.ts similarity index 56% rename from src/utils/xml.js rename to src/utils/xml.ts index d60cc1b3..3b132e17 100644 --- a/src/utils/xml.js +++ b/src/utils/xml.ts @@ -4,46 +4,15 @@ */ import { XMLParser } from 'fast-xml-parser' -/** - * Recursively set a field on each node of the tree pointing to the node's parent. - * - * @param {Object} node The child node. - * @param {Object} parent The parent node. - */ -const setNodeParent = function (node, parent) { - // Assume that we've already run this function if so. - if ('$parent' in node) { - return - } - node.$parent = parent - const childNodes = node.node ?? [] - for (const child of childNodes) { - setNodeParent(child, node) - } -} - -/** - * Handle top level of parent-setting recursion before passing to setNodeParent. - * - * @param {Object} node The child node. - * @param {Object} parent The parent node. - */ -export const setParent = function (node, parent) { - if (node.schema) { - node.$parent = null - setNodeParent(node.schema, null) - } else { - setNodeParent(node, parent) - } -} +import { type HedSchemaXMLObject, type HedSchemaRootElement, type NodeElement } from '../schema/xmlType' /** * Parse the schema XML data. * - * @param {string} data The XML data. - * @returns {Promise} The schema XML data. + * @param data The XML data. + * @returns The schema XML data. */ -export async function parseSchemaXML(data) { +export default function parseSchemaXML(data: string): HedSchemaXMLObject { const alwaysArray = new Set(['node', 'property', 'attribute', 'value', 'unit']) const parser = new XMLParser({ ignoreAttributes: false, @@ -57,5 +26,38 @@ export async function parseSchemaXML(data) { return alwaysArray.has(name) }, }) - return parser.parse(data) + + const xmlData = parser.parse(data) as HedSchemaXMLObject + setParent(xmlData.HED) + return xmlData +} + +/** + * Handle top level of parent-setting recursion before passing to setNodeParent. + * + * @param rootElement The root element of the XML tree. + */ +function setParent(rootElement: HedSchemaRootElement): void { + const childNodes = rootElement.schema.node ?? [] + for (const child of childNodes) { + setNodeParent(child, null) + } +} + +/** + * Recursively set a field on each node of the tree pointing to the node's parent. + * + * @param node The child node. + * @param parent The parent node. + */ +function setNodeParent(node: NodeElement, parent: NodeElement | null): void { + // Assume that we've already run this function if so. + if ('$parent' in node) { + return + } + node.$parent = parent + const childNodes = node.node ?? [] + for (const child of childNodes) { + setNodeParent(child, node) + } } diff --git a/tests/jsonTestData/bidsTests.data.js b/tests/jsonTestData/bidsTests.data.js index 130dda91..b47f85be 100644 --- a/tests/jsonTestData/bidsTests.data.js +++ b/tests/jsonTestData/bidsTests.data.js @@ -1303,7 +1303,7 @@ export const bidsTestData = [ eventsString: 'onset\tduration\tvehicle\tspeed\n' + '19\t6\ttrain\t5\n', sidecarErrors: [ BidsHedIssue.fromHedIssue( - generateIssue('missingPlaceholder', { string: 'Blue,Speed', sidecarKey: 'speed' }), + generateIssue('missingPlaceholder', { string: 'Blue,Speed', sidecarKey: 'speed', filePath: 'invalid-no-placeholder-value-column.json' }), { path: 'invalid-no-placeholder-value-column.json', }, @@ -1312,7 +1312,7 @@ export const bidsTestData = [ tsvErrors: [], comboErrors: [ BidsHedIssue.fromHedIssue( - generateIssue('missingPlaceholder', { string: 'Blue,Speed', sidecarKey: 'speed' }), + generateIssue('missingPlaceholder', { string: 'Blue,Speed', sidecarKey: 'speed', filePath: 'invalid-no-placeholder-value-column.tsv' }), { path: 'invalid-no-placeholder-value-column.tsv', }, @@ -1332,7 +1332,7 @@ export const bidsTestData = [ eventsString: 'onset\tduration\tvehicle\tspeed\n' + '19\t6\ttrain\t5\n', sidecarErrors: [ BidsHedIssue.fromHedIssue( - generateIssue('invalidSidecarPlaceholder', { sidecarKey: 'speed', string: 'Label/#, Speed/# mph' }), + generateIssue('invalidSidecarPlaceholder', { sidecarKey: 'speed', string: 'Label/#, Speed/# mph', filePath: 'invalid-multiple-placeholders-in-value-column.json' }), { path: 'invalid-multiple-placeholders-in-value-column.json', }, @@ -1341,7 +1341,7 @@ export const bidsTestData = [ tsvErrors: [], comboErrors: [ BidsHedIssue.fromHedIssue( - generateIssue('invalidSidecarPlaceholder', { sidecarKey: 'speed', string: 'Label/#, Speed/# mph' }), + generateIssue('invalidSidecarPlaceholder', { sidecarKey: 'speed', string: 'Label/#, Speed/# mph', filePath: 'invalid-multiple-placeholders-in-value-column.tsv' }), { path: 'invalid-multiple-placeholders-in-value-column.tsv', }, @@ -1524,6 +1524,7 @@ export const bidsTestData = [ contents: '', defContents: '(Acceleration/4.5 m-per-s^2,Red)', sidecarKey: 'speed', + filePath: 'invalid-def-expand-no-group.json', }), { path: 'invalid-def-expand-no-group.json', @@ -1537,6 +1538,7 @@ export const bidsTestData = [ contents: '', defContents: '(Acceleration/4.5 m-per-s^2,Red)', sidecarKey: 'speed', + filePath: 'invalid-def-expand-no-group.tsv', }), { path: 'invalid-def-expand-no-group.tsv', @@ -1557,7 +1559,7 @@ export const bidsTestData = [ eventsString: 'onset\tduration\tspeed\n' + '19\t6\t5\n', sidecarErrors: [ BidsHedIssue.fromHedIssue( - generateIssue('missingDefinitionForDef', { definition: 'missingdef', sidecarKey: 'speed' }), + generateIssue('missingDefinitionForDef', { definition: 'missingdef', sidecarKey: 'speed', filePath: 'invalid-missing-definition-for-def.json' }), { path: 'invalid-missing-definition-for-def.json', }, @@ -1566,7 +1568,7 @@ export const bidsTestData = [ tsvErrors: [], comboErrors: [ BidsHedIssue.fromHedIssue( - generateIssue('missingDefinitionForDef', { definition: 'missingdef', sidecarKey: 'speed' }), + generateIssue('missingDefinitionForDef', { definition: 'missingdef', sidecarKey: 'speed', filePath: 'invalid-missing-definition-for-def.tsv' }), { path: 'invalid-missing-definition-for-def.tsv', }, @@ -1586,7 +1588,7 @@ export const bidsTestData = [ eventsString: 'onset\tduration\tspeed\n' + '19\t6\t5\n', sidecarErrors: [ BidsHedIssue.fromHedIssue( - generateIssue('missingDefinitionForDefExpand', { definition: 'missingdefexpand', sidecarKey: 'speed' }), + generateIssue('missingDefinitionForDefExpand', { definition: 'missingdefexpand', sidecarKey: 'speed', filePath: 'invalid-missing-definition-for-def-expand.json' }), { path: 'invalid-missing-definition-for-def-expand.json', }, @@ -1595,7 +1597,7 @@ export const bidsTestData = [ tsvErrors: [], comboErrors: [ BidsHedIssue.fromHedIssue( - generateIssue('missingDefinitionForDefExpand', { definition: 'missingdefexpand', sidecarKey: 'speed' }), + generateIssue('missingDefinitionForDefExpand', { definition: 'missingdefexpand', sidecarKey: 'speed', filePath: 'invalid-missing-definition-for-def-expand.tsv' }), { path: 'invalid-missing-definition-for-def-expand.tsv', }, diff --git a/tests/jsonTests/definitionManagerCreationTests.spec.js b/tests/jsonTests/definitionManagerCreationTests.spec.js index 670687f0..9af4419e 100644 --- a/tests/jsonTests/definitionManagerCreationTests.spec.js +++ b/tests/jsonTests/definitionManagerCreationTests.spec.js @@ -1,7 +1,8 @@ +import path from 'node:path' + import chai from 'chai' const assert = chai.assert import { beforeAll, describe, afterAll } from '@jest/globals' -import path from 'node:path' import { buildSchemas } from '../../src/schema/init' import { SchemaSpec, SchemasSpec } from '../../src/schema/specs' diff --git a/tests/jsonTests/schemaBuildTests.spec.js b/tests/jsonTests/schemaBuildTests.spec.js index e888cf42..6745b16e 100644 --- a/tests/jsonTests/schemaBuildTests.spec.js +++ b/tests/jsonTests/schemaBuildTests.spec.js @@ -7,7 +7,7 @@ import { BidsJsonFile } from '../../src/bids' import { shouldRun } from '../testHelpers/testUtilities' import { schemaBuildTestData } from '../jsonTestData/schemaBuildTests.data' -import { Schemas } from '../../src/schema/containers' +import { HedSchemas } from '../../src/schema/containers' // Ability to select individual tests to run const skipMap = new Map() @@ -31,7 +31,7 @@ describe('Schema build validation', () => { assert.strictEqual(caughtErrorString, expectedErrorString, header) if (expectedErrorCode === null) { - assert.instanceOf(schema, Schemas, header + caughtErrorString) + assert.instanceOf(schema, HedSchemas, header + caughtErrorString) } } diff --git a/tests/otherTests/dataset.spec.js b/tests/otherTests/dataset.spec.js index 012371c7..21bd89d2 100644 --- a/tests/otherTests/dataset.spec.js +++ b/tests/otherTests/dataset.spec.js @@ -4,7 +4,7 @@ import fs from 'node:fs' import { toMatchIssue } from '../testHelpers/toMatchIssue' import { BidsDataset } from '../../src/bids/types/dataset' import { BidsDirectoryAccessor, BidsFileAccessor } from '../../src/bids/datasetParser' -import { Schemas } from '../../src/schema/containers' +import { HedSchemas } from '../../src/schema/containers' expect.extend({ toMatchIssue(receivedError, expectedCode, expectedParams) { @@ -28,7 +28,7 @@ describe('BidsDataset', () => { const [dataset, issues] = await BidsDataset.create(demoDataRoot, BidsDirectoryAccessor) expect(dataset).toBeInstanceOf(BidsDataset) expect(issues.length).toBe(0) - expect(dataset.hedSchemas).toBeInstanceOf(Schemas) + expect(dataset.hedSchemas).toBeInstanceOf(HedSchemas) expect(dataset.sidecarMap.size).toBe(9) }) @@ -37,8 +37,8 @@ describe('BidsDataset', () => { fs.mkdirSync(emptyDir, { recursive: true }) const [, issues] = await BidsDataset.create(emptyDir, BidsDirectoryAccessor) expect(issues.length).toBe(1) - expect(issues[0].internalCode).toBe('missingSchemaSpecification') - expect(issues[0].hedCode).toBe('SCHEMA_LOAD_FAILED') + expect(issues[0].hedIssue.internalCode).toBe('missingSchemaSpecification') + expect(issues[0].subCode).toBe('SCHEMA_LOAD_FAILED') }) }) @@ -51,7 +51,7 @@ describe('BidsDataset', () => { expect(issues).toEqual([]) expect(dataset.hedSchemas).toBeDefined() expect(dataset.hedSchemas).not.toBeNull() - expect(dataset.hedSchemas).toBeInstanceOf(Schemas) + expect(dataset.hedSchemas).toBeInstanceOf(HedSchemas) }) it('should throw "missingSchemaSpecification" if dataset_description.json is missing', async () => { @@ -106,7 +106,7 @@ describe('BidsDataset', () => { expect(issues.length).toBe(0) }) - it('should handle JSON parsing errors gracefully', async () => { + it.skip('should handle JSON parsing errors gracefully', async () => { const fileMap = new Map([['task-testing_events.json', {}]]) const accessor = new BidsFileAccessor('/fake/dir', fileMap) @@ -133,7 +133,7 @@ describe('BidsDataset', () => { describe('validate', () => { it('should return an empty array if there are no validation issues', async () => { const [dataset, issues] = await BidsDataset.create(demoDataRoot, BidsDirectoryAccessor) - expect(dataset.hedSchemas).toBeInstanceOf(Schemas) + expect(dataset.hedSchemas).toBeInstanceOf(HedSchemas) expect(issues).toEqual([]) const tissues = await dataset.validate() diff --git a/tests/otherTests/datasetParser.spec.js b/tests/otherTests/datasetParser.spec.js index fe35ddb7..fc214874 100644 --- a/tests/otherTests/datasetParser.spec.js +++ b/tests/otherTests/datasetParser.spec.js @@ -74,12 +74,6 @@ describe('BidsFileAccessor', () => { expect(accessor.organizedPaths.get('_scans').get('json')).toEqual(['sub-02/sub-02_scans.json']) expect(accessor.organizedPaths.get('participants').get('tsv')).toEqual([]) }) - - it('should throw an error when calling the abstract create method', async () => { - await expect(BidsFileAccessor.create('/my/dataset')).rejects.toThrow( - "BidsFileAccessor.create for '/my/dataset' must be implemented by a subclass.", - ) - }) }) describe('BidsDirectoryAccessor', () => { diff --git a/tests/otherTests/issueParameters.spec.js b/tests/otherTests/issueParameters.spec.js index bb89a7c5..249a8e7c 100644 --- a/tests/otherTests/issueParameters.spec.js +++ b/tests/otherTests/issueParameters.spec.js @@ -1,6 +1,6 @@ import { describe, test } from '@jest/globals' import { BidsSidecar } from '../../src/bids/types/json' -import { IssueError, generateIssue, updateIssueParameters } from '../../src/issues/issues' +import { IssueError, generateIssue, addIssueParameters } from '../../src/issues/issues' describe('Issue Parameters Tests', () => { // Common test data @@ -456,7 +456,7 @@ describe('Issue Parameters Tests', () => { parameterB: 'valueB', } - updateIssueParameters(issues, newParameters) + addIssueParameters(issues, newParameters) expect(issue1.issue.parameters).toHaveProperty('parameter1', 'value1') expect(issue1.issue.parameters).toHaveProperty('parameter2', 'value2') @@ -478,7 +478,7 @@ describe('Issue Parameters Tests', () => { parameter2: 'value2', } - updateIssueParameters(issues, newParameters) + addIssueParameters(issues, newParameters) expect(issue.issue.parameters).toHaveProperty('parameter1', 'originalValue') expect(issue.issue.parameters).toHaveProperty('parameter2', 'value2') @@ -488,7 +488,7 @@ describe('Issue Parameters Tests', () => { const issues = [] const newParameters = { parameter1: 'value1' } - expect(() => updateIssueParameters(issues, newParameters)).not.toThrow() + expect(() => addIssueParameters(issues, newParameters)).not.toThrow() }) test('should handle an empty parameters object', () => { @@ -498,7 +498,7 @@ describe('Issue Parameters Tests', () => { const newParameters = {} - updateIssueParameters(issues, newParameters) + addIssueParameters(issues, newParameters) expect(issue.issue.parameters).toEqual(originalParameters) }) diff --git a/tests/otherTests/issues.spec.js b/tests/otherTests/issues.spec.js index 1dc494e3..615bd6b6 100644 --- a/tests/otherTests/issues.spec.js +++ b/tests/otherTests/issues.spec.js @@ -1,5 +1,5 @@ -import { BidsHedIssue } from '../../src/bids/types/issues.js' -import { Issue, IssueError, generateIssue } from '../../src/issues/issues.js' +import { BidsHedIssue } from '../../src/bids/types/issues' +import { Issue, IssueError, generateIssue } from '../../src/issues/issues' describe('BidsHedIssue', () => { describe('transformToBids', () => { @@ -20,7 +20,7 @@ describe('BidsHedIssue', () => { expect(result[0].file).toBe(testFile) }) - it('should use the file from IssueError if available', () => { + it.skip('should use the file from IssueError if available', () => { const issueFile = { path: '/test/issueFile.tsv' } const issue = generateIssue('genericError') issue.file = issueFile diff --git a/tests/otherTests/schema.spec.js b/tests/otherTests/schema.spec.js index 336bc395..9584ea9c 100644 --- a/tests/otherTests/schema.spec.js +++ b/tests/otherTests/schema.spec.js @@ -2,14 +2,13 @@ import chai from 'chai' const assert = chai.assert import { beforeAll, describe, it } from '@jest/globals' -import { generateIssue } from '../../src/issues/issues' +import { generateIssue, IssueError } from '../../src/issues/issues' import { PartneredSchema } from '../../src/schema/containers' import { buildSchemas, buildSchemasFromVersion } from '../../src/schema/init' import { SchemaSpec, SchemasSpec } from '../../src/schema/specs' import { buildSchemasSpec } from '../../src/bids/schema' -import { BidsJsonFile } from '../../src/bids/index.js' -import { IssueError } from '../../src/issues/issues.js' -import { getLocalSchemaVersions } from '../../src/schema/config.js' +import { BidsJsonFile } from '../../src/bids/index' +import { getLocalSchemaVersions } from '../../src/schema/config' describe('HED schemas', () => { describe('Schema loading', () => { diff --git a/tsconfig.json b/tsconfig.json index cfcccefa..4385579a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,15 +1,18 @@ { "compilerOptions": { + "outDir": "./built", "allowJs": true, "checkJs": false, - "target": "es2020", - "module": "es2020", + "target": "esnext", + "module": "esnext", "moduleResolution": "node", "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "downlevelIteration": true, - "typeRoots": ["./node_modules/@types", "./types"] + "typeRoots": ["./node_modules/@types"], + "noImplicitAny": true, + "resolveJsonModule": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] diff --git a/typedoc.json b/typedoc.json index a50b6cee..bb655428 100644 --- a/typedoc.json +++ b/typedoc.json @@ -4,6 +4,5 @@ "out": "docs/html", "readme": "README.md", "name": "HED JavaScript Library", - "cleanOutputDir": true, - "exclude": ["**/*test.ts"] + "cleanOutputDir": true } diff --git a/types/README.md b/types/README.md deleted file mode 100644 index c4064c52..00000000 --- a/types/README.md +++ /dev/null @@ -1,5 +0,0 @@ -## Experimental types - -This directory contains a typescript-friendly specification of the external API including -specifications of the properties of objects returned by the API that are not in the public -interface. This specification is experimental and may change in future releases. diff --git a/types/index.d.ts b/types/index.d.ts deleted file mode 100644 index 6fc93e2b..00000000 --- a/types/index.d.ts +++ /dev/null @@ -1,672 +0,0 @@ -// Type definitions for hed-validator -// Project: https://github.com/hed-standard/hed-javascript -// Definitions by: GitHub Copilot - -// BIDS exports -export class BidsDataset { - /** Map of BIDS sidecar files that contain HED annotations */ - sidecarMap: Map - - /** The HED schemas used to validate this dataset */ - hedSchemas: Schemas | null - - /** Factory method to create a BidsDataset */ - static create( - rootOrFiles: string | object, - fileAccessorClass: typeof BidsFileAccessor, - ): Promise<[BidsDataset | null, BidsHedIssue[]]> - - /** Load and set the HED schemas for this dataset */ - setHedSchemas(): Promise - - /** Find and parse all JSON sidecar files in the dataset */ - setSidecars(): Promise - - /** Validate the dataset and return any issues */ - validate(): Promise -} - -export class BidsJsonFile { - /** The name of this file */ - name: string - /** The Object representing this file data */ - file: object | null - /** This file's JSON data */ - jsonData: Record - - /** Constructor for a BIDS JSON file */ - constructor(name: string, file: object | null, jsonData: Record) - - /** Validate this file against HED schemas */ - validate(schemas: Schemas | undefined): BidsHedIssue[] - - /** Whether this file has any HED data */ - get hasHedData(): boolean - - /** The validator class used to validate this file */ - get validatorClass(): any -} - -export class BidsSidecar extends BidsJsonFile { - /** The extracted keys for this sidecar */ - sidecarKeys: Map - /** The extracted HED data for this sidecar */ - hedData: Map - /** The parsed HED data for this sidecar */ - parsedHedData: Map - /** The mapping of column splice references */ - columnSpliceMapping: Map> - /** The set of column splice references */ - columnSpliceReferences: Set - /** The object that manages definitions */ - definitions: DefinitionManager - - /** Constructor for BidsSidecar */ - constructor( - name: string, - file: object | null, - sidecarData?: Record, - defManager?: DefinitionManager | null, - ) - - /** Whether this file has any HED data */ - get hasHedData(): boolean - - /** Parse this sidecar's HED strings within the sidecar structure */ - parseSidecarKeys(hedSchemas: Schemas, fullValidation?: boolean): [Issue[], Issue[]] - - /** Validate this file against HED schemas */ - validate(schemas: Schemas | undefined): BidsHedIssue[] - - /** The validator class used to validate this file */ - get validatorClass(): any -} - -export class BidsTsvFile { - /** The name of this file */ - name: string - /** The Object representing this file data */ - file: object | null - /** This file's parsed TSV data */ - parsedTsv: Map - /** HED strings in the "HED" column of the TSV data */ - hedColumnHedStrings: string[] - /** The pseudo-sidecar object representing the merged sidecar data */ - mergedSidecar: BidsSidecar - - /** Constructor for BidsTsvFile */ - constructor( - name: string, - file: object | null, - tsvData: string | Map | Record, - mergedDictionary?: Record, - defManager?: DefinitionManager | null, - ) - - /** Determine whether this file has any HED data */ - get hasHedData(): boolean - - /** Whether this TSV file is a timeline file */ - get isTimelineFile(): boolean - - /** Validate this file against HED schemas */ - validate(schemas: Schemas | undefined): BidsHedIssue[] - - /** The validator class used to validate this file */ - get validatorClass(): any -} - -export class BidsHedIssue { - /** The file object associated with this issue */ - file: object - /** The underlying HED issue object */ - hedIssue: Issue - /** The BIDS-compliant issue code */ - code: string - /** The HED-specific issue code. */ - subCode: string - /** The severity of the issue (e.g., 'error' or 'warning') */ - severity: 'error' | 'warning' - /** The human-readable issue message */ - issueMessage: string - /** The line number where the issue occurred */ - line: number | undefined - /** The path to the file where the issue occurred */ - location: string | undefined - - constructor(hedIssue: Issue, file: object | null) - - /** - * Converts one or more HED issues into BIDS-compatible issues. - */ - static fromHedIssues( - hedIssues: Error | Issue[], - file: object | null, - extraParameters?: Record, - ): BidsHedIssue[] -} - -export class BidsFileAccessor { - /** Map of relative file paths to file representations */ - fileMap: Map - - /** Organized paths */ - organizedPaths: Map> - - /** Constructor for BidsDirectoryAccessor */ - constructor(datasetRootDirectory: string, fileMap: Map) - - /** Factory method to create a BidsFileAccessor */ - static create(rootOrFiles: string | object): Promise - - /** Get file content */ - getFileContent(filePath: string): Promise -} - -export class BidsDirectoryAccessor { - /** Map of relative file paths to file representations */ - fileMap: Map - - /** Organized paths */ - organizedPaths: Map> - - /** Constructor for BidsDirectoryAccessor */ - constructor(datasetRootDirectory: string, fileMap: Map) - - /** Factory method to create a BidsDirectoryAccessor */ - static create(datasetRootDirectory: string): Promise - - /** Get file content from the filesystem */ - getFileContent(relativePath: string): Promise -} - -export function buildBidsSchemas(datasetDescription: BidsJsonFile): Promise - -// Issues exports -export class IssueError extends Error { - /** The associated HED issue */ - issue: Issue - - protected constructor(issue: Issue, ...params: any[]) - - /** Generate a new Issue and immediately throw it as an IssueError */ - static generateAndThrow(internalCode: string, parameters?: Record): never - - /** Generate and throw an internal error */ - static generateAndThrowInternalError(message?: string): never -} - -export class Issue { - /** The internal error code */ - internalCode: string - /** The HED 3 error code */ - hedCode: string - /** Issue severity level */ - level: 'error' | 'warning' - /** The detailed error message */ - message: string - /** Additional parameters */ - parameters: Record - - constructor(internalCode: string, hedCode: string, level: 'error' | 'warning', parameters?: Record) - - /** Override of Object.prototype.toString */ - toString(): string - - /** (Re-)generate the issue message */ - generateMessage(): void -} - -/** - * Generate a new issue object. - * - * @param internalCode The internal error code. - * @param parameters The error string parameters. - * @returns An object representing the issue. - */ -export function generateIssue(internalCode: string, parameters?: Record): Issue - -/** - * Update the parameters of a list of issues. - * - * @param issues The list of issues (different types can be intermixed). - * @param parameters The parameters to add. - */ -export function updateIssueParameters(issues: (IssueError | Issue)[], parameters: Record): void - -// Parser exports -export class Definition { - /** The name of the definition */ - name: string - /** The parsed HED tag representing the definition */ - defTag: ParsedHedTag - /** The parsed HED group representing the definition */ - defGroup: ParsedHedGroup - /** Placeholder information */ - placeholder: string - - /** Evaluate the definition and return contents with any issues */ - evaluateDefinition( - tag: ParsedHedTag, - hedSchema: Schemas, - placeholderAllowed: boolean, - ): [string | null, Issue[], Issue[]] - - /** Check if this definition is equivalent to another */ - equivalent(other: Definition): boolean - - /** Create a definition from a HED string */ - static createDefinition(hedString: string, hedSchemas: Schemas): [Definition | null, Issue[], Issue[]] - - /** Factory method to create a Definition from a group */ - static createDefinitionFromGroup(definitionGroup: ParsedHedGroup): [Definition | null, Issue[], Issue[]] -} - -export class DefinitionManager { - /** Map of definitions */ - definitions: Map - - constructor() - - /** Add a list of definitions to this manager */ - addDefinitions(defs: Definition[]): Issue[] - - /** Add a single definition to this manager */ - addDefinition(definition: Definition): Issue[] - - /** Check Def tags in a HED string for missing or incorrectly used definitions */ - validateDefs(hedString: ParsedHedString, hedSchemas: Schemas, placeholderAllowed: boolean): Issue[] - - /** Check Def-expand tags in a HED string for missing or incorrectly used definitions */ - validateDefExpands(hedString: ParsedHedString, hedSchemas: Schemas, placeholderAllowed: boolean): Issue[] -} - -// Parsed HED types (used by Definition) -export interface TagSpec { - /** The tag this spec represents */ - tag: string - /** The schema prefix for this tag, if any */ - library: string - /** Start position in the original string */ - start: number - /** End position in the original string */ - end: number -} - -export interface ParsedHedSubstring { - /** The original pre-parsed version of the HED tag */ - originalTag: string - /** The bounds of the HED tag in the original HED string */ - originalBounds: number[] - /** The normalized version of the object */ - normalized: string - - /** Format this substring nicely */ - format(long?: boolean): string - /** Override of Object.prototype.toString */ - toString(): string -} - -export interface ParsedHedColumnSplice extends ParsedHedSubstring { - /** The original pre-parsed version of the column splice (from ParsedHedSubstring) */ - originalTag: string - /** The normalized version of the column splice */ - normalized: string - - /** Format this column splice template nicely */ - format(long?: boolean): string - /** Override of Object.prototype.toString */ - toString(): string - /** Determine if this column splice is equivalent to another */ - equivalent(other: ParsedHedColumnSplice): boolean -} - -export class ParsedHedTag { - /** The original pre-parsed version of the HED tag (from ParsedHedSubstring) */ - originalTag: string - /** The bounds of the HED tag in the original HED string (from ParsedHedSubstring) */ - originalBounds: number[] - /** The formatted canonical version of the HED tag */ - formattedTag: string - /** The canonical form of the HED tag */ - canonicalTag: string - /** The HED schema this tag belongs to */ - schema: Schema - /** The schema's representation of this tag */ - schemaTag: SchemaTag - /** The tag value */ - _value: string - /** Split value for two-level tags */ - _splitValue?: string - /** The normalized tag */ - normalized: string - /** The remaining part of the tag after the portion actually in the schema */ - _remainder: string - /** The units if any */ - _units: string - - /** Constructor for ParsedHedTag */ - constructor(tagSpec: TagSpec, hedSchemas: Schemas, hedString: string) - - /** Format this tag nicely */ - format(long?: boolean): string - /** Override of Object.prototype.toString */ - toString(): string - - /** Check if this tag has a specific attribute */ - hasAttribute(attribute: string): boolean - - /** Check if this tag is equivalent to another */ - equivalent(other: ParsedHedTag): boolean -} - -export class ParsedHedGroup { - /** The original pre-parsed version of the HED group (from ParsedHedSubstring) */ - originalTag: string - /** The bounds of the HED group in the original HED string (from ParsedHedSubstring) */ - originalBounds: number[] - /** The normalized group string */ - normalized: string - /** The parsed HED tags, groups, or splices in the HED tag group at the top level */ - tags: (ParsedHedTag | ParsedHedGroup | ParsedHedColumnSplice)[] - /** The top-level parsed HED tags in this string */ - topTags: ParsedHedTag[] - /** The top-level parsed HED groups in this string */ - topGroups: ParsedHedGroup[] - /** All the parsed HED tags in this string */ - allTags: ParsedHedTag[] - /** Reserved HED group tags. This only covers top group tags in the group */ - reservedTags: Map - /** The top-level child subgroups containing Def-expand tags */ - defExpandChildren: ParsedHedGroup[] - /** The top-level Def tags */ - defTags: ParsedHedTag[] - /** The top-level Def-expand tags */ - defExpandTags: ParsedHedTag[] - /** The total number of top-level Def tags and top-level Def-expand groups */ - defCount: number - /** The unique top-level tag requiring a Def or Def-expand group, if any */ - requiresDefTag: ParsedHedTag[] | null - - /** Constructor for ParsedHedGroup */ - constructor(parsedHedTags: any[], hedString: string, originalBounds: number[]) - - /** Format this group nicely */ - format(long?: boolean): string - /** Override of Object.prototype.toString */ - toString(): string - /** Check if this group is equivalent to another */ - equivalent(other: ParsedHedGroup): boolean - /** Generator that yields subgroups containing a specific tag name */ - subParsedGroupIterator(tagName: string): Generator - /** Generator that yields all column splices in this group and subgroups */ - columnSpliceIterator(): Generator -} - -export class ParsedHedString { - /** The original HED string */ - hedString: string - /** The tag groups in the string (top-level) */ - tagGroups: ParsedHedGroup[] - /** All the top-level tags in the string */ - topLevelTags: ParsedHedTag[] - /** All the tags in the string at all levels */ - tags: ParsedHedTag[] - /** All the column splices in the string at all levels */ - columnSplices: ParsedHedColumnSplice[] - /** The tags in the top-level tag groups in the string, split into arrays */ - topLevelGroupTags: ParsedHedTag[] - /** The top-level definition tag groups in the string */ - definitions: ParsedHedGroup[] - /** The normalized string */ - normalized: string - - /** Constructor for ParsedHedString */ - constructor(hedString: string, parsedTags: (ParsedHedTag | ParsedHedGroup | ParsedHedColumnSplice)[]) - - /** Format this HED string nicely */ - format(long?: boolean): string - /** Override of Object.prototype.toString */ - toString(): string -} - -// Schema types -export class Schema { - /** The HED schema version */ - version: string - /** The HED library schema name */ - library: string - /** This schema's prefix in the active schema set */ - prefix: string - /** The collection of schema entries */ - entries: SchemaEntries - /** The standard HED schema version this schema is linked to */ - withStandard: string - - constructor(xmlData: object, entries: SchemaEntries) -} - -export class SchemaEntries { - /** The schema's properties */ - properties: SchemaEntryManager - /** The schema's attributes */ - attributes: SchemaEntryManager - /** The schema's value classes */ - valueClasses: SchemaEntryManager - /** The schema's unit classes */ - unitClasses: SchemaEntryManager - /** The schema's unit modifiers */ - unitModifiers: SchemaEntryManager - /** The schema's tags */ - tags: SchemaEntryManager - - constructor(schemaParser: object) -} - -export class SchemaEntryManager { - /** The definitions managed by this entry manager */ - protected _definitions: Map - - constructor(definitions: Map) - - [Symbol.iterator](): IterableIterator<[string, T]> - keys(): IterableIterator - values(): IterableIterator - hasEntry(name: string): boolean - getEntry(name: string): T - getEntriesWithBooleanAttribute(booleanAttributeName: string): Map - filter(fn: (entry: [string, T]) => boolean): Map - get length(): number -} - -export class SchemaEntry { - /** The name of this schema entry */ - name: string - - constructor(name: string) - - hasBooleanAttribute(attributeName: string): boolean -} - -export class SchemaProperty extends SchemaEntry { - /** The type of the property */ - protected _propertyType: string - - constructor(name: string, propertyType: string) - - /** Whether this property describes a schema category */ - get isCategoryProperty(): boolean - /** Whether this property describes a data type */ - get isTypeProperty(): boolean - /** Whether this property describes a role */ - get isRoleProperty(): boolean -} - -export class SchemaAttribute extends SchemaEntry { - /** The categories of elements this schema attribute applies to */ - protected _categoryProperties: Set - /** The data type of this schema attribute */ - protected _typeProperty: SchemaProperty - /** The set of role properties for this schema attribute */ - protected _roleProperties: Set - - constructor(name: string, properties: SchemaProperty[]) - - /** The categories of elements this schema attribute applies to */ - get categoryProperty(): Set | SchemaProperty | undefined - /** The data type property of this schema attribute */ - get typeProperty(): SchemaProperty - /** The set of role properties for this schema attribute */ - get roleProperties(): Set -} - -export class SchemaEntryWithAttributes extends SchemaEntry { - /** The set of boolean attributes this schema entry has */ - booleanAttributes: Set - /** The collection of value attributes this schema entry has */ - valueAttributes: Map - /** The set of boolean attribute names this schema entry has */ - booleanAttributeNames: Set - /** The collection of value attribute names this schema entry has */ - valueAttributeNames: Map - - constructor(name: string, booleanAttributes: Set, valueAttributes: Map) - - hasAttribute(attributeName: string): boolean - hasBooleanAttribute(attributeName: string): boolean - getValue(attributeName: string): any -} - -export class SchemaUnit extends SchemaEntryWithAttributes { - /** The type of this unit */ - unitType: SchemaUnitClass - /** The SI unit this unit is based on */ - siUnit: string - /** The default SI unit for this unit's type */ - defaultSiUnit: string - /** The unit symbol */ - unitSymbol: string - - constructor( - name: string, - booleanAttributes: Set, - valueAttributes: Map, - unitType: SchemaUnitClass, - ) -} - -export class SchemaUnitClass extends SchemaEntryWithAttributes { - /** The units in this class */ - units: SchemaUnit[] - - constructor( - name: string, - booleanAttributes: Set, - valueAttributes: Map, - units: SchemaUnit[], - ) -} - -export class SchemaUnitModifier extends SchemaEntryWithAttributes { - /** The SI prefix for this unit modifier */ - siPrefix: string - /** The factor this unit modifier represents */ - factor: number - - constructor( - name: string, - booleanAttributes: Set, - valueAttributes: Map, - siPrefix: string, - factor: number, - ) -} - -export class SchemaValueClass extends SchemaEntryWithAttributes {} - -export class SchemaTag extends SchemaEntryWithAttributes { - /** The parent tag of this tag */ - parent: SchemaTag - - constructor( - name: string, - booleanAttributes: Set, - valueAttributes: Map, - parent: SchemaTag, - ) - - get shortTagName(): string - get longTagName(): string - isUnitClass(): boolean - isTakesValueClass(): boolean - parentHasAttribute(attributeName: string): boolean -} - -export class Schemas { - /** The imported HED schemas */ - schemas: Map - - constructor(schemas: Schema | Map) - - /** Get schema by name */ - getSchema(name?: string): Schema - - /** The base schema, i.e. the schema with no prefix, if one is defined. */ - get baseSchema(): Schema -} - -// Schema exports -export function getLocalSchemaVersions(): string[] - -export function buildSchemasFromVersion(version: string): Promise - -// Parser exports -/** - * Parse a HED string. - * - * @param hedString A (possibly already parsed) HED string. - * @param hedSchemas The collection of HED schemas. - * @param definitionsAllowed True if definitions are allowed. - * @param placeholdersAllowed True if placeholders are allowed. - * @param fullValidation True if full validation is required. - * @returns The parsed HED string and any issues found. - */ -export function parseHedString( - hedString: string | ParsedHedString, - hedSchemas: Schemas, - definitionsAllowed: boolean, - placeholdersAllowed: boolean, - fullValidation: boolean, -): [ParsedHedString, Issue[], Issue[]] - -/** - * Parse a HED string in a standalone context. - * - * @param hedString A (possibly already parsed) HED string. - * @param hedSchemas The collection of HED schemas. - * @param defManager The definition manager to use for parsing definitions. - * @returns The parsed HED string and any issues found. - */ -export function parseStandaloneString( - hedString: string | ParsedHedString, - hedSchemas: Schemas, - defManager?: DefinitionManager | null, -): [ParsedHedString, Issue[], Issue[]] - -/** - * Parse a list of HED strings. - * - * @param hedStrings A list of HED strings. - * @param hedSchemas The collection of HED schemas. - * @param definitionsAllowed True if definitions are allowed - * @param placeholdersAllowed True if placeholders are allowed - * @param fullValidation True if full validation is required. - * @returns The parsed HED strings and any issues found. - */ -export function parseHedStrings( - hedStrings: (string | ParsedHedString)[], - hedSchemas: Schemas, - definitionsAllowed: boolean, - placeholdersAllowed: boolean, - fullValidation: boolean, -): [ParsedHedString[], Issue[], Issue[]] diff --git a/types/test.ts b/types/test.ts deleted file mode 100644 index 1c450c74..00000000 --- a/types/test.ts +++ /dev/null @@ -1,389 +0,0 @@ -import { - // BIDS - BidsDataset, - BidsJsonFile, - BidsSidecar, - BidsTsvFile, - BidsHedIssue, - BidsFileAccessor, - BidsDirectoryAccessor, - buildBidsSchemas, - - // Issues - IssueError, - Issue, - - // Parser - Definition, - DefinitionManager, - - // Parsed HED types - ParsedHedTag, - ParsedHedGroup, - ParsedHedString, - TagSpec, - ParsedHedColumnSplice, - - // Schema - Schema, - SchemaEntries, - SchemaEntryManager, - SchemaEntry, - SchemaProperty, - SchemaAttribute, - SchemaEntryWithAttributes, - SchemaUnit, - SchemaUnitClass, - SchemaUnitModifier, - SchemaValueClass, - SchemaTag, - Schemas, - - // Parser functions - parseStandaloneString, - parseHedString, - parseHedStrings, - - // Schema functions - getLocalSchemaVersions, - buildSchemasFromVersion, - generateIssue, -} from 'hed-validator' - -// This is a type-only test file. -// It is not intended to be run, but to be type-checked. -// This is why many functions are async and not awaited. - -// Mock data and objects -const fakePath = '/fake/path' -const fakeFile: object = { name: 'fake.json', path: fakePath } -const fakeHedString = 'Event' - -async function testBids(schemas: Schemas) { - // BidsDataset - const [dataset, bidsIssues] = await BidsDataset.create(fakePath, BidsDirectoryAccessor) - if (dataset) { - dataset.setHedSchemas() - dataset.setSidecars() - dataset.validate() - const sidecar: BidsSidecar | undefined = dataset.sidecarMap.get('sidecar.json') - if (sidecar) { - console.log(sidecar.hasHedData) - } - } - console.log(bidsIssues) - - // BidsJsonFile - const bidsJsonFile = new BidsJsonFile('test.json', fakeFile, {}) - console.log(bidsJsonFile.name, bidsJsonFile.file, bidsJsonFile.jsonData, bidsJsonFile.hasHedData) - bidsJsonFile.validate(schemas) - - // BidsSidecar - const defManager = new DefinitionManager() - const bidsSidecar = new BidsSidecar('sidecar.json', fakeFile, {}, defManager) - console.log( - bidsSidecar.sidecarKeys, - bidsSidecar.hedData, - bidsSidecar.parsedHedData, - bidsSidecar.columnSpliceMapping, - bidsSidecar.columnSpliceReferences, - bidsSidecar.definitions, - bidsSidecar.hasHedData, - ) - bidsSidecar.parseSidecarKeys(schemas, true) - bidsSidecar.validate(schemas) - - // BidsTsvFile - const bidsTsvFile = new BidsTsvFile('events.tsv', fakeFile, 'data', {}, defManager) - console.log( - bidsTsvFile.name, - bidsTsvFile.file, - bidsTsvFile.parsedTsv, - bidsTsvFile.hedColumnHedStrings, - bidsTsvFile.mergedSidecar, - bidsTsvFile.hasHedData, - bidsTsvFile.isTimelineFile, - ) - bidsTsvFile.validate(schemas) - - // BidsHedIssue - const issue = generateIssue('genericError', { hedCode: 'code' }) - const bidsHedIssue = new BidsHedIssue(issue, fakeFile) - console.log( - bidsHedIssue.file, - bidsHedIssue.hedIssue, - bidsHedIssue.code, - bidsHedIssue.subCode, - bidsHedIssue.severity, - bidsHedIssue.issueMessage, - bidsHedIssue.line, - bidsHedIssue.location, - ) - - // BidsFileAccessor - const fileAccessor = await BidsFileAccessor.create(fakePath) - console.log(fileAccessor.fileMap, fileAccessor.organizedPaths) - fileAccessor.getFileContent('file.txt') - - // BidsDirectoryAccessor - const directoryAccessor = await BidsDirectoryAccessor.create(fakePath) - console.log(directoryAccessor.fileMap, directoryAccessor.organizedPaths) - directoryAccessor.getFileContent('file.txt') - - // buildBidsSchemas - await buildBidsSchemas(bidsJsonFile) -} - -function testIssues() { - // Issue - const issue = generateIssue('genericError', { a: 1 }) - console.log(issue.internalCode, issue.hedCode, issue.level, issue.message, issue.parameters) - issue.generateMessage() - console.log(issue.toString()) - - // IssueError - try { - IssueError.generateAndThrow('internalCode', { param: [1, 2] }) - } catch (e: unknown) { - if (e instanceof IssueError) { - console.log((e as IssueError).issue) - } - } - try { - IssueError.generateAndThrowInternalError('internal error message') - } catch (e: unknown) { - if (e instanceof IssueError) { - console.log((e as IssueError).issue) - } - } -} - -function testParser( - schemas: Schemas, - parsedString: ParsedHedString, - parsedTag: ParsedHedTag, - parsedGroup: ParsedHedGroup, -) { - // Definition - const [definition, defIssues, defIssues2] = Definition.createDefinition(fakeHedString, schemas) - if (definition) { - console.log(definition.name, definition.defTag, definition.defGroup, definition.placeholder) - definition.evaluateDefinition(parsedTag, schemas, true) - const [definitionFromGroup, defGroupIssues, defGroupIssues2] = Definition.createDefinitionFromGroup(parsedGroup) - if (definitionFromGroup) { - definition.equivalent(definitionFromGroup) - } - console.log(defGroupIssues, defGroupIssues2) - } - console.log(defIssues, defIssues2) - - // DefinitionManager - const defManager = new DefinitionManager() - if (definition) { - defManager.addDefinition(definition) - defManager.addDefinitions([definition]) - } - defManager.validateDefs(parsedString, schemas, true) - defManager.validateDefExpands(parsedString, schemas, true) - - // ParsedHedTag - const tagSpec: TagSpec = { tag: 'tag', library: 'lib', start: 0, end: 3 } - const parsedHedTag = new ParsedHedTag(tagSpec, schemas, fakeHedString) - const schema: Schema = parsedHedTag.schema - const schemaTag: SchemaTag = parsedHedTag.schemaTag - console.log( - parsedHedTag.originalTag, - parsedHedTag.originalBounds, - parsedHedTag.formattedTag, - parsedHedTag.canonicalTag, - schema, - schemaTag, - ) - parsedHedTag.format(true) - parsedHedTag.toString() - parsedHedTag.hasAttribute('attribute') - parsedHedTag.equivalent(parsedTag) - - // ParsedHedGroup - const parsedHedGroup = new ParsedHedGroup([parsedTag, parsedGroup], fakeHedString, [0, 1]) - const tags: (ParsedHedTag | ParsedHedGroup | ParsedHedColumnSplice)[] = parsedHedGroup.tags - console.log( - parsedHedGroup.originalTag, - parsedHedGroup.originalBounds, - tags, - parsedHedGroup.topTags, - parsedHedGroup.topGroups, - parsedHedGroup.allTags, - parsedHedGroup.defExpandChildren, - parsedHedGroup.defTags, - parsedHedGroup.defExpandTags, - parsedHedGroup.defCount, - parsedHedGroup.requiresDefTag, - ) - parsedHedGroup.format(true) - parsedHedGroup.toString() - parsedHedGroup.equivalent(parsedGroup) - const subGroupIterator = parsedHedGroup.subParsedGroupIterator('tag') - console.log(subGroupIterator.next().value) - const spliceIterator = parsedHedGroup.columnSpliceIterator() - const splice: ParsedHedColumnSplice = spliceIterator.next().value - console.log(splice) - - // ParsedHedString - const parsedHedString = new ParsedHedString(fakeHedString, [parsedTag, parsedGroup, splice]) - console.log( - parsedHedString.hedString, - parsedHedString.tagGroups, - parsedHedString.topLevelTags, - parsedHedString.tags, - parsedHedString.columnSplices, - parsedHedString.topLevelGroupTags, - parsedHedString.definitions, - parsedHedString.normalized, - ) - parsedHedString.format(true) - parsedHedString.toString() -} - -function testSchemaTypes(schemas: Schemas) { - // Schema - const schema: Schema = schemas.getSchema('') - if (!schema) { - return - } - console.log(schema.version, schema.library, schema.prefix, schema.entries, schema.withStandard) - - // SchemaEntries - const entries: SchemaEntries = schema.entries - console.log( - entries.properties, - entries.attributes, - entries.valueClasses, - entries.unitClasses, - entries.unitModifiers, - entries.tags, - ) - - // SchemaEntryManager - const manager: SchemaEntryManager = entries.tags - for (const [key, value] of manager) { - console.log(key, value) - } - console.log(manager.keys().next().value) - console.log(manager.values().next().value) - console.log(manager.hasEntry('entry')) - console.log(manager.getEntry('entry')) - console.log(manager.getEntriesWithBooleanAttribute('attribute')) - console.log(manager.filter(([, v]) => v.hasBooleanAttribute('attribute'))) - console.log(manager.length) - - // SchemaEntry - const entry: SchemaEntry = manager.getEntry('entry') - if (entry) { - console.log(entry.name) - entry.hasBooleanAttribute('attribute') - } - - // SchemaProperty - const property: SchemaProperty = entries.properties.values().next().value - if (property) { - console.log(property.isCategoryProperty, property.isTypeProperty, property.isRoleProperty) - } - - // SchemaAttribute - const attribute: SchemaAttribute = entries.attributes.values().next().value - if (attribute) { - console.log(attribute.categoryProperty, attribute.typeProperty, attribute.roleProperties) - } - - // SchemaEntryWithAttributes - const entryWithAttrs: SchemaEntryWithAttributes = entries.tags.values().next().value - if (entryWithAttrs) { - console.log( - entryWithAttrs.booleanAttributes, - entryWithAttrs.valueAttributes, - entryWithAttrs.booleanAttributeNames, - entryWithAttrs.valueAttributeNames, - ) - entryWithAttrs.hasAttribute('attribute') - entryWithAttrs.hasBooleanAttribute('attribute') - entryWithAttrs.getValue('attribute') - } - - // SchemaUnit - const unitClass: SchemaUnitClass = entries.unitClasses.values().next().value - if (unitClass) { - const unit: SchemaUnit = unitClass.units[0] - if (unit) { - console.log(unit.unitType, unit.siUnit, unit.defaultSiUnit, unit.unitSymbol) - } - } - - // SchemaUnitClass - if (unitClass) { - console.log(unitClass.units) - } - - // SchemaUnitModifier - const unitModifier: SchemaUnitModifier = entries.unitModifiers.values().next().value - if (unitModifier) { - console.log(unitModifier.siPrefix, unitModifier.factor) - } - - // SchemaValueClass - const valueClass: SchemaValueClass = entries.valueClasses.values().next().value - console.log(valueClass.name) - - // SchemaTag - const tag: SchemaTag = entries.tags.values().next().value - if (tag) { - console.log(tag.parent, tag.shortTagName, tag.longTagName) - tag.isUnitClass() - tag.isTakesValueClass() - tag.parentHasAttribute('attribute') - } -} - -async function testSchemaFunctions() { - // getLocalSchemaVersions - const versions = getLocalSchemaVersions() - console.log(versions) - - // buildSchemasFromVersion - if (versions.length > 0) { - const schemas = await buildSchemasFromVersion(versions[0]) - if (schemas) { - // Schemas - console.log(schemas.schemas, schemas.baseSchema) - schemas.getSchema('') - - // parseStandaloneString - const [parsedString, issues, issues5] = parseStandaloneString(fakeHedString, schemas) - console.log(parsedString, issues, issues5) - - // parseHedString - const [parsedHedString, issues2, issues3] = parseHedString(fakeHedString, schemas, true, true, true) - console.log(parsedHedString, issues2, issues3) - - // parseHedStrings - const [parsedHedStrings, issues4, issues6] = parseHedStrings([fakeHedString], schemas, true, true, true) - console.log(parsedHedStrings, issues4, issues6) - - // Run other tests that require schemas - await testBids(schemas) - if (parsedHedString && parsedHedString.tags.length > 0 && parsedHedString.tagGroups.length > 0) { - const parsedTag = parsedHedString.tags[0] as ParsedHedTag - const parsedGroup = parsedHedString.tagGroups[0] - testParser(schemas, parsedHedString, parsedTag, parsedGroup) - } - testSchemaTypes(schemas) - } - } -} - -async function main() { - testIssues() - await testSchemaFunctions() -} - -main().catch(console.error) diff --git a/types/tsconfig.json b/types/tsconfig.json deleted file mode 100644 index 7cafc647..00000000 --- a/types/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "baseUrl": "..", - "paths": { - "hed-validator": ["./types/index.d.ts"] - }, - "downlevelIteration": true - }, - "include": ["./test.ts"] -} diff --git a/xml-transformer.js b/xml-transformer.cjs similarity index 100% rename from xml-transformer.js rename to xml-transformer.cjs