From d33453e7c1286d60bae6cb909485620f05befd17 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Thu, 20 Aug 2026 19:08:54 +0300 Subject: [PATCH 01/14] refactor(SOF-8032): convert ide to TypeScript, make ComputedEntityMixin generic [release] ide was the last package in the stack still built with babel from plain JS, with its public types supplied by a hand-written, never-typechecked compute.d.ts. ComputedEntityMixin declared `readonly compute: unknown`, which collides with every consumer's generated schema mixin also declaring a real `compute` (TS2320 requires identical types across multiple extends) - forcing wode's Workflow.ts and jode's Job.ts to redeclare `compute` with a `// TODO: fix ComputedEntityMixin and remove this`, and wode's Subworkflow.ts to `Omit` with no explanation. - Convert src/js/*.js -> *.ts, delete the hand-written compute.d.ts, add tsconfig.json/tsconfig-transpile.json (matching code/made/wode/jode), transpile switches from babel to `tsc -p tsconfig-transpile.json`. - ComputedEntityMixin is now generic over the compute payload (defaults to esse's ComputeArgumentsSchema | undefined). Dependents (clusterFqdn, clusterJid, timeLimit, computeQueue, errors) derive from C via indexed access instead of being hardcoded `unknown`. `compute`'s own optionality toggles on whether `undefined extends C` (`ComputeField`): confirmed via wode's Workflow.ts/Subworkflow.ts that TS2320 treats an optional property (`compute?: X`, what esse's WorkflowSchema/SubworkflowMixinSchema actually declare) and a required property typed `X | undefined` (what a naive `compute: C` gives once C includes undefined) as *not* identical, so a fixed modifier can never match both those schemas and esse's JobSchema (compute required, no undefined) at once - only the conditional does. - Fixed a latent runtime bug found while auditing: computedEntityMixin's `compute` was getter-only. Applied after a generated schema mixin (as in jode's Job.ts and wode's Subworkflow.ts), its getter-only descriptor silently overwrote the schema mixin's get+set pair, so `job.compute = x` threw `TypeError: Cannot set property compute which has only a getter` even though the merged interface claimed compute was writable. `compute` now has a real setter, verified with a descriptor probe before and after plus a new regression test. - bucket/filesRootDir guard against absent compute.cluster.fqdn (real per the schema - both stay non-throwing, returning ""). - getApproximateCharge now throws a clear error when compute.timeLimit is missing instead of silently producing a 0 charge (would otherwise let a job through business_object.ts's balance check for free). - Dropped @babel/* (7 packages) from dependencies - only moment/pluralize are real runtime deps; added @mat3ra/tsconfig, typescript, ts-node, @types/{node,mocha,pluralize}. Deleted .babelrc/.mocharc.json (ts-node registers directly in the test script now, matching jode). - Collapsed enums.ts's RMSNotificationsHandler class into a single getNotificationValue() function: it was never exported, had exactly one call site (always "PBS"), and the "adjust to make modular ... SLURM etc." TODO it existed for was never implemented (no SLURM config object anywhere) - a premature abstraction for a fixed 5-string computation. EMAIL_NOTIFICATIONS' runtime output is unchanged. Not run through eslint - same situation as jode: @exabyte-io/eslint-config uses @babel/eslint-parser, which cannot parse the converted .ts files (`npm run lint`/`lint:fix` now error standalone), and ide's own CI already passes `skip-eslint: true` for this reason. The pre-commit hook itself is unaffected: it runs lint-staged, whose src/js/**/*.js and tests/js/**/*.js globs now simply match zero files and skip gracefully (verified). Wiring up @typescript-eslint against the pinned eslint-config-airbnb chain is a separate, larger change (same conclusion reached for jode). Verified: `tsc --noEmit` clean, `npm test` 8/8 passing (incl. 2 new regression tests for the setter fix and the timeLimit guard), `npm run transpile` emits dist/js/*.d.ts including index.d.ts, and a runtime smoke test against the compiled dist/js output (not just ts-node). --- .babelrc | 17 - .mocharc.json | 6 - .nycrc | 17 +- package-lock.json | 463 +++++++++++++++--- package.json | 25 +- src/js/compute.d.ts | 38 -- src/js/compute.js | 166 ------- src/js/compute.ts | 261 ++++++++++ src/js/default.js | 20 - src/js/default.ts | 28 ++ src/js/enums.js | 67 --- src/js/enums.ts | 37 ++ src/js/{index.js => index.ts} | 2 + src/js/nodes/{enums.js => enums.ts} | 15 +- src/js/utils/{time.js => time.ts} | 34 +- tests/js/compute.test.js | 35 -- tests/js/compute.test.ts | 54 ++ tests/js/setup.js | 2 - .../time.tests.js => tests/js/time.test.ts | 2 +- tsconfig-transpile.json | 4 + tsconfig.json | 4 + 21 files changed, 835 insertions(+), 462 deletions(-) delete mode 100644 .babelrc delete mode 100644 .mocharc.json delete mode 100644 src/js/compute.d.ts delete mode 100644 src/js/compute.js create mode 100644 src/js/compute.ts delete mode 100644 src/js/default.js create mode 100644 src/js/default.ts delete mode 100644 src/js/enums.js create mode 100644 src/js/enums.ts rename src/js/{index.js => index.ts} (79%) rename src/js/nodes/{enums.js => enums.ts} (88%) rename src/js/utils/{time.js => time.ts} (63%) delete mode 100644 tests/js/compute.test.js create mode 100644 tests/js/compute.test.ts delete mode 100644 tests/js/setup.js rename src/js/utils/time.tests.js => tests/js/time.test.ts (95%) create mode 100644 tsconfig-transpile.json create mode 100644 tsconfig.json diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 4fce77d..0000000 --- a/.babelrc +++ /dev/null @@ -1,17 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "current" - } - } - ], - "@babel/preset-react", - ], - "plugins": [ - "@babel/plugin-proposal-class-properties" - ] -} - diff --git a/.mocharc.json b/.mocharc.json deleted file mode 100644 index 7c8d67a..0000000 --- a/.mocharc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extension": ["js"], - "spec": "tests/js/**/*.test.js", - "require": ["@babel/register/lib", "tests/js/setup.js"] -} - diff --git a/.nycrc b/.nycrc index 6d6542d..59490a1 100644 --- a/.nycrc +++ b/.nycrc @@ -1,8 +1,19 @@ { "all": true, "include": [ - "src/**/*.js", - "src/**/*.jsx" + "src/**/*.ts" + ], + "exclude": [ + "tests/**/*", + "**/*.test.ts", + "**/*.d.ts" + ], + "reporter": [ + "text" + ], + "sourceMap": true, + "instrument": true, + "require": [ + "ts-node/register" ] } - diff --git a/package-lock.json b/package-lock.json index b299557..780d1fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,6 @@ "version": "0.0.0", "license": "Apache-2.0", "dependencies": { - "@babel/cli": "7.16.0", - "@babel/core": "7.16.0", - "@babel/eslint-parser": "7.16.3", - "@babel/plugin-proposal-class-properties": "7.16.0", - "@babel/preset-env": "7.16.4", - "@babel/preset-react": "7.16.7", - "@babel/register": "^7.16.0", - "@babel/runtime-corejs3": "7.16.8", - "chai": "^4.3.4", "moment": "^2.17.1", "pluralize": "^3.0.0" }, @@ -25,6 +16,10 @@ "@exabyte-io/eslint-config": "^2022.11.17-0", "@mat3ra/code": "2026.8.18-0", "@mat3ra/esse": "2026.8.18-2", + "@mat3ra/tsconfig": "^2024.6.3-0", + "@types/mocha": "^10.0.10", + "@types/node": "^20.19.43", + "@types/pluralize": "^0.0.33", "chai": "^4.3.4", "eslint": "7.32.0", "eslint-config-airbnb": "19.0.2", @@ -41,7 +36,9 @@ "mixwith": "^0.1.1", "mocha": "^9.1.3", "nyc": "^15.1.0", - "prettier": "^2.7.1" + "prettier": "^2.7.1", + "ts-node": "^10.9.2", + "typescript": "^5.6.6" }, "engines": { "node": ">=14.0.0" @@ -117,6 +114,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.16.0.tgz", "integrity": "sha512-WLrM42vKX/4atIoQB+eb0ovUof53UUvecb4qGjU2PDDWRiZr50ZpiV8NpcLo7iSxeGYrRG0Mqembsa+UrTAV6Q==", + "dev": true, "dependencies": { "commander": "^4.0.1", "convert-source-map": "^1.1.0", @@ -145,6 +143,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", @@ -159,6 +158,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -168,6 +168,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.16.0", "@babel/generator": "^7.16.0", @@ -197,6 +198,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -205,6 +207,7 @@ "version": "7.16.3", "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.16.3.tgz", "integrity": "sha512-iB4ElZT0jAt7PKVaeVulOECdGe6UnmA/O0P9jlF5g5GBOwDVbna8AXhHRu4s27xQf6OkveyA8iTDv1jHdDejgQ==", + "dev": true, "dependencies": { "eslint-scope": "^5.1.1", "eslint-visitor-keys": "^2.1.0", @@ -222,6 +225,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -230,6 +234,7 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.8", @@ -246,6 +251,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" @@ -258,6 +264,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.29.7", @@ -274,6 +281,7 @@ "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" @@ -283,6 +291,7 @@ "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": { "semver": "bin/semver.js" @@ -292,12 +301,14 @@ "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/@babel/helper-create-class-features-plugin": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", @@ -319,6 +330,7 @@ "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": { "semver": "bin/semver.js" @@ -328,6 +340,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", @@ -345,6 +358,7 @@ "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": { "semver": "bin/semver.js" @@ -354,6 +368,7 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.17.7", "@babel/helper-plugin-utils": "^7.16.7", @@ -370,6 +385,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -378,6 +394,7 @@ "version": "7.18.9", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -386,6 +403,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -395,6 +413,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.29.7", @@ -408,6 +427,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.29.7", @@ -421,6 +441,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.29.7", @@ -438,6 +459,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" @@ -450,6 +472,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -459,6 +482,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", @@ -476,6 +500,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.29.7", @@ -493,6 +518,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.29.7", @@ -506,6 +532,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -515,6 +542,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -524,6 +552,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -533,6 +562,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.29.7", @@ -547,6 +577,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.29.7", @@ -560,6 +591,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -573,6 +605,7 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.8" @@ -621,6 +654,7 @@ "version": "7.29.7", "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.29.7.tgz", "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -653,6 +687,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", @@ -688,6 +723,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz", "integrity": "sha512-WAz4R9bvozx4qwf74M+sfqPMKfSqwM0phxPTR6iJIi8robgzXwkEgmeJG1gEKhm6sDqT/U9aV3lfcqybIpev8w==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", + "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6", @@ -706,6 +742,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.0.tgz", "integrity": "sha512-mCF3HcuZSY9Fcx56Lbn+CGdT44ioBMMvjNVldpKtj8tpniETdLjnxdHI1+sDWXIM1nNt+EanJOZ3IG9lzVjs7A==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.16.0", "@babel/helper-plugin-utils": "^7.14.5" @@ -722,6 +759,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-static-block instead.", + "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6", @@ -739,6 +777,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead.", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-dynamic-import": "^7.8.3" @@ -755,6 +794,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" @@ -771,6 +811,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead.", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-json-strings": "^7.8.3" @@ -787,6 +828,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" @@ -803,6 +845,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -819,6 +862,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -835,6 +879,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", + "dev": true, "dependencies": { "@babel/compat-data": "^7.18.8", "@babel/helper-compilation-targets": "^7.18.9", @@ -854,6 +899,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" @@ -870,6 +916,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", @@ -887,6 +934,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", + "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -903,6 +951,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-create-class-features-plugin": "^7.18.6", @@ -921,6 +970,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.", + "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -936,6 +986,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -947,6 +998,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -958,6 +1010,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -972,6 +1025,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -983,6 +1037,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" }, @@ -1026,6 +1081,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1037,6 +1093,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1052,6 +1109,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -1063,6 +1121,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1074,6 +1133,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -1085,6 +1145,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1096,6 +1157,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1107,6 +1169,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1118,6 +1181,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1132,6 +1196,7 @@ "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, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1179,6 +1244,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1212,6 +1278,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.29.7", @@ -1229,6 +1296,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1244,6 +1312,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1293,6 +1362,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", @@ -1313,6 +1383,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", @@ -1329,6 +1400,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", @@ -1345,6 +1417,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", @@ -1361,6 +1434,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1426,6 +1500,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1457,6 +1532,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", @@ -1473,6 +1549,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.29.7", @@ -1506,6 +1583,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1537,6 +1615,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1552,6 +1631,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.29.7", @@ -1568,6 +1648,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.29.7", @@ -1584,6 +1665,7 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.8.tgz", "integrity": "sha512-6iSnEK0zlkLKU4heofK/AdmRD4e2SHVpJMtrwnTCzhnaM98ria4rTrOXBBi45BTTYnJtO8txnPsX4fChYXkmeA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.29.7", @@ -1602,6 +1684,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.29.7", @@ -1618,6 +1701,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", @@ -1634,6 +1718,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1701,6 +1786,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", @@ -1733,6 +1819,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", @@ -1749,6 +1836,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1799,6 +1887,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1814,6 +1903,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.29.7.tgz", "integrity": "sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1829,6 +1919,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.29.7.tgz", "integrity": "sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", @@ -1848,6 +1939,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.29.7.tgz", "integrity": "sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/plugin-transform-react-jsx": "^7.29.7" @@ -1863,6 +1955,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.29.7.tgz", "integrity": "sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", @@ -1879,6 +1972,7 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.8.tgz", "integrity": "sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1911,6 +2005,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1926,6 +2021,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1941,6 +2037,7 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.8.tgz", "integrity": "sha512-4S9ksMGVWUshvgK0mKfvZky7leuG5/uoFVwMpAomJ8bMoDJiNHRVmc1EglwW/CmGVSqqWpEbXm9FmbRit22qoA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", @@ -1957,6 +2054,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1972,6 +2070,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1987,6 +2086,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -2022,6 +2122,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -2054,6 +2155,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", @@ -2087,6 +2189,7 @@ "version": "7.16.4", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.4.tgz", "integrity": "sha512-v0QtNd81v/xKj4gNKeuAerQ/azeNn/G1B1qMLeXOcV8+4TWlD2j3NV1u8q29SDFBXx/NBq5kyEAO+0mpRgacjA==", + "dev": true, "dependencies": { "@babel/compat-data": "^7.16.4", "@babel/helper-compilation-targets": "^7.16.3", @@ -2174,6 +2277,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -2182,6 +2286,7 @@ "version": "0.1.5", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", @@ -2197,6 +2302,7 @@ "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz", "integrity": "sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.16.7", "@babel/helper-validator-option": "^7.16.7", @@ -2236,6 +2342,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.29.7.tgz", "integrity": "sha512-AMGJoWuES861riy6pcB0fphE1YXybtQnBYQMuIyPv6mKLiosfa79BKTnAOyx215c/3RJPJpdQwoHZ3earVH7AA==", + "dev": true, "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", @@ -2265,6 +2372,7 @@ "version": "7.16.8", "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.16.8.tgz", "integrity": "sha512-3fKhuICS1lMz0plI5ktOE/yEtBRMVxplzRkdn6mJQ197XiY0JnrzYV0+Mxozq3JZ8SBV9Ecurmw1XsGbwOf+Sg==", + "dev": true, "dependencies": { "core-js-pure": "^3.20.2", "regenerator-runtime": "^0.13.4" @@ -2277,6 +2385,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -2291,6 +2400,7 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -2309,6 +2419,7 @@ "version": "7.29.8", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", @@ -2378,6 +2489,7 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.1.1", @@ -2397,6 +2509,7 @@ "version": "13.17.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -2441,6 +2554,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", "deprecated": "Use @eslint/config-array instead", + "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.0", "debug": "^4.1.1", @@ -2454,7 +2568,8 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "deprecated": "Use @eslint/object-schema instead" + "deprecated": "Use @eslint/object-schema instead", + "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -2549,6 +2664,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -2570,6 +2686,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -2579,12 +2696,14 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -3144,6 +3263,16 @@ "semver": "bin/semver.js" } }, + "node_modules/@mat3ra/tsconfig": { + "version": "2024.6.3-0", + "resolved": "https://registry.npmjs.org/@mat3ra/tsconfig/-/tsconfig-2024.6.3-0.tgz", + "integrity": "sha512-dA3gKDSl9+vlZvot1DN+UXfZPgxPSyHkZrkfVCTOWvzwm/3NdJ4cTzrTxjBIWUrZukpVVtH5ao8FbXLB6RDQzQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@mat3ra/utils": { "version": "2026.6.11-0", "resolved": "https://registry.npmjs.org/@mat3ra/utils/-/utils-2026.6.11-0.tgz", @@ -3328,10 +3457,25 @@ "node": ">= 18" } }, + "node_modules/@mat3ra/utils/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "dev": true, "optional": true }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { @@ -3431,16 +3575,22 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz", - "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "undici-types": "~8.3.0" + "undici-types": "~6.21.0" } }, + "node_modules/@types/pluralize": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/pluralize/-/pluralize-0.0.33.tgz", + "integrity": "sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==", + "dev": true, + "license": "MIT" + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -3457,6 +3607,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -3468,6 +3619,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -3515,6 +3667,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3569,6 +3722,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, "engines": { "node": ">=6" } @@ -3604,6 +3758,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -3612,6 +3767,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -3623,7 +3779,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "devOptional": true, + "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3661,6 +3817,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -3758,6 +3915,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, "engines": { "node": ">=8" } @@ -3781,6 +3939,7 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "dev": true, "dependencies": { "@babel/compat-data": "^7.17.7", "@babel/helper-define-polyfill-provider": "^0.3.2", @@ -3794,6 +3953,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -3802,6 +3962,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.4.0.tgz", "integrity": "sha512-YxFreYwUfglYKdLUGvIF2nJEsGwj+RhWSX/ije3D2vQPOXuyMLMtg/cCGMDpOA7Nd+MwlNdnGODbd2EwUZPlsw==", + "dev": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.3.0", "core-js-compat": "^3.18.0" @@ -3814,6 +3975,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "dev": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.3.1" }, @@ -3824,12 +3986,14 @@ "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==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/baseline-browser-mapping": { "version": "2.11.14", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.14.tgz", "integrity": "sha512-JyJ954WzuIR8/FFzX0o5krdSTrBAkcCSRfWSleRsIHSWV+cZe2FI1PKggVkFke1hBldRs+LRxUczzE9iPmgZww==", + "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -3842,7 +4006,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8" } @@ -3851,6 +4015,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3860,7 +4025,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "devOptional": true, + "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -3878,6 +4043,7 @@ "version": "4.28.8", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -3910,7 +4076,8 @@ "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==" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "node_modules/caching-transform": { "version": "4.0.0", @@ -3968,6 +4135,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { "node": ">=6" } @@ -3988,6 +4156,7 @@ "version": "1.0.30001809", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -4026,6 +4195,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -4057,7 +4227,7 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "devOptional": true, + "dev": true, "funding": [ { "type": "individual", @@ -4231,6 +4401,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -4244,6 +4415,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -4251,7 +4423,8 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/colorette": { "version": "2.0.19", @@ -4263,6 +4436,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, "engines": { "node": ">= 6" } @@ -4279,7 +4453,8 @@ "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true }, "node_modules/complex.js": { "version": "2.0.11", @@ -4317,7 +4492,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/confusing-browser-globals": { "version": "1.0.11", @@ -4329,6 +4505,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, "dependencies": { "safe-buffer": "~5.1.1" } @@ -4337,6 +4514,7 @@ "version": "3.50.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.50.0.tgz", "integrity": "sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==", + "dev": true, "license": "MIT", "dependencies": { "browserslist": "^4.28.7" @@ -4353,6 +4531,7 @@ "version": "3.50.0", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.50.0.tgz", "integrity": "sha512-6GP3Pxz4IKyWjAfa747vIu/jilB5z29JWROLqH/b+pXVcpgh6tM06ZIBwSuglgVqzDYURhOK6oEzTrG0bCHitA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "engines": { @@ -4374,6 +4553,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4416,6 +4596,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4463,7 +4644,8 @@ "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==" + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "node_modules/default-require-extensions": { "version": "3.0.0", @@ -4515,6 +4697,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, "dependencies": { "esutils": "^2.0.2" }, @@ -4532,17 +4715,20 @@ "version": "1.5.406", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.406.tgz", "integrity": "sha512-hWH5ORBi3d0IipnMh7BN5GDTaAmrSSSWmznwt2zltdiRNEWoEQyTwF0FFSBxzHO7hLSRT6loQu3IQGV0wg/Tvg==", + "dev": true, "license": "ISC" }, "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==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, "dependencies": { "ansi-colors": "^4.1.1" }, @@ -4591,6 +4777,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4632,6 +4819,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4648,6 +4836,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -4657,6 +4846,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.3", @@ -5156,6 +5346,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -5168,6 +5359,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, "dependencies": { "eslint-visitor-keys": "^1.1.0" }, @@ -5182,6 +5374,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, "engines": { "node": ">=4" } @@ -5190,6 +5383,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, "engines": { "node": ">=10" } @@ -5198,6 +5392,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, "dependencies": { "@babel/highlight": "^7.10.4" } @@ -5206,6 +5401,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -5220,6 +5416,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5235,6 +5432,7 @@ "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, "dependencies": { "color-name": "~1.1.4" }, @@ -5245,12 +5443,14 @@ "node_modules/eslint/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/eslint/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, "engines": { "node": ">=10" }, @@ -5262,6 +5462,7 @@ "version": "13.17.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -5276,6 +5477,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -5284,6 +5486,7 @@ "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -5298,6 +5501,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -5309,6 +5513,7 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, "dependencies": { "acorn": "^7.4.0", "acorn-jsx": "^5.3.1", @@ -5322,6 +5527,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, "engines": { "node": ">=4" } @@ -5330,6 +5536,7 @@ "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, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -5342,6 +5549,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -5353,6 +5561,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "engines": { "node": ">=4.0" } @@ -5361,6 +5570,7 @@ "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, "dependencies": { "estraverse": "^5.2.0" }, @@ -5372,6 +5582,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "engines": { "node": ">=4.0" } @@ -5380,6 +5591,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, "engines": { "node": ">=4.0" } @@ -5388,6 +5600,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -5418,7 +5631,8 @@ "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==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "node_modules/fast-diff": { "version": "1.2.0", @@ -5429,12 +5643,14 @@ "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==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "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==" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "node_modules/fast-uri": { "version": "3.1.5", @@ -5457,6 +5673,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -5468,7 +5685,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "devOptional": true, + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5480,6 +5697,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, "dependencies": { "commondir": "^1.0.1", "make-dir": "^2.0.0", @@ -5493,6 +5711,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, "dependencies": { "locate-path": "^3.0.0" }, @@ -5513,6 +5732,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, "dependencies": { "flatted": "^3.1.0", "rimraf": "^3.0.2" @@ -5524,7 +5744,8 @@ "node_modules/flatted": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", - "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==" + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "dev": true }, "node_modules/foreach": { "version": "2.0.6", @@ -5579,12 +5800,14 @@ "node_modules/fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==" + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/fsevents": { "version": "2.3.2", @@ -5604,6 +5827,7 @@ "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": { "url": "https://github.com/sponsors/ljharb" @@ -5630,7 +5854,8 @@ "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true }, "node_modules/functions-have-names": { "version": "1.2.3", @@ -5645,6 +5870,7 @@ "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, "engines": { "node": ">=6.9.0" } @@ -5723,6 +5949,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -5742,6 +5969,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -5789,6 +6017,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -5861,6 +6090,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -5912,6 +6142,7 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, "engines": { "node": ">= 4" } @@ -5920,6 +6151,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -5935,6 +6167,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "engines": { "node": ">=0.8.19" } @@ -5953,6 +6186,7 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5961,7 +6195,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/internal-slot": { "version": "1.0.3", @@ -5993,7 +6228,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "devOptional": true, + "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -6039,6 +6274,7 @@ "version": "2.16.2", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.3" @@ -6069,6 +6305,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6077,6 +6314,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "engines": { "node": ">=8" } @@ -6085,6 +6323,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -6141,7 +6380,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.12.0" } @@ -6174,6 +6413,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, "dependencies": { "isobject": "^3.0.1" }, @@ -6305,12 +6545,14 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6494,12 +6736,14 @@ "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==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -6521,6 +6765,7 @@ "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" @@ -6660,17 +6905,20 @@ "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==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "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==" + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true }, "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" @@ -6696,6 +6944,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6719,6 +6968,7 @@ "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, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -6882,6 +7132,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -6900,7 +7151,8 @@ "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==" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true }, "node_modules/lodash.flattendeep": { "version": "4.4.0", @@ -6911,12 +7163,14 @@ "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==" + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==" + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true }, "node_modules/log-symbols": { "version": "4.1.0", @@ -7095,6 +7349,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -7106,6 +7361,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" @@ -7200,6 +7456,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7479,6 +7736,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -7496,7 +7754,8 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, "node_modules/node-preload": { "version": "0.2.1", @@ -7514,6 +7773,7 @@ "version": "2.0.53", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -7523,7 +7783,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.10.0" } @@ -7962,6 +8222,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "dependencies": { "wrappy": "1" } @@ -7985,6 +8246,7 @@ "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -8001,6 +8263,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "dependencies": { "p-try": "^2.0.0" }, @@ -8015,6 +8278,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, "dependencies": { "p-limit": "^2.0.0" }, @@ -8041,6 +8305,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, "engines": { "node": ">=6" } @@ -8064,6 +8329,7 @@ "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, "dependencies": { "callsites": "^3.0.0" }, @@ -8075,6 +8341,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, "engines": { "node": ">=4" } @@ -8083,6 +8350,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -8091,6 +8359,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -8098,7 +8367,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==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/pathval": { "version": "1.1.1", @@ -8113,13 +8383,14 @@ "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==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8.6" }, @@ -8143,6 +8414,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, "engines": { "node": ">=6" } @@ -8151,6 +8423,7 @@ "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" @@ -8160,6 +8433,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, "dependencies": { "find-up": "^3.0.0" }, @@ -8176,6 +8450,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, "engines": { "node": ">= 0.8.0" } @@ -8223,6 +8498,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -8242,6 +8518,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, "engines": { "node": ">=6" } @@ -8265,7 +8542,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "devOptional": true, + "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -8277,12 +8554,14 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2" @@ -8294,7 +8573,8 @@ "node_modules/regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", @@ -8317,6 +8597,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, "engines": { "node": ">=8" }, @@ -8328,6 +8609,7 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2", @@ -8354,12 +8636,14 @@ "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.13.2", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz", "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.1.0" @@ -8393,6 +8677,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -8407,6 +8692,7 @@ "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -8428,6 +8714,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "engines": { "node": ">=4" } @@ -8465,6 +8752,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -8487,7 +8775,8 @@ "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "node_modules/seed-random": { "version": "2.2.0", @@ -8507,6 +8796,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, "bin": { "semver": "bin/semver" } @@ -8530,6 +8820,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, "dependencies": { "kind-of": "^6.0.2" }, @@ -8541,6 +8832,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -8552,6 +8844,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } @@ -8580,6 +8873,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, "engines": { "node": ">=6" } @@ -8588,6 +8882,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -8604,6 +8899,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -8618,6 +8914,7 @@ "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, "dependencies": { "color-name": "~1.1.4" }, @@ -8628,12 +8925,14 @@ "node_modules/slice-ansi/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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -8642,6 +8941,7 @@ "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==", + "dev": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -8651,6 +8951,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -8721,7 +9022,8 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true }, "node_modules/string-argv": { "version": "0.3.1", @@ -8736,6 +9038,7 @@ "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, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -8796,6 +9099,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -8825,6 +9129,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "engines": { "node": ">=8" }, @@ -8836,6 +9141,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -8847,6 +9153,7 @@ "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, "engines": { "node": ">= 0.4" }, @@ -8858,6 +9165,7 @@ "version": "6.8.0", "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "dev": true, "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", @@ -8873,6 +9181,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -8887,7 +9196,8 @@ "node_modules/table/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true }, "node_modules/test-exclude": { "version": "6.0.0", @@ -8906,7 +9216,8 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/through": { "version": "2.3.8", @@ -8973,7 +9284,7 @@ "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==", - "devOptional": true, + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -9094,6 +9405,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -9114,6 +9426,7 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, "engines": { "node": ">=10" }, @@ -9140,9 +9453,9 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "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": { @@ -9150,7 +9463,7 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { @@ -9195,17 +9508,17 @@ "dev": true }, "node_modules/undici-types": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", - "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "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": { "node": ">=4" @@ -9215,6 +9528,7 @@ "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": { "unicode-canonical-property-names-ecmascript": "^2.0.0", @@ -9228,6 +9542,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -9237,6 +9552,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -9246,6 +9562,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -9276,6 +9593,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -9298,7 +9616,8 @@ "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", @@ -9354,6 +9673,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -9390,6 +9710,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9453,7 +9774,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/write-file-atomic": { "version": "3.0.3", @@ -9479,7 +9801,8 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/yaml": { "version": "1.10.2", diff --git a/package.json b/package.json index 978491b..03a38dc 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,13 @@ "version": "0.0.0", "description": "Infrastructure DEfinitions", "scripts": { - "test": "nyc --reporter=text mocha --recursive --bail", + "test": "nyc --reporter=text mocha --recursive --bail --require ts-node/register 'tests/js/**/*.ts'", "lint": "eslint --cache src/js tests/js && prettier --write src/js tests/js", "lint:fix": "eslint --fix --cache src/js tests/js && prettier --write src/js tests/js", "lint:staged": "lint-staged", "prepare": "husky install", "prettier": "prettier --check src/js tests/js", - "transpile": "babel --out-dir dist/js src/js && cp src/js/compute.d.ts dist/js/compute.d.ts", + "transpile": "tsc -p tsconfig-transpile.json", "prepublishOnly": "npm run transpile" }, "repository": { @@ -17,10 +17,10 @@ "url": "https://github.com/Exabyte-io/ide.git" }, "main": "dist/js/index.js", + "types": "dist/js/index.d.ts", "files": [ "/dist", - "/src/js", - ".babelrc" + "/src/js" ], "author": "Exabyte Inc.", "bugs": { @@ -29,22 +29,17 @@ "license": "Apache-2.0", "homepage": "https://github.com/Exabyte-io/ide", "dependencies": { - "@babel/cli": "7.16.0", - "@babel/core": "7.16.0", - "@babel/eslint-parser": "7.16.3", - "@babel/plugin-proposal-class-properties": "7.16.0", - "@babel/preset-env": "7.16.4", - "@babel/preset-react": "7.16.7", - "@babel/register": "^7.16.0", - "@babel/runtime-corejs3": "7.16.8", - "chai": "^4.3.4", "moment": "^2.17.1", "pluralize": "^3.0.0" }, "devDependencies": { "@mat3ra/code": "2026.8.18-0", "@mat3ra/esse": "2026.8.18-2", + "@mat3ra/tsconfig": "^2024.6.3-0", "@exabyte-io/eslint-config": "^2022.11.17-0", + "@types/mocha": "^10.0.10", + "@types/node": "^20.19.43", + "@types/pluralize": "^0.0.33", "chai": "^4.3.4", "eslint": "7.32.0", "eslint-config-airbnb": "19.0.2", @@ -61,7 +56,9 @@ "mixwith": "^0.1.1", "mocha": "^9.1.3", "nyc": "^15.1.0", - "prettier": "^2.7.1" + "prettier": "^2.7.1", + "ts-node": "^10.9.2", + "typescript": "^5.6.6" }, "peerDependencies": { "@mat3ra/code": "*", diff --git a/src/js/compute.d.ts b/src/js/compute.d.ts deleted file mode 100644 index 260defb..0000000 --- a/src/js/compute.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** Instance API mixed by {@link computedEntityMixin}. */ -export type ComputedEntityMixin = { - setCompute(compute: unknown): void; - unsetCompute(): void; - getApproximateCharge( - settings: { baseChargeRate: number }, - queueMultipliers?: Record | null, - ): number; - - readonly compute: unknown; - readonly clusterJid: unknown; - readonly clusterFqdn: string | undefined; - readonly clusterFqdnShort: string; - readonly timeLimit: unknown; - readonly computeQueue: unknown; - readonly computePPN: number; - readonly computeNodes: number; - readonly computeNodesAndPPN: string; - readonly timePrediction: number; - readonly errors: unknown[]; - readonly hasWarnings: boolean; - readonly warnings: Array<{ condition: boolean; message: string }>; - readonly isExternalJob: boolean; - readonly bucket: string; - readonly filesRootDir: string; -}; - -export type WithComputedEntity = T & ComputedEntityMixin; - -export function computedEntityMixin(prototype: object): void; - -/** Non-mixin exports from compute.js (loose). */ -export function getHomeDir(isEnterprise: boolean, username: string): string; -export function getDefaultClusterQuota( - defaultClusterHostname: string, - username: string, - isEnterprise?: boolean, -): unknown[]; diff --git a/src/js/compute.js b/src/js/compute.js deleted file mode 100644 index c0e1764..0000000 --- a/src/js/compute.js +++ /dev/null @@ -1,166 +0,0 @@ -import pluralize from "pluralize"; - -import { getExternalBucket } from "./default"; -import { QUEUE_TYPES } from "./nodes/enums"; -import { wallTimeToHours } from "./utils/time"; - -export function getHomeDir(isEnterprise, username) { - return isEnterprise ? `/cluster-???-share/groups/${username}` : `/cluster-???-home/${username}`; -} - -export function getDefaultClusterQuota(defaultClusterHostname, username, isEnterprise = false) { - return [ - { - hostname: defaultClusterHostname, - path: getHomeDir(isEnterprise, username), - items: { - bused: 0, - bsoft: 0, - bhard: 10737418240, - iused: 0, - isoft: 0, - ihard: 10737418240, - }, - }, - ]; -} - -/** - * @param {Object} prototype — typically `SomeEntity.prototype` - */ -export function computedEntityMixin(prototype) { - const computedEntityProperties = { - _computeProp(key, defaultValue) { - return this.prop("compute." + key, defaultValue); - }, - - get compute() { - return this.prop("compute"); - }, - - setCompute(compute) { - if (compute.queue === QUEUE_TYPES.debug) delete compute.maxCPU; - this.setProp("compute", compute); - }, - - unsetCompute() { - delete this._json.compute; - }, - - /** - * @summary Returns job ID in the Resource Management System. - */ - get clusterJid() { - return this._computeProp("cluster.jid"); - }, - - /** - * @summary Returns cluster fqdn, where the job was/will be calculated - */ - get clusterFqdn() { - return this._computeProp("cluster.fqdn"); - }, - - get clusterFqdnShort() { - return ((this.clusterFqdn || "").match(/cluster-\d\d\d/) || [])[0] || ""; - }, - - /** - * @summary Returns time limit (in seconds) set by user on job creation. - */ - get timeLimit() { - return this._computeProp("timeLimit"); - }, - - get computeQueue() { - return this._computeProp("queue"); - }, - - get computePPN() { - return this._computeProp("ppn", 1); - }, - - get computeNodes() { - return this._computeProp("nodes", 1); - }, - - get computeNodesAndPPN() { - return ( - this.computeNodes + - " " + - pluralize("node", this.computeNodes) + - " x " + - this.computePPN + - " " + - pluralize("core", this.computePPN) - ); - }, - - getApproximateCharge(settings, queueMultipliers = null) { - const timeLimitInHours = wallTimeToHours(this.timeLimit); - - const queueMultiplier = queueMultipliers ? queueMultipliers[this.computeQueue] : 1; - const rateModifier = this.owner?.serviceLevel?.nameBasedModifier || 1; - const chargeRate = settings.baseChargeRate * rateModifier * queueMultiplier; - - return chargeRate * timeLimitInHours * this.computePPN; - }, - - // eslint-disable-next-line class-methods-use-this - get timePrediction() { - return 0; - }, - - get errors() { - return this._computeProp("errors", []); - }, - - get hasWarnings() { - return this.warnings.map((o) => o.condition).some((x) => x); - }, - - /* - * Array of warning Objects: [{condition: Boolean, message: String}]. Computed in-memory per Entity. - */ - // eslint-disable-next-line class-methods-use-this - get warnings() { - return []; - }, - - get isExternalJob() { - return this.prop("isExternal", false); - }, - - /** - * @summary Returns the bucket name for this object storage items. Bucket name is constructed from cluster FQDN. - * @example master-vagrant-cluster-001.exabyte.io ==> vagrant-cluster-001 - */ - get bucket() { - if (this.isExternalJob) { - return this._getExternalBucket - ? this._getExternalBucket().name - : getExternalBucket().name; - } - return this.clusterFqdn.match(/master-(.*).(exabyte.io|mat3ra.com)/)[1]; - }, - - /* - * @summary: returns files root directory. - * For items created before 01/11/2018 00:00:00 UTC, path is started with either /home or /share. - * For items after 01/11/2018 00:00:00 UTC, path is started with either /cluster-00N-home or /cluster-00N-share. - */ - get filesRootDir() { - if (this.isExternalJob) return `${this.prop("owner").slug}/${this.id}`; - if (new Date(this.createdAt).getTime() <= 1515628800000) return this.workDir; - const clusterAlias = this.clusterFqdn.match( - /master.*(cluster-.*).(exabyte.io|mat3ra.com)/, - )[1]; - const prefix = this.owner.isPersonal - ? `/${clusterAlias}-home` - : `/${clusterAlias}-share`; - return `${prefix}/${this.workDir.split("/").slice(2).join("/")}`; - }, - }; - - Object.defineProperties(prototype, Object.getOwnPropertyDescriptors(computedEntityProperties)); -} diff --git a/src/js/compute.ts b/src/js/compute.ts new file mode 100644 index 0000000..2c3da89 --- /dev/null +++ b/src/js/compute.ts @@ -0,0 +1,261 @@ +import type { ComputeArgumentsSchema } from "@mat3ra/esse/dist/js/types"; +import pluralize from "pluralize"; + +import { getExternalBucket } from "./default"; +import { QUEUE_TYPES } from "./nodes/enums"; +import { wallTimeToHours } from "./utils/time"; + +/** + * Any compute payload ide can operate on. Defaults to esse's own `compute` arguments schema + * (from `job/compute.json`), or absent entirely - json-schema-to-typescript *inlines* `compute` + * per schema, so `JobSchema["compute"]` (required) and `WorkflowSchema["compute"]` / + * `SubworkflowMixinSchema["compute"]` (optional) are structurally identical to + * `ComputeArgumentsSchema` but not referentially the same type. Consumers should parameterize + * `ComputedEntityMixin` explicitly rather than rely on this default. + */ +export type AnyComputeSchema = ComputeArgumentsSchema | undefined; + +/** `compute` itself, with absence stripped - what every derived getter actually reads. */ +type Compute = NonNullable; + +type Cluster = NonNullable["cluster"]>; + +/** + * `compute`'s own optionality has to track whether `undefined` is part of `C`: TS2320 treats an + * optional property (`compute?: X`, as esse's `WorkflowSchema`/`SubworkflowMixinSchema` declare + * it - `compute` itself is optional in the JSON schema) and a required property typed `X | + * undefined` (what a naive `compute: C` would give when `C` includes `undefined`, as it does for + * those two) as *not identical*, independent of the value type - so a single fixed optionality + * modifier can never match both those schemas and esse's `JobSchema` (`compute` required, no + * `undefined`) at once. Toggling it by whether `undefined extends C` is what makes all three + * identical to their respective generated schema mixin. + */ +type ComputeField = undefined extends C + ? { compute?: C } + : { compute: C }; + +/** Instance API mixed by {@link computedEntityMixin}. */ +export type ComputedEntityMixin = + ComputeField & { + setCompute(compute: Compute): void; + unsetCompute(): void; + getApproximateCharge( + settings: { baseChargeRate: number }, + queueMultipliers?: Record | null, + ): number; + + readonly clusterJid: Cluster["jid"]; + readonly clusterFqdn: Cluster["fqdn"]; + readonly clusterFqdnShort: string; + readonly timeLimit: Compute["timeLimit"] | undefined; + readonly computeQueue: Compute["queue"] | undefined; + readonly computePPN: number; + readonly computeNodes: number; + readonly computeNodesAndPPN: string; + readonly timePrediction: number; + readonly errors: NonNullable["errors"]>; + readonly hasWarnings: boolean; + /** Intentionally not `readonly` - hosts are meant to override this with their own getter. */ + warnings: Array<{ condition: boolean; message: string }>; + readonly isExternalJob: boolean; + readonly bucket: string; + readonly filesRootDir: string; + }; + +export type WithComputedEntity = + T & ComputedEntityMixin; + +/** What {@link computedEntityMixin} reads off its host besides the mixed-in members themselves. */ +type ComputedEntityHost = { + _json: { compute?: C; isExternal?: boolean; owner?: unknown; workDir?: string }; + _getExternalBucket?: () => ReturnType; + id?: string; + createdAt?: string | number; + workDir?: string; + owner?: { slug?: string; isPersonal?: boolean; serviceLevel?: { nameBasedModifier?: number } }; + prop(name: string, defaultValue?: T): T; + setProp(name: string, value: unknown): void; +}; + +type Self = ComputedEntityHost & ComputedEntityMixin; + +export function getHomeDir(isEnterprise: boolean, username: string): string { + return isEnterprise ? `/cluster-???-share/groups/${username}` : `/cluster-???-home/${username}`; +} + +export function getDefaultClusterQuota( + defaultClusterHostname: string, + username: string, + isEnterprise = false, +) { + return [ + { + hostname: defaultClusterHostname, + path: getHomeDir(isEnterprise, username), + items: { + bused: 0, + bsoft: 0, + bhard: 10737418240, + iused: 0, + isoft: 0, + ihard: 10737418240, + }, + }, + ]; +} + +/** + * @param prototype — typically `SomeEntity.prototype` + */ +export function computedEntityMixin( + prototype: object, +): void { + // @ts-expect-error — same idiom as the generated *SchemaMixin files: this object literal only + // supplies ComputedEntityMixin's own members; the host (_json/prop/setProp) comes from + // whatever `prototype` actually is, so `this` below is typed via this const's annotation. + const computedEntityProperties: Self = { + get compute() { + return this.prop("compute"); + }, + + set compute(value) { + this.setProp("compute", value); + }, + + setCompute(compute) { + if (compute.queue === QUEUE_TYPES.debug) delete compute.maxCPU; + this.setProp("compute", compute); + }, + + unsetCompute() { + delete this._json.compute; + }, + + /** + * @summary Returns job ID in the Resource Management System. + */ + get clusterJid() { + return this.prop["jid"]>("compute.cluster.jid"); + }, + + /** + * @summary Returns cluster fqdn, where the job was/will be calculated + */ + get clusterFqdn() { + return this.prop["fqdn"]>("compute.cluster.fqdn"); + }, + + get clusterFqdnShort() { + return ((this.clusterFqdn || "").match(/cluster-\d\d\d/) || [])[0] || ""; + }, + + /** + * @summary Returns time limit (in seconds) set by user on job creation. + */ + get timeLimit() { + return this.prop["timeLimit"] | undefined>("compute.timeLimit"); + }, + + get computeQueue() { + return this.prop["queue"] | undefined>("compute.queue"); + }, + + get computePPN() { + return this.prop("compute.ppn", 1); + }, + + get computeNodes() { + return this.prop("compute.nodes", 1); + }, + + get computeNodesAndPPN() { + return ( + this.computeNodes + + " " + + pluralize("node", this.computeNodes) + + " x " + + this.computePPN + + " " + + pluralize("core", this.computePPN) + ); + }, + + getApproximateCharge(settings, queueMultipliers = null) { + const { timeLimit } = this; + if (!timeLimit) { + throw new Error("getApproximateCharge: compute.timeLimit is not set"); + } + const timeLimitInHours = wallTimeToHours(timeLimit); + + const queue = this.computeQueue; + const queueMultiplier = (queueMultipliers && queue && queueMultipliers[queue]) || 1; + const rateModifier = this.owner?.serviceLevel?.nameBasedModifier || 1; + const chargeRate = settings.baseChargeRate * rateModifier * queueMultiplier; + + return chargeRate * timeLimitInHours * this.computePPN; + }, + + // eslint-disable-next-line class-methods-use-this + get timePrediction() { + return 0; + }, + + get errors() { + return this.prop["errors"]>>("compute.errors", []); + }, + + get hasWarnings() { + return this.warnings.map((o) => o.condition).some((x) => x); + }, + + /* + * Array of warning Objects: [{condition: Boolean, message: String}]. Computed in-memory per Entity. + */ + // eslint-disable-next-line class-methods-use-this + get warnings() { + return []; + }, + + set warnings(_value) { + // Intentionally a no-op: hosts override this getter+setter pair wholesale to supply + // their own warnings; this mixin's own value is always the empty-array default above. + }, + + get isExternalJob() { + return this.prop("isExternal", false); + }, + + /** + * @summary Returns the bucket name for this object storage items. Bucket name is constructed from cluster FQDN. + * @example master-vagrant-cluster-001.exabyte.io ==> vagrant-cluster-001 + */ + get bucket() { + if (this.isExternalJob) { + return this._getExternalBucket + ? this._getExternalBucket().name + : getExternalBucket().name; + } + return this.clusterFqdn?.match(/master-(.*).(exabyte.io|mat3ra.com)/)?.[1] ?? ""; + }, + + /* + * @summary: returns files root directory. + * For items created before 01/11/2018 00:00:00 UTC, path is started with either /home or /share. + * For items after 01/11/2018 00:00:00 UTC, path is started with either /cluster-00N-home or /cluster-00N-share. + */ + get filesRootDir() { + if (this.isExternalJob) return `${this.owner?.slug}/${this.id}`; + if (new Date(this.createdAt ?? "").getTime() <= 1515628800000) return this.workDir ?? ""; + const clusterAlias = this.clusterFqdn?.match( + /master.*(cluster-.*).(exabyte.io|mat3ra.com)/, + )?.[1]; + const prefix = this.owner?.isPersonal ? `/${clusterAlias}-home` : `/${clusterAlias}-share`; + return `${prefix}/${(this.workDir ?? "").split("/").slice(2).join("/")}`; + }, + }; + + Object.defineProperties( + prototype, + Object.getOwnPropertyDescriptors(computedEntityProperties), + ); +} diff --git a/src/js/default.js b/src/js/default.js deleted file mode 100644 index a5da95c..0000000 --- a/src/js/default.js +++ /dev/null @@ -1,20 +0,0 @@ -export function getDefaultComputeConfig() { - return { - ppn: 1, - nodes: 1, - queue: "D", - timeLimit: "01:00:00", - notify: "n", - cluster: { - fqdn: "", - }, - }; -} - -export function getExternalBucket() { - return { - name: "", - provider: "", - region: "", - }; -} diff --git a/src/js/default.ts b/src/js/default.ts new file mode 100644 index 0000000..353bb50 --- /dev/null +++ b/src/js/default.ts @@ -0,0 +1,28 @@ +import type { ComputeArgumentsSchema } from "@mat3ra/esse/dist/js/types"; + +export function getDefaultComputeConfig(): ComputeArgumentsSchema { + return { + ppn: 1, + nodes: 1, + queue: "D", + timeLimit: "01:00:00", + notify: "n", + cluster: { + fqdn: "", + }, + }; +} + +export interface ExternalBucket { + name: string; + provider: string; + region: string; +} + +export function getExternalBucket(): ExternalBucket { + return { + name: "", + provider: "", + region: "", + }; +} diff --git a/src/js/enums.js b/src/js/enums.js deleted file mode 100644 index cb01a65..0000000 --- a/src/js/enums.js +++ /dev/null @@ -1,67 +0,0 @@ -export const EMAIL_NOTIFICATION_OPTIONS_PBS = [ - { - label: "never", - value: "n", - }, - { - label: "abort", - value: "a", - }, - { - label: "begin", - value: "b", - }, - { - label: "end", - value: "e", - }, -]; - -// TODO: adjust to make modular to work not only with PBS, but SLURM etc. -class RMSNotificationsHandler { - constructor(type) { - this.type = type; - } - - get options() { - const config = { - PBS: EMAIL_NOTIFICATION_OPTIONS_PBS, - }; - return config[this.type] || EMAIL_NOTIFICATION_OPTIONS_PBS; - } - - _getOptionValueByLabel(label) { - return (this.options.find((o) => o.label === label) || {}).value; - } - - get never() { - return this._getOptionValueByLabel("never"); - } - - get abort() { - return this._getOptionValueByLabel("abort"); - } - - get begin() { - return this._getOptionValueByLabel("begin"); - } - - get end() { - return this._getOptionValueByLabel("end"); - } - - get abe() { - return this.abort + this.begin + this.end; - } -} - -const handler = new RMSNotificationsHandler("PBS"); - -export const EMAIL_NOTIFICATIONS = { - never: handler.never, - abort: handler.abort, - begin: handler.begin, - end: handler.end, - // abort, begin, and end - abe: handler.abe, -}; diff --git a/src/js/enums.ts b/src/js/enums.ts new file mode 100644 index 0000000..ac2a1e0 --- /dev/null +++ b/src/js/enums.ts @@ -0,0 +1,37 @@ +type NotificationLabel = "never" | "abort" | "begin" | "end"; + +export const EMAIL_NOTIFICATION_OPTIONS_PBS: Array<{ label: NotificationLabel; value: string }> = [ + { + label: "never", + value: "n", + }, + { + label: "abort", + value: "a", + }, + { + label: "begin", + value: "b", + }, + { + label: "end", + value: "e", + }, +]; + +function getNotificationValue(label: NotificationLabel): string { + const option = EMAIL_NOTIFICATION_OPTIONS_PBS.find((o) => o.label === label); + if (!option) { + throw new Error(`Unknown notification label: ${label}`); + } + return option.value; +} + +export const EMAIL_NOTIFICATIONS = { + never: getNotificationValue("never"), + abort: getNotificationValue("abort"), + begin: getNotificationValue("begin"), + end: getNotificationValue("end"), + // abort, begin, and end + abe: getNotificationValue("abort") + getNotificationValue("begin") + getNotificationValue("end"), +}; diff --git a/src/js/index.js b/src/js/index.ts similarity index 79% rename from src/js/index.js rename to src/js/index.ts index 167e8fd..bd4afb4 100644 --- a/src/js/index.js +++ b/src/js/index.ts @@ -1,5 +1,7 @@ export { computedEntityMixin, getDefaultClusterQuota, getHomeDir } from "./compute"; +export type { AnyComputeSchema, ComputedEntityMixin, WithComputedEntity } from "./compute"; export { getDefaultComputeConfig, getExternalBucket } from "./default"; +export type { ExternalBucket } from "./default"; export { EMAIL_NOTIFICATION_OPTIONS_PBS, EMAIL_NOTIFICATIONS } from "./enums"; export { QUEUE_TYPES, QUEUE_DISPLAY, ETA, TIME_LIMIT_TYPES, IS_RESTARTABLE } from "./nodes/enums"; diff --git a/src/js/nodes/enums.js b/src/js/nodes/enums.ts similarity index 88% rename from src/js/nodes/enums.js rename to src/js/nodes/enums.ts index ef8796d..6bc483a 100644 --- a/src/js/nodes/enums.js +++ b/src/js/nodes/enums.ts @@ -24,9 +24,12 @@ export const QUEUE_TYPES = { gpuPSavingFast: "GPSF", gpuP2SavingFast: "GP2SF", gpuP4SavingFast: "GP4SF", -}; +} as const; -export const QUEUE_DISPLAY = { +// Note: has more keys than esse's `job/compute.json#properties/queue` union (GPOF/GP2OF/GP4OF/ +// GPSF/GP2SF/GP4SF are not in the schema), so this stays a plain string index rather than typed +// against that union. +export const QUEUE_DISPLAY: Record = { D: "debug (D)", OR: "ordinary regular (OR)", OR4: "4 cores ordinary regular (OR4)", @@ -55,6 +58,7 @@ export const QUEUE_DISPLAY = { GP2SF: "2 GPUs saving fast (GP2SF)", GP4SF: "4 GPUs saving fast (GP4SF)", }; + export const ETA = { withinOneMin: { display: "within 1 min", @@ -76,13 +80,14 @@ export const ETA = { display: "more than 1 hour", order: 50, }, -}; +} as const; + export const TIME_LIMIT_TYPES = { single: "per single attempt", compound: "compound", -}; +} as const; export const IS_RESTARTABLE = { yes: true, no: false, -}; +} as const; diff --git a/src/js/utils/time.js b/src/js/utils/time.ts similarity index 63% rename from src/js/utils/time.js rename to src/js/utils/time.ts index 32ee3ef..6adb40c 100644 --- a/src/js/utils/time.js +++ b/src/js/utils/time.ts @@ -1,6 +1,8 @@ import moment from "moment"; -const MULTIPLIERS = { +type WallTimeUnit = "s" | "m" | "h" | "d"; + +const MULTIPLIERS: Record = { s: 1, m: 60, h: 3600, @@ -9,11 +11,11 @@ const MULTIPLIERS = { /** * @summary Converts walltime in '00:05:00' to on of the specified units - s (seconds), m (minutes), h (hours), d (days). - * @param walltime {String} walltime in '00:05:00' format (max value '99:999:59:59' - 99 days, 999 hours, 59 minutes, 59 seconds). - * @param units {String} s (seconds), m (minutes), h (hours). - * @return {Number} Walltime in the specified units. + * @param walltime walltime in '00:05:00' format (max value '99:999:59:59' - 99 days, 999 hours, 59 minutes, 59 seconds). + * @param units s (seconds), m (minutes), h (hours), d (days). + * @return Walltime in the specified units. */ -export function wallTimeTo(walltime, units) { +export function wallTimeTo(walltime: string, units: WallTimeUnit): number { if (["s", "m", "h", "d"].indexOf(units) < 0) { throw new Error(`Unexpected units - ${units}`); } @@ -37,47 +39,43 @@ export function wallTimeTo(walltime, units) { return totalSeconds / MULTIPLIERS[units]; } -export function wallTimeToSeconds(walltime) { +export function wallTimeToSeconds(walltime: string): number { return wallTimeTo(walltime, "s"); } -export function wallTimeToMinutes(walltime) { +export function wallTimeToMinutes(walltime: string): number { return wallTimeTo(walltime, "m"); } -export function wallTimeToHours(walltime) { +export function wallTimeToHours(walltime: string): number { return wallTimeTo(walltime, "h"); } -export function wallTimeToDays(walltime) { +export function wallTimeToDays(walltime: string): number { return wallTimeTo(walltime, "d"); } /** * @summary Converts python time format (e.g. 1493124101.243714) to js (1493124101243) - * @param timestamp {Number} - * @return {Number} */ -export function pythonUnixTimeToJs(timestamp) { - // eslint-disable-next-line radix - return parseInt(timestamp * 1000); +export function pythonUnixTimeToJs(timestamp: number): number { + return Math.trunc(timestamp * 1000); } /** * @summary Converts days to months. 30 days equal to 1 month. - * @param days {Number} */ -export function daysToMonths(days) { +export function daysToMonths(days: number): string { const months = days / 30; return months + (months === 1 ? " month" : " months"); } -export function timestampToDate(timestamp = false, millisec = false) { +export function timestampToDate(timestamp: number | false = false, millisec = false): string { return timestamp ? moment(timestamp * (millisec ? 1 : 1000)).format("MMM D, YYYY, HH:mm A") : ""; } -export function daysAgoToDate(days) { +export function daysAgoToDate(days: number): Date { return moment().utc().startOf("day").subtract(days, "days").toDate(); } diff --git a/tests/js/compute.test.js b/tests/js/compute.test.js deleted file mode 100644 index dbbffeb..0000000 --- a/tests/js/compute.test.js +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint-disable prettier/prettier */ -/* eslint-disable max-classes-per-file */ -import { InMemoryEntity } from "@mat3ra/code/dist/js/entity"; -import { expect } from "chai"; - -import { computedEntityMixin } from "../../src/js/compute"; -import { getDefaultComputeConfig } from "../../src/js/default"; - -class Computer extends InMemoryEntity {} -computedEntityMixin(Computer.prototype); - -function assertApproximateCharge(compute, expectedCharge) { - const settings = { baseChargeRate: 1 }; - const queueMultipliers = { - D: 2, - OR: 1, - }; - - const app = new Computer({ compute }); - const charge = app.getApproximateCharge(settings, queueMultipliers); - expect(charge).to.equal(expectedCharge); -} - -describe("Model", () => { - it("can be created", () => { - const config = getDefaultComputeConfig(); - expect(config.ppn).to.equal(1); - }); - - it("calculates approximate charge", () => { - assertApproximateCharge({ queue: "D", timeLimit: "01:00:00" }, 2); - assertApproximateCharge({ queue: "D", timeLimit: "70:00:00" }, 140); - assertApproximateCharge({ queue: "OR", timeLimit: "70:00:00" }, 70); - }); -}); diff --git a/tests/js/compute.test.ts b/tests/js/compute.test.ts new file mode 100644 index 0000000..f7dc093 --- /dev/null +++ b/tests/js/compute.test.ts @@ -0,0 +1,54 @@ +import { InMemoryEntity } from "@mat3ra/code/dist/js/entity"; +import type { BaseInMemoryEntitySchema, ComputeArgumentsSchema } from "@mat3ra/esse/dist/js/types"; +import { expect } from "chai"; + +import { type ComputedEntityMixin, computedEntityMixin } from "../../src/js/compute"; +import { getDefaultComputeConfig } from "../../src/js/default"; + +type ComputerSchema = BaseInMemoryEntitySchema & { compute?: ComputeArgumentsSchema }; + +// This interface merge is the type-level regression test for the mixin's declaration-merge +// pattern: `compute` must be assignable to `ComputeArgumentsSchema | undefined`, mutable, and +// identical across every interface that declares it (see `ComputedEntityMixin`'s own docs). +interface Computer extends ComputedEntityMixin {} + +class Computer extends InMemoryEntity {} +computedEntityMixin(Computer.prototype); + +function assertApproximateCharge(compute: ComputeArgumentsSchema, expectedCharge: number) { + const settings = { baseChargeRate: 1 }; + const queueMultipliers = { + D: 2, + OR: 1, + }; + + const app = new Computer({ compute }); + const charge = app.getApproximateCharge(settings, queueMultipliers); + expect(charge).to.equal(expectedCharge); +} + +describe("Model", () => { + it("can be created", () => { + const config = getDefaultComputeConfig(); + expect(config.ppn).to.equal(1); + }); + + it("calculates approximate charge", () => { + assertApproximateCharge({ queue: "D", nodes: 1, ppn: 1, timeLimit: "01:00:00" }, 2); + assertApproximateCharge({ queue: "D", nodes: 1, ppn: 1, timeLimit: "70:00:00" }, 140); + assertApproximateCharge({ queue: "OR", nodes: 1, ppn: 1, timeLimit: "70:00:00" }, 70); + }); + + it("throws when compute.timeLimit is not set", () => { + const app = new Computer({}); + expect(() => app.getApproximateCharge({ baseChargeRate: 1 })).to.throw( + "getApproximateCharge: compute.timeLimit is not set", + ); + }); + + it("allows setting and reading compute back (mutable, not readonly)", () => { + const app = new Computer({}); + app.compute = { queue: "D", nodes: 1, ppn: 1, timeLimit: "01:00:00" }; + expect(app.compute?.queue).to.equal("D"); + }); +}); diff --git a/tests/js/setup.js b/tests/js/setup.js deleted file mode 100644 index c693a94..0000000 --- a/tests/js/setup.js +++ /dev/null @@ -1,2 +0,0 @@ -// eslint-disable-next-line no-unused-vars -import chai from "chai"; diff --git a/src/js/utils/time.tests.js b/tests/js/time.test.ts similarity index 95% rename from src/js/utils/time.tests.js rename to tests/js/time.test.ts index 130953c..f778fd6 100644 --- a/src/js/utils/time.tests.js +++ b/tests/js/time.test.ts @@ -1,6 +1,6 @@ import { expect } from "chai"; -import { wallTimeToDays, wallTimeToHours, wallTimeToMinutes, wallTimeToSeconds } from "./time"; +import { wallTimeToDays, wallTimeToHours, wallTimeToMinutes, wallTimeToSeconds } from "../../src/js/utils/time"; describe("time", () => { it("wallTimeToSeconds", () => { diff --git a/tsconfig-transpile.json b/tsconfig-transpile.json new file mode 100644 index 0000000..c4e247d --- /dev/null +++ b/tsconfig-transpile.json @@ -0,0 +1,4 @@ +{ + "extends": "@mat3ra/tsconfig/tsconfig-js-py-transpile.json", + "include": ["src/js"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b404238 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@mat3ra/tsconfig/tsconfig-js-py-dev.json", + "include": ["src/js", "tests/js"] +} From 388dd3856ae71fd3fbf78b363b589257bd1dc986 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Thu, 20 Aug 2026 23:24:48 +0300 Subject: [PATCH 02/14] refactor(SOF-8032): store EMAIL_NOTIFICATION_OPTIONS_PBS as a key/value object [release] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Was Array<{label, value}>, purely to support the array .find()-by-label lookup that getNotificationValue() (née RMSNotificationsHandler) did internally - now redundant now that lookup is a direct property access. EMAIL_NOTIFICATIONS' runtime output is unchanged (verified). The one real external consumer of the array shape is web-app's Cypress e2e helper (ComputeFormWidget.ts, `.find(o => o.label === optionName)`) - updated in a paired web-app change/commit to do a plain property lookup instead, landed together with web-app's ide repin so nothing breaks in between. --- src/js/enums.ts | 44 +++++++++++--------------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/src/js/enums.ts b/src/js/enums.ts index ac2a1e0..7db8238 100644 --- a/src/js/enums.ts +++ b/src/js/enums.ts @@ -1,37 +1,15 @@ -type NotificationLabel = "never" | "abort" | "begin" | "end"; - -export const EMAIL_NOTIFICATION_OPTIONS_PBS: Array<{ label: NotificationLabel; value: string }> = [ - { - label: "never", - value: "n", - }, - { - label: "abort", - value: "a", - }, - { - label: "begin", - value: "b", - }, - { - label: "end", - value: "e", - }, -]; - -function getNotificationValue(label: NotificationLabel): string { - const option = EMAIL_NOTIFICATION_OPTIONS_PBS.find((o) => o.label === label); - if (!option) { - throw new Error(`Unknown notification label: ${label}`); - } - return option.value; -} +export const EMAIL_NOTIFICATION_OPTIONS_PBS = { + never: "n", + abort: "a", + begin: "b", + end: "e", +}; export const EMAIL_NOTIFICATIONS = { - never: getNotificationValue("never"), - abort: getNotificationValue("abort"), - begin: getNotificationValue("begin"), - end: getNotificationValue("end"), + ...EMAIL_NOTIFICATION_OPTIONS_PBS, // abort, begin, and end - abe: getNotificationValue("abort") + getNotificationValue("begin") + getNotificationValue("end"), + abe: + EMAIL_NOTIFICATION_OPTIONS_PBS.abort + + EMAIL_NOTIFICATION_OPTIONS_PBS.begin + + EMAIL_NOTIFICATION_OPTIONS_PBS.end, }; From 8db04e1f76ce9ce52d601853a4ec5915cf621c4f Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Thu, 20 Aug 2026 23:58:17 +0300 Subject: [PATCH 03/14] refactor(SOF-8032): make computedEntityMixin extend InMemoryEntity like our generated mixins [release] computedEntityMixin took `prototype: object` and returned `void`, with its own hand-rolled ComputedEntityHost host-contract type (a `prop(name: string, default?): T` signature unrelated to the real one) - unlike every generated `*SchemaMixin` and hand-written mixin in `code`/`wode`/`jode`, which take `item: InMemoryEntity` and return `asserts item is T & Mixin`. - computedEntityMixin(item: InMemoryEntity): asserts item is T & ComputedEntityMixin. S is the extra host schema (owner/isExternal/ createdAt/workDir - not part of esse's bare job/workflow schemas, so any concrete schema missing them still satisfies S's constraint since they're all optional) layered on top of InMemoryEntity itself, matching how a generated mixin's own `properties: InMemoryEntity & X` + @ts-expect-error idiom works. - Dropped every `this.prop("compute.foo.bar", default)` dotted-path call. InMemoryEntity.prop's real signature is `prop(name: K, ...)` - a single top-level key, not a dotted path - so passing "compute.cluster.jid" never actually typechecked against it (the previous ComputedEntityHost silently allowed it via a fictional loose `prop(name: string)` signature that doesn't match the real one). Getters now read `this.compute?.cluster?.jid` etc. through the (now real) `compute` getter instead - verified identical at runtime against the compiled dist/js output, not just ts-node. - `set compute`/`setCompute` write directly to `this._json.compute` instead of calling `this.setProp("compute", value)`: setProp's signature (`value: S[typeof name]`) isn't call-site generic the way `prop`'s `` is, so it resolves to the full S[keyof S] union rather than the one field's type when S is itself a still-abstract generic parameter (a real gap in code's own types, not something to work around with a cast here) - same fix and same reasoning as jode's Job.setMaterial. - owner/createdAt/workDir (bucket, filesRootDir, getApproximateCharge) now read via `this.prop("owner")` etc. instead of `this.owner`/`this.createdAt` /`this.workDir` direct property access - InMemoryEntity only exposes S's keys through prop()/setProp(), not as direct instance properties, unless an actual mixin adds real getters for them. Verified: tsc clean, npm test 8/8, and a runtime check against the compiled dist/js output for every rewritten getter (clusterJid/clusterFqdn/ clusterFqdnShort/timeLimit/computeQueue/computePPN/computeNodes/errors/ bucket/filesRootDir) plus the compute setter and getApproximateCharge - all identical to pre-rewrite behavior. Also cross-checked by temporarily copying the rebuilt dist/js into wode's and jode's node_modules and running their own tsc+test suites against it (both green) before restoring their real installed copies via npm ci. --- src/js/compute.ts | 101 +++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/src/js/compute.ts b/src/js/compute.ts index 2c3da89..9e2a797 100644 --- a/src/js/compute.ts +++ b/src/js/compute.ts @@ -1,4 +1,5 @@ -import type { ComputeArgumentsSchema } from "@mat3ra/esse/dist/js/types"; +import { InMemoryEntity } from "@mat3ra/code/dist/js/entity"; +import type { BaseInMemoryEntitySchema, ComputeArgumentsSchema } from "@mat3ra/esse/dist/js/types"; import pluralize from "pluralize"; import { getExternalBucket } from "./default"; @@ -62,22 +63,35 @@ export type ComputedEntityMixin = - T & ComputedEntityMixin; +export type WithComputedEntity< + T, + C extends AnyComputeSchema = ComputeArgumentsSchema | undefined, +> = T & ComputedEntityMixin; -/** What {@link computedEntityMixin} reads off its host besides the mixed-in members themselves. */ -type ComputedEntityHost = { - _json: { compute?: C; isExternal?: boolean; owner?: unknown; workDir?: string }; - _getExternalBucket?: () => ReturnType; - id?: string; - createdAt?: string | number; - workDir?: string; - owner?: { slug?: string; isPersonal?: boolean; serviceLevel?: { nameBasedModifier?: number } }; - prop(name: string, defaultValue?: T): T; - setProp(name: string, value: unknown): void; -}; +/** + * The host schema {@link computedEntityMixin} needs beyond `compute` itself and the base + * `InMemoryEntity` fields (`_id`, `slug`, ...): `owner`/`isExternal`/`createdAt`/`workDir` are not + * part of esse's bare `job`/`workflow` schemas (they come from a host application's own further + * composition, e.g. web-app's `webapp/job`), so a caller's concrete schema only needs to be + * *assignable to* this - which any schema missing these (all-optional) fields already is. + */ +type ComputedEntityHostSchema = BaseInMemoryEntitySchema & + ComputeField & { + isExternal?: boolean; + createdAt?: string | number; + workDir?: string; + owner?: { + slug?: string; + isPersonal?: boolean; + serviceLevel?: { nameBasedModifier?: number }; + }; + }; -type Self = ComputedEntityHost & ComputedEntityMixin; +/** What `this` resolves to inside the property literal below - same idiom as a generated `*SchemaMixin`. */ +type Self> = InMemoryEntity & + ComputedEntityMixin & { + _getExternalBucket?: () => ReturnType; + }; export function getHomeDir(isEnterprise: boolean, username: string): string { return isEnterprise ? `/cluster-???-share/groups/${username}` : `/cluster-???-home/${username}`; @@ -105,44 +119,50 @@ export function getDefaultClusterQuota( } /** - * @param prototype — typically `SomeEntity.prototype` + * @param item — typically `SomeEntity.prototype` */ -export function computedEntityMixin( - prototype: object, -): void { +export function computedEntityMixin< + C extends AnyComputeSchema = ComputeArgumentsSchema | undefined, + S extends ComputedEntityHostSchema = ComputedEntityHostSchema, + T extends InMemoryEntity = InMemoryEntity, +>(item: InMemoryEntity): asserts item is T & ComputedEntityMixin { // @ts-expect-error — same idiom as the generated *SchemaMixin files: this object literal only // supplies ComputedEntityMixin's own members; the host (_json/prop/setProp) comes from - // whatever `prototype` actually is, so `this` below is typed via this const's annotation. - const computedEntityProperties: Self = { + // whatever `item` actually is, so `this` below is typed via this const's annotation. + const computedEntityProperties: Self = { get compute() { - return this.prop("compute"); + return this.prop("compute"); }, + // `setProp`'s signature (`value: S[typeof name]`) isn't call-site generic, so it can't + // narrow to a single field's type when `S` is itself a still-abstract generic type + // parameter (real gap in `code`'s own types) - write directly to `_json` instead, exactly + // like a generated `*SchemaMixin`'s own setter would if it had this same problem. set compute(value) { - this.setProp("compute", value); + this._json.compute = value; }, setCompute(compute) { if (compute.queue === QUEUE_TYPES.debug) delete compute.maxCPU; - this.setProp("compute", compute); + this._json.compute = compute; }, unsetCompute() { - delete this._json.compute; + this.unsetProp("compute"); }, /** * @summary Returns job ID in the Resource Management System. */ get clusterJid() { - return this.prop["jid"]>("compute.cluster.jid"); + return this.compute?.cluster?.jid; }, /** * @summary Returns cluster fqdn, where the job was/will be calculated */ get clusterFqdn() { - return this.prop["fqdn"]>("compute.cluster.fqdn"); + return this.compute?.cluster?.fqdn; }, get clusterFqdnShort() { @@ -153,19 +173,19 @@ export function computedEntityMixin["timeLimit"] | undefined>("compute.timeLimit"); + return this.compute?.timeLimit; }, get computeQueue() { - return this.prop["queue"] | undefined>("compute.queue"); + return this.compute?.queue; }, get computePPN() { - return this.prop("compute.ppn", 1); + return this.compute?.ppn ?? 1; }, get computeNodes() { - return this.prop("compute.nodes", 1); + return this.compute?.nodes ?? 1; }, get computeNodesAndPPN() { @@ -189,7 +209,7 @@ export function computedEntityMixin["errors"]>>("compute.errors", []); + return this.compute?.errors ?? []; }, get hasWarnings() { @@ -244,18 +264,17 @@ export function computedEntityMixin Date: Fri, 21 Aug 2026 00:07:22 +0300 Subject: [PATCH 04/14] refactor(SOF-8032): remove dead filesRootDir getter and its now-unused host fields [release] filesRootDir has zero consumers anywhere in the stack - web-app forked its own safer rewrite (Job.getFilesRootDirClient, taking owner as a param instead of reading this.owner, and using match?.[1] instead of a bare [1]) instead of using this getter, and nothing else calls it (verified via `grep -rn "\.filesRootDir\b"` across every reference/* package and web-app, plus ide's own tests). Dropped createdAt/workDir/owner.slug/owner.isPersonal/this.id from ComputedEntityHostSchema/the implementation along with it - they existed solely to support filesRootDir. owner.serviceLevel.nameBasedModifier stays (getApproximateCharge still reads it). Verified: tsc clean, npm test 8/8, and cross-checked by copying the rebuilt dist/js into wode's and jode's node_modules and running their own tsc+test suites against it (both green) before restoring their real installed copies via npm ci. --- src/js/compute.ts | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/src/js/compute.ts b/src/js/compute.ts index 9e2a797..bc87457 100644 --- a/src/js/compute.ts +++ b/src/js/compute.ts @@ -60,7 +60,6 @@ export type ComputedEntityMixin; readonly isExternalJob: boolean; readonly bucket: string; - readonly filesRootDir: string; }; export type WithComputedEntity< @@ -70,21 +69,15 @@ export type WithComputedEntity< /** * The host schema {@link computedEntityMixin} needs beyond `compute` itself and the base - * `InMemoryEntity` fields (`_id`, `slug`, ...): `owner`/`isExternal`/`createdAt`/`workDir` are not - * part of esse's bare `job`/`workflow` schemas (they come from a host application's own further - * composition, e.g. web-app's `webapp/job`), so a caller's concrete schema only needs to be - * *assignable to* this - which any schema missing these (all-optional) fields already is. + * `InMemoryEntity` fields (`_id`, `slug`, ...): `owner`/`isExternal` are not part of esse's bare + * `job`/`workflow` schemas (they come from a host application's own further composition, e.g. + * web-app's `webapp/job`), so a caller's concrete schema only needs to be *assignable to* this - + * which any schema missing these (all-optional) fields already is. */ type ComputedEntityHostSchema = BaseInMemoryEntitySchema & ComputeField & { isExternal?: boolean; - createdAt?: string | number; - workDir?: string; - owner?: { - slug?: string; - isPersonal?: boolean; - serviceLevel?: { nameBasedModifier?: number }; - }; + owner?: { serviceLevel?: { nameBasedModifier?: number } }; }; /** What `this` resolves to inside the property literal below - same idiom as a generated `*SchemaMixin`. */ @@ -257,23 +250,6 @@ export function computedEntityMixin< } return this.clusterFqdn?.match(/master-(.*).(exabyte.io|mat3ra.com)/)?.[1] ?? ""; }, - - /* - * @summary: returns files root directory. - * For items created before 01/11/2018 00:00:00 UTC, path is started with either /home or /share. - * For items after 01/11/2018 00:00:00 UTC, path is started with either /cluster-00N-home or /cluster-00N-share. - */ - get filesRootDir() { - const owner = this.prop("owner"); - const workDir = this.prop("workDir") ?? ""; - if (this.isExternalJob) return `${owner?.slug}/${this.id}`; - if (new Date(this.prop("createdAt") ?? "").getTime() <= 1515628800000) return workDir; - const clusterAlias = this.clusterFqdn?.match( - /master.*(cluster-.*).(exabyte.io|mat3ra.com)/, - )?.[1]; - const prefix = owner?.isPersonal ? `/${clusterAlias}-home` : `/${clusterAlias}-share`; - return `${prefix}/${workDir.split("/").slice(2).join("/")}`; - }, }; Object.defineProperties(item, Object.getOwnPropertyDescriptors(computedEntityProperties)); From 9be76b68646a435d995d4fb0b85c0f656e5bb82a Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Fri, 21 Aug 2026 00:19:29 +0300 Subject: [PATCH 05/14] refactor(SOF-8032): drop owner/serviceLevel lookup from getApproximateCharge [release] getApproximateCharge read this.prop("owner")?.serviceLevel?.nameBasedModifier directly - owner/serviceLevel with that shape are 100% web-app structures (CoreAccount + ServiceLevel), not something esse's bare job/workflow schema or ide itself has any business knowing about. The only real caller (business_object.ts's ensureSufficientBalance) already loads the owner account right there to check its balance, so it can compute the rate modifier itself. getApproximateCharge now takes `rateModifier` as part of its existing `settings` object (defaults to 1, same as before) instead of reaching for `this.prop("owner")`. owner/serviceLevel dropped from ComputedEntityHostSchema entirely - nothing else in the mixin used them (filesRootDir, the other consumer, was already removed). web-app's own call site needs a paired follow-up (pass `owner.serviceLevel?.nameBasedModifier` as `settings.rateModifier`) - not done here, same as the ide.d.ts-shim/entityTypes.ts cleanup already deferred earlier in this branch's history. Verified: tsc clean, npm test 9/9 (added a regression test for rateModifier), and cross-checked by copying the rebuilt dist/js into wode's and jode's node_modules and running their own tsc+test suites against it (both green) before restoring their real installed copies via npm ci. --- src/js/compute.ts | 11 +++++------ tests/js/compute.test.ts | 8 ++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/js/compute.ts b/src/js/compute.ts index bc87457..14dbb91 100644 --- a/src/js/compute.ts +++ b/src/js/compute.ts @@ -41,7 +41,7 @@ export type ComputedEntityMixin): void; unsetCompute(): void; getApproximateCharge( - settings: { baseChargeRate: number }, + settings: { baseChargeRate: number; rateModifier?: number }, queueMultipliers?: Record | null, ): number; @@ -69,15 +69,14 @@ export type WithComputedEntity< /** * The host schema {@link computedEntityMixin} needs beyond `compute` itself and the base - * `InMemoryEntity` fields (`_id`, `slug`, ...): `owner`/`isExternal` are not part of esse's bare - * `job`/`workflow` schemas (they come from a host application's own further composition, e.g. + * `InMemoryEntity` fields (`_id`, `slug`, ...): `isExternal` is not part of esse's bare + * `job`/`workflow` schemas (it comes from a host application's own further composition, e.g. * web-app's `webapp/job`), so a caller's concrete schema only needs to be *assignable to* this - - * which any schema missing these (all-optional) fields already is. + * which any schema missing it (an optional field) already is. */ type ComputedEntityHostSchema = BaseInMemoryEntitySchema & ComputeField & { isExternal?: boolean; - owner?: { serviceLevel?: { nameBasedModifier?: number } }; }; /** What `this` resolves to inside the property literal below - same idiom as a generated `*SchemaMixin`. */ @@ -202,7 +201,7 @@ export function computedEntityMixin< const queue = this.computeQueue; const queueMultiplier = (queueMultipliers && queue && queueMultipliers[queue]) || 1; - const rateModifier = this.prop("owner")?.serviceLevel?.nameBasedModifier || 1; + const rateModifier = settings.rateModifier || 1; const chargeRate = settings.baseChargeRate * rateModifier * queueMultiplier; return chargeRate * timeLimitInHours * this.computePPN; diff --git a/tests/js/compute.test.ts b/tests/js/compute.test.ts index f7dc093..0d3a03a 100644 --- a/tests/js/compute.test.ts +++ b/tests/js/compute.test.ts @@ -39,6 +39,14 @@ describe("Model", () => { assertApproximateCharge({ queue: "OR", nodes: 1, ppn: 1, timeLimit: "70:00:00" }, 70); }); + it("applies settings.rateModifier to the approximate charge", () => { + const app = new Computer({ + compute: { queue: "D", nodes: 1, ppn: 1, timeLimit: "01:00:00" }, + }); + const charge = app.getApproximateCharge({ baseChargeRate: 1, rateModifier: 2 }, { D: 2 }); + expect(charge).to.equal(4); + }); + it("throws when compute.timeLimit is not set", () => { const app = new Computer({}); expect(() => app.getApproximateCharge({ baseChargeRate: 1 })).to.throw( From ea093567f7555b640c4483adf87e6020c28e6891 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Fri, 21 Aug 2026 01:31:36 +0300 Subject: [PATCH 06/14] refactor(SOF-8032): split computedEntityMixin into computedEntityMixin + infrastructureMixin [release] Audited every member against real usage across web-app, wode, jode, and the designer packages (wove/workflow-designer/job-designer/jove): compute/ setCompute/unsetCompute are genuinely shared (workflow-designer's own Subworkflow.tsx reads/writes subworkflow.compute directly), but clusterJid/clusterFqdn/clusterFqdnShort/timeLimit/computeQueue/computePPN/ computeNodes/computeNodesAndPPN/errors/getApproximateCharge/isExternalJob/ bucket/warnings/hasWarnings have zero non-Job callers anywhere in the stack - wode itself never touches any of them despite merging the type. - computedEntityMixin keeps only what's derived from `compute` itself: compute/setCompute/unsetCompute plus the compute-derived readouts (cluster*, timeLimit, computeQueue, computePPN, computeNodes, computeNodesAndPPN, errors). Makes sense for any entity carrying a compute config - Job, Workflow, or Subworkflow. - New infrastructureMixin (src/js/infrastructure.ts) holds what only makes sense for a job actually running somewhere: getApproximateCharge (billing), bucket/isExternalJob (cloud storage), warnings/hasWarnings. Requires computedEntityMixin already applied to the same host (reads clusterFqdn/ timeLimit/computeQueue/computePPN from it). - Dropped timePrediction entirely - hardcoded `return 0`, zero consumers anywhere, even in ide's own tests. - wode's Workflow.ts/Subworkflow.ts and jode's Job.ts need ZERO changes - verified by cross-checking their own tsc+test suites against the split dist/js before this commit. Neither ever referenced anything now in infrastructureMixin. Only web-app's own imports/jobs/job.ts (which calls job.bucket/job.getApproximateCharge/overrides warnings) needs infrastructureMixin added - separate follow-up commit there. Verified: tsc clean, npm test 9/9, a runtime check against the compiled dist/js output for both mixins together (clusterFqdn -> bucket -> getApproximateCharge -> isExternalJob -> warnings, all identical to pre-split behavior), and cross-checked wode's and jode's own tsc+test suites against the rebuilt dist before this commit. --- src/js/compute.ts | 95 +++++---------------------------- src/js/index.ts | 9 +++- src/js/infrastructure.ts | 111 +++++++++++++++++++++++++++++++++++++++ tests/js/compute.test.ts | 4 +- 4 files changed, 136 insertions(+), 83 deletions(-) create mode 100644 src/js/infrastructure.ts diff --git a/src/js/compute.ts b/src/js/compute.ts index 14dbb91..e8d0339 100644 --- a/src/js/compute.ts +++ b/src/js/compute.ts @@ -2,9 +2,7 @@ import { InMemoryEntity } from "@mat3ra/code/dist/js/entity"; import type { BaseInMemoryEntitySchema, ComputeArgumentsSchema } from "@mat3ra/esse/dist/js/types"; import pluralize from "pluralize"; -import { getExternalBucket } from "./default"; import { QUEUE_TYPES } from "./nodes/enums"; -import { wallTimeToHours } from "./utils/time"; /** * Any compute payload ide can operate on. Defaults to esse's own `compute` arguments schema @@ -17,7 +15,7 @@ import { wallTimeToHours } from "./utils/time"; export type AnyComputeSchema = ComputeArgumentsSchema | undefined; /** `compute` itself, with absence stripped - what every derived getter actually reads. */ -type Compute = NonNullable; +export type Compute = NonNullable; type Cluster = NonNullable["cluster"]>; @@ -31,19 +29,21 @@ type Cluster = NonNullable["cluster"]>; * `undefined`) at once. Toggling it by whether `undefined extends C` is what makes all three * identical to their respective generated schema mixin. */ -type ComputeField = undefined extends C +export type ComputeField = undefined extends C ? { compute?: C } : { compute: C }; -/** Instance API mixed by {@link computedEntityMixin}. */ +/** + * Instance API mixed by {@link computedEntityMixin} - the compute payload itself, plus everything + * that's purely derived from it (cluster info, queue/node/ppn readouts, compute errors). Every + * one of these makes sense for any entity that carries a `compute` config (Job, Workflow, + * Subworkflow) - see {@link import("./infrastructure").infrastructureMixin} for the + * Job-specific behavior (billing, cloud storage bucket, warnings) that doesn't belong here. + */ export type ComputedEntityMixin = ComputeField & { setCompute(compute: Compute): void; unsetCompute(): void; - getApproximateCharge( - settings: { baseChargeRate: number; rateModifier?: number }, - queueMultipliers?: Record | null, - ): number; readonly clusterJid: Cluster["jid"]; readonly clusterFqdn: Cluster["fqdn"]; @@ -53,13 +53,7 @@ export type ComputedEntityMixin["errors"]>; - readonly hasWarnings: boolean; - /** Intentionally not `readonly` - hosts are meant to override this with their own getter. */ - warnings: Array<{ condition: boolean; message: string }>; - readonly isExternalJob: boolean; - readonly bucket: string; }; export type WithComputedEntity< @@ -68,22 +62,15 @@ export type WithComputedEntity< > = T & ComputedEntityMixin; /** - * The host schema {@link computedEntityMixin} needs beyond `compute` itself and the base - * `InMemoryEntity` fields (`_id`, `slug`, ...): `isExternal` is not part of esse's bare - * `job`/`workflow` schemas (it comes from a host application's own further composition, e.g. - * web-app's `webapp/job`), so a caller's concrete schema only needs to be *assignable to* this - - * which any schema missing it (an optional field) already is. + * The host schema {@link computedEntityMixin} needs beyond the base `InMemoryEntity` fields + * (`_id`, `slug`, ...): just `compute` itself. */ -type ComputedEntityHostSchema = BaseInMemoryEntitySchema & - ComputeField & { - isExternal?: boolean; - }; +export type ComputedEntityHostSchema = BaseInMemoryEntitySchema & + ComputeField; /** What `this` resolves to inside the property literal below - same idiom as a generated `*SchemaMixin`. */ type Self> = InMemoryEntity & - ComputedEntityMixin & { - _getExternalBucket?: () => ReturnType; - }; + ComputedEntityMixin; export function getHomeDir(isEnterprise: boolean, username: string): string { return isEnterprise ? `/cluster-???-share/groups/${username}` : `/cluster-???-home/${username}`; @@ -192,63 +179,9 @@ export function computedEntityMixin< ); }, - getApproximateCharge(settings, queueMultipliers = null) { - const { timeLimit } = this; - if (!timeLimit) { - throw new Error("getApproximateCharge: compute.timeLimit is not set"); - } - const timeLimitInHours = wallTimeToHours(timeLimit); - - const queue = this.computeQueue; - const queueMultiplier = (queueMultipliers && queue && queueMultipliers[queue]) || 1; - const rateModifier = settings.rateModifier || 1; - const chargeRate = settings.baseChargeRate * rateModifier * queueMultiplier; - - return chargeRate * timeLimitInHours * this.computePPN; - }, - - // eslint-disable-next-line class-methods-use-this - get timePrediction() { - return 0; - }, - get errors() { return this.compute?.errors ?? []; }, - - get hasWarnings() { - return this.warnings.map((o) => o.condition).some((x) => x); - }, - - /* - * Array of warning Objects: [{condition: Boolean, message: String}]. Computed in-memory per Entity. - */ - // eslint-disable-next-line class-methods-use-this - get warnings() { - return []; - }, - - set warnings(_value) { - // Intentionally a no-op: hosts override this getter+setter pair wholesale to supply - // their own warnings; this mixin's own value is always the empty-array default above. - }, - - get isExternalJob() { - return this.prop("isExternal", false); - }, - - /** - * @summary Returns the bucket name for this object storage items. Bucket name is constructed from cluster FQDN. - * @example master-vagrant-cluster-001.exabyte.io ==> vagrant-cluster-001 - */ - get bucket() { - if (this.isExternalJob) { - return this._getExternalBucket - ? this._getExternalBucket().name - : getExternalBucket().name; - } - return this.clusterFqdn?.match(/master-(.*).(exabyte.io|mat3ra.com)/)?.[1] ?? ""; - }, }; Object.defineProperties(item, Object.getOwnPropertyDescriptors(computedEntityProperties)); diff --git a/src/js/index.ts b/src/js/index.ts index bd4afb4..5effd7d 100644 --- a/src/js/index.ts +++ b/src/js/index.ts @@ -1,7 +1,14 @@ export { computedEntityMixin, getDefaultClusterQuota, getHomeDir } from "./compute"; -export type { AnyComputeSchema, ComputedEntityMixin, WithComputedEntity } from "./compute"; +export type { + AnyComputeSchema, + ComputedEntityHostSchema, + ComputedEntityMixin, + WithComputedEntity, +} from "./compute"; export { getDefaultComputeConfig, getExternalBucket } from "./default"; export type { ExternalBucket } from "./default"; +export { infrastructureMixin } from "./infrastructure"; +export type { InfrastructureHostSchema, InfrastructureMixin } from "./infrastructure"; export { EMAIL_NOTIFICATION_OPTIONS_PBS, EMAIL_NOTIFICATIONS } from "./enums"; export { QUEUE_TYPES, QUEUE_DISPLAY, ETA, TIME_LIMIT_TYPES, IS_RESTARTABLE } from "./nodes/enums"; diff --git a/src/js/infrastructure.ts b/src/js/infrastructure.ts new file mode 100644 index 0000000..cbca0ff --- /dev/null +++ b/src/js/infrastructure.ts @@ -0,0 +1,111 @@ +import { InMemoryEntity } from "@mat3ra/code/dist/js/entity"; +import type { BaseInMemoryEntitySchema } from "@mat3ra/esse/dist/js/types"; + +import type { AnyComputeSchema, ComputedEntityMixin } from "./compute"; +import { getExternalBucket } from "./default"; +import { wallTimeToHours } from "./utils/time"; + +/** + * Instance API mixed by {@link infrastructureMixin} - the parts of the old, single + * `computedEntityMixin` that aren't derived from `compute` itself and only make sense for a job + * actually running somewhere (billing, cloud storage bucket, execution warnings) - as opposed to + * {@link import("./compute").ComputedEntityMixin}'s `compute`-derived readouts, which apply + * equally to a Workflow/Subworkflow template that never runs on its own. + * + * Requires {@link ComputedEntityMixin} to already be mixed into the same host - reads + * `clusterFqdn`/`timeLimit`/`computeQueue`/`computePPN` from it. + */ +export type InfrastructureMixin = { + getApproximateCharge( + settings: { baseChargeRate: number; rateModifier?: number }, + queueMultipliers?: Record | null, + ): number; + + readonly hasWarnings: boolean; + /** Intentionally not `readonly` - hosts are meant to override this with their own getter. */ + warnings: Array<{ condition: boolean; message: string }>; + readonly isExternalJob: boolean; + readonly bucket: string; +}; + +/** + * The host schema {@link infrastructureMixin} needs beyond the base `InMemoryEntity` fields: + * `isExternal` is not part of esse's bare `job` schema (it comes from a host application's own + * further composition, e.g. web-app's `webapp/job`), so a caller's concrete schema only needs to + * be *assignable to* this - which any schema missing it (an optional field) already is. + */ +export type InfrastructureHostSchema = BaseInMemoryEntitySchema & { isExternal?: boolean }; + +/** What `this` resolves to inside the property literal below - same idiom as a generated `*SchemaMixin`. */ +type Self = InMemoryEntity & + ComputedEntityMixin & + InfrastructureMixin & { + _getExternalBucket?: () => ReturnType; + }; + +/** + * @param item — typically `SomeEntity.prototype`, and must already have {@link computedEntityMixin} + * applied to it. + */ +export function infrastructureMixin< + C extends AnyComputeSchema = undefined, + S extends InfrastructureHostSchema = InfrastructureHostSchema, + T extends InMemoryEntity = InMemoryEntity, +>(item: InMemoryEntity): asserts item is T & InfrastructureMixin { + // @ts-expect-error — same idiom as the generated *SchemaMixin files: this object literal only + // supplies InfrastructureMixin's own members; the host (_json/prop/setProp, plus + // ComputedEntityMixin's clusterFqdn/timeLimit/computeQueue/computePPN) comes from whatever + // `item` actually is, so `this` below is typed via this const's annotation. + const infrastructureProperties: Self = { + getApproximateCharge(settings, queueMultipliers = null) { + const { timeLimit } = this; + if (!timeLimit) { + throw new Error("getApproximateCharge: compute.timeLimit is not set"); + } + const timeLimitInHours = wallTimeToHours(timeLimit); + + const queue = this.computeQueue; + const queueMultiplier = (queueMultipliers && queue && queueMultipliers[queue]) || 1; + const rateModifier = settings.rateModifier || 1; + const chargeRate = settings.baseChargeRate * rateModifier * queueMultiplier; + + return chargeRate * timeLimitInHours * this.computePPN; + }, + + get hasWarnings() { + return this.warnings.map((o) => o.condition).some((x) => x); + }, + + /* + * Array of warning Objects: [{condition: Boolean, message: String}]. Computed in-memory per Entity. + */ + // eslint-disable-next-line class-methods-use-this + get warnings() { + return []; + }, + + set warnings(_value) { + // Intentionally a no-op: hosts override this getter+setter pair wholesale to supply + // their own warnings; this mixin's own value is always the empty-array default above. + }, + + get isExternalJob() { + return this.prop("isExternal", false); + }, + + /** + * @summary Returns the bucket name for this object storage items. Bucket name is constructed from cluster FQDN. + * @example master-vagrant-cluster-001.exabyte.io ==> vagrant-cluster-001 + */ + get bucket() { + if (this.isExternalJob) { + return this._getExternalBucket + ? this._getExternalBucket().name + : getExternalBucket().name; + } + return this.clusterFqdn?.match(/master-(.*).(exabyte.io|mat3ra.com)/)?.[1] ?? ""; + }, + }; + + Object.defineProperties(item, Object.getOwnPropertyDescriptors(infrastructureProperties)); +} diff --git a/tests/js/compute.test.ts b/tests/js/compute.test.ts index 0d3a03a..67b3532 100644 --- a/tests/js/compute.test.ts +++ b/tests/js/compute.test.ts @@ -4,16 +4,18 @@ import { expect } from "chai"; import { type ComputedEntityMixin, computedEntityMixin } from "../../src/js/compute"; import { getDefaultComputeConfig } from "../../src/js/default"; +import { type InfrastructureMixin, infrastructureMixin } from "../../src/js/infrastructure"; type ComputerSchema = BaseInMemoryEntitySchema & { compute?: ComputeArgumentsSchema }; // This interface merge is the type-level regression test for the mixin's declaration-merge // pattern: `compute` must be assignable to `ComputeArgumentsSchema | undefined`, mutable, and // identical across every interface that declares it (see `ComputedEntityMixin`'s own docs). -interface Computer extends ComputedEntityMixin {} +interface Computer extends ComputedEntityMixin, InfrastructureMixin {} class Computer extends InMemoryEntity {} computedEntityMixin(Computer.prototype); +infrastructureMixin(Computer.prototype); function assertApproximateCharge(compute: ComputeArgumentsSchema, expectedCharge: number) { const settings = { baseChargeRate: 1 }; From 17e163b5c4180ce15a7d2a406a02f7248212e44d Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Fri, 21 Aug 2026 01:40:01 +0300 Subject: [PATCH 07/14] refactor(SOF-8032): drop warnings/isExternal from infrastructureMixin [release] Both were being silently shadowed in web-app's own imports/jobs/job.ts: computedEntityMixin(Job.prototype) runs after the class body, and Object.defineProperties always overwrites, so it was already clobbering job.ts's own get bucket()/get warnings() (verified empirically) - meaning job.warnings has ALWAYS returned [] in production and the hasBeenActiveMoreThanOnce message never actually surfaced. Rather than reorder application to make the mixin's own no-op warnings/isExternal-aware bucket win, dropping them here lets the host's own override take effect at all, and lets isExternal-based job bucketing be retired outright. - warnings/hasWarnings removed entirely - InfrastructureMixin now only has getApproximateCharge and bucket. web-app's own job.ts's warnings override (real behavior, no longer shadowed) is a separate follow-up commit there. - isExternalJob removed; bucket no longer branches on it - just clusterFqdn-derived, always. getExternalBucket/ExternalBucket (default.ts) dropped too - existed solely for the removed branch, zero consumers anywhere in the stack (verified). - wode/jode need zero changes (re-verified) - neither used warnings/ isExternalJob/bucket. Verified: tsc clean, npm test 9/9, runtime check against compiled dist/js confirming isExternalJob/warnings are gone and bucket still resolves correctly, plus wode's and jode's own tsc+test suites cross-checked against the rebuilt dist before this commit. --- src/js/default.ts | 14 ------------- src/js/index.ts | 3 +-- src/js/infrastructure.ts | 45 +++------------------------------------- 3 files changed, 4 insertions(+), 58 deletions(-) diff --git a/src/js/default.ts b/src/js/default.ts index 353bb50..7503219 100644 --- a/src/js/default.ts +++ b/src/js/default.ts @@ -12,17 +12,3 @@ export function getDefaultComputeConfig(): ComputeArgumentsSchema { }, }; } - -export interface ExternalBucket { - name: string; - provider: string; - region: string; -} - -export function getExternalBucket(): ExternalBucket { - return { - name: "", - provider: "", - region: "", - }; -} diff --git a/src/js/index.ts b/src/js/index.ts index 5effd7d..28035de 100644 --- a/src/js/index.ts +++ b/src/js/index.ts @@ -5,8 +5,7 @@ export type { ComputedEntityMixin, WithComputedEntity, } from "./compute"; -export { getDefaultComputeConfig, getExternalBucket } from "./default"; -export type { ExternalBucket } from "./default"; +export { getDefaultComputeConfig } from "./default"; export { infrastructureMixin } from "./infrastructure"; export type { InfrastructureHostSchema, InfrastructureMixin } from "./infrastructure"; export { EMAIL_NOTIFICATION_OPTIONS_PBS, EMAIL_NOTIFICATIONS } from "./enums"; diff --git a/src/js/infrastructure.ts b/src/js/infrastructure.ts index cbca0ff..f65630b 100644 --- a/src/js/infrastructure.ts +++ b/src/js/infrastructure.ts @@ -2,13 +2,12 @@ import { InMemoryEntity } from "@mat3ra/code/dist/js/entity"; import type { BaseInMemoryEntitySchema } from "@mat3ra/esse/dist/js/types"; import type { AnyComputeSchema, ComputedEntityMixin } from "./compute"; -import { getExternalBucket } from "./default"; import { wallTimeToHours } from "./utils/time"; /** * Instance API mixed by {@link infrastructureMixin} - the parts of the old, single * `computedEntityMixin` that aren't derived from `compute` itself and only make sense for a job - * actually running somewhere (billing, cloud storage bucket, execution warnings) - as opposed to + * actually running somewhere (billing, cloud storage bucket) - as opposed to * {@link import("./compute").ComputedEntityMixin}'s `compute`-derived readouts, which apply * equally to a Workflow/Subworkflow template that never runs on its own. * @@ -21,27 +20,15 @@ export type InfrastructureMixin = { queueMultipliers?: Record | null, ): number; - readonly hasWarnings: boolean; - /** Intentionally not `readonly` - hosts are meant to override this with their own getter. */ - warnings: Array<{ condition: boolean; message: string }>; - readonly isExternalJob: boolean; readonly bucket: string; }; -/** - * The host schema {@link infrastructureMixin} needs beyond the base `InMemoryEntity` fields: - * `isExternal` is not part of esse's bare `job` schema (it comes from a host application's own - * further composition, e.g. web-app's `webapp/job`), so a caller's concrete schema only needs to - * be *assignable to* this - which any schema missing it (an optional field) already is. - */ -export type InfrastructureHostSchema = BaseInMemoryEntitySchema & { isExternal?: boolean }; +export type InfrastructureHostSchema = BaseInMemoryEntitySchema; /** What `this` resolves to inside the property literal below - same idiom as a generated `*SchemaMixin`. */ type Self = InMemoryEntity & ComputedEntityMixin & - InfrastructureMixin & { - _getExternalBucket?: () => ReturnType; - }; + InfrastructureMixin; /** * @param item — typically `SomeEntity.prototype`, and must already have {@link computedEntityMixin} @@ -72,37 +59,11 @@ export function infrastructureMixin< return chargeRate * timeLimitInHours * this.computePPN; }, - get hasWarnings() { - return this.warnings.map((o) => o.condition).some((x) => x); - }, - - /* - * Array of warning Objects: [{condition: Boolean, message: String}]. Computed in-memory per Entity. - */ - // eslint-disable-next-line class-methods-use-this - get warnings() { - return []; - }, - - set warnings(_value) { - // Intentionally a no-op: hosts override this getter+setter pair wholesale to supply - // their own warnings; this mixin's own value is always the empty-array default above. - }, - - get isExternalJob() { - return this.prop("isExternal", false); - }, - /** * @summary Returns the bucket name for this object storage items. Bucket name is constructed from cluster FQDN. * @example master-vagrant-cluster-001.exabyte.io ==> vagrant-cluster-001 */ get bucket() { - if (this.isExternalJob) { - return this._getExternalBucket - ? this._getExternalBucket().name - : getExternalBucket().name; - } return this.clusterFqdn?.match(/master-(.*).(exabyte.io|mat3ra.com)/)?.[1] ?? ""; }, }; From 6c4177b3597a9fe7e09ea0e57ecd263f24b646ef Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Fri, 21 Aug 2026 18:13:13 +0300 Subject: [PATCH 08/14] fix(SOF-8032): tie timeLimit/computeQueue optionality to compute's own, not unconditional [release] Spotted by inspection: timeLimit/computeQueue were typed `Compute["timeLimit"] | undefined` / `Compute["queue"] | undefined` unconditionally, regardless of whether `compute` itself might be absent - even though esse's ComputeArgumentsSchema requires queue/nodes/ppn/ timeLimit *within* compute (required: ["queue","nodes","ppn","timeLimit"]). So for Job (compute required, no undefined in C), job.timeLimit was typed `string | undefined` even though it can only actually be undefined if compute itself is missing - which the type already promises won't happen. clusterJid/clusterFqdn didn't have this bug: `cluster` genuinely is optional *within* compute (not in that required list), so `Cluster["jid"]`/`["fqdn"]` already carry `| undefined` correctly from the schema itself, no manual suffix needed. computePPN/computeNodes/errors don't have it either - their getters have `?? default` fallbacks (`this.compute?.ppn ?? 1`, `?? []`), so they're genuinely never undefined regardless of whether compute exists. - New `RequiredWithCompute` - same `undefined extends C` conditional `ComputeField` already uses, applied one level down: `V` when compute is guaranteed present, `V | undefined` when it might not be. - timeLimit/computeQueue now use it instead of a bare `| undefined` suffix. Verified: tsc clean, npm test 10/10 (added a compile-time regression test: required-compute's timeLimit assigns to plain `string` cleanly, optional-compute's needs @ts-expect-error against the same assignment), and cross-checked wode's and jode's own tsc+test suites against the rebuilt dist before this commit (Workflow/Subworkflow's timeLimit correctly stays `string | undefined`; Job's is now plain `string`). --- src/js/compute.ts | 13 +++++++++++-- tests/js/compute.test.ts | 29 ++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/js/compute.ts b/src/js/compute.ts index e8d0339..8d0f929 100644 --- a/src/js/compute.ts +++ b/src/js/compute.ts @@ -33,6 +33,15 @@ export type ComputeField = undefined extends C ? { compute?: C } : { compute: C }; +/** + * `V`, unless `compute` itself might be absent (`undefined extends C`) - then `V | undefined`. + * `queue`/`nodes`/`ppn`/`timeLimit` are all required *within* `ComputeArgumentsSchema` (whenever + * `compute` exists, they exist too), so their own `| undefined` must come from `compute`'s + * optionality, not be tacked on unconditionally - same reasoning as {@link ComputeField}, just + * applied one level down instead of to `compute` itself. + */ +type RequiredWithCompute = undefined extends C ? V | undefined : V; + /** * Instance API mixed by {@link computedEntityMixin} - the compute payload itself, plus everything * that's purely derived from it (cluster info, queue/node/ppn readouts, compute errors). Every @@ -48,8 +57,8 @@ export type ComputedEntityMixin["jid"]; readonly clusterFqdn: Cluster["fqdn"]; readonly clusterFqdnShort: string; - readonly timeLimit: Compute["timeLimit"] | undefined; - readonly computeQueue: Compute["queue"] | undefined; + readonly timeLimit: RequiredWithCompute["timeLimit"]>; + readonly computeQueue: RequiredWithCompute["queue"]>; readonly computePPN: number; readonly computeNodes: number; readonly computeNodesAndPPN: string; diff --git a/tests/js/compute.test.ts b/tests/js/compute.test.ts index 67b3532..37ce778 100644 --- a/tests/js/compute.test.ts +++ b/tests/js/compute.test.ts @@ -11,12 +11,23 @@ type ComputerSchema = BaseInMemoryEntitySchema & { compute?: ComputeArgumentsSch // This interface merge is the type-level regression test for the mixin's declaration-merge // pattern: `compute` must be assignable to `ComputeArgumentsSchema | undefined`, mutable, and // identical across every interface that declares it (see `ComputedEntityMixin`'s own docs). -interface Computer extends ComputedEntityMixin, InfrastructureMixin {} +interface Computer + extends ComputedEntityMixin, + InfrastructureMixin {} class Computer extends InMemoryEntity {} computedEntityMixin(Computer.prototype); infrastructureMixin(Computer.prototype); +// `queue`/`nodes`/`ppn`/`timeLimit` are required *within* ComputeArgumentsSchema, so once +// `compute` itself is guaranteed present (a required-compute schema, as Job's is), they must be +// too - not `| undefined` regardless, as they'd be for a schema where `compute` itself may be +// absent (Workflow/Subworkflow's `compute?`). +type RequiredComputeSchema = BaseInMemoryEntitySchema & { compute: ComputeArgumentsSchema }; +interface RequiredComputer extends ComputedEntityMixin {} +class RequiredComputer extends InMemoryEntity {} +computedEntityMixin(RequiredComputer.prototype); + function assertApproximateCharge(compute: ComputeArgumentsSchema, expectedCharge: number) { const settings = { baseChargeRate: 1 }; const queueMultipliers = { @@ -61,4 +72,20 @@ describe("Model", () => { app.compute = { queue: "D", nodes: 1, ppn: 1, timeLimit: "01:00:00" }; expect(app.compute?.queue).to.equal("D"); }); + + it("types timeLimit as plain string when compute itself is required, string | undefined otherwise", () => { + const required = new RequiredComputer({ + compute: { queue: "D", nodes: 1, ppn: 1, timeLimit: "01:00:00" }, + }); + // Required case: assigning to plain `string` must compile cleanly - no @ts-expect-error. + const requiredTimeLimit: string = required.timeLimit; + expect(requiredTimeLimit).to.equal("01:00:00"); + + const optional = new Computer({}); + // Optional case (Computer's compute is `ComputeArgumentsSchema | undefined`): timeLimit + // stays `string | undefined`, so the same assignment must fail to compile. + // @ts-expect-error - Computer's timeLimit is string | undefined, not string + const optionalTimeLimit: string = optional.timeLimit; + expect(optionalTimeLimit).to.equal(undefined); + }); }); From b47eeb3fe966837cf318bd1dcaedf29b35f6a3a6 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Tue, 8 Sep 2026 23:28:36 +0300 Subject: [PATCH 09/14] feat(SOF-8032): move Queue to ide, DI'd for queueSettings and jobs [release] The host app's Queue class mixed client-safe schema-derived getters with a getETA() that queried its jobs collection directly, which meant every consumer of Queue (including client-side rendering) pulled in a Mongo/Meteor dependency. Move the whole class here instead, with queueSettings and jobs passed to the constructor rather than reached for via a global - keeps this class free of any host-app-specific or Mongo/Meteor dependency, safe to construct on both client and server. Co-Authored-By: Claude Sonnet 5 --- src/js/index.ts | 2 + src/js/nodes/queue.ts | 178 +++++++++++++++++++++++++++++++++++++++++ tests/js/queue.test.ts | 124 ++++++++++++++++++++++++++++ 3 files changed, 304 insertions(+) create mode 100644 src/js/nodes/queue.ts create mode 100644 tests/js/queue.test.ts diff --git a/src/js/index.ts b/src/js/index.ts index 28035de..0fb551a 100644 --- a/src/js/index.ts +++ b/src/js/index.ts @@ -10,6 +10,8 @@ export { infrastructureMixin } from "./infrastructure"; export type { InfrastructureHostSchema, InfrastructureMixin } from "./infrastructure"; export { EMAIL_NOTIFICATION_OPTIONS_PBS, EMAIL_NOTIFICATIONS } from "./enums"; export { QUEUE_TYPES, QUEUE_DISPLAY, ETA, TIME_LIMIT_TYPES, IS_RESTARTABLE } from "./nodes/enums"; +export { default as Queue } from "./nodes/queue"; +export type { QueueHostSchema, QueueSettings, QueueJobFinder } from "./nodes/queue"; export { wallTimeTo, diff --git a/src/js/nodes/queue.ts b/src/js/nodes/queue.ts new file mode 100644 index 0000000..1f16288 --- /dev/null +++ b/src/js/nodes/queue.ts @@ -0,0 +1,178 @@ +import { InMemoryEntity } from "@mat3ra/code/dist/js/entity"; +import type { AnyObject } from "@mat3ra/esse/dist/js/esse/types"; +import type { BaseInMemoryEntitySchema, QueueSchema } from "@mat3ra/esse/dist/js/types"; + +import { ETA, QUEUE_DISPLAY } from "./enums"; + +export type QueueHostSchema = BaseInMemoryEntitySchema & QueueSchema & { hostname: string }; + +/** Per-queue admin-configured override, e.g. a host app's own cluster settings entry. */ +export type QueueSettings = { + displayName?: string; + maxPPN?: number; +}; + +/** + * Minimal shape {@link Queue.getETA} needs from the host app's jobs store - deliberately not + * `Mongo.Collection`, so this class carries no Meteor/Mongo dependency of its own; a real + * collection satisfies this structurally, no adapter needed. + */ +export interface QueueJobFinder { + findOneAsync(selector: { + "compute.queue": string; + startTime: string; + }): Promise<{ startTime?: string } | null | undefined>; +} + +const sortedETA = Object.values(ETA).sort((a, b) => b.order - a.order); + +/** + * A cluster queue. `queueSettings` (admin-configured per-queue overrides - a friendlier display + * name, a premium maxPPN) and `jobs` (used only by {@link getETA}, to estimate wait time from + * currently submitted jobs) are both passed in rather than reached for via a host-app-specific + * singleton or global collection - keeps this class free of any such dependency, so it's equally + * safe to construct on the client (where `jobs` is simply omitted - there's no live jobs store to + * query there) and on the server (where the host app passes its own jobs collection). + */ +class Queue extends InMemoryEntity implements QueueSchema { + declare _json: QueueHostSchema & AnyObject; + + private readonly queueSettings?: QueueSettings; + + private readonly jobs?: QueueJobFinder; + + constructor( + hostname: string, + config: QueueSchema, + queueSettings?: QueueSettings, + jobs?: QueueJobFinder, + ) { + // Ensure `displayName` is always present (schema field) even if backend omits it. + const displayName = + config.displayName || + queueSettings?.displayName || + (config.name in QUEUE_DISPLAY ? QUEUE_DISPLAY[config.name] : config.name); + + super({ ...config, hostname, displayName }); + this.queueSettings = queueSettings; + this.jobs = jobs; + } + + get name() { + return this.requiredProp("name"); + } + + set name(value) { + this.setProp("name", value); + } + + get maxNodes() { + return this.requiredProp("maxNodes"); + } + + set maxNodes(value) { + this.setProp("maxNodes", value); + } + + get availableNodes() { + return this.requiredProp("availableNodes"); + } + + set availableNodes(value) { + this.setProp("availableNodes", value); + } + + get currentNodes() { + return this.requiredProp("currentNodes"); + } + + set currentNodes(value) { + this.setProp("currentNodes", value); + } + + get capacity() { + return this.prop("capacity"); + } + + set capacity(value) { + this.setProp("capacity", value); + } + + get displayName() { + return this.prop("displayName"); + } + + set displayName(value) { + this.setProp("displayName", value); + } + + get hostname(): string { + return this.requiredProp("hostname"); + } + + get maxAvailableNodect(): number { + return this.availableNodes; + } + + get currentNodect(): number { + return this.currentNodes; + } + + get nodeLimit(): number { + return this.maxNodes; + } + + get load(): number { + // eslint-disable-next-line eqeqeq + return this.maxAvailableNodect == 0 ? 100 : (this.currentNodect / this.nodeLimit) * 100; + } + + get maxPPN(): number { + return this.queueSettings?.maxPPN ?? this.requiredProp("maxPPN"); + } + + set maxPPN(value) { + this.setProp("maxPPN", value); + } + + /** + * @summary Maximum CPU count per node. This parameter is used to let backend job submission + * infrastructure know that this job is to be charged for the maximum CPU per + * node instead of the actual ppn. For premium/fast queues where resources are + * provisioned on-demand and exclusively per user. + */ + get defaultMaxPPN(): number { + return this.maxPPN; + } + + /** + * @summary Estimates this queue's wait time from currently submitted jobs. Resolves to + * `ETA.withinFiveMin` (same fallback as "nothing queued") if no `jobs` finder was supplied + * at construction - the client-side case, where there's no live jobs store to query. + */ + async getETA() { + // eslint-disable-next-line eqeqeq + if (this.maxAvailableNodect == 0) { + return ETA.moreThanHour; + } + if (!this.jobs) { + return ETA.withinFiveMin; + } + + const { jobs, name } = this; + const job = await sortedETA.reduce>( + async (acc, value) => { + const job = await acc; + if (job) { + return job; + } + return jobs.findOneAsync({ "compute.queue": name, startTime: value.display }); + }, + Promise.resolve(undefined), + ); + + return Object.values(ETA).find((x) => x.display === job?.startTime) || ETA.withinFiveMin; + } +} + +export default Queue; diff --git a/tests/js/queue.test.ts b/tests/js/queue.test.ts new file mode 100644 index 0000000..3c9c754 --- /dev/null +++ b/tests/js/queue.test.ts @@ -0,0 +1,124 @@ +import type { QueueSchema } from "@mat3ra/esse/dist/js/types"; +import { expect } from "chai"; + +import Queue, { type QueueJobFinder, type QueueSettings } from "../../src/js/nodes/queue"; + +function makeQueue( + config: Partial = {}, + queueSettings?: QueueSettings, + jobs?: QueueJobFinder, +) { + return new Queue( + "cluster-001", + { + name: "OR", + maxPPN: 16, + maxNodes: 10, + availableNodes: 10, + currentNodes: 0, + ...config, + }, + queueSettings, + jobs, + ); +} + +describe("Queue", () => { + it("derives maxAvailableNodect/currentNodect/nodeLimit from the schema's own fields", () => { + const queue = makeQueue({ availableNodes: 7, currentNodes: 3, maxNodes: 10 }); + expect(queue.maxAvailableNodect).to.equal(7); + expect(queue.currentNodect).to.equal(3); + expect(queue.nodeLimit).to.equal(10); + }); + + it("exposes the hostname it was constructed with", () => { + const queue = new Queue("cluster-002", { + name: "OR", + maxPPN: 16, + maxNodes: 10, + availableNodes: 10, + currentNodes: 0, + }); + expect(queue.hostname).to.equal("cluster-002"); + }); + + it("reports 100% load when no nodes are available", () => { + const queue = makeQueue({ availableNodes: 0, currentNodes: 0, maxNodes: 10 }); + expect(queue.load).to.equal(100); + }); + + it("computes load as a percentage of nodes in use out of the node limit", () => { + const queue = makeQueue({ availableNodes: 5, currentNodes: 5, maxNodes: 10 }); + expect(queue.load).to.equal(50); + }); + + it("falls back to the schema's own name for displayName when nothing else provides one", () => { + const queue = makeQueue({ name: "OR" }); + expect(queue.displayName).to.equal("ordinary regular (OR)"); + }); + + it("prefers the config's own displayName over everything else", () => { + const queue = makeQueue({ displayName: "Custom name" }); + expect(queue.displayName).to.equal("Custom name"); + }); + + it("prefers queueSettings.displayName over the QUEUE_DISPLAY fallback", () => { + const queue = makeQueue({}, { displayName: "Settings name" }); + expect(queue.displayName).to.equal("Settings name"); + }); + + it("uses the schema's own maxPPN when queueSettings doesn't override it", () => { + const queue = makeQueue({ maxPPN: 8 }); + expect(queue.maxPPN).to.equal(8); + expect(queue.defaultMaxPPN).to.equal(8); + }); + + it("prefers queueSettings.maxPPN over the schema's own value", () => { + const queue = makeQueue({ maxPPN: 8 }, { maxPPN: 32 }); + expect(queue.maxPPN).to.equal(32); + expect(queue.defaultMaxPPN).to.equal(32); + }); + + describe("getETA", () => { + it("returns 'more than 1 hour' when no nodes are available, without consulting jobs", async () => { + const queue = makeQueue( + { availableNodes: 0, currentNodes: 0, maxNodes: 10 }, + undefined, + { findOneAsync: () => Promise.reject(new Error("should not be called")) }, + ); + expect((await queue.getETA()).display).to.equal("more than 1 hour"); + }); + + it("returns 'less than 5 min' when no jobs finder was supplied (the client-side case)", async () => { + const queue = makeQueue({ availableNodes: 5, currentNodes: 5, maxNodes: 10 }); + expect((await queue.getETA()).display).to.equal("less than 5 min"); + }); + + it("returns 'less than 5 min' when the jobs finder has nothing queued for this queue", async () => { + const queue = makeQueue( + { availableNodes: 5, currentNodes: 5, maxNodes: 10 }, + undefined, + { + findOneAsync: () => Promise.resolve(undefined), + }, + ); + expect((await queue.getETA()).display).to.equal("less than 5 min"); + }); + + it("matches the highest-order ETA among currently submitted jobs for this queue", async () => { + const queue = makeQueue( + { availableNodes: 5, currentNodes: 5, maxNodes: 10 }, + undefined, + { + findOneAsync: (selector) => + Promise.resolve( + selector.startTime === "within 1 hour" + ? { startTime: "within 1 hour" } + : undefined, + ), + }, + ); + expect((await queue.getETA()).display).to.equal("within 1 hour"); + }); + }); +}); From 800db23952415ab90f94eb379ed7b756b1d6e647 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Tue, 8 Sep 2026 23:44:43 +0300 Subject: [PATCH 10/14] refactor(SOF-8032): drop Queue.getETA and its jobs DI [release] getETA had exactly one caller in the host app, which now implements it itself as a private use-case method operating on the jobs collection it already has direct access to. Queue no longer needs a jobs finder at all, simplifying it back down to just queueSettings DI. Co-Authored-By: Claude Sonnet 5 --- src/js/index.ts | 2 +- src/js/nodes/queue.ts | 65 +++++------------------------------------- tests/js/queue.test.ts | 52 ++------------------------------- 3 files changed, 10 insertions(+), 109 deletions(-) diff --git a/src/js/index.ts b/src/js/index.ts index 0fb551a..4755f55 100644 --- a/src/js/index.ts +++ b/src/js/index.ts @@ -11,7 +11,7 @@ export type { InfrastructureHostSchema, InfrastructureMixin } from "./infrastruc export { EMAIL_NOTIFICATION_OPTIONS_PBS, EMAIL_NOTIFICATIONS } from "./enums"; export { QUEUE_TYPES, QUEUE_DISPLAY, ETA, TIME_LIMIT_TYPES, IS_RESTARTABLE } from "./nodes/enums"; export { default as Queue } from "./nodes/queue"; -export type { QueueHostSchema, QueueSettings, QueueJobFinder } from "./nodes/queue"; +export type { QueueHostSchema, QueueSettings } from "./nodes/queue"; export { wallTimeTo, diff --git a/src/js/nodes/queue.ts b/src/js/nodes/queue.ts index 1f16288..1b820e8 100644 --- a/src/js/nodes/queue.ts +++ b/src/js/nodes/queue.ts @@ -2,7 +2,7 @@ import { InMemoryEntity } from "@mat3ra/code/dist/js/entity"; import type { AnyObject } from "@mat3ra/esse/dist/js/esse/types"; import type { BaseInMemoryEntitySchema, QueueSchema } from "@mat3ra/esse/dist/js/types"; -import { ETA, QUEUE_DISPLAY } from "./enums"; +import { QUEUE_DISPLAY } from "./enums"; export type QueueHostSchema = BaseInMemoryEntitySchema & QueueSchema & { hostname: string }; @@ -12,41 +12,20 @@ export type QueueSettings = { maxPPN?: number; }; -/** - * Minimal shape {@link Queue.getETA} needs from the host app's jobs store - deliberately not - * `Mongo.Collection`, so this class carries no Meteor/Mongo dependency of its own; a real - * collection satisfies this structurally, no adapter needed. - */ -export interface QueueJobFinder { - findOneAsync(selector: { - "compute.queue": string; - startTime: string; - }): Promise<{ startTime?: string } | null | undefined>; -} - -const sortedETA = Object.values(ETA).sort((a, b) => b.order - a.order); - /** * A cluster queue. `queueSettings` (admin-configured per-queue overrides - a friendlier display - * name, a premium maxPPN) and `jobs` (used only by {@link getETA}, to estimate wait time from - * currently submitted jobs) are both passed in rather than reached for via a host-app-specific - * singleton or global collection - keeps this class free of any such dependency, so it's equally - * safe to construct on the client (where `jobs` is simply omitted - there's no live jobs store to - * query there) and on the server (where the host app passes its own jobs collection). + * name, a premium maxPPN) is passed in rather than reached for via a host-app-specific singleton + * - keeps this class free of any such dependency. Estimating a queue's wait time needs a live + * query against the host app's own jobs store, which only ever makes sense server-side and has + * no place on a class that's also instantiated to represent a queue on the client - see the host + * app's own use case for that piece (e.g. web-app's `backendEntities/QueuesInfo`). */ class Queue extends InMemoryEntity implements QueueSchema { declare _json: QueueHostSchema & AnyObject; private readonly queueSettings?: QueueSettings; - private readonly jobs?: QueueJobFinder; - - constructor( - hostname: string, - config: QueueSchema, - queueSettings?: QueueSettings, - jobs?: QueueJobFinder, - ) { + constructor(hostname: string, config: QueueSchema, queueSettings?: QueueSettings) { // Ensure `displayName` is always present (schema field) even if backend omits it. const displayName = config.displayName || @@ -55,7 +34,6 @@ class Queue extends InMemoryEntity implements QueueSchema { super({ ...config, hostname, displayName }); this.queueSettings = queueSettings; - this.jobs = jobs; } get name() { @@ -144,35 +122,6 @@ class Queue extends InMemoryEntity implements QueueSchema { get defaultMaxPPN(): number { return this.maxPPN; } - - /** - * @summary Estimates this queue's wait time from currently submitted jobs. Resolves to - * `ETA.withinFiveMin` (same fallback as "nothing queued") if no `jobs` finder was supplied - * at construction - the client-side case, where there's no live jobs store to query. - */ - async getETA() { - // eslint-disable-next-line eqeqeq - if (this.maxAvailableNodect == 0) { - return ETA.moreThanHour; - } - if (!this.jobs) { - return ETA.withinFiveMin; - } - - const { jobs, name } = this; - const job = await sortedETA.reduce>( - async (acc, value) => { - const job = await acc; - if (job) { - return job; - } - return jobs.findOneAsync({ "compute.queue": name, startTime: value.display }); - }, - Promise.resolve(undefined), - ); - - return Object.values(ETA).find((x) => x.display === job?.startTime) || ETA.withinFiveMin; - } } export default Queue; diff --git a/tests/js/queue.test.ts b/tests/js/queue.test.ts index 3c9c754..5eacc23 100644 --- a/tests/js/queue.test.ts +++ b/tests/js/queue.test.ts @@ -1,13 +1,9 @@ import type { QueueSchema } from "@mat3ra/esse/dist/js/types"; import { expect } from "chai"; -import Queue, { type QueueJobFinder, type QueueSettings } from "../../src/js/nodes/queue"; +import Queue, { type QueueSettings } from "../../src/js/nodes/queue"; -function makeQueue( - config: Partial = {}, - queueSettings?: QueueSettings, - jobs?: QueueJobFinder, -) { +function makeQueue(config: Partial = {}, queueSettings?: QueueSettings) { return new Queue( "cluster-001", { @@ -19,7 +15,6 @@ function makeQueue( ...config, }, queueSettings, - jobs, ); } @@ -78,47 +73,4 @@ describe("Queue", () => { expect(queue.maxPPN).to.equal(32); expect(queue.defaultMaxPPN).to.equal(32); }); - - describe("getETA", () => { - it("returns 'more than 1 hour' when no nodes are available, without consulting jobs", async () => { - const queue = makeQueue( - { availableNodes: 0, currentNodes: 0, maxNodes: 10 }, - undefined, - { findOneAsync: () => Promise.reject(new Error("should not be called")) }, - ); - expect((await queue.getETA()).display).to.equal("more than 1 hour"); - }); - - it("returns 'less than 5 min' when no jobs finder was supplied (the client-side case)", async () => { - const queue = makeQueue({ availableNodes: 5, currentNodes: 5, maxNodes: 10 }); - expect((await queue.getETA()).display).to.equal("less than 5 min"); - }); - - it("returns 'less than 5 min' when the jobs finder has nothing queued for this queue", async () => { - const queue = makeQueue( - { availableNodes: 5, currentNodes: 5, maxNodes: 10 }, - undefined, - { - findOneAsync: () => Promise.resolve(undefined), - }, - ); - expect((await queue.getETA()).display).to.equal("less than 5 min"); - }); - - it("matches the highest-order ETA among currently submitted jobs for this queue", async () => { - const queue = makeQueue( - { availableNodes: 5, currentNodes: 5, maxNodes: 10 }, - undefined, - { - findOneAsync: (selector) => - Promise.resolve( - selector.startTime === "within 1 hour" - ? { startTime: "within 1 hour" } - : undefined, - ), - }, - ); - expect((await queue.getETA()).display).to.equal("within 1 hour"); - }); - }); }); From 81c4c3dc8a4be28b71d5ca7e0ff0b240aafbed10 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Tue, 6 Oct 2026 16:31:05 +0300 Subject: [PATCH 11/14] chore(SOF-8032): drop GPOF and GP2OF queues, removed from production [release] Co-Authored-By: Claude Sonnet 5.5 --- src/js/nodes/enums.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/js/nodes/enums.ts b/src/js/nodes/enums.ts index 6bc483a..c9bb451 100644 --- a/src/js/nodes/enums.ts +++ b/src/js/nodes/enums.ts @@ -15,8 +15,6 @@ export const QUEUE_TYPES = { gpuOrdinaryFast: "GOF", gpu4OrdinaryFast: "G4OF", gpu8OrdinaryFast: "G8OF", - gpuPOrdinaryFast: "GPOF", - gpuP2OrdinaryFast: "GP2OF", gpuP4OrdinaryFast: "GP4OF", gpuSavingFast: "GSF", gpu4SavingFast: "G4SF", @@ -26,9 +24,9 @@ export const QUEUE_TYPES = { gpuP4SavingFast: "GP4SF", } as const; -// Note: has more keys than esse's `job/compute.json#properties/queue` union (GPOF/GP2OF/GP4OF/ -// GPSF/GP2SF/GP4SF are not in the schema), so this stays a plain string index rather than typed -// against that union. +// Note: has more keys than esse's `job/compute.json#properties/queue` union (GP4OF/GPSF/GP2SF/ +// GP4SF are not in the schema), so this stays a plain string index rather than typed against +// that union. export const QUEUE_DISPLAY: Record = { D: "debug (D)", OR: "ordinary regular (OR)", @@ -48,8 +46,6 @@ export const QUEUE_DISPLAY: Record = { GOF: "1 GPU ordinary fast (GOF)", G4OF: "4 GPUs ordinary fast (G4OF)", G8OF: "8 GPUs ordinary fast (G8OF)", - GPOF: "1 GPU ordinary fast (GPOF)", - GP2OF: "2 GPUs ordinary fast (GP2OF)", GP4OF: "4 GPUs ordinary fast (GP4OF)", GSF: "1 GPU saving fast (GSF)", G4SF: "4 GPUs saving fast (G4SF)", From 62d4727ed23408bc6be034ee04341a4dcf33a9d3 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Wed, 7 Oct 2026 23:42:07 +0300 Subject: [PATCH 12/14] feat(SOF-8032): take the queue names from esse's QueueNameEnum [release] QUEUE_TYPES is now an alias of esse's generated QueueNameEnum instead of a hand-kept copy, so the queue names have one source of truth. QUEUE_DISPLAY is typed Record, so adding a queue in esse fails to compile until it gets a label here; the pre-2020 "OF+"/"SF+" spellings (renamed to OFplus/SFplus in 2020-10) move to a separate legacy map. Esse's queue field is now the enum rather than a literal union, so the default compute config and tests use enum members instead of bare strings. Repin @mat3ra/esse to the WIP release that adds QueueNameEnum (wip-eed5826). Co-Authored-By: Claude Sonnet 5.5 --- package-lock.json | 53 ++++++++++++++-------------------------- package.json | 2 +- src/js/default.ts | 4 ++- src/js/nodes/enums.ts | 52 ++++++++++++++++----------------------- tests/js/compute.test.ts | 24 ++++++++++++------ tests/js/queue.test.ts | 7 +++--- 6 files changed, 65 insertions(+), 77 deletions(-) diff --git a/package-lock.json b/package-lock.json index 780d1fc..4f21a60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "devDependencies": { "@exabyte-io/eslint-config": "^2022.11.17-0", "@mat3ra/code": "2026.8.18-0", - "@mat3ra/esse": "2026.8.18-2", + "@mat3ra/esse": "https://github.com/mat3ra/esse/releases/download/wip-eed5826/esse.tgz", "@mat3ra/tsconfig": "^2024.6.3-0", "@types/mocha": "^10.0.10", "@types/node": "^20.19.43", @@ -2818,9 +2818,9 @@ } }, "node_modules/@mat3ra/esse": { - "version": "2026.8.18-2", - "resolved": "https://registry.npmjs.org/@mat3ra/esse/-/esse-2026.8.18-2.tgz", - "integrity": "sha512-SMkofrqu/P5EKwTkgpi1Q5Mxfca6ZIvhWEK+YeUJDqu203+Z6plAaKx4N4xWFEt91jzNI5Slml/EFiaoVbiFzw==", + "version": "0.0.0", + "resolved": "https://github.com/mat3ra/esse/releases/download/wip-eed5826/esse.tgz", + "integrity": "sha512-qv+it6mfpjLLZ8Z2xaHzSMi8s2X6kyGFVAtbZPl+2zD0gUqmlyTW8h1iqSNIz1xk9N41lg/526JPgus+emEH2Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2845,7 +2845,8 @@ "json-schema-deref-sync": "^0.14.0", "json-schema-merge-allof": "^0.8.1", "json-schema-to-typescript": "^15.0.4", - "lodash": "^4.18.1" + "lodash": "^4.18.1", + "marked": "^14.1.4" }, "engines": { "node": ">=14.0.0" @@ -4973,21 +4974,6 @@ "eslint-plugin-import": "*" } }, - "node_modules/eslint-import-resolver-meteor": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-meteor/-/eslint-import-resolver-meteor-0.4.0.tgz", - "integrity": "sha512-BSqvgt6QZvk9EGhDGnM4azgbxyBD8b0y6FYA52WFzpWpHcZV9ys8PxM33bx8dlCy3HyopRLLsMUnlhTpZzsZmQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "object-assign": "^4.0.1", - "resolve": "^1.1.6" - }, - "peerDependencies": { - "eslint-plugin-import": ">=1.4.0" - } - }, "node_modules/eslint-import-resolver-node": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", @@ -5272,20 +5258,6 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -7377,6 +7349,19 @@ "dev": true, "license": "ISC" }, + "node_modules/marked": { + "version": "14.1.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.4.tgz", + "integrity": "sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/mathjs": { "version": "5.10.3", "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-5.10.3.tgz", diff --git a/package.json b/package.json index 03a38dc..e5e64b0 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ }, "devDependencies": { "@mat3ra/code": "2026.8.18-0", - "@mat3ra/esse": "2026.8.18-2", + "@mat3ra/esse": "https://github.com/mat3ra/esse/releases/download/wip-eed5826/esse.tgz", "@mat3ra/tsconfig": "^2024.6.3-0", "@exabyte-io/eslint-config": "^2022.11.17-0", "@types/mocha": "^10.0.10", diff --git a/src/js/default.ts b/src/js/default.ts index 7503219..8de81b7 100644 --- a/src/js/default.ts +++ b/src/js/default.ts @@ -1,10 +1,12 @@ import type { ComputeArgumentsSchema } from "@mat3ra/esse/dist/js/types"; +import { QUEUE_TYPES } from "./nodes/enums"; + export function getDefaultComputeConfig(): ComputeArgumentsSchema { return { ppn: 1, nodes: 1, - queue: "D", + queue: QUEUE_TYPES.debug, timeLimit: "01:00:00", notify: "n", cluster: { diff --git a/src/js/nodes/enums.ts b/src/js/nodes/enums.ts index c9bb451..9b70b20 100644 --- a/src/js/nodes/enums.ts +++ b/src/js/nodes/enums.ts @@ -1,33 +1,13 @@ -export const QUEUE_TYPES = { - debug: "D", - ordinaryRegular: "OR", - ordinaryRegular4: "OR4", - ordinaryRegular8: "OR8", - ordinaryRegular16: "OR16", - savingRegular: "SR", - savingRegular4: "SR4", - savingRegular8: "SR8", - savingRegular16: "SR16", - ordinaryFast: "OF", - ordinaryFastPlus: "OFplus", - savingFast: "SF", - savingFastPlus: "SFplus", - gpuOrdinaryFast: "GOF", - gpu4OrdinaryFast: "G4OF", - gpu8OrdinaryFast: "G8OF", - gpuP4OrdinaryFast: "GP4OF", - gpuSavingFast: "GSF", - gpu4SavingFast: "G4SF", - gpu8SavingFast: "G8SF", - gpuPSavingFast: "GPSF", - gpuP2SavingFast: "GP2SF", - gpuP4SavingFast: "GP4SF", -} as const; +import type { QueueNameEnum } from "@mat3ra/esse/dist/js/types"; -// Note: has more keys than esse's `job/compute.json#properties/queue` union (GP4OF/GPSF/GP2SF/ -// GP4SF are not in the schema), so this stays a plain string index rather than typed against -// that union. -export const QUEUE_DISPLAY: Record = { +/** + * The queue names live in esse (`compute/queue-name-enum`); re-exported under ide's existing name. + */ +export { QueueNameEnum as QUEUE_TYPES } from "@mat3ra/esse/dist/js/types"; + +// Keyed by `QueueNameEnum` (esse `compute/queue-name-enum`) so adding a queue there fails to +// compile until it gets a label here. +const QUEUE_DISPLAY_BY_NAME: Record = { D: "debug (D)", OR: "ordinary regular (OR)", OR4: "4 cores ordinary regular (OR4)", @@ -38,10 +18,8 @@ export const QUEUE_DISPLAY: Record = { SR8: "8 cores saving regular (SR8)", SR16: "16 cores saving regular (SR16)", OF: "ordinary fast (OF)", - "OF+": "ordinary fast plus (OF+)", OFplus: "ordinary fast plus (OFplus)", SF: "saving fast (SF)", - "SF+": "saving fast plus (SF+)", SFplus: "saving fast plus (SFplus)", GOF: "1 GPU ordinary fast (GOF)", G4OF: "4 GPUs ordinary fast (G4OF)", @@ -55,6 +33,18 @@ export const QUEUE_DISPLAY: Record = { GP4SF: "4 GPUs saving fast (GP4SF)", }; +/** Pre-2020 spellings of `OFplus`/`SFplus` (renamed 2020-10); legacy job documents may still store them. */ +const LEGACY_QUEUE_DISPLAY: Record = { + "OF+": "ordinary fast plus (OF+)", + "SF+": "saving fast plus (SF+)", +}; + +// Plain string index: the backend may report queue names that are not (yet) in `QueueNameEnum`. +export const QUEUE_DISPLAY: Record = { + ...QUEUE_DISPLAY_BY_NAME, + ...LEGACY_QUEUE_DISPLAY, +}; + export const ETA = { withinOneMin: { display: "within 1 min", diff --git a/tests/js/compute.test.ts b/tests/js/compute.test.ts index 37ce778..cf3a3ce 100644 --- a/tests/js/compute.test.ts +++ b/tests/js/compute.test.ts @@ -1,5 +1,6 @@ import { InMemoryEntity } from "@mat3ra/code/dist/js/entity"; import type { BaseInMemoryEntitySchema, ComputeArgumentsSchema } from "@mat3ra/esse/dist/js/types"; +import { QueueNameEnum } from "@mat3ra/esse/dist/js/types"; import { expect } from "chai"; import { type ComputedEntityMixin, computedEntityMixin } from "../../src/js/compute"; @@ -47,14 +48,23 @@ describe("Model", () => { }); it("calculates approximate charge", () => { - assertApproximateCharge({ queue: "D", nodes: 1, ppn: 1, timeLimit: "01:00:00" }, 2); - assertApproximateCharge({ queue: "D", nodes: 1, ppn: 1, timeLimit: "70:00:00" }, 140); - assertApproximateCharge({ queue: "OR", nodes: 1, ppn: 1, timeLimit: "70:00:00" }, 70); + assertApproximateCharge( + { queue: QueueNameEnum.debug, nodes: 1, ppn: 1, timeLimit: "01:00:00" }, + 2, + ); + assertApproximateCharge( + { queue: QueueNameEnum.debug, nodes: 1, ppn: 1, timeLimit: "70:00:00" }, + 140, + ); + assertApproximateCharge( + { queue: QueueNameEnum.ordinaryRegular, nodes: 1, ppn: 1, timeLimit: "70:00:00" }, + 70, + ); }); it("applies settings.rateModifier to the approximate charge", () => { const app = new Computer({ - compute: { queue: "D", nodes: 1, ppn: 1, timeLimit: "01:00:00" }, + compute: { queue: QueueNameEnum.debug, nodes: 1, ppn: 1, timeLimit: "01:00:00" }, }); const charge = app.getApproximateCharge({ baseChargeRate: 1, rateModifier: 2 }, { D: 2 }); expect(charge).to.equal(4); @@ -69,13 +79,13 @@ describe("Model", () => { it("allows setting and reading compute back (mutable, not readonly)", () => { const app = new Computer({}); - app.compute = { queue: "D", nodes: 1, ppn: 1, timeLimit: "01:00:00" }; - expect(app.compute?.queue).to.equal("D"); + app.compute = { queue: QueueNameEnum.debug, nodes: 1, ppn: 1, timeLimit: "01:00:00" }; + expect(app.compute?.queue).to.equal(QueueNameEnum.debug); }); it("types timeLimit as plain string when compute itself is required, string | undefined otherwise", () => { const required = new RequiredComputer({ - compute: { queue: "D", nodes: 1, ppn: 1, timeLimit: "01:00:00" }, + compute: { queue: QueueNameEnum.debug, nodes: 1, ppn: 1, timeLimit: "01:00:00" }, }); // Required case: assigning to plain `string` must compile cleanly - no @ts-expect-error. const requiredTimeLimit: string = required.timeLimit; diff --git a/tests/js/queue.test.ts b/tests/js/queue.test.ts index 5eacc23..58777d1 100644 --- a/tests/js/queue.test.ts +++ b/tests/js/queue.test.ts @@ -1,4 +1,5 @@ import type { QueueSchema } from "@mat3ra/esse/dist/js/types"; +import { QueueNameEnum } from "@mat3ra/esse/dist/js/types"; import { expect } from "chai"; import Queue, { type QueueSettings } from "../../src/js/nodes/queue"; @@ -7,7 +8,7 @@ function makeQueue(config: Partial = {}, queueSettings?: QueueSetti return new Queue( "cluster-001", { - name: "OR", + name: QueueNameEnum.ordinaryRegular, maxPPN: 16, maxNodes: 10, availableNodes: 10, @@ -28,7 +29,7 @@ describe("Queue", () => { it("exposes the hostname it was constructed with", () => { const queue = new Queue("cluster-002", { - name: "OR", + name: QueueNameEnum.ordinaryRegular, maxPPN: 16, maxNodes: 10, availableNodes: 10, @@ -48,7 +49,7 @@ describe("Queue", () => { }); it("falls back to the schema's own name for displayName when nothing else provides one", () => { - const queue = makeQueue({ name: "OR" }); + const queue = makeQueue({ name: QueueNameEnum.ordinaryRegular }); expect(queue.displayName).to.equal("ordinary regular (OR)"); }); From a3f16457c8dca9b958fbc7263d8297d1f4b5fb17 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Wed, 7 Oct 2026 23:44:03 +0300 Subject: [PATCH 13/14] fix(SOF-8032): restore peer entries dropped from the lockfile by --legacy-peer-deps [release] The esse repin regenerated package-lock.json with --legacy-peer-deps, which dropped eslint-import-resolver-meteor and eslint-plugin-react-hooks, so CI's `npm ci` rejected the lockfile as out of sync and the WIP release never published. Regenerate without the flag. Co-Authored-By: Claude Sonnet 5.5 --- package-lock.json | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/package-lock.json b/package-lock.json index 4f21a60..79fa5e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4974,6 +4974,21 @@ "eslint-plugin-import": "*" } }, + "node_modules/eslint-import-resolver-meteor": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-meteor/-/eslint-import-resolver-meteor-0.4.0.tgz", + "integrity": "sha512-BSqvgt6QZvk9EGhDGnM4azgbxyBD8b0y6FYA52WFzpWpHcZV9ys8PxM33bx8dlCy3HyopRLLsMUnlhTpZzsZmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "object-assign": "^4.0.1", + "resolve": "^1.1.6" + }, + "peerDependencies": { + "eslint-plugin-import": ">=1.4.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", @@ -5258,6 +5273,20 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", From 62c8f6609bbfc3e1538079033c03f757acf48495 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Thu, 8 Oct 2026 18:22:06 +0300 Subject: [PATCH 14/14] chore(SOF-8032): repin @mat3ra/esse to the published 2026.10.8-0 Replaces the esse WIP tarball with the registry release; the schema and source are identical to wip-eed5826. Co-Authored-By: Claude Sonnet 5.5 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 79fa5e1..dd3d478 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "devDependencies": { "@exabyte-io/eslint-config": "^2022.11.17-0", "@mat3ra/code": "2026.8.18-0", - "@mat3ra/esse": "https://github.com/mat3ra/esse/releases/download/wip-eed5826/esse.tgz", + "@mat3ra/esse": "2026.10.8-0", "@mat3ra/tsconfig": "^2024.6.3-0", "@types/mocha": "^10.0.10", "@types/node": "^20.19.43", @@ -2818,9 +2818,9 @@ } }, "node_modules/@mat3ra/esse": { - "version": "0.0.0", - "resolved": "https://github.com/mat3ra/esse/releases/download/wip-eed5826/esse.tgz", - "integrity": "sha512-qv+it6mfpjLLZ8Z2xaHzSMi8s2X6kyGFVAtbZPl+2zD0gUqmlyTW8h1iqSNIz1xk9N41lg/526JPgus+emEH2Q==", + "version": "2026.10.8-0", + "resolved": "https://registry.npmjs.org/@mat3ra/esse/-/esse-2026.10.8-0.tgz", + "integrity": "sha512-swm0DB/QsgdBDLd4l3X2t5eT1wWg/Ukm2+sya9EXvF4r291o4bTOZBbqO4PUPzl1iVFI34JdOG8KgbecD+FwPg==", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index e5e64b0..65173bb 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ }, "devDependencies": { "@mat3ra/code": "2026.8.18-0", - "@mat3ra/esse": "https://github.com/mat3ra/esse/releases/download/wip-eed5826/esse.tgz", + "@mat3ra/esse": "2026.10.8-0", "@mat3ra/tsconfig": "^2024.6.3-0", "@exabyte-io/eslint-config": "^2022.11.17-0", "@types/mocha": "^10.0.10",