From 8f215ab52a64ec8d1e85676707201bc79efb17ed Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Sun, 19 Oct 2025 15:41:49 +0200 Subject: [PATCH 01/20] Version 1.1.0 --- .github/workflows/js.yml | 5 +- .gitignore | 1 + README.md | 240 ++--- package-lock.json | 1004 +++++++++++--------- package.json | 9 +- src/details.ts | 11 +- src/index.ts | 472 +++++++-- tests/assets/response_generateStrings.json | 9 - tests/assets/response_getDetails.json | 13 - tests/assets/response_intersection.json | 4 - tests/assets/response_isEquivalentTo.json | 4 - tests/assets/response_isSubsetOf.json | 4 - tests/assets/response_subtraction.json | 4 - tests/assets/response_union.json | 4 - tests/integration.test.ts | 169 ++++ tests/term-operation.test.ts | 96 -- 16 files changed, 1217 insertions(+), 832 deletions(-) delete mode 100644 tests/assets/response_generateStrings.json delete mode 100644 tests/assets/response_getDetails.json delete mode 100644 tests/assets/response_intersection.json delete mode 100644 tests/assets/response_isEquivalentTo.json delete mode 100644 tests/assets/response_isSubsetOf.json delete mode 100644 tests/assets/response_subtraction.json delete mode 100644 tests/assets/response_union.json create mode 100644 tests/integration.test.ts diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index a03f498..16d0997 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -21,4 +21,7 @@ jobs: cache: 'npm' - run: npm ci - run: npm run build - - run: npm test + - name: Run tests + env: + REGEXSOLVER_API_TOKEN: ${{ secrets.REGEXSOLVER_API_TOKEN }} + run: npm test diff --git a/.gitignore b/.gitignore index 2db4792..de6177b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/ .node_modules/ +.env built/* tests/cases/rwc/* tests/cases/perf/* diff --git a/README.md b/README.md index 807a6f9..04de20f 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,30 @@ -# RegexSolver Node.js API Client +# RegexSolver JS API Client [Homepage](https://regexsolver.com) | [Online Demo](https://regexsolver.com/demo) | [Documentation](https://docs.regexsolver.com) | [Developer Console](https://console.regexsolver.com) -This repository contains the source code of the Node.js library for [RegexSolver](https://regexsolver.com) API. - -RegexSolver is a powerful regular expression manipulation toolkit, that gives you the power to manipulate regex as if -they were sets. +**RegexSolver** is a powerful toolkit for building, combining, and analyzing regular expressions. It is designed for constraint solvers, test generators, and other systems that need advanced regex operations. ## Installation ```sh -npm install regexsolver +npm i regexsolver +# or +yarn add regexsolver +# or +pnpm add regexsolver ``` ## Usage -In order to use the library you need to generate an API Token on our [Developer Console](https://console.regexsolver.com/). +1. Create an API token in the [Developer Console](https://console.regexsolver.com/). +2. Initialize the client and start working with terms: ```javascript import { RegexSolver, Term } from 'regexsolver'; -RegexSolver.initialize("YOUR TOKEN HERE"); +// Set REGEXSOLVER_API_TOKEN in your env and call initialize(), +// or pass the token directly: +RegexSolver.initialize(); // or RegexSolver.initialize('YOUR_API_TOKEN') const term1 = Term.regex("(abc|de|fg){2,}"); const term2 = Term.regex("de.*"); @@ -29,179 +33,129 @@ const term3 = Term.regex(".*abc"); const term4 = Term.regex(".+(abc|de).+"); term1.intersection(term2, term3) - .then(result => result.subtraction(term4)) - .then(result => console.log(result.toString())); + .then(result => result.difference(term4)) + .then(result => result.getPattern()) + .then(result => console.log(result)); // de(fg)*abc ``` -## Features +## Key Concepts & Limitations -- [Intersection](#intersection) -- [Union](#union) -- [Subtraction / Difference](#subtraction--difference) -- [Equivalence](#equivalence) -- [Subset](#subset) -- [Details](#details) -- [Generate Strings](#generate-strings) +RegexSolver supports a subset of regular expressions that adhere to the principles of regular languages. Here are the key characteristics and limitations of the regular expressions supported by RegexSolver: +- **Anchored Expressions:** All regular expressions in RegexSolver are anchored. This means that the expressions are treated as if they start and end at the boundaries of the input text. For example, the expression `abc` will match the string "abc" but not "xabc" or "abcx". +- **Lookahead/Lookbehind:** RegexSolver does not support lookahead (`(?=...)`) or lookbehind (`(?<=...)`) assertions. Using them returns an error. +- **Pure Regular Expressions:** RegexSolver focuses on pure regular expressions as defined in regular language theory. This means features that extend beyond regular languages, such as backreferences (`\1`, `\2`, etc.), are not supported. Any use of backreference would return an error. +- **Greedy/Ungreedy Quantifiers:** The concept of ungreedy (`*?`, `+?`, `??`) quantifiers is not supported. All quantifiers are treated as greedy. For example, `a*` or `a*?` will match the longest possible sequence of "a"s. +- **Line Feed and Dot:** RegexSolver handles all characters the same way. The dot `.` matches any Unicode character including line feed (`\n`). +- **Empty Regular Expressions:** The empty language (matches no string) is represented by constructs like `[]` (empty character class). This is distinct from the empty string. -### Intersection -#### Request +## Response Formats -Compute the intersection of the provided terms and return the resulting term. +The API can handle terms in two formats: +- `regex`: a regular expression pattern +- `fair`: FAIR (Fast Automaton Internal Representation), a stable, signed format used internally by the engine -The maximum number of terms is currently limited to 10. +By default, the engine returns whatever the operation produces, with no extra convertion. Override with `response_format`: ```javascript -const term1 = Term.regex("(abc|de){2}"); -const term2 = Term.regex("de.*"); -const term3 = Term.regex(".*abc"); - -term1.intersection(term2, term3).then(result => { - console.log(result.toString()); -}); -``` +import { Term, ResponseFormat } from 'regexsolver'; -#### Response - -``` -regex=deabc -``` - -### Union - -Compute the union of the provided terms and return the resulting term. - -The maximum number of terms is currently limited to 10. - -#### Request - -```javascript -const term1 = Term.regex("abc"); -const term2 = Term.regex("de"); -const term3 = Term.regex("fghi"); +const term = Term.regex('abcde'); -term1.union(term2, term3).then(result => { - console.log(result.toString()); +term.union(Term.regex('de'), { responseFormat: ResponseFormat.REGEX }).then(result => { + console.log(result.toString()); // regex=(abc)?de }); -``` - -#### Response - -``` -regex=(abc|de|fghi) -``` - -### Subtraction / Difference - -Compute the first term minus the second and return the resulting term. -#### Request - -```javascript -const term1 = Term.regex("(abc|de)"); -const term2 = Term.regex("de"); - -term1.subtraction(term2).then(result => { - console.log(result.toString()); +term.intersection(Term.regex('de.*'), { responseFormat: ResponseFormat.FAIR }).then(result => { + console.log(result.toString()); // fair=... }); - ``` -#### Response - -``` -regex=abc -``` +If the format does not matter, omit `response_format` or set it to `ResponseFormat.ANY`. -### Equivalence +Regardless of the format, you can always call `get_pattern()` to obtain the regex pattern of a term. -Analyze if the two provided terms are equivalent. +## Bounding execution time -#### Request +Set a server-side compute timeout in milliseconds with `execution_timeout`: ```javascript -const term1 = Term.regex("(abc|de)"); -const term2 = Term.regex("(abc|de)*"); - -term1.isEquivalentTo(term2).then(result => { - console.log(result); -}); -``` - -#### Response - -``` -false -``` - -### Subset - -Analyze if the second term is a subset of the first. - -#### Request - -```javascript -const term1 = Term.regex("de"); -const term2 = Term.regex("(abc|de)"); - -term1.isSubsetOf(term2).then(result => { - console.log(result); -}); +import { ApiError, Term } from 'regexsolver'; +// Limit the server-side compute time to 5 ms +Term.regex('.*ab.*c(de|fg).*dab.*c(de|fg).*ab.*c(de|fg).*dab.*c') + .difference(Term.regex('.*abc.*'), { executionTimeout: 5 }) + .then(res => {/* */}) + .catch(err => { + if (err instanceof ApiError) { + console.log(err.message); // The operation took too much time. + } else { + throw err; + } + }); ``` -#### Response +Timeout is best effort. The exact time is not guaranteed. -``` -true -``` +## API Overview -### Details +`Term` exposes the following methods. -Compute the details of the provided term. +### Build +| Method | Return | Description | +| -------- | ------- | ------- | +| `Term.fair(fair: string)` | `Term` | Creates a term from a FAIR. | +| `Term.regex(regex: string)` | `Term` | Creates a term from a regex pattern. | -The computed details are: +### Analyze -- **Cardinality:** the number of possible values. -- **Length:** the minimum and maximum length of possible values. -- **Empty:** true if is an empty set (does not contain any value), false otherwise. -- **Total:** true if is a total set (contains all values), false otherwise. +| Method | Return | Description | +| -------- | ------- | ------- | +| `t.equivalent(term: Term, opts?)` | `Promise` | `true` if `t` and `term` accept exactly the same language. Supports `executionTimeout`. | +| `t.getCardinality()` | `Promise` | Returns the cardinality of the term (i.e., the number of possible matched strings). | +| `t.getDetails()` | `Promise
` | Returns cardinality, length bounds, and if it is empty or total. | +| `t.getDot()` | `Promise` | Returns a Graphviz DOT representation of the automaton for the term. | +| `t.getFair()` | `string` | Returns the FAIR of the term if defined. | +| `t.getLength()` | `Promise` | Returns the minimum and maximum length of matched strings. | +| `t.getPattern()` | `Promise` | Returns a regular expression pattern for the term. | +| `t.isEmpty()` | `Promise` | `true` if the term matches no string. | +| `t.isEmptyString()` | `Promise` | `true` if the term matches only the empty string. | +| `t.isTotal()` | `Promise` | `true` if the term matches all possible strings. | +| `t.subset(term: Term, opts?)` | `Promise` | `true` if every string matched by `t` is also matched by `term`. Supports `executionTimeout`. | -#### Request - -```javascript -const term = Term.regex("(abc|de)"); - -term.getDetails().then(details => { - console.log(details.toString()); -}); -``` +### Compute -#### Response +| Method | Return | Description | +| -------- | ------- | ------- | +| `t.concat(...terms: Term[], opts?)` | `Promise` | Concatenates `t` with the given terms. Supports `responseFormat` and `executionTimeout`. | +| `t.difference(term: Term, opts?)` | `Promise` | Computes the difference `t - term`. Supports `responseFormat` and `executionTimeout`. | +| `t.intersection(...terms: Term[], opts?)` | `Promise` | Computes the intersection of `t` with the given terms. Supports `responseFormat` and `executionTimeout`. | +| `t.repeat(min: number, max?: number, opts?)` | `Promise` | Computes the repetition of the term between `min` and `max` times; if `max` is `null`, the repetition is unbounded. Supports `responseFormat` and `executionTimeout`. | +| `t.union(...terms: Term[], opts?)` | `Promise` | Computes the union of `t` with the given terms. Supports `responseFormat` and `executionTimeout`. | -``` -Details[cardinality=Integer(2), length=Length[minimum=2, maximum=3], empty=false, total=false] -``` +### Generate -### Generate Strings +| Method | Return | Description | +| -------- | ------- | ------- | +| `t.generateStrings(count: int)` | `Promise` | Generates up to `count` unique example strings matched by `t`. Supports `executionTimeout`. | -Generate the given number of strings that can be matched by the provided term. +### Other +| Method | Return | Description | +| -------- | ------- | ------- | +| `t.serialize()` | `string` | Returns a serialized form of `t`. | +| `Term.deserialize(string: string)` | `Term` | Returns a deserialized term from the given `string`. | -The maximum number of strings to generate is currently limited to 200. +## Cross-Language Support -#### Request +If you want to use this library with other programming languages, we provide: +- [regexsolver-java](https://github.com/RegexSolver/regexsolver-java) +- [regexsolver-python](https://github.com/RegexSolver/regexsolver-python) -```javascript -const term = Term.regex("(abc|de){2}"); +For more information about how to use the wrappers, you can refer to our [guide](https://docs.regexsolver.com/getting-started.html). -term.generateStrings(3).then(result => { - console.log(result); -}); -``` +You can also take a look at [regexsolver](https://github.com/RegexSolver/regexsolver) which contains the source code of the engine. -#### Response +## License -``` -[ 'deabc', 'abcde', 'dede' ] -``` +This project is licensed under the MIT License. diff --git a/package-lock.json b/package-lock.json index abe586e..e92e583 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,48 +9,36 @@ "version": "1.0.4", "license": "MIT", "dependencies": { - "axios": "^1.7.4" + "axios": "^1.12.2" }, "devDependencies": { "@types/jest": "^29.5.12", + "dotenv": "^17.2.3", "jest": "^29.7.0", "nock": "^13.5.4", "ts-jest": "^29.2.4", "typescript": "^5.5.4" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", - "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", "dev": true, "license": "MIT", "engines": { @@ -58,22 +46,23 @@ } }, "node_modules/@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -89,31 +78,32 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", - "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -121,31 +111,40 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", - "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.2" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -155,33 +154,19 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -189,9 +174,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { @@ -199,9 +184,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -209,121 +194,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "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, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "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, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "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, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/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==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "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, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "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, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", - "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.2" + "@babel/types": "^7.28.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -388,13 +279,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -430,13 +321,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -556,13 +447,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", - "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -572,49 +463,48 @@ } }, "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", - "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.2", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", - "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -947,34 +837,31 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "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/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6.0.0" + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", "engines": { @@ -982,16 +869,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "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.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "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": { @@ -1041,9 +928,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "license": "MIT", "dependencies": { @@ -1062,13 +949,13 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.20.7" + "@babel/types": "^7.28.2" } }, "node_modules/@types/graceful-fs": { @@ -1109,9 +996,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.12", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", - "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1120,13 +1007,13 @@ } }, "node_modules/@types/node": { - "version": "22.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.3.0.tgz", - "integrity": "sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g==", + "version": "24.7.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz", + "integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.18.2" + "undici-types": "~7.14.0" } }, "node_modules/@types/stack-utils": { @@ -1219,13 +1106,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true, - "license": "MIT" - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1233,13 +1113,13 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, @@ -1316,9 +1196,9 @@ } }, "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", "dev": true, "license": "MIT", "dependencies": { @@ -1339,7 +1219,7 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0 || ^8.0.0-0" } }, "node_modules/babel-preset-jest": { @@ -1366,10 +1246,20 @@ "dev": true, "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.16.tgz", + "integrity": "sha512-OMu3BGQ4E7P1ErFsIPpbJh0qvDudM/UuJeHgkAvfWe+0HFJCXh+t/l8L6fVLR55RI/UbKrVLnAXZSVwd9ysWYw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -1391,9 +1281,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", "dev": true, "funding": [ { @@ -1410,11 +1300,13 @@ } ], "license": "MIT", + "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -1453,6 +1345,19 @@ "dev": true, "license": "MIT" }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1474,9 +1379,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001651", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", - "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", + "version": "1.0.30001750", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001750.tgz", + "integrity": "sha512-cuom0g5sdX6rw00qOoLNSFCJ9/mYIsuSOA+yzpDw8eopiFqcVwQvZHqov0vmEighRxX++cfC0Vg1G+1Iy/mSpQ==", "dev": true, "funding": [ { @@ -1538,9 +1443,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", - "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true, "license": "MIT" }, @@ -1646,9 +1551,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -1661,13 +1566,13 @@ } }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "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.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1679,9 +1584,9 @@ } }, "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", + "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1732,26 +1637,37 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" }, - "bin": { - "ejs": "bin/cli.js" + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, "node_modules/electron-to-chromium": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.7.tgz", - "integrity": "sha512-6FTNWIWMxMy/ZY6799nBlPtF1DFDQ6VQJ7yyDP27SJNt5lwtQ5ufqVvHylb3fdQefvRcgA3fKcFMJi9OLwBRNw==", + "version": "1.5.235", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.235.tgz", + "integrity": "sha512-i/7ntLFwOdoHY7sgjlTIDo4Sl8EdoTjWIaKinYOVfC6bOp71bmwenyZthWHcasxgHDNWbWxvG9M3Ia116zIaYQ==", "dev": true, "license": "ISC" }, @@ -1776,19 +1692,64 @@ "license": "MIT" }, "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "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": { @@ -1886,39 +1847,6 @@ "bser": "2.1.1" } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -1947,9 +1875,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "funding": [ { "type": "individual", @@ -1967,13 +1895,15 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -2006,7 +1936,6 @@ "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" @@ -2032,6 +1961,30 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -2042,6 +1995,19 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -2077,14 +2043,16 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/graceful-fs": { @@ -2094,6 +2062,28 @@ "dev": true, "license": "ISC" }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2104,11 +2094,37 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2191,9 +2207,9 @@ "license": "MIT" }, "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { @@ -2284,9 +2300,9 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -2327,9 +2343,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2340,31 +2356,13 @@ "node": ">=8" } }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -2842,9 +2840,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -2977,16 +2975,16 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-parse-even-better-errors": { @@ -3090,9 +3088,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -3119,6 +3117,15 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -3127,9 +3134,9 @@ "license": "MIT" }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -3184,10 +3191,20 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "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" }, @@ -3198,10 +3215,17 @@ "dev": true, "license": "MIT" }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, "node_modules/nock": { - "version": "13.5.4", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.4.tgz", - "integrity": "sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3221,9 +3245,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.23", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", + "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==", "dev": true, "license": "MIT" }, @@ -3388,9 +3412,9 @@ "license": "MIT" }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "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" }, @@ -3408,9 +3432,9 @@ } }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "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": { @@ -3523,19 +3547,22 @@ } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3564,9 +3591,9 @@ } }, "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "license": "MIT", "engines": { @@ -3794,16 +3821,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3818,21 +3835,21 @@ } }, "node_modules/ts-jest": { - "version": "29.2.4", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.4.tgz", - "integrity": "sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==", + "version": "29.4.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.5.tgz", + "integrity": "sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==", "dev": true, "license": "MIT", "dependencies": { - "bs-logger": "0.x", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.3", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" }, "bin": { "ts-jest": "cli.js" @@ -3842,10 +3859,11 @@ }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", "typescript": ">=4.3 <6" }, "peerDependenciesMeta": { @@ -3863,13 +3881,16 @@ }, "esbuild": { "optional": true + }, + "jest-util": { + "optional": true } } }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -3879,6 +3900,19 @@ "node": ">=10" } }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -3903,11 +3937,12 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "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", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3916,17 +3951,31 @@ "node": ">=14.17" } }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/undici-types": { - "version": "6.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.18.2.tgz", - "integrity": "sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", + "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", "dev": true, "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -3944,8 +3993,8 @@ ], "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -3995,6 +4044,13 @@ "node": ">= 8" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index 0ca10bb..a29f68e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "regexsolver", - "version": "1.0.4", + "version": "1.1.0", "main": "lib/index.js", "typings": "lib/index.d.ts", "types": "lib/index.d.ts", @@ -28,7 +28,7 @@ ], "author": "RegexSolver", "license": "MIT", - "description": "RegexSolver allows you to manipulate regular expressions as sets, enabling operations such as intersection, union, and subtraction.", + "description": "RegexSolver is a powerful toolkit for building, combining, and analyzing regular expressions.", "repository": { "type": "git", "url": "git+https://github.com/RegexSolver/regexsolver-js.git" @@ -41,13 +41,14 @@ "Alexandre van Beurden (https://github.com/alexvbrdn)" ], "dependencies": { - "axios": "^1.7.4" + "axios": "^1.12.2" }, "devDependencies": { "@types/jest": "^29.5.12", - "ts-jest": "^29.2.4", + "dotenv": "^17.2.3", "jest": "^29.7.0", "nock": "^13.5.4", + "ts-jest": "^29.2.4", "typescript": "^5.5.4" } } diff --git a/src/details.ts b/src/details.ts index a2a6769..c657845 100644 --- a/src/details.ts +++ b/src/details.ts @@ -1,18 +1,19 @@ export class Cardinality { constructor( - public type: 'Infinite' | 'BigInteger' | 'Integer', + public type: 'infinite' | 'bigInteger' | 'integer', public value?: number ) { } isInfinite(): boolean { - return this.type == 'Infinite'; + return this.type == 'infinite'; } toString(): string { - if (this.type == 'Integer') { - return this.type + '(' + this.value + ')'; + const cap1 = s => s ? s[0].toUpperCase() + s.slice(1) : s; + if (this.type == 'integer') { + return cap1(this.type) + '(' + this.value + ')'; } else { - return this.type; + return cap1(this.type); } } } diff --git a/src/index.ts b/src/index.ts index 8ab2f86..515de30 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,56 +19,122 @@ export class RegexSolver { return RegexSolver.instance; } - static initialize(apiToken, baseURL = null) { + static initialize(apiToken: string = null, baseURL: string = null) { const instance = RegexSolver.getInstance(); + if (!apiToken) { + apiToken = process.env.REGEXSOLVER_API_TOKEN; + } if (!baseURL) { - baseURL = "https://api.regexsolver.com/" + baseURL = process.env.REGEXSOLVER_BASE_URL; + if (!baseURL) { + baseURL = "https://api.regexsolver.com/" + } } instance.apiClient = axios.create({ - baseURL: baseURL, + baseURL, headers: { 'Authorization': `Bearer ${apiToken}`, - 'User-Agent': 'RegexSolver Node.js / 1.0.4', + 'User-Agent': 'RegexSolver JS / 1.1.0', } }); } - computeIntersection(request: MultiTermsRequest): Promise { - return this.apiClient.post('/api/compute/intersection', request) - .then(response => loadTerm(response.data)) + // Analyze + + analyzeDetails(term: Term): Promise
{ + return this.apiClient.post('/api/analyze/details', term) + .then(response => loadDetails(response.data)) .catch(error => { throw new ApiError(error.message) }); } - computeUnion(request: MultiTermsRequest): Promise { - return this.apiClient.post('/api/compute/union', request) - .then(response => loadTerm(response.data)) + analyzeCardinality(term: Term): Promise { + return this.apiClient.post('/api/analyze/cardinality', term) + .then(response => loadCardinality(response.data)) .catch(error => { throw new ApiError(error.message) }); } - computeSubtraction(request: MultiTermsRequest): Promise { - return this.apiClient.post('/api/compute/subtraction', request) - .then(response => loadTerm(response.data)) + analyzeLength(term: Term): Promise { + return this.apiClient.post('/api/analyze/length', term) + .then(response => loadLength(response.data)) .catch(error => { throw new ApiError(error.message) }); } - getDetails(term: Term): Promise
{ - return this.apiClient.post('/api/analyze/details', term) - .then(response => loadDetails(response.data)) + analyzeEquivalent(request: MultiTermsRequest): Promise { + return this.apiClient.post('/api/analyze/equivalent', request) + .then(response => response.data.value) .catch(error => { throw new ApiError(error.message) }); } - equivalence(request: MultiTermsRequest): Promise { - return this.apiClient.post('/api/analyze/equivalence', request) + analyzeSubset(request: MultiTermsRequest): Promise { + return this.apiClient.post('/api/analyze/subset', request) .then(response => response.data.value) .catch(error => { throw new ApiError(error.message) }); } - subset(request: MultiTermsRequest): Promise { - return this.apiClient.post('/api/analyze/subset', request) + analyzeEmpty(request: Term): Promise { + return this.apiClient.post('/api/analyze/empty', request) + .then(response => response.data.value) + .catch(error => { throw new ApiError(error.message) }); + } + + analyzeTotal(request: Term): Promise { + return this.apiClient.post('/api/analyze/total', request) .then(response => response.data.value) .catch(error => { throw new ApiError(error.message) }); } + analyzeEmptyString(request: Term): Promise { + return this.apiClient.post('/api/analyze/empty_string', request) + .then(response => response.data.value) + .catch(error => { throw new ApiError(error.message) }); + } + + analyzeDot(request: Term): Promise { + return this.apiClient.post('/api/analyze/dot', request) + .then(response => response.data.value) + .catch(error => { throw new ApiError(error.message) }); + } + + analyzePattern(request: Term): Promise { + return this.apiClient.post('/api/analyze/pattern', request) + .then(response => response.data.value) + .catch(error => { throw new ApiError(error.message) }); + } + + // Compute + + computeRepeat(request: RepeatRequest): Promise { // todo + return this.apiClient.post('/api/compute/repeat', request) + .then(response => loadTerm(response.data)) + .catch(error => { throw new ApiError(error.message) }); + } + + computeIntersection(request: MultiTermsRequest): Promise { + return this.apiClient.post('/api/compute/intersection', request) + .then(response => loadTerm(response.data)) + .catch(error => { throw new ApiError(error.message) }); + } + + computeUnion(request: MultiTermsRequest): Promise { + return this.apiClient.post('/api/compute/union', request) + .then(response => loadTerm(response.data)) + .catch(error => { throw new ApiError(error.message) }); + } + + computeDifference(request: MultiTermsRequest): Promise { + return this.apiClient.post('/api/compute/difference', request) + .then(response => loadTerm(response.data)) + .catch(error => { throw new ApiError(error.message) }); + } + + computeConcat(request: MultiTermsRequest): Promise { + return this.apiClient.post('/api/compute/concat', request) + .then(response => loadTerm(response.data)) + .catch(error => { throw new ApiError(error.message) }); + } + + // Generate + generateStrings(request: GenerateStringsRequest): Promise { return this.apiClient.post('/api/generate/strings', request) .then(response => response.data.value) @@ -76,92 +142,312 @@ export class RegexSolver { } } +export type TermType = "fair" | "regex"; + +interface OperationOptions { + responseFormat?: ResponseFormat; + executionTimeout?: number; +} export class Term { - private static readonly REGEX_PREFIX = 'regex'; - private static readonly FAIR_PREFIX = 'fair'; - private static readonly UNKNOWN_PREFIX = 'unknown'; + readonly type: TermType; + readonly value: string; private details?: Details; + private cardinality?: Cardinality; + private length?: Length; + private empty?: boolean; + private total?: boolean; + private emptyString?: boolean; + private dot?: string; + private pattern?: string; + constructor( - private type: 'regex' | 'fair', - private value: string + type: TermType, + value: string ) { + this.type = type; + this.value = value; } - static regex(pattern: string): Term { - return new Term(Term.REGEX_PREFIX, pattern); - } - + /** + * Initialize a Fast Automaton Internal Representation (FAIR). + */ static fair(fair: string): Term { - return new Term(Term.FAIR_PREFIX, fair); + return new Term("fair", fair); } - getType(): 'regex' | 'fair' { - return this.type; + /** + * Initialize a regex. + */ + static regex(pattern: string): Term { + return new Term("regex", pattern); } - getFair(): string | void { - if (this.type == Term.FAIR_PREFIX) { - return this.value; - } - return null; + // Analyze + + /** + * Check whether this term is equivalent to another. + * + * @param term The term to compare against. + * @param opts Execution options. + * + * @returns `true` if both terms accept exactly the same language. + */ + async equivalent(term: Term, opts?: OperationOptions): Promise { + const options = RequestOptionsBuilder.fromArgs({ execution_timeout: opts?.executionTimeout }); + return await RegexSolver.getInstance().analyzeEquivalent({ terms: [this, term], options }); } - getPattern(): string | void { - if (this.type == Term.REGEX_PREFIX) { - return this.value; + /** + * Get the cardinality of this term. + * + * Results are cached on the instance to avoid repeated API calls. + * + * @returns A `Cardinality` object describing how many distinct strings are matched. + */ + async getCardinality(): Promise { + if (this.cardinality) { + return this.cardinality; + } else if (this.details) { + return this.details.cardinality; } - return null; + this.cardinality = await RegexSolver.getInstance().analyzeCardinality(this); + return this.cardinality; } + /** + * Analyze this term and return detailed information including cardinality, length, and whether it is empty or total. + * + * Results are cached on the instance to avoid repeated API calls. + */ async getDetails(): Promise
{ if (this.details) { return this.details; } - this.details = await RegexSolver.getInstance().getDetails(this); + this.details = await RegexSolver.getInstance().analyzeDetails(this); return this.details; } - async generateStrings(count: number): Promise { - return await RegexSolver.getInstance().generateStrings({ term: this, count }); + /** + * Get the GraphViz DOT representation of this term. + * + * Results are cached on the instance to avoid repeated API calls. + * + * @returns A DOT language string describing the automaton for this term. + */ + async getDot(): Promise { + if (this.dot) { + return this.dot; + } + this.dot = await RegexSolver.getInstance().analyzeDot(this); + return this.dot; } - async intersection(...terms: Term[]): Promise { - return await RegexSolver.getInstance().computeIntersection({ terms: [this, ...terms] }); + /** + * Return the Fast Automaton Internal Representation (FAIR). + */ + getFair(): string | null { + return this.type === "fair" ? this.value : null; } - async union(...terms: Term[]): Promise { - return await RegexSolver.getInstance().computeUnion({ terms: [this, ...terms] }); + /** + * Get the length bounds of this term. + * + * Results are cached on the instance to avoid repeated API calls. + * + * @returns A `Length` object with the minimum and maximum string length matched by this term. + */ + async getLength(): Promise { + if (this.length) { + return this.length; + } else if (this.details) { + return this.details.length; + } + this.length = await RegexSolver.getInstance().analyzeLength(this); + return this.length; } - async subtraction(term: Term): Promise { - return await RegexSolver.getInstance().computeSubtraction({ terms: [this, term] }); + /** + * Return the regular expression pattern. + * + * If the term is not a regex the pattern will be resolved. + * + * Results are cached on the instance to avoid repeated API calls. + */ + async getPattern(): Promise { + if (this.type === "regex") return this.value; + if (this.pattern) return this.pattern; + this.pattern = await RegexSolver.getInstance().analyzePattern(this); + return this.pattern; } - async isEquivalentTo(term: Term): Promise { - return await RegexSolver.getInstance().equivalence({ terms: [this, term] }); + getType(): TermType { + return this.type; } - async isSubsetOf(term: Term): Promise { - return await RegexSolver.getInstance().subset({ terms: [this, term] }); + /** + * Check whether this term matches no string. + * + * Results are cached on the instance to avoid repeated API calls. + */ + async isEmpty(): Promise { + if (this.empty) { + return this.empty; + } else if (this.details) { + return this.details.empty; + } + this.empty = await RegexSolver.getInstance().analyzeEmpty(this); + return this.empty; } - serialize(): string { - let prefix = Term.UNKNOWN_PREFIX; - if (this.type == Term.REGEX_PREFIX) { - prefix = Term.REGEX_PREFIX; - } else if (this.type == Term.FAIR_PREFIX) { - prefix = Term.FAIR_PREFIX; + /** + * Check whether this term matches only the empty string. + * + * Results are cached on the instance to avoid repeated API calls. + */ + async isEmptyString(): Promise { + if (this.emptyString) { + return this.emptyString; } - return prefix + "=" + this.value; + this.emptyString = await RegexSolver.getInstance().analyzeEmptyString(this); + return this.emptyString; } - static deserialize(string: string): Term | void { - if (string.startsWith(Term.REGEX_PREFIX)) { - return Term.regex(string.substring(this.REGEX_PREFIX.length + 1)); - } else if (string.startsWith(Term.FAIR_PREFIX)) { - return Term.fair(string.substring(this.FAIR_PREFIX.length + 1)); + /** + * Check whether this term matches all possible strings. + * + * Results are cached on the instance to avoid repeated API calls. + */ + async isTotal(): Promise { + if (this.total) { + return this.total; + } else if (this.details) { + return this.details.total; } + this.total = await RegexSolver.getInstance().analyzeTotal(this); + return this.total; + } + + /** + * Check whether this term is a subset of another. + * + * @param term The term to compare against. + * @param opts Execution options. + * @returns `true` if every string matched by this term is also matched by `term`. + */ + async subset(term: Term, opts?: OperationOptions): Promise { + const options = RequestOptionsBuilder.fromArgs({ execution_timeout: opts?.executionTimeout }); + return await RegexSolver.getInstance().analyzeSubset({ terms: [this, term], options }); + } + + // Compute + + private getMultiTermsRequest(args: (Term | OperationOptions)[]): MultiTermsRequest { + const last = args[args.length - 1]; + let options: RequestOptions; + let terms: Term[]; + if (last instanceof Term) { + options = undefined; + terms = args as Term[]; + } else { + options = RequestOptionsBuilder.fromArgs({ response_format: last.responseFormat, execution_timeout: last.executionTimeout }); + terms = args.slice(0, -1) as Term[]; + } + + return { terms: [this, ...terms], options }; + } + + /** + * Concatenate this term with one or more other terms. + * + * @returns A new term representing the concatenation. + */ + async concat(t1: Term, ...rest: Term[]): Promise; + async concat(t1: Term, ...termsAndOpts: [...terms: Term[], opts: OperationOptions]): Promise; + async concat(...args: (Term | OperationOptions)[]): Promise { + return await RegexSolver.getInstance().computeConcat(this.getMultiTermsRequest(args)); + } + + /** + * Compute the difference between this term and another. + * + * @returns A new term representing the set difference (this - term). + */ + async difference(term: Term, opts?: OperationOptions): Promise { + const options = RequestOptionsBuilder.fromArgs({ response_format: opts?.responseFormat, execution_timeout: opts?.executionTimeout }); + return await RegexSolver.getInstance().computeDifference({ terms: [this, term], options }); + } + + /** + * Compute the intersection of this term with one or more other terms. + * + * @returns A new term representing the intersection. + */ + async intersection(t1: Term, ...rest: Term[]): Promise; + async intersection(t1: Term, ...termsAndOpts: [...terms: Term[], opts: OperationOptions]): Promise; + async intersection(...args: (Term | OperationOptions)[]): Promise { + return await RegexSolver.getInstance().computeIntersection(this.getMultiTermsRequest(args)); + } + + /** + * Computes the repetition of the term between `min` and `max` times; if `max` is `null`, the repetition is unbounded. + * + * @param min The lower bound of the repetition. + * @param max The upper bound of the repetition, if `null` the repetition is unbounded. + * @param opts Execution options. + * @returns A new term representing the repetition. + */ + async repeat(min: number, max?: number | null, opts?: OperationOptions): Promise { + const options = RequestOptionsBuilder.fromArgs({ response_format: opts.responseFormat, execution_timeout: opts.executionTimeout }); + return await RegexSolver.getInstance().computeRepeat({ term: this, min, max, options }); + } + + /** + * Compute the union of this term with one or more other terms. + * + * @returns A new term representing the union. + */ + async union(t1: Term, ...rest: Term[]): Promise; + async union(t1: Term, ...termsAndOpts: [...terms: Term[], opts: OperationOptions]): Promise; + async union(...args: (Term | OperationOptions)[]): Promise { + return await RegexSolver.getInstance().computeUnion(this.getMultiTermsRequest(args)); + } + + // Generate + + /** + * Generate up to `count` example strings that match this term. + * + * @param count Maximum number of unique strings to generate. + * @param opts Execution options. + * @returns A list of strings matched by this term. + */ + async generateStrings(count: number, opts?: OperationOptions): Promise { + const options = RequestOptionsBuilder.fromArgs({ execution_timeout: opts?.executionTimeout }); + return await RegexSolver.getInstance().generateStrings({ term: this, count, options }); + } + + // Others + + /** + * @returns a string representation of this term in the format `=`, which can later be parsed by `deserialize()`. + */ + serialize(): string { + return `${this.type}=${this.value}`; + } + + /** + * Parse a string representation produced by `serialize()`. + * + * @param input The serialized term, e.g. `"regex=abc"`. + * @returns A Term instance, or `null` if the input is empty or invalid. + */ + static deserialize(input: string | null | undefined): Term | null { + if (!input || !input.includes("=")) return null; + let pos = input.indexOf("="); + const [prefix, value] = [input.slice(0, pos), input.slice(pos + 1)]; + if (prefix === "regex") return Term.regex(value); + if (prefix === "fair") return Term.fair(value); return null; } @@ -171,7 +457,7 @@ export class Term { } interface TermTransient { - type: 'regex' | 'fair'; + type: TermType; value: string; } @@ -193,17 +479,69 @@ function loadDetails(data: TransientDetails): Details { return new Details(cardinality, length, data.empty, data.total); } +function loadCardinality(data: Cardinality): Cardinality { + return new Cardinality(data.type, data.value); +} + +function loadLength(data: { min: number, max?: number | null }): Length { + return new Length(data.min, data.max); +} + export class ApiError extends Error { constructor(message: string) { super("The API returned the following error: " + message); } } +export enum ResponseFormat { + ANY = 'any', + REGEX = 'regex', + FAIR = 'fair' +}; + +interface ResponseOptions { + format?: ResponseFormat; +} + +interface ExecutionOptions { + timeout?: number; +} + +interface RequestOptions { + schema_version?: number; + response?: ResponseOptions; + execution?: ExecutionOptions; +} + interface MultiTermsRequest { terms: Term[]; + options?: RequestOptions; } interface GenerateStringsRequest { term: Term; count: number; + options?: RequestOptions; +} + +interface RepeatRequest { + term: Term; + min: number; + max?: number | null; + options?: RequestOptions; +} + +class RequestOptionsBuilder { + static fromArgs(args?: { response_format?: ResponseFormat | null; execution_timeout?: number | null }): RequestOptions | undefined { + const response = args?.response_format ? { format: args.response_format } : undefined; + const execution = args?.execution_timeout ? { timeout: args.execution_timeout } : undefined; + if (response || execution) { + return { + schema_version: 1, + response, + execution, + }; + } + return undefined; + } } \ No newline at end of file diff --git a/tests/assets/response_generateStrings.json b/tests/assets/response_generateStrings.json deleted file mode 100644 index 9ee8883..0000000 --- a/tests/assets/response_generateStrings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "type": "strings", - "value": [ - "abcde", - "dede", - "deabc", - "abcabc" - ] -} \ No newline at end of file diff --git a/tests/assets/response_getDetails.json b/tests/assets/response_getDetails.json deleted file mode 100644 index 65e0539..0000000 --- a/tests/assets/response_getDetails.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "type": "details", - "cardinality": { - "type": "Integer", - "value": 2 - }, - "length": [ - 2, - 3 - ], - "empty": false, - "total": false -} \ No newline at end of file diff --git a/tests/assets/response_intersection.json b/tests/assets/response_intersection.json deleted file mode 100644 index e6b1a7a..0000000 --- a/tests/assets/response_intersection.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "regex", - "value": "deabc" -} \ No newline at end of file diff --git a/tests/assets/response_isEquivalentTo.json b/tests/assets/response_isEquivalentTo.json deleted file mode 100644 index 25147f3..0000000 --- a/tests/assets/response_isEquivalentTo.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "boolean", - "value": false -} \ No newline at end of file diff --git a/tests/assets/response_isSubsetOf.json b/tests/assets/response_isSubsetOf.json deleted file mode 100644 index 84ed493..0000000 --- a/tests/assets/response_isSubsetOf.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "boolean", - "value": true -} \ No newline at end of file diff --git a/tests/assets/response_subtraction.json b/tests/assets/response_subtraction.json deleted file mode 100644 index 478ac72..0000000 --- a/tests/assets/response_subtraction.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "regex", - "value": "abc" -} \ No newline at end of file diff --git a/tests/assets/response_union.json b/tests/assets/response_union.json deleted file mode 100644 index 27dae5e..0000000 --- a/tests/assets/response_union.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "regex", - "value": "(abc|de|fghi)" -} \ No newline at end of file diff --git a/tests/integration.test.ts b/tests/integration.test.ts new file mode 100644 index 0000000..25ce305 --- /dev/null +++ b/tests/integration.test.ts @@ -0,0 +1,169 @@ +import { ApiError, RegexSolver, ResponseFormat, Term } from '../src/index'; + +describe('integration test', () => { + beforeAll(() => { + require('dotenv').config(); + RegexSolver.initialize(); + }); + + // Analyze + + it('analyze cardinality', async () => { + const term = Term.regex('[0-4]'); + const c = await term.getCardinality(); + expect(c.toString()).toEqual('Integer(5)'); + }); + + it('analyze details', async () => { + const term = Term.regex('(abc|de)'); + const details = await term.getDetails(); + expect(details.toString()).toEqual( + 'Details[cardinality=Integer(2), length=Length[minimum=2, maximum=3], empty=false, total=false]' + ); + }); + + it('analyze details infinite', async () => { + const term = Term.regex('.*'); + const details = await term.getDetails(); + expect(details.toString()).toEqual( + 'Details[cardinality=Infinite, length=Length[minimum=0, maximum=null], empty=false, total=true]' + ); + }); + + it('analyze details empty', async () => { + const term = Term.regex('[]'); + const details = await term.getDetails(); + expect(details.toString()).toEqual( + 'Details[cardinality=Integer(0), length=Length[minimum=null, maximum=null], empty=true, total=false]' + ); + }); + + it('analyze dot', async () => { + const term = Term.regex('(abc|de)'); + const dot = await term.getDot(); + expect(dot.startsWith('digraph ')).toBe(true); + }); + + it('analyze empty string', async () => { + const term = Term.regex(''); + const result = await term.isEmptyString(); + expect(result).toBe(true); + }); + + it('analyze empty', async () => { + const term = Term.regex('[]'); + const result = await term.isEmpty(); + expect(result).toBe(true); + }); + + it('analyze total', async () => { + const term = Term.regex('.*'); + const result = await term.isTotal(); + expect(result).toBe(true); + }); + + it('analyze equivalent', async () => { + const term1 = Term.regex('(abc|de)'); + const term2 = Term.fair(' { + const term = Term.regex('[]'); + const length = await term.getLength(); + expect(length.toString()).toEqual('Length[minimum=null, maximum=null]'); + }); + + it('analyze length', async () => { + const term = Term.regex('(abc)?'); + const length = await term.getLength(); + expect(length.toString()).toEqual('Length[minimum=0, maximum=3]'); + }); + + it('analyze pattern', async () => { + const term = Term.regex('abc.*'); + const pattern = await term.getPattern(); + expect(pattern).toEqual('abc.*'); + }); + + it('analyze subset', async () => { + const term1 = Term.regex('de'); + const term2 = Term.regex('(abc|de)'); + const result = await term1.subset(term2); + expect(result).toBe(true); + }); + + // Compute + + it('compute concat', async () => { + const term1 = Term.regex('abc'); + const term2 = Term.regex('de'); + const result = await term1.concat(term2, { responseFormat: ResponseFormat.REGEX }); + expect(result.toString()).toEqual('regex=abcde'); + }); + + it('compute difference', async () => { + const term1 = Term.regex('(abc|de)'); + const term2 = Term.regex('de'); + const result = await term1.difference(term2, { responseFormat: ResponseFormat.REGEX }); + expect(result.toString()).toEqual('regex=abc'); + }); + + it('compute intersection', async () => { + const term1 = Term.regex('(abc|de){2}'); + const term2 = Term.regex('de.*'); + const term3 = Term.regex('.*abc'); + const result = await term1.intersection(term2, term3, { responseFormat: ResponseFormat.REGEX }); + expect(result.toString()).toEqual('regex=deabc'); + }); + + it('compute repeat', async () => { + const term = Term.regex('abc'); + const result = await term.repeat(3, 5, { responseFormat: ResponseFormat.REGEX }); + expect(result.toString()).toEqual('regex=(abc){3,5}'); + }); + + it('compute union', async () => { + const term1 = Term.regex('abc'); + const term2 = Term.regex('de'); + const term3 = Term.regex('fghi'); + const result = await term1.union(term2, term3, { responseFormat: ResponseFormat.REGEX }); + expect(result.toString()).toEqual('regex=(abc|de|fghi)'); + }); + + // Generate + + it('generate strings', async () => { + const term = Term.regex('(abc|de){2}'); + const strings = await term.generateStrings(10); + expect(strings.length).toEqual(4); + }); + + // README examples + + it('readme quickstart', () => { + const term1 = Term.regex("(abc|de|fg){2,}"); + const term2 = Term.regex("de.*"); + const term3 = Term.regex(".*abc"); + + const term4 = Term.regex(".+(abc|de).+"); + + term1.intersection(term2, term3) + .then(result => result.difference(term4)) + .then(result => result.getPattern()) + .then(result => expect(result).toEqual('de(fg)*abc')); // de(fg)*abc + }); + + it('readme response format', () => { + const term = Term.regex('abcde'); + + term.union(Term.regex('de'), { responseFormat: ResponseFormat.REGEX }).then(result => { + expect(result.toString()).toEqual('regex=(abc)?de'); + }); + + term.intersection(Term.regex('de.*'), { responseFormat: ResponseFormat.FAIR }).then(result => { + expect(result.toString().startsWith("fair=")).toBeTruthy(); + }); + }); +}); \ No newline at end of file diff --git a/tests/term-operation.test.ts b/tests/term-operation.test.ts index e310381..2101202 100644 --- a/tests/term-operation.test.ts +++ b/tests/term-operation.test.ts @@ -9,102 +9,6 @@ describe('term operations', () => { RegexSolver.initialize("TOKEN"); }); - it('get details', async () => { - const response = JSON.parse(await fs.readFile("tests/assets/response_getDetails.json", 'utf-8')); - nock('https://api.regexsolver.com/api/') - .post('/analyze/details') - .reply(200, response); - - const term = Term.regex("(abc|de)"); - const details = await term.getDetails(); - - expect(details.toString()).toEqual("Details[cardinality=Integer(2), length=Length[minimum=2, maximum=3], empty=false, total=false]"); - }); - - it('generate strings', async () => { - const response = JSON.parse(await fs.readFile("tests/assets/response_generateStrings.json", 'utf-8')); - nock('https://api.regexsolver.com/api/') - .post('/generate/strings') - .reply(200, response); - - const term = Term.regex("(abc|de){2}"); - const strings = await term.generateStrings(10); - - expect(strings.length).toEqual(4); - }); - - it('intersection', async () => { - const response = JSON.parse(await fs.readFile("tests/assets/response_intersection.json", 'utf-8')); - nock('https://api.regexsolver.com/api/') - .post('/compute/intersection') - .reply(200, response); - - const term1 = Term.regex("(abc|de){2}"); - const term2 = Term.regex("de.*"); - const term3 = Term.regex(".*abc"); - - const result = await term1.intersection(term2, term3); - - expect(result.toString()).toEqual("regex=deabc"); - }); - - it('union', async () => { - const response = JSON.parse(await fs.readFile("tests/assets/response_union.json", 'utf-8')); - nock('https://api.regexsolver.com/api/') - .post('/compute/union') - .reply(200, response); - - const term1 = Term.regex("abc"); - const term2 = Term.regex("de"); - const term3 = Term.regex("fghi"); - - const result = await term1.union(term2, term3); - - expect(result.toString()).toEqual("regex=(abc|de|fghi)"); - }); - - it('subtraction', async () => { - const response = JSON.parse(await fs.readFile("tests/assets/response_subtraction.json", 'utf-8')); - nock('https://api.regexsolver.com/api/') - .post('/compute/subtraction') - .reply(200, response); - - const term1 = Term.regex("(abc|de)"); - const term2 = Term.regex("de"); - - const result = await term1.subtraction(term2); - - expect(result.toString()).toEqual("regex=abc"); - }); - - it('is equivalent to', async () => { - const response = JSON.parse(await fs.readFile("tests/assets/response_isEquivalentTo.json", 'utf-8')); - nock('https://api.regexsolver.com/api/') - .post('/analyze/equivalence') - .reply(200, response); - - const term1 = Term.regex("(abc|de)"); - const term2 = Term.fair("rgmsW[1g2LvP=Gr&V>sLc#w-!No&(oq@Sf>X).?lI3{uh{80qWEH[#0.pHq@B-9o[LpP-a#fYI+"); - - const result = await term1.isEquivalentTo(term2); - - expect(result).toBe(false); - }); - - it('is subset of', async () => { - const response = JSON.parse(await fs.readFile("tests/assets/response_isSubsetOf.json", 'utf-8')); - nock('https://api.regexsolver.com/api/') - .post('/analyze/subset') - .reply(200, response); - - const term1 = Term.regex("de"); - const term2 = Term.regex("(abc|de)"); - - const result = await term1.isSubsetOf(term2); - - expect(result).toBe(true); - }); - it('error response correctly handled', async () => { const response = JSON.parse(await fs.readFile("tests/assets/response_error.json", 'utf-8')); nock('https://api.regexsolver.com/api/') From 12db80f28c5c674ea7dbf394879627d67e6c530c Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Sun, 19 Oct 2025 15:42:01 +0200 Subject: [PATCH 02/20] fix readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04de20f..bbc3e94 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ Timeout is best effort. The exact time is not guaranteed. | Method | Return | Description | | -------- | ------- | ------- | -| `t.generateStrings(count: int)` | `Promise` | Generates up to `count` unique example strings matched by `t`. Supports `executionTimeout`. | +| `t.generateStrings(count: int, opts?)` | `Promise` | Generates up to `count` unique example strings matched by `t`. Supports `executionTimeout`. | ### Other | Method | Return | Description | From 13293bba9d09bb2736be24135949367690988af0 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Sun, 19 Oct 2025 21:38:34 +0200 Subject: [PATCH 03/20] fix readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bbc3e94..7f4acf9 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ The API can handle terms in two formats: - `regex`: a regular expression pattern - `fair`: FAIR (Fast Automaton Internal Representation), a stable, signed format used internally by the engine -By default, the engine returns whatever the operation produces, with no extra convertion. Override with `response_format`: +By default, the engine returns whatever the operation produces, with no extra convertion. Override with `responseFormat`: ```javascript import { Term, ResponseFormat } from 'regexsolver'; @@ -72,13 +72,13 @@ term.intersection(Term.regex('de.*'), { responseFormat: ResponseFormat.FAIR }).t }); ``` -If the format does not matter, omit `response_format` or set it to `ResponseFormat.ANY`. +If the format does not matter, omit `responseFormat` or set it to `ResponseFormat.ANY`. -Regardless of the format, you can always call `get_pattern()` to obtain the regex pattern of a term. +Regardless of the format, you can always call `getPattern()` to obtain the regex pattern of a term. ## Bounding execution time -Set a server-side compute timeout in milliseconds with `execution_timeout`: +Set a server-side compute timeout in milliseconds with `executionTimeout`: ```javascript import { ApiError, Term } from 'regexsolver'; From 20b58a9104124af7f8d69a86b7152e89ec31176c Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Mon, 20 Oct 2025 22:10:37 +0200 Subject: [PATCH 04/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f4acf9..7d688d0 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ Timeout is best effort. The exact time is not guaranteed. | Method | Return | Description | | -------- | ------- | ------- | -| `t.generateStrings(count: int, opts?)` | `Promise` | Generates up to `count` unique example strings matched by `t`. Supports `executionTimeout`. | +| `t.generateStrings(count: number, opts?)` | `Promise` | Generates up to `count` unique example strings matched by `t`. Supports `executionTimeout`. | ### Other | Method | Return | Description | From 741c423db324fb895407c71a798139ffae937bbc Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:45:37 +0200 Subject: [PATCH 05/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d688d0..0439498 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ term.union(Term.regex('de'), { responseFormat: ResponseFormat.REGEX }).then(resu console.log(result.toString()); // regex=(abc)?de }); -term.intersection(Term.regex('de.*'), { responseFormat: ResponseFormat.FAIR }).then(result => { +term.union(Term.regex('de'), { responseFormat: ResponseFormat.FAIR }).then(result => { console.log(result.toString()); // fair=... }); ``` From 0a5fa07b866f0be436bb7dcbfd58997ec732fe97 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Sun, 26 Oct 2025 15:08:21 +0100 Subject: [PATCH 06/20] remove getDetails --- README.md | 1 - src/details.ts | 43 ------- src/index.ts | 226 +++++++++++++++++------------------ tests/integration.test.ts | 26 +--- tests/term-operation.test.ts | 2 +- 5 files changed, 110 insertions(+), 188 deletions(-) delete mode 100644 src/details.ts diff --git a/README.md b/README.md index 0439498..afecc27 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,6 @@ Timeout is best effort. The exact time is not guaranteed. | -------- | ------- | ------- | | `t.equivalent(term: Term, opts?)` | `Promise` | `true` if `t` and `term` accept exactly the same language. Supports `executionTimeout`. | | `t.getCardinality()` | `Promise` | Returns the cardinality of the term (i.e., the number of possible matched strings). | -| `t.getDetails()` | `Promise
` | Returns cardinality, length bounds, and if it is empty or total. | | `t.getDot()` | `Promise` | Returns a Graphviz DOT representation of the automaton for the term. | | `t.getFair()` | `string` | Returns the FAIR of the term if defined. | | `t.getLength()` | `Promise` | Returns the minimum and maximum length of matched strings. | diff --git a/src/details.ts b/src/details.ts deleted file mode 100644 index c657845..0000000 --- a/src/details.ts +++ /dev/null @@ -1,43 +0,0 @@ -export class Cardinality { - constructor( - public type: 'infinite' | 'bigInteger' | 'integer', - public value?: number - ) { } - - isInfinite(): boolean { - return this.type == 'infinite'; - } - - toString(): string { - const cap1 = s => s ? s[0].toUpperCase() + s.slice(1) : s; - if (this.type == 'integer') { - return cap1(this.type) + '(' + this.value + ')'; - } else { - return cap1(this.type); - } - } -} - -export class Length { - constructor( - public minimum: number, - public maximum?: number - ) { } - - toString(): string { - return "Length[minimum=" + this.minimum + ", maximum=" + this.maximum + "]"; - } -} - -export class Details { - constructor( - public cardinality: Cardinality, - public length: Length, - public empty: boolean, - public total: boolean - ) { } - - toString(): string { - return "Details[cardinality=" + this.cardinality + ", length=" + this.length + ", empty=" + this.empty + ", total=" + this.total + "]"; - } -} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 515de30..4028dbe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,73 @@ import axios, { AxiosInstance } from "axios"; -import { Cardinality, Details, Length } from "./details"; + +interface OperationOptions { + responseFormat?: ResponseFormat; + executionTimeout?: number; +} + +interface TermTransient { + type: TermType; + value: string; +} + +function loadTerm(term: TermTransient): Term { + return new Term(term.type, term.value); +} + +function loadCardinality(data: Cardinality): Cardinality { + return new Cardinality(data.type, data.value); +} + +function loadLength(data: { min: number, max?: number | null }): Length { + return new Length(data.min, data.max); +} + +interface ResponseOptions { + format?: ResponseFormat; +} + +interface ExecutionOptions { + timeout?: number; +} + +interface RequestOptions { + schema_version?: number; + response?: ResponseOptions; + execution?: ExecutionOptions; +} + +interface MultiTermsRequest { + terms: Term[]; + options?: RequestOptions; +} + +interface GenerateStringsRequest { + term: Term; + count: number; + options?: RequestOptions; +} + +interface RepeatRequest { + term: Term; + min: number; + max?: number | null; + options?: RequestOptions; +} + +class RequestOptionsBuilder { + static fromArgs(args?: { response_format?: ResponseFormat | null; execution_timeout?: number | null }): RequestOptions | undefined { + const response = args?.response_format ? { format: args.response_format } : undefined; + const execution = args?.execution_timeout ? { timeout: args.execution_timeout } : undefined; + if (response || execution) { + return { + schema_version: 1, + response, + execution, + }; + } + return undefined; + } +} export class RegexSolver { private static instance: RegexSolver; @@ -27,7 +95,7 @@ export class RegexSolver { if (!baseURL) { baseURL = process.env.REGEXSOLVER_BASE_URL; if (!baseURL) { - baseURL = "https://api.regexsolver.com/" + baseURL = "https://api.regexsolver.com/v1/" } } instance.apiClient = axios.create({ @@ -41,62 +109,56 @@ export class RegexSolver { // Analyze - analyzeDetails(term: Term): Promise
{ - return this.apiClient.post('/api/analyze/details', term) - .then(response => loadDetails(response.data)) - .catch(error => { throw new ApiError(error.message) }); - } - analyzeCardinality(term: Term): Promise { - return this.apiClient.post('/api/analyze/cardinality', term) + return this.apiClient.post('/analyze/cardinality', term) .then(response => loadCardinality(response.data)) .catch(error => { throw new ApiError(error.message) }); } analyzeLength(term: Term): Promise { - return this.apiClient.post('/api/analyze/length', term) + return this.apiClient.post('/analyze/length', term) .then(response => loadLength(response.data)) .catch(error => { throw new ApiError(error.message) }); } analyzeEquivalent(request: MultiTermsRequest): Promise { - return this.apiClient.post('/api/analyze/equivalent', request) + return this.apiClient.post('/analyze/equivalent', request) .then(response => response.data.value) .catch(error => { throw new ApiError(error.message) }); } analyzeSubset(request: MultiTermsRequest): Promise { - return this.apiClient.post('/api/analyze/subset', request) + return this.apiClient.post('/analyze/subset', request) .then(response => response.data.value) .catch(error => { throw new ApiError(error.message) }); } analyzeEmpty(request: Term): Promise { - return this.apiClient.post('/api/analyze/empty', request) + return this.apiClient.post('/analyze/empty', request) .then(response => response.data.value) .catch(error => { throw new ApiError(error.message) }); } analyzeTotal(request: Term): Promise { - return this.apiClient.post('/api/analyze/total', request) + return this.apiClient.post('/analyze/total', request) .then(response => response.data.value) .catch(error => { throw new ApiError(error.message) }); } analyzeEmptyString(request: Term): Promise { - return this.apiClient.post('/api/analyze/empty_string', request) + return this.apiClient.post('/analyze/empty_string', request) .then(response => response.data.value) .catch(error => { throw new ApiError(error.message) }); } analyzeDot(request: Term): Promise { - return this.apiClient.post('/api/analyze/dot', request) + return this.apiClient.post('/analyze/dot', request) .then(response => response.data.value) .catch(error => { throw new ApiError(error.message) }); } analyzePattern(request: Term): Promise { - return this.apiClient.post('/api/analyze/pattern', request) + return this.apiClient.post('/analyze/pattern', request) .then(response => response.data.value) .catch(error => { throw new ApiError(error.message) }); } @@ -104,31 +166,31 @@ export class RegexSolver { // Compute computeRepeat(request: RepeatRequest): Promise { // todo - return this.apiClient.post('/api/compute/repeat', request) + return this.apiClient.post('/compute/repeat', request) .then(response => loadTerm(response.data)) .catch(error => { throw new ApiError(error.message) }); } computeIntersection(request: MultiTermsRequest): Promise { - return this.apiClient.post('/api/compute/intersection', request) + return this.apiClient.post('/compute/intersection', request) .then(response => loadTerm(response.data)) .catch(error => { throw new ApiError(error.message) }); } computeUnion(request: MultiTermsRequest): Promise { - return this.apiClient.post('/api/compute/union', request) + return this.apiClient.post('/compute/union', request) .then(response => loadTerm(response.data)) .catch(error => { throw new ApiError(error.message) }); } computeDifference(request: MultiTermsRequest): Promise { - return this.apiClient.post('/api/compute/difference', request) + return this.apiClient.post('/compute/difference', request) .then(response => loadTerm(response.data)) .catch(error => { throw new ApiError(error.message) }); } computeConcat(request: MultiTermsRequest): Promise { - return this.apiClient.post('/api/compute/concat', request) + return this.apiClient.post('/compute/concat', request) .then(response => loadTerm(response.data)) .catch(error => { throw new ApiError(error.message) }); } @@ -136,7 +198,7 @@ export class RegexSolver { // Generate generateStrings(request: GenerateStringsRequest): Promise { - return this.apiClient.post('/api/generate/strings', request) + return this.apiClient.post('/generate/strings', request) .then(response => response.data.value) .catch(error => { throw new ApiError(error.message) }); } @@ -144,15 +206,10 @@ export class RegexSolver { export type TermType = "fair" | "regex"; -interface OperationOptions { - responseFormat?: ResponseFormat; - executionTimeout?: number; -} export class Term { readonly type: TermType; readonly value: string; - private details?: Details; private cardinality?: Cardinality; private length?: Length; private empty?: boolean; @@ -208,26 +265,11 @@ export class Term { async getCardinality(): Promise { if (this.cardinality) { return this.cardinality; - } else if (this.details) { - return this.details.cardinality; } this.cardinality = await RegexSolver.getInstance().analyzeCardinality(this); return this.cardinality; } - /** - * Analyze this term and return detailed information including cardinality, length, and whether it is empty or total. - * - * Results are cached on the instance to avoid repeated API calls. - */ - async getDetails(): Promise
{ - if (this.details) { - return this.details; - } - this.details = await RegexSolver.getInstance().analyzeDetails(this); - return this.details; - } - /** * Get the GraphViz DOT representation of this term. * @@ -260,8 +302,6 @@ export class Term { async getLength(): Promise { if (this.length) { return this.length; - } else if (this.details) { - return this.details.length; } this.length = await RegexSolver.getInstance().analyzeLength(this); return this.length; @@ -293,8 +333,6 @@ export class Term { async isEmpty(): Promise { if (this.empty) { return this.empty; - } else if (this.details) { - return this.details.empty; } this.empty = await RegexSolver.getInstance().analyzeEmpty(this); return this.empty; @@ -321,8 +359,6 @@ export class Term { async isTotal(): Promise { if (this.total) { return this.total; - } else if (this.details) { - return this.details.total; } this.total = await RegexSolver.getInstance().analyzeTotal(this); return this.total; @@ -456,36 +492,6 @@ export class Term { } } -interface TermTransient { - type: TermType; - value: string; -} - -function loadTerm(term: TermTransient): Term { - return new Term(term.type, term.value); -} - -interface TransientDetails { - cardinality: Cardinality; - length: number[]; - empty: boolean; - total: boolean; -} - -function loadDetails(data: TransientDetails): Details { - const cardinality = new Cardinality(data.cardinality.type, data.cardinality.value); - const length = new Length(data.length[0], data.length[1]); - - return new Details(cardinality, length, data.empty, data.total); -} - -function loadCardinality(data: Cardinality): Cardinality { - return new Cardinality(data.type, data.value); -} - -function loadLength(data: { min: number, max?: number | null }): Length { - return new Length(data.min, data.max); -} export class ApiError extends Error { constructor(message: string) { @@ -499,49 +505,33 @@ export enum ResponseFormat { FAIR = 'fair' }; -interface ResponseOptions { - format?: ResponseFormat; -} - -interface ExecutionOptions { - timeout?: number; -} - -interface RequestOptions { - schema_version?: number; - response?: ResponseOptions; - execution?: ExecutionOptions; -} +export class Cardinality { + constructor( + public type: 'infinite' | 'bigInteger' | 'integer', + public value?: number + ) { } -interface MultiTermsRequest { - terms: Term[]; - options?: RequestOptions; -} + isInfinite(): boolean { + return this.type == 'infinite'; + } -interface GenerateStringsRequest { - term: Term; - count: number; - options?: RequestOptions; + toString(): string { + const cap1 = s => s ? s[0].toUpperCase() + s.slice(1) : s; + if (this.type == 'integer') { + return cap1(this.type) + '(' + this.value + ')'; + } else { + return cap1(this.type); + } + } } -interface RepeatRequest { - term: Term; - min: number; - max?: number | null; - options?: RequestOptions; -} +export class Length { + constructor( + public minimum: number, + public maximum?: number + ) { } -class RequestOptionsBuilder { - static fromArgs(args?: { response_format?: ResponseFormat | null; execution_timeout?: number | null }): RequestOptions | undefined { - const response = args?.response_format ? { format: args.response_format } : undefined; - const execution = args?.execution_timeout ? { timeout: args.execution_timeout } : undefined; - if (response || execution) { - return { - schema_version: 1, - response, - execution, - }; - } - return undefined; + toString(): string { + return "Length[minimum=" + this.minimum + ", maximum=" + this.maximum + "]"; } } \ No newline at end of file diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 25ce305..0d300b4 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -1,4 +1,4 @@ -import { ApiError, RegexSolver, ResponseFormat, Term } from '../src/index'; +import { RegexSolver, ResponseFormat, Term } from '../src/index'; describe('integration test', () => { beforeAll(() => { @@ -14,30 +14,6 @@ describe('integration test', () => { expect(c.toString()).toEqual('Integer(5)'); }); - it('analyze details', async () => { - const term = Term.regex('(abc|de)'); - const details = await term.getDetails(); - expect(details.toString()).toEqual( - 'Details[cardinality=Integer(2), length=Length[minimum=2, maximum=3], empty=false, total=false]' - ); - }); - - it('analyze details infinite', async () => { - const term = Term.regex('.*'); - const details = await term.getDetails(); - expect(details.toString()).toEqual( - 'Details[cardinality=Infinite, length=Length[minimum=0, maximum=null], empty=false, total=true]' - ); - }); - - it('analyze details empty', async () => { - const term = Term.regex('[]'); - const details = await term.getDetails(); - expect(details.toString()).toEqual( - 'Details[cardinality=Integer(0), length=Length[minimum=null, maximum=null], empty=true, total=false]' - ); - }); - it('analyze dot', async () => { const term = Term.regex('(abc|de)'); const dot = await term.getDot(); diff --git a/tests/term-operation.test.ts b/tests/term-operation.test.ts index 2101202..074705a 100644 --- a/tests/term-operation.test.ts +++ b/tests/term-operation.test.ts @@ -11,7 +11,7 @@ describe('term operations', () => { it('error response correctly handled', async () => { const response = JSON.parse(await fs.readFile("tests/assets/response_error.json", 'utf-8')); - nock('https://api.regexsolver.com/api/') + nock('https://api.regexsolver.com/v1/') .post('/compute/intersection') .reply(200, response); From 77dc66221927ab4d34379f991b6aecb5ac8971b7 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Tue, 24 Mar 2026 21:13:40 +0100 Subject: [PATCH 07/20] WIP: Update the wrapper --- .gitignore | 2 +- .openapi-generator/FILES | 5 + .openapi-generator/VERSION | 1 + generate-api.sh | 12 + index.ts | 18 + jest.config.js | 7 +- openapitools.json | 7 + package-lock.json | 2684 ++++++++++++++-------- package.json | 14 +- src/RateLimiter.ts | 47 + src/RegexSolverClient.ts | 594 +++++ src/exceptions/index.ts | 91 + src/generated/.openapi-generator-ignore | 7 + src/generated/.openapi-generator/FILES | 5 + src/generated/.openapi-generator/VERSION | 1 + src/generated/api.ts | 1618 +++++++++++++ src/generated/base.ts | 62 + src/generated/common.ts | 127 + src/generated/configuration.ts | 121 + src/generated/index.ts | 18 + src/index.ts | 543 +---- src/models/Cardinality.ts | 36 + src/models/Length.ts | 23 + src/models/ResponseFormat.ts | 17 + src/models/Term.ts | 76 + tests/RateLimiter.test.ts | 58 + tests/assets/response_error.json | 4 - tests/client.test.ts | 140 ++ tests/integration.test.ts | 145 -- tests/serialization.test.ts | 18 - tests/term-operation.test.ts | 27 - tests/term.test.ts | 45 + tsconfig.json | 32 +- 33 files changed, 4878 insertions(+), 1727 deletions(-) create mode 100644 .openapi-generator/FILES create mode 100644 .openapi-generator/VERSION create mode 100755 generate-api.sh create mode 100644 index.ts create mode 100644 openapitools.json create mode 100644 src/RateLimiter.ts create mode 100644 src/RegexSolverClient.ts create mode 100644 src/exceptions/index.ts create mode 100644 src/generated/.openapi-generator-ignore create mode 100644 src/generated/.openapi-generator/FILES create mode 100644 src/generated/.openapi-generator/VERSION create mode 100644 src/generated/api.ts create mode 100644 src/generated/base.ts create mode 100644 src/generated/common.ts create mode 100644 src/generated/configuration.ts create mode 100644 src/generated/index.ts create mode 100644 src/models/Cardinality.ts create mode 100644 src/models/Length.ts create mode 100644 src/models/ResponseFormat.ts create mode 100644 src/models/Term.ts create mode 100644 tests/RateLimiter.test.ts delete mode 100644 tests/assets/response_error.json create mode 100644 tests/client.test.ts delete mode 100644 tests/integration.test.ts delete mode 100644 tests/serialization.test.ts delete mode 100644 tests/term-operation.test.ts create mode 100644 tests/term.test.ts diff --git a/.gitignore b/.gitignore index de6177b..9c37a32 100644 --- a/.gitignore +++ b/.gitignore @@ -64,4 +64,4 @@ TEST-results.xml package-lock.json .eslintcache *v8.log -/lib/ \ No newline at end of file +/lib/ diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES new file mode 100644 index 0000000..53250c0 --- /dev/null +++ b/.openapi-generator/FILES @@ -0,0 +1,5 @@ +api.ts +base.ts +common.ts +configuration.ts +index.ts diff --git a/.openapi-generator/VERSION b/.openapi-generator/VERSION new file mode 100644 index 0000000..2540a3a --- /dev/null +++ b/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.20.0 diff --git a/generate-api.sh b/generate-api.sh new file mode 100755 index 0000000..fb1d15f --- /dev/null +++ b/generate-api.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +SPEC_FILE="../m-lab/shared/openapi.yaml" +OUT_DIR="./src/generated" + +echo "Generating TypeScript Axios client..." +openapi-generator-cli generate \ + -i "$SPEC_FILE" \ + -g typescript-axios \ + -o "$OUT_DIR" + +echo "Generation complete. Metadata is at root, generated source is in src/generated." diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..31e5aa5 --- /dev/null +++ b/index.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * RegexSolver + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export * from "./api"; +export * from "./configuration"; + diff --git a/jest.config.js b/jest.config.js index b07ae7b..e63ddf2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,4 +1,5 @@ module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', -}; \ No newline at end of file + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/tests/**/*.test.ts'], +}; diff --git a/openapitools.json b/openapitools.json new file mode 100644 index 0000000..91d9c43 --- /dev/null +++ b/openapitools.json @@ -0,0 +1,7 @@ +{ + "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "7.21.0" + } +} diff --git a/package-lock.json b/package-lock.json index e92e583..cda5800 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,33 +1,33 @@ { "name": "regexsolver", - "version": "1.0.4", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "regexsolver", - "version": "1.0.4", + "version": "1.1.0", "license": "MIT", "dependencies": { - "axios": "^1.12.2" + "axios": "^1.13.6" }, "devDependencies": { - "@types/jest": "^29.5.12", - "dotenv": "^17.2.3", - "jest": "^29.7.0", - "nock": "^13.5.4", - "ts-jest": "^29.2.4", - "typescript": "^5.5.4" + "@types/jest": "^30.0.0", + "@types/node": "^25.5.0", + "axios-mock-adapter": "^2.1.0", + "jest": "^30.3.0", + "ts-jest": "^29.4.6", + "typescript": "^5.9.3" } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -36,9 +36,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", - "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "dev": true, "license": "MIT", "engines": { @@ -46,22 +46,21 @@ } }, "node_modules/@babel/core": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", - "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.4", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.4", - "@babel/types": "^7.28.4", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -78,14 +77,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -95,13 +94,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -122,29 +121,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -154,9 +153,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, "license": "MIT", "engines": { @@ -174,9 +173,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -194,27 +193,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.4" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -279,13 +278,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -321,13 +320,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -447,13 +446,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -463,33 +462,33 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", - "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" }, "engines": { @@ -497,14 +496,14 @@ } }, "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -517,6 +516,58 @@ "dev": true, "license": "MIT" }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -545,61 +596,60 @@ } }, "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.3.0.tgz", + "integrity": "sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", + "@jest/types": "30.3.0", "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", + "chalk": "^4.1.2", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", "slash": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.3.0.tgz", + "integrity": "sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", + "@jest/console": "30.3.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.3.0", + "jest-config": "30.3.0", + "jest-haste-map": "30.3.0", + "jest-message-util": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-resolve-dependencies": "30.3.0", + "jest-runner": "30.3.0", + "jest-runtime": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "jest-watcher": "30.3.0", + "pretty-format": "30.3.0", + "slash": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" @@ -610,117 +660,150 @@ } } }, + "node_modules/@jest/diff-sequences": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", + "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.3.0.tgz", + "integrity": "sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", + "@jest/fake-timers": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "jest-mock": "^29.7.0" + "jest-mock": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.3.0.tgz", + "integrity": "sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==", "dev": true, "license": "MIT", "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" + "expect": "30.3.0", + "jest-snapshot": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", + "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", "dev": true, "license": "MIT", "dependencies": { - "jest-get-type": "^29.6.3" + "@jest/get-type": "30.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.3.0.tgz", + "integrity": "sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", + "@jest/types": "30.3.0", + "@sinonjs/fake-timers": "^15.0.0", "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-util": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.3.0.tgz", + "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.3.0", + "@jest/expect": "30.3.0", + "@jest/types": "30.3.0", + "jest-mock": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" + "@types/node": "*", + "jest-regex-util": "30.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.3.0.tgz", + "integrity": "sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", + "@jest/console": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "@jridgewell/trace-mapping": "^0.3.25", "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", + "istanbul-lib-source-maps": "^5.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", + "jest-worker": "30.3.0", "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", + "string-length": "^4.0.2", "v8-to-istanbul": "^9.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" @@ -732,108 +815,124 @@ } }, "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", + "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.27.8" + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.3.0.tgz", + "integrity": "sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "@jest/console": "30.3.0", + "@jest/types": "30.3.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz", + "integrity": "sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", + "@jest/test-result": "30.3.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", "slash": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.3.0.tgz", + "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", + "@babel/core": "^7.27.4", + "@jest/types": "30.3.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.3.0", + "pirates": "^4.0.7", "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" + "write-file-atomic": "^5.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", + "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jridgewell/gen-mapping": { @@ -886,10 +985,47 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT" }, @@ -904,13 +1040,24 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz", + "integrity": "sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, "node_modules/@types/babel__core": { @@ -958,16 +1105,6 @@ "@babel/types": "^7.28.2" } }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -996,24 +1133,24 @@ } }, "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", "dev": true, "license": "MIT", "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" + "expect": "^30.0.0", + "pretty-format": "^30.0.0" } }, "node_modules/@types/node": { - "version": "24.7.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz", - "integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==", + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.14.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/stack-utils": { @@ -1024,9 +1161,9 @@ "license": "MIT" }, "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, "license": "MIT", "dependencies": { @@ -1040,6 +1177,306 @@ "dev": true, "license": "MIT" }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1057,13 +1494,16 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { @@ -1096,6 +1536,19 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1113,86 +1566,83 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, + "node_modules/axios-mock-adapter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-2.1.0.tgz", + "integrity": "sha512-AZUe4OjECGCNNssH8SOdtneiQELsqTsat3SQQCWLPjN436/H+L9AjWfV7bF+Zg/YL9cgbhrz5671hoh+Tbn98w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "is-buffer": "^2.0.5" + }, + "peerDependencies": { + "axios": ">= 0.17.0" + } + }, "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.3.0.tgz", + "integrity": "sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", + "@jest/transform": "30.3.0", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.3.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", "slash": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "@babel/core": "^7.8.0" + "@babel/core": "^7.11.0 || ^8.0.0-0" } }, "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, "license": "BSD-3-Clause", + "workspaces": [ + "test/babel-8" + ], "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", "test-exclude": "^6.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" + "node": ">=12" } }, "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.3.0.tgz", + "integrity": "sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" + "@types/babel__core": "^7.20.5" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/babel-preset-current-node-syntax": { @@ -1223,20 +1673,20 @@ } }, "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.3.0.tgz", + "integrity": "sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==", "dev": true, "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" + "babel-plugin-jest-hoist": "30.3.0", + "babel-preset-current-node-syntax": "^1.2.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" } }, "node_modules/balanced-match": { @@ -1247,43 +1697,32 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.16", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.16.tgz", - "integrity": "sha512-OMu3BGQ4E7P1ErFsIPpbJh0qvDudM/UuJeHgkAvfWe+0HFJCXh+t/l8L6fVLR55RI/UbKrVLnAXZSVwd9ysWYw==", + "version": "2.10.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz", + "integrity": "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0" } }, "node_modules/browserslist": { - "version": "4.26.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", - "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -1300,13 +1739,12 @@ } ], "license": "MIT", - "peer": true, "dependencies": { - "baseline-browser-mapping": "^2.8.9", - "caniuse-lite": "^1.0.30001746", - "electron-to-chromium": "^1.5.227", - "node-releases": "^2.0.21", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -1379,9 +1817,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001750", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001750.tgz", - "integrity": "sha512-cuom0g5sdX6rw00qOoLNSFCJ9/mYIsuSOA+yzpDw8eopiFqcVwQvZHqov0vmEighRxX++cfC0Vg1G+1Iy/mSpQ==", + "version": "1.0.30001780", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", + "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", "dev": true, "funding": [ { @@ -1427,9 +1865,9 @@ } }, "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -1443,9 +1881,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", "dev": true, "license": "MIT" }, @@ -1464,6 +1902,69 @@ "node": ">=12" } }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -1476,9 +1977,9 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "dev": true, "license": "MIT" }, @@ -1528,28 +2029,6 @@ "dev": true, "license": "MIT" }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1584,9 +2063,9 @@ } }, "node_modules/dedent": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", - "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1627,29 +2106,6 @@ "node": ">=8" } }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dotenv": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", - "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1664,10 +2120,17 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/electron-to-chromium": { - "version": "1.5.235", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.235.tgz", - "integrity": "sha512-i/7ntLFwOdoHY7sgjlTIDo4Sl8EdoTjWIaKinYOVfC6bOp71bmwenyZthWHcasxgHDNWbWxvG9M3Ia116zIaYQ==", + "version": "1.5.321", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz", + "integrity": "sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==", "dev": true, "license": "ISC" }, @@ -1685,9 +2148,9 @@ } }, "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==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, "license": "MIT" }, @@ -1804,32 +2267,48 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", + "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" + "@jest/expect-utils": "30.3.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-util": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1847,19 +2326,6 @@ "bser": "2.1.1" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -1894,10 +2360,27 @@ } } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -2022,22 +2505,22 @@ } }, "node_modules/glob": { - "version": "7.2.3", - "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", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2206,20 +2689,28 @@ "dev": true, "license": "MIT" }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, "node_modules/is-fullwidth-code-point": { @@ -2242,16 +2733,6 @@ "node": ">=6" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -2300,9 +2781,9 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -2328,15 +2809,15 @@ } }, "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, "license": "BSD-3-Clause", "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "istanbul-lib-coverage": "^3.0.0" }, "engines": { "node": ">=10" @@ -2356,24 +2837,39 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.3.0.tgz", + "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" + "@jest/core": "30.3.0", + "@jest/types": "30.3.0", + "import-local": "^3.2.0", + "jest-cli": "30.3.0" }, "bin": { "jest": "bin/jest.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" @@ -2385,76 +2881,75 @@ } }, "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.3.0.tgz", + "integrity": "sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==", "dev": true, "license": "MIT", "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", + "execa": "^5.1.1", + "jest-util": "30.3.0", "p-limit": "^3.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.3.0.tgz", + "integrity": "sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", + "@jest/environment": "30.3.0", + "@jest/expect": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "chalk": "^4.0.0", + "chalk": "^4.1.2", "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.3.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-runtime": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", + "pretty-format": "30.3.0", + "pure-rand": "^7.0.0", "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "stack-utils": "^2.0.6" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.3.0.tgz", + "integrity": "sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" + "@jest/core": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "yargs": "^17.7.2" }, "bin": { "jest": "bin/jest.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" @@ -2466,215 +2961,210 @@ } }, "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.3.0.tgz", + "integrity": "sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.3.0", + "@jest/types": "30.3.0", + "babel-jest": "30.3.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-circus": "30.3.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-runner": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", + "pretty-format": "30.3.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { "@types/node": "*", + "esbuild-register": ">=3.4.0", "ts-node": ">=9.0.0" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "esbuild-register": { + "optional": true + }, "ts-node": { "optional": true } } }, "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", + "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "@jest/diff-sequences": "30.3.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", "dev": true, "license": "MIT", "dependencies": { - "detect-newline": "^3.0.0" + "detect-newline": "^3.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.3.0.tgz", + "integrity": "sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "jest-util": "30.3.0", + "pretty-format": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.3.0.tgz", + "integrity": "sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", + "@jest/environment": "30.3.0", + "@jest/fake-timers": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "jest-mock": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", + "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", + "@jest/types": "30.3.0", "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.3.0", + "jest-worker": "30.3.0", + "picomatch": "^4.0.3", "walker": "^1.0.8" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "optionalDependencies": { - "fsevents": "^2.3.2" + "fsevents": "^2.3.3" } }, "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz", + "integrity": "sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==", "dev": true, "license": "MIT", "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "@jest/get-type": "30.1.0", + "pretty-format": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", + "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.3.0", + "pretty-format": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", + "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.3.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3", + "pretty-format": "30.3.0", "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "stack-utils": "^2.0.6" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", + "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", + "@jest/types": "30.3.0", "@types/node": "*", - "jest-util": "^29.7.0" + "jest-util": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-pnp-resolver": { @@ -2696,153 +3186,154 @@ } }, "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.3.0.tgz", + "integrity": "sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz", + "integrity": "sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==", "dev": true, "license": "MIT", "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.3.0.tgz", + "integrity": "sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", + "@jest/console": "30.3.0", + "@jest/environment": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "chalk": "^4.0.0", + "chalk": "^4.1.2", "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.3.0", + "jest-haste-map": "30.3.0", + "jest-leak-detector": "30.3.0", + "jest-message-util": "30.3.0", + "jest-resolve": "30.3.0", + "jest-runtime": "30.3.0", + "jest-util": "30.3.0", + "jest-watcher": "30.3.0", + "jest-worker": "30.3.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.3.0.tgz", + "integrity": "sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", + "@jest/environment": "30.3.0", + "@jest/fake-timers": "30.3.0", + "@jest/globals": "30.3.0", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", + "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.3.0", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.3.0", + "graceful-fs": "^4.2.11", + "jest-diff": "30.3.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", + "pretty-format": "30.3.0", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -2853,39 +3344,39 @@ } }, "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", + "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", + "@jest/types": "30.3.0", "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", "leven": "^3.1.0", - "pretty-format": "^29.7.0" + "pretty-format": "30.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-validate/node_modules/camelcase": { @@ -2902,39 +3393,40 @@ } }, "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.3.0.tgz", + "integrity": "sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" + "jest-util": "30.3.0", + "string-length": "^4.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", + "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", - "jest-util": "^29.7.0", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.3.0", "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "supports-color": "^8.1.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-worker/node_modules/supports-color": { @@ -2961,9 +3453,9 @@ "license": "MIT" }, "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==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { @@ -2994,13 +3486,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "license": "ISC" - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -3014,16 +3499,6 @@ "node": ">=6" } }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -3088,9 +3563,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -3133,20 +3608,6 @@ "dev": true, "license": "MIT" }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -3179,16 +3640,19 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.2" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -3201,6 +3665,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3208,6 +3682,22 @@ "dev": true, "license": "MIT" }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3222,21 +3712,6 @@ "dev": true, "license": "MIT" }, - "node_modules/nock": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", - "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "propagate": "^2.0.0" - }, - "engines": { - "node": ">= 10.13" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -3245,9 +3720,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.23", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", - "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==", + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", "dev": true, "license": "MIT" }, @@ -3355,6 +3830,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -3404,12 +3886,29 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, - "license": "MIT" + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" }, "node_modules/picocolors": { "version": "1.1.1", @@ -3419,13 +3918,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -3455,18 +3954,18 @@ } }, "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", + "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/pretty-format/node_modules/ansi-styles": { @@ -3482,30 +3981,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -3513,9 +3988,9 @@ "license": "MIT" }, "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", "dev": true, "funding": [ { @@ -3546,27 +4021,6 @@ "node": ">=0.10.0" } }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", @@ -3590,16 +4044,6 @@ "node": ">=8" } }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -3634,18 +4078,17 @@ } }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "MIT" + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/slash": { "version": "3.0.0", @@ -3712,7 +4155,49 @@ "node": ">=10" } }, + "node_modules/string-length/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", @@ -3727,7 +4212,54 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", @@ -3740,6 +4272,16 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -3786,17 +4328,20 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", "dev": true, "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, "engines": { - "node": ">= 0.4" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/synckit" } }, "node_modules/test-exclude": { @@ -3814,30 +4359,63 @@ "node": ">=8" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "BSD-3-Clause" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", "dependencies": { - "is-number": "^7.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=8.0" + "node": "*" } }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/ts-jest": { - "version": "29.4.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.5.tgz", - "integrity": "sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==", + "version": "29.4.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", + "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", "dev": true, "license": "MIT", "dependencies": { @@ -3888,9 +4466,9 @@ } }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -3913,6 +4491,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -3942,7 +4528,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3966,16 +4551,51 @@ } }, "node_modules/undici-types": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", - "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -4052,6 +4672,25 @@ "license": "MIT" }, "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", @@ -4069,6 +4708,64 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4077,17 +4774,17 @@ "license": "ISC" }, "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "signal-exit": "^4.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/y18n": { @@ -4136,6 +4833,51 @@ "node": ">=12" } }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index a29f68e..21239fd 100644 --- a/package.json +++ b/package.json @@ -41,14 +41,14 @@ "Alexandre van Beurden (https://github.com/alexvbrdn)" ], "dependencies": { - "axios": "^1.12.2" + "axios": "^1.13.6" }, "devDependencies": { - "@types/jest": "^29.5.12", - "dotenv": "^17.2.3", - "jest": "^29.7.0", - "nock": "^13.5.4", - "ts-jest": "^29.2.4", - "typescript": "^5.5.4" + "@types/jest": "^30.0.0", + "@types/node": "^25.5.0", + "axios-mock-adapter": "^2.1.0", + "jest": "^30.3.0", + "ts-jest": "^29.4.6", + "typescript": "^5.9.3" } } diff --git a/src/RateLimiter.ts b/src/RateLimiter.ts new file mode 100644 index 0000000..470e514 --- /dev/null +++ b/src/RateLimiter.ts @@ -0,0 +1,47 @@ +export class RateLimiter { + private isBlocked = false; + private blockPromise: Promise | null = null; + private blockTimeout: NodeJS.Timeout | null = null; + + async wait(): Promise { + if (this.isBlocked && this.blockPromise) { + await this.blockPromise; + } + } + + trigger(retryAfterSeconds: number): void { + if (this.isBlocked) { + return; + } + + this.isBlocked = true; + const waitTime = retryAfterSeconds * 1000; + + let resolveBlock: () => void; + this.blockPromise = new Promise((resolve) => { + resolveBlock = resolve; + }); + + if (this.blockTimeout) { + clearTimeout(this.blockTimeout); + } + + this.blockTimeout = setTimeout(() => { + this.isBlocked = false; + this.blockPromise = null; + this.blockTimeout = null; + resolveBlock(); + }, waitTime); + } +} + +const rateLimiters = new Map(); + +export function getRateLimiter(apiToken: string): RateLimiter { + let limiter = rateLimiters.get(apiToken); + if (!limiter) { + limiter = new RateLimiter(); + rateLimiters.set(apiToken, limiter); + } + return limiter; +} diff --git a/src/RegexSolverClient.ts b/src/RegexSolverClient.ts new file mode 100644 index 0000000..9c4df89 --- /dev/null +++ b/src/RegexSolverClient.ts @@ -0,0 +1,594 @@ +import axios, { AxiosError, AxiosInstance, AxiosResponse } from "axios"; +import { + AnalyzeApi, + ComputeApi, + GenerateApi, + Configuration, + TermRequest, + MultiTermsRequest, + TwoTermsRequest, + RepeatRequest, + GenerateStringsRequest, + RequestOptions, +} from "./generated"; +import { Term } from "./models/Term"; +import { Cardinality } from "./models/Cardinality"; +import { Length } from "./models/Length"; +import { ResponseFormat } from "./models/ResponseFormat"; +import * as Exceptions from "./exceptions"; +import { getRateLimiter, RateLimiter } from "./RateLimiter"; + +const VERSION = "1.1.0"; + +export interface RegexSolverConfig { + apiToken: string; + baseUrl?: string; +} + +export class RegexSolverClient { + private readonly apiToken: string; + private readonly analyzeApi: AnalyzeApi; + private readonly computeApi: ComputeApi; + private readonly generateApi: GenerateApi; + private readonly axiosInstance: AxiosInstance; + private readonly rateLimiter: RateLimiter; + + constructor(config: RegexSolverConfig) { + this.apiToken = config.apiToken; + const baseUrl = config.baseUrl || "https://api.regexsolver.com/v1"; + this.rateLimiter = getRateLimiter(this.apiToken); + + const axiosConfig = { + baseURL: baseUrl, + headers: { + "User-Agent": `RegexSolver JS / ${VERSION}`, + Authorization: `Bearer ${this.apiToken}`, + }, + }; + + this.axiosInstance = axios.create(axiosConfig); + + // Add rate limit interceptor + this.axiosInstance.interceptors.request.use(async (requestConfig) => { + await this.rateLimiter.wait(); + return requestConfig; + }); + + const apiConfiguration = new Configuration({ + accessToken: this.apiToken, + basePath: baseUrl, + }); + + this.analyzeApi = new AnalyzeApi( + apiConfiguration, + baseUrl, + this.axiosInstance, + ); + this.computeApi = new ComputeApi( + apiConfiguration, + baseUrl, + this.axiosInstance, + ); + this.generateApi = new GenerateApi( + apiConfiguration, + baseUrl, + this.axiosInstance, + ); + } + + private buildOptions( + executionTimeout?: number, + responseFormat?: ResponseFormat | string, + ): RequestOptions { + const options: RequestOptions = { schemaVersion: 1 }; + if (executionTimeout !== undefined) { + options.execution = { timeout: executionTimeout }; + } + if (responseFormat !== undefined) { + options.response = { format: responseFormat as any }; + } + return options; + } + + private async executeWithRetry( + apiCall: () => Promise>, + ): Promise> { + let retried = false; + + while (true) { + try { + return await apiCall(); + } catch (error) { + if (axios.isAxiosError(error) && error.response) { + const statusCode = error.response.status; + if (statusCode === 429) { + if (retried) { + throw this.mapError(error); + } + retried = true; + const retryAfter = parseFloat( + error.response.headers["retry-after"] || "1", + ); + + this.rateLimiter.trigger(retryAfter); + continue; + } + throw this.mapError(error); + } + throw error; + } + } + } + + private mapError(error: AxiosError): Error { + if (!error.response) return error; + + const statusCode = error.response.status; + const data = error.response.data; + const message = data?.error || error.message; + const errorCode = data?.errorCode; + + switch (statusCode) { + case 400: + if (errorCode === "InvalidJson") + return new Exceptions.InvalidJson(message); + if (errorCode === "TooManyTerms") + return new Exceptions.TooManyTerms(message); + if (errorCode === "TimeoutTooLarge") + return new Exceptions.TimeoutTooLarge(message); + if (errorCode === "TimeoutExceeded") + return new Exceptions.TimeoutExceeded(message); + if (errorCode === "InvalidNumberOfStringsToGenerate") + return new Exceptions.InvalidNumberOfStringsToGenerate(message); + return new Exceptions.RegexSolverError(message, 400, errorCode); + case 401: + if (errorCode === "MissingOrMalformedToken") + return new Exceptions.MissingOrMalformedToken(message); + if (errorCode === "InvalidToken") + return new Exceptions.InvalidToken(message); + return new Exceptions.RegexSolverError(message, 401, errorCode); + case 403: + if (errorCode === "QuotaExceeded") + return new Exceptions.QuotaExceeded(message); + return new Exceptions.RegexSolverError(message, 403, errorCode); + case 404: + return new Exceptions.NotFound(message); + case 429: + const retryAfter = parseFloat( + error.response.headers["retry-after"] || "1", + ); + return new Exceptions.TooManyRequestsError(message, retryAfter); + case 500: + return new Exceptions.InternalServerError(message); + default: + return new Exceptions.RegexSolverError(message, statusCode, errorCode); + } + } + + // --- ANALYZE OPERATIONS --- + + /** + * Computes how many unique strings the term matches. + * @param term Target term to analyze. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns Cardinality object. + */ + public async getCardinality( + term: Term, + executionTimeout?: number, + ): Promise { + if (term.cardinality !== null) { + return term.cardinality; + } + + const request: TermRequest = { + term: term.toDto(), + options: this.buildOptions(executionTimeout), + }; + const response = await this.executeWithRetry(() => + this.analyzeApi.cardinality(request), + ); + const cardinality = Cardinality.fromDto(response.data.data); + term.cardinality = cardinality; + return cardinality; + } + + /** + * Compute the minimum and maximum length of strings matched by the term. + * @param term Target term to analyze. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns Length object. + */ + public async getLength( + term: Term, + executionTimeout?: number, + ): Promise { + if (term.length !== null) { + return term.length; + } + + const request: TermRequest = { + term: term.toDto(), + options: this.buildOptions(executionTimeout), + }; + const response = await this.executeWithRetry(() => + this.analyzeApi.length(request), + ); + const length = Length.fromDto(response.data.data); + term.length = length; + return length; + } + + /** + * Check if the two terms accept exactly the same language. + * @param term1 First term. + * @param term2 Second term. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns True if both terms accept the same language. + */ + public async equivalent( + term1: Term, + term2: Term, + executionTimeout?: number, + ): Promise { + const request: TwoTermsRequest = { + terms: [term1.toDto(), term2.toDto()], + options: this.buildOptions(executionTimeout), + }; + const response = await this.executeWithRetry(() => + this.analyzeApi.equivalent(request), + ); + return response.data.data.value; + } + + /** + * Check if the first term's language is a subset of the second term's language. + * @param subset Candidate subset term. + * @param superset Candidate superset term. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns True if subset's language is contained within superset's language. + */ + public async subset( + subset: Term, + superset: Term, + executionTimeout?: number, + ): Promise { + const request: TwoTermsRequest = { + terms: [subset.toDto(), superset.toDto()], + options: this.buildOptions(executionTimeout), + }; + const response = await this.executeWithRetry(() => + this.analyzeApi.subset(request), + ); + return response.data.data.value; + } + + /** + * Check if the term matches no strings. + * @param term Target term to analyze. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns True if language is empty. + */ + public async isEmpty( + term: Term, + executionTimeout?: number, + ): Promise { + if (term.empty !== null) { + return term.empty; + } + + const request: TermRequest = { + term: term.toDto(), + options: this.buildOptions(executionTimeout), + }; + const response = await this.executeWithRetry(() => + this.analyzeApi.empty(request), + ); + const isEmpty = response.data.data.value; + term.empty = isEmpty; + + if (isEmpty) { + term.cardinality = new Cardinality("integer", 0); + term.length = new Length(null, null); + } + + return isEmpty; + } + + /** + * Check if the term matches only the empty string. + * @param term Target term to analyze. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns True if language contains only the empty string. + */ + public async isEmptyString( + term: Term, + executionTimeout?: number, + ): Promise { + if (term.emptyString !== null) { + return term.emptyString; + } + + const request: TermRequest = { + term: term.toDto(), + options: this.buildOptions(executionTimeout), + }; + const response = await this.executeWithRetry(() => + this.analyzeApi.emptyString(request), + ); + const isEmptyString = response.data.data.value; + term.emptyString = isEmptyString; + + if (isEmptyString) { + term.cardinality = new Cardinality("integer", 1); + term.length = new Length(0, 0); + } + + return isEmptyString; + } + + /** + * Check if the term matches all the possible strings. + * @param term Target term to analyze. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns True if language contains all possible strings. + */ + public async isTotal( + term: Term, + executionTimeout?: number, + ): Promise { + if (term.total !== null) { + return term.total; + } + + const request: TermRequest = { + term: term.toDto(), + options: this.buildOptions(executionTimeout), + }; + const response = await this.executeWithRetry(() => + this.analyzeApi.total(request), + ); + const isTotal = response.data.data.value; + term.total = isTotal; + + if (isTotal) { + term.cardinality = new Cardinality("infinite"); + term.length = new Length(0, null); + } + + return isTotal; + } + + /** + * Return a regular expression pattern that represents the term. + * @param term Target term to analyze. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns Regex pattern string. + */ + public async getPattern( + term: Term, + executionTimeout?: number, + ): Promise { + if (term.pattern !== null) { + return term.pattern; + } + + const request: TermRequest = { + term: term.toDto(), + options: this.buildOptions(executionTimeout), + }; + const response = await this.executeWithRetry(() => + this.analyzeApi.pattern(request), + ); + const pattern = response.data.data.value; + term.pattern = pattern; + return pattern; + } + + /** + * Build a Graphviz DOT representation of the term's automaton. + * @param term Target term to analyze. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns DOT string. + */ + public async getDot(term: Term, executionTimeout?: number): Promise { + if (term.dot !== null) { + return term.dot; + } + + const request: TermRequest = { + term: term.toDto(), + options: this.buildOptions(executionTimeout), + }; + const response = await this.executeWithRetry(() => + this.analyzeApi.dot(request), + ); + const dot = response.data.data.value; + term.dot = dot; + return dot; + } + + // --- COMPUTE OPERATIONS --- + + /** + * Concatenate the given terms in order. + * @param terms Array of terms to concatenate. + * @param responseFormat Desired format of the returned term. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns New term representing the concatenation. + */ + public async concat( + terms: Term[], + responseFormat?: ResponseFormat | string, + executionTimeout?: number, + ): Promise { + const request: MultiTermsRequest = { + terms: terms.map((t) => t.toDto()), + options: this.buildOptions(executionTimeout, responseFormat), + }; + const response = await this.executeWithRetry(() => + this.computeApi.concat(request), + ); + return Term.fromDto(response.data.data); + } + + /** + * Computes the intersection of the given terms. + * @param terms Array of terms to intersect. + * @param responseFormat Desired format of the returned term. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns New term representing the intersection. + */ + public async intersection( + terms: Term[], + responseFormat?: ResponseFormat | string, + executionTimeout?: number, + ): Promise { + const request: MultiTermsRequest = { + terms: terms.map((t) => t.toDto()), + options: this.buildOptions(executionTimeout, responseFormat), + }; + const response = await this.executeWithRetry(() => + this.computeApi.intersection(request), + ); + return Term.fromDto(response.data.data); + } + + /** + * Computes the union of the given terms. + * @param terms Array of terms to unite. + * @param responseFormat Desired format of the returned term. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns New term representing the union. + */ + public async union( + terms: Term[], + responseFormat?: ResponseFormat | string, + executionTimeout?: number, + ): Promise { + const request: MultiTermsRequest = { + terms: terms.map((t) => t.toDto()), + options: this.buildOptions(executionTimeout, responseFormat), + }; + const response = await this.executeWithRetry(() => + this.computeApi.union(request), + ); + return Term.fromDto(response.data.data); + } + + /** + * Computes the difference between the two provided terms. + * @param base Term to subtract from. + * @param excluded Term to exclude. + * @param responseFormat Desired format of the returned term. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns New term representing the difference. + */ + public async difference( + base: Term, + excluded: Term, + responseFormat?: ResponseFormat | string, + executionTimeout?: number, + ): Promise { + const request: TwoTermsRequest = { + terms: [base.toDto(), excluded.toDto()], + options: this.buildOptions(executionTimeout, responseFormat), + }; + const response = await this.executeWithRetry(() => + this.computeApi.difference(request), + ); + return Term.fromDto(response.data.data); + } + + /** + * Repeat a term between 'min' and 'max' times. + * @param term Term to repeat. + * @param min Minimum number of repetitions. + * @param max Maximum number of repetitions (optional, unbounded if null). + * @param responseFormat Desired format of the returned term. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns New term representing the repetition. + */ + public async repeat( + term: Term, + min: number, + max?: number | null, + responseFormat?: ResponseFormat | string, + executionTimeout?: number, + ): Promise { + const request: RepeatRequest = { + term: term.toDto(), + min, + max, + options: this.buildOptions(executionTimeout, responseFormat), + }; + const response = await this.executeWithRetry(() => + this.computeApi.repeat(request), + ); + return Term.fromDto(response.data.data); + } + + /** + * Computes the complement of the given term. + * @param term Term to complement. + * @param responseFormat Desired format of the returned term. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns New term representing the complement. + */ + public async complement( + term: Term, + responseFormat?: ResponseFormat | string, + executionTimeout?: number, + ): Promise { + const request: TermRequest = { + term: term.toDto(), + options: this.buildOptions(executionTimeout, responseFormat), + }; + const response = await this.executeWithRetry(() => + this.computeApi.complement(request), + ); + return Term.fromDto(response.data.data); + } + + // --- GENERATE OPERATIONS --- + + /** + * Generates up to `limit` distinct strings matched by 'term', skipping the first 'offset' strings. + * @param term Source term to generate strings from. + * @param limit Maximum number of unique strings to return. + * @param offset Number of matched strings to skip before starting to collect the results. Used for pagination. + * @param executionTimeout Timeout in milliseconds for the operation. + * @returns Array of unique strings. + */ + public async generateStrings( + term: Term, + limit: number, + offset: number, + executionTimeout?: number, + ): Promise { + let termToUse = term; + let returnStableTerm = false; + + if (term.stableTerm !== null) { + termToUse = term.stableTerm; + } else { + returnStableTerm = true; + } + + const request: GenerateStringsRequest = { + term: termToUse.toDto(), + limit, + offset, + returnStableTerm, + options: this.buildOptions(executionTimeout), + }; + const response = await this.executeWithRetry(() => + this.generateApi.strings(request), + ); + const data = response.data.data; + if (data.term) { + term.stableTerm = Term.fromDto(data.term); + } + return data.strings.value; + } +} diff --git a/src/exceptions/index.ts b/src/exceptions/index.ts new file mode 100644 index 0000000..5e5bc3b --- /dev/null +++ b/src/exceptions/index.ts @@ -0,0 +1,91 @@ +export class RegexSolverError extends Error { + public readonly errorCode?: string; + public readonly statusCode?: number; + + constructor(message: string, statusCode?: number, errorCode?: string) { + super(message); + this.name = 'RegexSolverError'; + this.statusCode = statusCode; + this.errorCode = errorCode; + } +} + +export class InvalidJson extends RegexSolverError { + constructor(message: string) { + super(message, 400, 'InvalidJson'); + this.name = 'InvalidJson'; + } +} + +export class TooManyTerms extends RegexSolverError { + constructor(message: string) { + super(message, 400, 'TooManyTerms'); + this.name = 'TooManyTerms'; + } +} + +export class TimeoutTooLarge extends RegexSolverError { + constructor(message: string) { + super(message, 400, 'TimeoutTooLarge'); + this.name = 'TimeoutTooLarge'; + } +} + +export class TimeoutExceeded extends RegexSolverError { + constructor(message: string) { + super(message, 400, 'TimeoutExceeded'); + this.name = 'TimeoutExceeded'; + } +} + +export class InvalidNumberOfStringsToGenerate extends RegexSolverError { + constructor(message: string) { + super(message, 400, 'InvalidNumberOfStringsToGenerate'); + this.name = 'InvalidNumberOfStringsToGenerate'; + } +} + +export class MissingOrMalformedToken extends RegexSolverError { + constructor(message: string) { + super(message, 401, 'MissingOrMalformedToken'); + this.name = 'MissingOrMalformedToken'; + } +} + +export class InvalidToken extends RegexSolverError { + constructor(message: string) { + super(message, 401, 'InvalidToken'); + this.name = 'InvalidToken'; + } +} + +export class QuotaExceeded extends RegexSolverError { + constructor(message: string) { + super(message, 403, 'QuotaExceeded'); + this.name = 'QuotaExceeded'; + } +} + +export class NotFound extends RegexSolverError { + constructor(message: string) { + super(message, 404); + this.name = 'NotFound'; + } +} + +export class TooManyRequestsError extends RegexSolverError { + public readonly retryAfter?: number; + + constructor(message: string, retryAfter?: number) { + super(message, 429); + this.name = 'TooManyRequestsError'; + this.retryAfter = retryAfter; + } +} + +export class InternalServerError extends RegexSolverError { + constructor(message: string) { + super(message, 500); + this.name = 'InternalServerError'; + } +} diff --git a/src/generated/.openapi-generator-ignore b/src/generated/.openapi-generator-ignore new file mode 100644 index 0000000..86832a5 --- /dev/null +++ b/src/generated/.openapi-generator-ignore @@ -0,0 +1,7 @@ +docs/ +git_push.sh +.npmignore +.travis.yml +.gitignore +README.md +LICENSE diff --git a/src/generated/.openapi-generator/FILES b/src/generated/.openapi-generator/FILES new file mode 100644 index 0000000..53250c0 --- /dev/null +++ b/src/generated/.openapi-generator/FILES @@ -0,0 +1,5 @@ +api.ts +base.ts +common.ts +configuration.ts +index.ts diff --git a/src/generated/.openapi-generator/VERSION b/src/generated/.openapi-generator/VERSION new file mode 100644 index 0000000..a29ba3d --- /dev/null +++ b/src/generated/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.21.0 diff --git a/src/generated/api.ts b/src/generated/api.ts new file mode 100644 index 0000000..01606a0 --- /dev/null +++ b/src/generated/api.ts @@ -0,0 +1,1618 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * RegexSolver + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from './configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction, replaceWithSerializableTypeIfNeeded } from './common'; +import type { RequestArgs } from './base'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError, operationServerMap } from './base'; + +/** + * @type Cardinality + * Number of unique strings matched by a term. + */ +export type Cardinality = { type: 'bigInteger' } & CardinalityBigInteger | { type: 'infinite' } & CardinalityInfinite | { type: 'integer' } & CardinalityInteger; + +export interface Cardinality200Response { + 'success': boolean; + 'data': Cardinality; +} +/** + * The set of matched strings is finite but too large to be returned. + */ +export interface CardinalityBigInteger { + 'type': CardinalityBigIntegerTypeEnum; +} + +export const CardinalityBigIntegerTypeEnum = { + BigInteger: 'bigInteger', +} as const; + +export type CardinalityBigIntegerTypeEnum = typeof CardinalityBigIntegerTypeEnum[keyof typeof CardinalityBigIntegerTypeEnum]; + +/** + * The set of matched strings is infinite. + */ +export interface CardinalityInfinite { + 'type': CardinalityInfiniteTypeEnum; +} + +export const CardinalityInfiniteTypeEnum = { + Infinite: 'infinite', +} as const; + +export type CardinalityInfiniteTypeEnum = typeof CardinalityInfiniteTypeEnum[keyof typeof CardinalityInfiniteTypeEnum]; + +/** + * The set of matched strings is finite. + */ +export interface CardinalityInteger { + 'type': CardinalityIntegerTypeEnum; + /** + * Exact count. + */ + 'value': number; +} + +export const CardinalityIntegerTypeEnum = { + Integer: 'integer', +} as const; + +export type CardinalityIntegerTypeEnum = typeof CardinalityIntegerTypeEnum[keyof typeof CardinalityIntegerTypeEnum]; + +export interface Concat200Response { + 'success': boolean; + 'data': Term; +} +export interface Dot200Response { + 'success': boolean; + 'data': ModelString; +} +export interface Empty200Response { + 'success': boolean; + 'data': ModelBoolean; +} +/** + * Standard error payload returned when success is false. + */ +export interface ErrorResponse { + 'success': boolean; + /** + * Human readable error message. + */ + 'error': string; + /** + * The error code. + */ + 'errorCode'?: string; +} +/** + * Change how the engine executes the operation. + */ +export interface ExecutionOptions { + /** + * Timeout in milliseconds for the operation. + */ + 'timeout'?: number; +} +/** + * Request to generate up to \'limit\' distinct strings matched by \'term\', skipping the first \'offset\' strings. + */ +export interface GenerateStringsRequest { + /** + * Source term to generate strings from. + */ + 'term': Term; + /** + * Maximum number of unique strings to return. + */ + 'limit': number; + /** + * Number of matched strings to skip before starting to collect the results. Used for pagination. + */ + 'offset': number; + /** + * If set to true, a stable term is returned. This term can be reused in subsequent calls to guarantee no strings are repeated. If the provided term is already stable, it will not be returned. + */ + 'returnStableTerm'?: boolean; + 'options'?: RequestOptions; +} +/** + * Response containing distinct strings generated from the requested \'term\'. + */ +export interface GenerateStringsResponse { + 'type': GenerateStringsResponseTypeEnum; + /** + * A stable term to use in subsequent calls to guarantee the uniqueness of generated strings. Omitted if \'returnStableTerm\' was false in the request, or if the provided term was already stable. + */ + 'term'?: Term; + /** + * The generated distinct strings. + */ + 'strings': Strings; +} + +export const GenerateStringsResponseTypeEnum = { + GeneratedStrings: 'generatedStrings', +} as const; + +export type GenerateStringsResponseTypeEnum = typeof GenerateStringsResponseTypeEnum[keyof typeof GenerateStringsResponseTypeEnum]; + +/** + * Minimum and maximum length of any string in the language. + */ +export interface Length { + 'type': LengthTypeEnum; + /** + * Shortest possible length, or null if empty. + */ + 'min': number | null; + /** + * Longest possible length, or null if unbounded. + */ + 'max': number | null; +} + +export const LengthTypeEnum = { + Length: 'length', +} as const; + +export type LengthTypeEnum = typeof LengthTypeEnum[keyof typeof LengthTypeEnum]; + +export interface Length200Response { + 'success': boolean; + 'data': Length; +} +/** + * Wrapper for a boolean value. + */ +export interface ModelBoolean { + 'type': ModelBooleanTypeEnum; + /** + * Boolean value. + */ + 'value': boolean; +} + +export const ModelBooleanTypeEnum = { + Boolean: 'boolean', +} as const; + +export type ModelBooleanTypeEnum = typeof ModelBooleanTypeEnum[keyof typeof ModelBooleanTypeEnum]; + +/** + * Wrapper for a string value. + */ +export interface ModelString { + 'type': ModelStringTypeEnum; + /** + * String value. + */ + 'value': string; +} + +export const ModelStringTypeEnum = { + String: 'string', +} as const; + +export type ModelStringTypeEnum = typeof ModelStringTypeEnum[keyof typeof ModelStringTypeEnum]; + +/** + * Request carrying 2 or more terms for n-ary operations. + */ +export interface MultiTermsRequest { + /** + * Terms to process. Order matters for some operations. + */ + 'terms': Array; + 'options'?: RequestOptions; +} +/** + * Request to repeat a term between \'min\' and \'max\' times. + */ +export interface RepeatRequest { + /** + * Term to repeat. + */ + 'term': Term; + /** + * Inclusive lower bound of repetitions. + */ + 'min': number; + /** + * Inclusive upper bound. If omitted or null, the repetition is unbounded. + */ + 'max'?: number | null; + 'options'?: RequestOptions; +} +/** + * Change how the engine handle the operation. + */ +export interface RequestOptions { + /** + * Client-expected schema version. + */ + 'schemaVersion': number; + 'response'?: ResponseOptions; + 'execution'?: ExecutionOptions; +} +/** + * Change how the engine returns results. + */ +export interface ResponseOptions { + /** + * Return format of the term. + */ + 'format'?: ResponseOptionsFormatEnum; +} + +export const ResponseOptionsFormatEnum = { + Any: 'any', + Fair: 'fair', + Regex: 'regex', +} as const; + +export type ResponseOptionsFormatEnum = typeof ResponseOptionsFormatEnum[keyof typeof ResponseOptionsFormatEnum]; + +/** + * Wrapper for a list of strings. + */ +export interface Strings { + 'type': StringsTypeEnum; + /** + * Array of unique strings. + */ + 'value': Array; +} + +export const StringsTypeEnum = { + Strings: 'strings', +} as const; + +export type StringsTypeEnum = typeof StringsTypeEnum[keyof typeof StringsTypeEnum]; + +export interface Strings200Response { + 'success': boolean; + 'data': GenerateStringsResponse; +} +/** + * @type Term + * Serialized term. + */ +export type Term = { type: 'fair' } & TermFair | { type: 'regex' } & TermRegex; + +/** + * Term encoded as FAIR (Fast Automaton Internal Representation), a stable, signed format used internally by the engine. + */ +export interface TermFair { + 'type': TermFairTypeEnum; + /** + * FAIR payload. + */ + 'value': string; +} + +export const TermFairTypeEnum = { + Fair: 'fair', +} as const; + +export type TermFairTypeEnum = typeof TermFairTypeEnum[keyof typeof TermFairTypeEnum]; + +/** + * Term encoded as a regular expression pattern. + */ +export interface TermRegex { + 'type': TermRegexTypeEnum; + /** + * Regular expression pattern. + */ + 'value': string; +} + +export const TermRegexTypeEnum = { + Regex: 'regex', +} as const; + +export type TermRegexTypeEnum = typeof TermRegexTypeEnum[keyof typeof TermRegexTypeEnum]; + +/** + * Request a single term. + */ +export interface TermRequest { + 'term': Term; + 'options'?: RequestOptions; +} +/** + * Request carrying exactly 2 terms. + */ +export interface TwoTermsRequest { + /** + * Exactly 2 terms. + */ + 'terms': Array; + 'options'?: RequestOptions; +} + +/** + * AnalyzeApi - axios parameter creator + */ +export const AnalyzeApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * Compute how many strings the term matches. + * @summary Cardinality + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + cardinality: async (termRequest: TermRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'termRequest' is not null or undefined + assertParamExists('cardinality', 'termRequest', termRequest) + const localVarPath = `/analyze/cardinality`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(termRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Build a Graphviz DOT representation of the term\'s automaton. + * @summary GraphViz Dot + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + dot: async (termRequest: TermRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'termRequest' is not null or undefined + assertParamExists('dot', 'termRequest', termRequest) + const localVarPath = `/analyze/dot`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(termRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Check if the term matches no strings. + * @summary Empty + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + empty: async (termRequest: TermRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'termRequest' is not null or undefined + assertParamExists('empty', 'termRequest', termRequest) + const localVarPath = `/analyze/empty`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(termRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Check if the term matches only the empty string. + * @summary Empty String Only + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + emptyString: async (termRequest: TermRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'termRequest' is not null or undefined + assertParamExists('emptyString', 'termRequest', termRequest) + const localVarPath = `/analyze/empty_string`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(termRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Check if the two terms accept exactly the same language. + * @summary Equivalent + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + equivalent: async (twoTermsRequest: TwoTermsRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'twoTermsRequest' is not null or undefined + assertParamExists('equivalent', 'twoTermsRequest', twoTermsRequest) + const localVarPath = `/analyze/equivalent`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(twoTermsRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Compute the minimum and maximum length of strings matched by the term. + * @summary Length + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + length: async (termRequest: TermRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'termRequest' is not null or undefined + assertParamExists('length', 'termRequest', termRequest) + const localVarPath = `/analyze/length`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(termRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Return a regular expression pattern that represents the term. + * @summary Pattern + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + pattern: async (termRequest: TermRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'termRequest' is not null or undefined + assertParamExists('pattern', 'termRequest', termRequest) + const localVarPath = `/analyze/pattern`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(termRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Check if the first term\'s language is a subset of the second term\'s language. + * @summary Subset + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + subset: async (twoTermsRequest: TwoTermsRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'twoTermsRequest' is not null or undefined + assertParamExists('subset', 'twoTermsRequest', twoTermsRequest) + const localVarPath = `/analyze/subset`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(twoTermsRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Check if the term matches all the possible strings. + * @summary Totality + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + total: async (termRequest: TermRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'termRequest' is not null or undefined + assertParamExists('total', 'termRequest', termRequest) + const localVarPath = `/analyze/total`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(termRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * AnalyzeApi - functional programming interface + */ +export const AnalyzeApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = AnalyzeApiAxiosParamCreator(configuration) + return { + /** + * Compute how many strings the term matches. + * @summary Cardinality + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async cardinality(termRequest: TermRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.cardinality(termRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AnalyzeApi.cardinality']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Build a Graphviz DOT representation of the term\'s automaton. + * @summary GraphViz Dot + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async dot(termRequest: TermRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.dot(termRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AnalyzeApi.dot']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Check if the term matches no strings. + * @summary Empty + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async empty(termRequest: TermRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.empty(termRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AnalyzeApi.empty']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Check if the term matches only the empty string. + * @summary Empty String Only + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async emptyString(termRequest: TermRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.emptyString(termRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AnalyzeApi.emptyString']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Check if the two terms accept exactly the same language. + * @summary Equivalent + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async equivalent(twoTermsRequest: TwoTermsRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.equivalent(twoTermsRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AnalyzeApi.equivalent']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Compute the minimum and maximum length of strings matched by the term. + * @summary Length + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async length(termRequest: TermRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.length(termRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AnalyzeApi.length']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Return a regular expression pattern that represents the term. + * @summary Pattern + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async pattern(termRequest: TermRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.pattern(termRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AnalyzeApi.pattern']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Check if the first term\'s language is a subset of the second term\'s language. + * @summary Subset + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async subset(twoTermsRequest: TwoTermsRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.subset(twoTermsRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AnalyzeApi.subset']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Check if the term matches all the possible strings. + * @summary Totality + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async total(termRequest: TermRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.total(termRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AnalyzeApi.total']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * AnalyzeApi - factory interface + */ +export const AnalyzeApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = AnalyzeApiFp(configuration) + return { + /** + * Compute how many strings the term matches. + * @summary Cardinality + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + cardinality(termRequest: TermRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.cardinality(termRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Build a Graphviz DOT representation of the term\'s automaton. + * @summary GraphViz Dot + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + dot(termRequest: TermRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.dot(termRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Check if the term matches no strings. + * @summary Empty + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + empty(termRequest: TermRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.empty(termRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Check if the term matches only the empty string. + * @summary Empty String Only + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + emptyString(termRequest: TermRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.emptyString(termRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Check if the two terms accept exactly the same language. + * @summary Equivalent + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + equivalent(twoTermsRequest: TwoTermsRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.equivalent(twoTermsRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Compute the minimum and maximum length of strings matched by the term. + * @summary Length + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + length(termRequest: TermRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.length(termRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Return a regular expression pattern that represents the term. + * @summary Pattern + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + pattern(termRequest: TermRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.pattern(termRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Check if the first term\'s language is a subset of the second term\'s language. + * @summary Subset + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + subset(twoTermsRequest: TwoTermsRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.subset(twoTermsRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Check if the term matches all the possible strings. + * @summary Totality + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + total(termRequest: TermRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.total(termRequest, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * AnalyzeApi - object-oriented interface + */ +export class AnalyzeApi extends BaseAPI { + /** + * Compute how many strings the term matches. + * @summary Cardinality + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public cardinality(termRequest: TermRequest, options?: RawAxiosRequestConfig) { + return AnalyzeApiFp(this.configuration).cardinality(termRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Build a Graphviz DOT representation of the term\'s automaton. + * @summary GraphViz Dot + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public dot(termRequest: TermRequest, options?: RawAxiosRequestConfig) { + return AnalyzeApiFp(this.configuration).dot(termRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Check if the term matches no strings. + * @summary Empty + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public empty(termRequest: TermRequest, options?: RawAxiosRequestConfig) { + return AnalyzeApiFp(this.configuration).empty(termRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Check if the term matches only the empty string. + * @summary Empty String Only + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public emptyString(termRequest: TermRequest, options?: RawAxiosRequestConfig) { + return AnalyzeApiFp(this.configuration).emptyString(termRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Check if the two terms accept exactly the same language. + * @summary Equivalent + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public equivalent(twoTermsRequest: TwoTermsRequest, options?: RawAxiosRequestConfig) { + return AnalyzeApiFp(this.configuration).equivalent(twoTermsRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Compute the minimum and maximum length of strings matched by the term. + * @summary Length + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public length(termRequest: TermRequest, options?: RawAxiosRequestConfig) { + return AnalyzeApiFp(this.configuration).length(termRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Return a regular expression pattern that represents the term. + * @summary Pattern + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public pattern(termRequest: TermRequest, options?: RawAxiosRequestConfig) { + return AnalyzeApiFp(this.configuration).pattern(termRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Check if the first term\'s language is a subset of the second term\'s language. + * @summary Subset + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public subset(twoTermsRequest: TwoTermsRequest, options?: RawAxiosRequestConfig) { + return AnalyzeApiFp(this.configuration).subset(twoTermsRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Check if the term matches all the possible strings. + * @summary Totality + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public total(termRequest: TermRequest, options?: RawAxiosRequestConfig) { + return AnalyzeApiFp(this.configuration).total(termRequest, options).then((request) => request(this.axios, this.basePath)); + } +} + + + +/** + * ComputeApi - axios parameter creator + */ +export const ComputeApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * Computes the complement of the given term. + * @summary Complement + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + complement: async (termRequest: TermRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'termRequest' is not null or undefined + assertParamExists('complement', 'termRequest', termRequest) + const localVarPath = `/compute/complement`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(termRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Concatenate the given terms in order. + * @summary Concatenation + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + concat: async (multiTermsRequest: MultiTermsRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'multiTermsRequest' is not null or undefined + assertParamExists('concat', 'multiTermsRequest', multiTermsRequest) + const localVarPath = `/compute/concat`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(multiTermsRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Computes the difference between the two provided terms. + * @summary Difference + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + difference: async (twoTermsRequest: TwoTermsRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'twoTermsRequest' is not null or undefined + assertParamExists('difference', 'twoTermsRequest', twoTermsRequest) + const localVarPath = `/compute/difference`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(twoTermsRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Computes the intersection of the given terms. + * @summary Intersection + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + intersection: async (multiTermsRequest: MultiTermsRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'multiTermsRequest' is not null or undefined + assertParamExists('intersection', 'multiTermsRequest', multiTermsRequest) + const localVarPath = `/compute/intersection`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(multiTermsRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Repeat a term between \'min\' and \'max\' times. + * @summary Repeat + * @param {RepeatRequest} repeatRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + repeat: async (repeatRequest: RepeatRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'repeatRequest' is not null or undefined + assertParamExists('repeat', 'repeatRequest', repeatRequest) + const localVarPath = `/compute/repeat`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(repeatRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Computes the union of the given terms. + * @summary Union + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + union: async (multiTermsRequest: MultiTermsRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'multiTermsRequest' is not null or undefined + assertParamExists('union', 'multiTermsRequest', multiTermsRequest) + const localVarPath = `/compute/union`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(multiTermsRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * ComputeApi - functional programming interface + */ +export const ComputeApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = ComputeApiAxiosParamCreator(configuration) + return { + /** + * Computes the complement of the given term. + * @summary Complement + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async complement(termRequest: TermRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.complement(termRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ComputeApi.complement']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Concatenate the given terms in order. + * @summary Concatenation + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async concat(multiTermsRequest: MultiTermsRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.concat(multiTermsRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ComputeApi.concat']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Computes the difference between the two provided terms. + * @summary Difference + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async difference(twoTermsRequest: TwoTermsRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.difference(twoTermsRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ComputeApi.difference']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Computes the intersection of the given terms. + * @summary Intersection + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async intersection(multiTermsRequest: MultiTermsRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.intersection(multiTermsRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ComputeApi.intersection']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Repeat a term between \'min\' and \'max\' times. + * @summary Repeat + * @param {RepeatRequest} repeatRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async repeat(repeatRequest: RepeatRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.repeat(repeatRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ComputeApi.repeat']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Computes the union of the given terms. + * @summary Union + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async union(multiTermsRequest: MultiTermsRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.union(multiTermsRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ComputeApi.union']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * ComputeApi - factory interface + */ +export const ComputeApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = ComputeApiFp(configuration) + return { + /** + * Computes the complement of the given term. + * @summary Complement + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + complement(termRequest: TermRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.complement(termRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Concatenate the given terms in order. + * @summary Concatenation + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + concat(multiTermsRequest: MultiTermsRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.concat(multiTermsRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Computes the difference between the two provided terms. + * @summary Difference + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + difference(twoTermsRequest: TwoTermsRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.difference(twoTermsRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Computes the intersection of the given terms. + * @summary Intersection + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + intersection(multiTermsRequest: MultiTermsRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.intersection(multiTermsRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Repeat a term between \'min\' and \'max\' times. + * @summary Repeat + * @param {RepeatRequest} repeatRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + repeat(repeatRequest: RepeatRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.repeat(repeatRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Computes the union of the given terms. + * @summary Union + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + union(multiTermsRequest: MultiTermsRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.union(multiTermsRequest, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * ComputeApi - object-oriented interface + */ +export class ComputeApi extends BaseAPI { + /** + * Computes the complement of the given term. + * @summary Complement + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public complement(termRequest: TermRequest, options?: RawAxiosRequestConfig) { + return ComputeApiFp(this.configuration).complement(termRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Concatenate the given terms in order. + * @summary Concatenation + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public concat(multiTermsRequest: MultiTermsRequest, options?: RawAxiosRequestConfig) { + return ComputeApiFp(this.configuration).concat(multiTermsRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Computes the difference between the two provided terms. + * @summary Difference + * @param {TwoTermsRequest} twoTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public difference(twoTermsRequest: TwoTermsRequest, options?: RawAxiosRequestConfig) { + return ComputeApiFp(this.configuration).difference(twoTermsRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Computes the intersection of the given terms. + * @summary Intersection + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public intersection(multiTermsRequest: MultiTermsRequest, options?: RawAxiosRequestConfig) { + return ComputeApiFp(this.configuration).intersection(multiTermsRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Repeat a term between \'min\' and \'max\' times. + * @summary Repeat + * @param {RepeatRequest} repeatRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public repeat(repeatRequest: RepeatRequest, options?: RawAxiosRequestConfig) { + return ComputeApiFp(this.configuration).repeat(repeatRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Computes the union of the given terms. + * @summary Union + * @param {MultiTermsRequest} multiTermsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public union(multiTermsRequest: MultiTermsRequest, options?: RawAxiosRequestConfig) { + return ComputeApiFp(this.configuration).union(multiTermsRequest, options).then((request) => request(this.axios, this.basePath)); + } +} + + + +/** + * GenerateApi - axios parameter creator + */ +export const GenerateApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * Generates up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. + * @summary Strings + * @param {GenerateStringsRequest} generateStringsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + strings: async (generateStringsRequest: GenerateStringsRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'generateStringsRequest' is not null or undefined + assertParamExists('strings', 'generateStringsRequest', generateStringsRequest) + const localVarPath = `/generate/strings`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(generateStringsRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * GenerateApi - functional programming interface + */ +export const GenerateApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = GenerateApiAxiosParamCreator(configuration) + return { + /** + * Generates up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. + * @summary Strings + * @param {GenerateStringsRequest} generateStringsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async strings(generateStringsRequest: GenerateStringsRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.strings(generateStringsRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['GenerateApi.strings']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * GenerateApi - factory interface + */ +export const GenerateApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = GenerateApiFp(configuration) + return { + /** + * Generates up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. + * @summary Strings + * @param {GenerateStringsRequest} generateStringsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + strings(generateStringsRequest: GenerateStringsRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.strings(generateStringsRequest, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * GenerateApi - object-oriented interface + */ +export class GenerateApi extends BaseAPI { + /** + * Generates up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. + * @summary Strings + * @param {GenerateStringsRequest} generateStringsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public strings(generateStringsRequest: GenerateStringsRequest, options?: RawAxiosRequestConfig) { + return GenerateApiFp(this.configuration).strings(generateStringsRequest, options).then((request) => request(this.axios, this.basePath)); + } +} + + + diff --git a/src/generated/base.ts b/src/generated/base.ts new file mode 100644 index 0000000..590d2ae --- /dev/null +++ b/src/generated/base.ts @@ -0,0 +1,62 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * RegexSolver + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from './configuration'; +// Some imports not used depending on template conditions +// @ts-ignore +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; + +export const BASE_PATH = "https://api.regexsolver.com/v1".replace(/\/+$/, ""); + +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +export interface RequestArgs { + url: string; + options: RawAxiosRequestConfig; +} + +export class BaseAPI { + protected configuration: Configuration | undefined; + + constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath ?? basePath; + } + } +}; + +export class RequiredError extends Error { + constructor(public field: string, msg?: string) { + super(msg); + this.name = "RequiredError" + } +} + +interface ServerMap { + [key: string]: { + url: string, + description: string, + }[]; +} + +export const operationServerMap: ServerMap = { +} diff --git a/src/generated/common.ts b/src/generated/common.ts new file mode 100644 index 0000000..14d5420 --- /dev/null +++ b/src/generated/common.ts @@ -0,0 +1,127 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * RegexSolver + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "./configuration"; +import type { RequestArgs } from "./base"; +import type { AxiosInstance, AxiosResponse } from 'axios'; +import { RequiredError } from "./base"; + +export const DUMMY_BASE_URL = 'https://example.com' + +/** + * + * @throws {RequiredError} + */ +export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) { + if (paramValue === null || paramValue === undefined) { + throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`); + } +} + +export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) { + if (configuration && configuration.apiKey) { + const localVarApiKeyValue = typeof configuration.apiKey === 'function' + ? await configuration.apiKey(keyParamName) + : await configuration.apiKey; + object[keyParamName] = localVarApiKeyValue; + } +} + +export const setBasicAuthToObject = function (object: any, configuration?: Configuration) { + if (configuration && (configuration.username || configuration.password)) { + object["auth"] = { username: configuration.username, password: configuration.password }; + } +} + +export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + object["Authorization"] = "Bearer " + accessToken; + } +} + +export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const localVarAccessTokenValue = typeof configuration.accessToken === 'function' + ? await configuration.accessToken(name, scopes) + : await configuration.accessToken; + object["Authorization"] = "Bearer " + localVarAccessTokenValue; + } +} + + +function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void { + if (parameter == null) return; + if (typeof parameter === "object") { + if (Array.isArray(parameter) || parameter instanceof Set) { + (parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key)); + } + else { + Object.keys(parameter).forEach(currentKey => + setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`) + ); + } + } + else { + if (urlSearchParams.has(key)) { + urlSearchParams.append(key, parameter); + } + else { + urlSearchParams.set(key, parameter); + } + } +} + +export const setSearchParams = function (url: URL, ...objects: any[]) { + const searchParams = new URLSearchParams(url.search); + setFlattenedQueryParams(searchParams, objects); + url.search = searchParams.toString(); +} + +/** + * JSON serialization helper function which replaces instances of unserializable types with serializable ones. + * This function will run for every key-value pair encountered by JSON.stringify while traversing an object. + * Converting a set to a string will return an empty object, so an intermediate conversion to an array is required. + */ +// @ts-ignore +export const replaceWithSerializableTypeIfNeeded = function(key: string, value: any) { + if (value instanceof Set) { + return Array.from(value); + } else { + return value; + } +} + +export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) { + const nonString = typeof value !== 'string'; + const needsSerialization = nonString && configuration && configuration.isJsonMime + ? configuration.isJsonMime(requestOptions.headers['Content-Type']) + : nonString; + return needsSerialization + ? JSON.stringify(value !== undefined ? value : {}, replaceWithSerializableTypeIfNeeded) + : (value || ""); +} + +export const toPathString = function (url: URL) { + return url.pathname + url.search + url.hash +} + +export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) { + return >(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs = {...axiosArgs.options, url: (axios.defaults.baseURL ? '' : configuration?.basePath ?? basePath) + axiosArgs.url}; + return axios.request(axiosRequestArgs); + }; +} diff --git a/src/generated/configuration.ts b/src/generated/configuration.ts new file mode 100644 index 0000000..324f346 --- /dev/null +++ b/src/generated/configuration.ts @@ -0,0 +1,121 @@ +/* tslint:disable */ +/** + * RegexSolver + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +interface AWSv4Configuration { + options?: { + region?: string + service?: string + } + credentials?: { + accessKeyId?: string + secretAccessKey?: string, + sessionToken?: string + } +} + +export interface ConfigurationParameters { + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + username?: string; + password?: string; + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + awsv4?: AWSv4Configuration; + basePath?: string; + serverIndex?: number; + baseOptions?: any; + formDataCtor?: new () => any; +} + +export class Configuration { + /** + * parameter for apiKey security + * @param name security name + */ + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + /** + * parameter for basic security + */ + username?: string; + /** + * parameter for basic security + */ + password?: string; + /** + * parameter for oauth2 security + * @param name security name + * @param scopes oauth2 scope + */ + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + /** + * parameter for aws4 signature security + * @param {Object} AWS4Signature - AWS4 Signature security + * @param {string} options.region - aws region + * @param {string} options.service - name of the service. + * @param {string} credentials.accessKeyId - aws access key id + * @param {string} credentials.secretAccessKey - aws access key + * @param {string} credentials.sessionToken - aws session token + * @memberof Configuration + */ + awsv4?: AWSv4Configuration; + /** + * override base path + */ + basePath?: string; + /** + * override server index + */ + serverIndex?: number; + /** + * base options for axios calls + */ + baseOptions?: any; + /** + * The FormData constructor that will be used to create multipart form data + * requests. You can inject this here so that execution environments that + * do not support the FormData class can still run the generated client. + * + * @type {new () => FormData} + */ + formDataCtor?: new () => any; + + constructor(param: ConfigurationParameters = {}) { + this.apiKey = param.apiKey; + this.username = param.username; + this.password = param.password; + this.accessToken = param.accessToken; + this.awsv4 = param.awsv4; + this.basePath = param.basePath; + this.serverIndex = param.serverIndex; + this.baseOptions = { + ...param.baseOptions, + headers: { + ...param.baseOptions?.headers, + }, + }; + this.formDataCtor = param.formDataCtor; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); + return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); + } +} diff --git a/src/generated/index.ts b/src/generated/index.ts new file mode 100644 index 0000000..31e5aa5 --- /dev/null +++ b/src/generated/index.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * RegexSolver + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export * from "./api"; +export * from "./configuration"; + diff --git a/src/index.ts b/src/index.ts index 4028dbe..968d9b8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,537 +1,6 @@ -import axios, { AxiosInstance } from "axios"; - -interface OperationOptions { - responseFormat?: ResponseFormat; - executionTimeout?: number; -} - -interface TermTransient { - type: TermType; - value: string; -} - -function loadTerm(term: TermTransient): Term { - return new Term(term.type, term.value); -} - -function loadCardinality(data: Cardinality): Cardinality { - return new Cardinality(data.type, data.value); -} - -function loadLength(data: { min: number, max?: number | null }): Length { - return new Length(data.min, data.max); -} - -interface ResponseOptions { - format?: ResponseFormat; -} - -interface ExecutionOptions { - timeout?: number; -} - -interface RequestOptions { - schema_version?: number; - response?: ResponseOptions; - execution?: ExecutionOptions; -} - -interface MultiTermsRequest { - terms: Term[]; - options?: RequestOptions; -} - -interface GenerateStringsRequest { - term: Term; - count: number; - options?: RequestOptions; -} - -interface RepeatRequest { - term: Term; - min: number; - max?: number | null; - options?: RequestOptions; -} - -class RequestOptionsBuilder { - static fromArgs(args?: { response_format?: ResponseFormat | null; execution_timeout?: number | null }): RequestOptions | undefined { - const response = args?.response_format ? { format: args.response_format } : undefined; - const execution = args?.execution_timeout ? { timeout: args.execution_timeout } : undefined; - if (response || execution) { - return { - schema_version: 1, - response, - execution, - }; - } - return undefined; - } -} - -export class RegexSolver { - private static instance: RegexSolver; - private apiClient: AxiosInstance; - - private constructor() { - if (!RegexSolver.instance) { - RegexSolver.instance = this; - } - return RegexSolver.instance; - } - - static getInstance() { - if (!RegexSolver.instance) { - RegexSolver.instance = new RegexSolver(); - } - return RegexSolver.instance; - } - - static initialize(apiToken: string = null, baseURL: string = null) { - const instance = RegexSolver.getInstance(); - if (!apiToken) { - apiToken = process.env.REGEXSOLVER_API_TOKEN; - } - if (!baseURL) { - baseURL = process.env.REGEXSOLVER_BASE_URL; - if (!baseURL) { - baseURL = "https://api.regexsolver.com/v1/" - } - } - instance.apiClient = axios.create({ - baseURL, - headers: { - 'Authorization': `Bearer ${apiToken}`, - 'User-Agent': 'RegexSolver JS / 1.1.0', - } - }); - } - - // Analyze - - analyzeCardinality(term: Term): Promise { - return this.apiClient.post('/analyze/cardinality', term) - .then(response => loadCardinality(response.data)) - .catch(error => { throw new ApiError(error.message) }); - } - - analyzeLength(term: Term): Promise { - return this.apiClient.post('/analyze/length', term) - .then(response => loadLength(response.data)) - .catch(error => { throw new ApiError(error.message) }); - } - - analyzeEquivalent(request: MultiTermsRequest): Promise { - return this.apiClient.post('/analyze/equivalent', request) - .then(response => response.data.value) - .catch(error => { throw new ApiError(error.message) }); - } - - analyzeSubset(request: MultiTermsRequest): Promise { - return this.apiClient.post('/analyze/subset', request) - .then(response => response.data.value) - .catch(error => { throw new ApiError(error.message) }); - } - - analyzeEmpty(request: Term): Promise { - return this.apiClient.post('/analyze/empty', request) - .then(response => response.data.value) - .catch(error => { throw new ApiError(error.message) }); - } - - analyzeTotal(request: Term): Promise { - return this.apiClient.post('/analyze/total', request) - .then(response => response.data.value) - .catch(error => { throw new ApiError(error.message) }); - } - - analyzeEmptyString(request: Term): Promise { - return this.apiClient.post('/analyze/empty_string', request) - .then(response => response.data.value) - .catch(error => { throw new ApiError(error.message) }); - } - - analyzeDot(request: Term): Promise { - return this.apiClient.post('/analyze/dot', request) - .then(response => response.data.value) - .catch(error => { throw new ApiError(error.message) }); - } - - analyzePattern(request: Term): Promise { - return this.apiClient.post('/analyze/pattern', request) - .then(response => response.data.value) - .catch(error => { throw new ApiError(error.message) }); - } - - // Compute - - computeRepeat(request: RepeatRequest): Promise { // todo - return this.apiClient.post('/compute/repeat', request) - .then(response => loadTerm(response.data)) - .catch(error => { throw new ApiError(error.message) }); - } - - computeIntersection(request: MultiTermsRequest): Promise { - return this.apiClient.post('/compute/intersection', request) - .then(response => loadTerm(response.data)) - .catch(error => { throw new ApiError(error.message) }); - } - - computeUnion(request: MultiTermsRequest): Promise { - return this.apiClient.post('/compute/union', request) - .then(response => loadTerm(response.data)) - .catch(error => { throw new ApiError(error.message) }); - } - - computeDifference(request: MultiTermsRequest): Promise { - return this.apiClient.post('/compute/difference', request) - .then(response => loadTerm(response.data)) - .catch(error => { throw new ApiError(error.message) }); - } - - computeConcat(request: MultiTermsRequest): Promise { - return this.apiClient.post('/compute/concat', request) - .then(response => loadTerm(response.data)) - .catch(error => { throw new ApiError(error.message) }); - } - - // Generate - - generateStrings(request: GenerateStringsRequest): Promise { - return this.apiClient.post('/generate/strings', request) - .then(response => response.data.value) - .catch(error => { throw new ApiError(error.message) }); - } -} - -export type TermType = "fair" | "regex"; - -export class Term { - readonly type: TermType; - readonly value: string; - - private cardinality?: Cardinality; - private length?: Length; - private empty?: boolean; - private total?: boolean; - private emptyString?: boolean; - private dot?: string; - private pattern?: string; - - constructor( - type: TermType, - value: string - ) { - this.type = type; - this.value = value; - } - - /** - * Initialize a Fast Automaton Internal Representation (FAIR). - */ - static fair(fair: string): Term { - return new Term("fair", fair); - } - - /** - * Initialize a regex. - */ - static regex(pattern: string): Term { - return new Term("regex", pattern); - } - - // Analyze - - /** - * Check whether this term is equivalent to another. - * - * @param term The term to compare against. - * @param opts Execution options. - * - * @returns `true` if both terms accept exactly the same language. - */ - async equivalent(term: Term, opts?: OperationOptions): Promise { - const options = RequestOptionsBuilder.fromArgs({ execution_timeout: opts?.executionTimeout }); - return await RegexSolver.getInstance().analyzeEquivalent({ terms: [this, term], options }); - } - - /** - * Get the cardinality of this term. - * - * Results are cached on the instance to avoid repeated API calls. - * - * @returns A `Cardinality` object describing how many distinct strings are matched. - */ - async getCardinality(): Promise { - if (this.cardinality) { - return this.cardinality; - } - this.cardinality = await RegexSolver.getInstance().analyzeCardinality(this); - return this.cardinality; - } - - /** - * Get the GraphViz DOT representation of this term. - * - * Results are cached on the instance to avoid repeated API calls. - * - * @returns A DOT language string describing the automaton for this term. - */ - async getDot(): Promise { - if (this.dot) { - return this.dot; - } - this.dot = await RegexSolver.getInstance().analyzeDot(this); - return this.dot; - } - - /** - * Return the Fast Automaton Internal Representation (FAIR). - */ - getFair(): string | null { - return this.type === "fair" ? this.value : null; - } - - /** - * Get the length bounds of this term. - * - * Results are cached on the instance to avoid repeated API calls. - * - * @returns A `Length` object with the minimum and maximum string length matched by this term. - */ - async getLength(): Promise { - if (this.length) { - return this.length; - } - this.length = await RegexSolver.getInstance().analyzeLength(this); - return this.length; - } - - /** - * Return the regular expression pattern. - * - * If the term is not a regex the pattern will be resolved. - * - * Results are cached on the instance to avoid repeated API calls. - */ - async getPattern(): Promise { - if (this.type === "regex") return this.value; - if (this.pattern) return this.pattern; - this.pattern = await RegexSolver.getInstance().analyzePattern(this); - return this.pattern; - } - - getType(): TermType { - return this.type; - } - - /** - * Check whether this term matches no string. - * - * Results are cached on the instance to avoid repeated API calls. - */ - async isEmpty(): Promise { - if (this.empty) { - return this.empty; - } - this.empty = await RegexSolver.getInstance().analyzeEmpty(this); - return this.empty; - } - - /** - * Check whether this term matches only the empty string. - * - * Results are cached on the instance to avoid repeated API calls. - */ - async isEmptyString(): Promise { - if (this.emptyString) { - return this.emptyString; - } - this.emptyString = await RegexSolver.getInstance().analyzeEmptyString(this); - return this.emptyString; - } - - /** - * Check whether this term matches all possible strings. - * - * Results are cached on the instance to avoid repeated API calls. - */ - async isTotal(): Promise { - if (this.total) { - return this.total; - } - this.total = await RegexSolver.getInstance().analyzeTotal(this); - return this.total; - } - - /** - * Check whether this term is a subset of another. - * - * @param term The term to compare against. - * @param opts Execution options. - * @returns `true` if every string matched by this term is also matched by `term`. - */ - async subset(term: Term, opts?: OperationOptions): Promise { - const options = RequestOptionsBuilder.fromArgs({ execution_timeout: opts?.executionTimeout }); - return await RegexSolver.getInstance().analyzeSubset({ terms: [this, term], options }); - } - - // Compute - - private getMultiTermsRequest(args: (Term | OperationOptions)[]): MultiTermsRequest { - const last = args[args.length - 1]; - let options: RequestOptions; - let terms: Term[]; - if (last instanceof Term) { - options = undefined; - terms = args as Term[]; - } else { - options = RequestOptionsBuilder.fromArgs({ response_format: last.responseFormat, execution_timeout: last.executionTimeout }); - terms = args.slice(0, -1) as Term[]; - } - - return { terms: [this, ...terms], options }; - } - - /** - * Concatenate this term with one or more other terms. - * - * @returns A new term representing the concatenation. - */ - async concat(t1: Term, ...rest: Term[]): Promise; - async concat(t1: Term, ...termsAndOpts: [...terms: Term[], opts: OperationOptions]): Promise; - async concat(...args: (Term | OperationOptions)[]): Promise { - return await RegexSolver.getInstance().computeConcat(this.getMultiTermsRequest(args)); - } - - /** - * Compute the difference between this term and another. - * - * @returns A new term representing the set difference (this - term). - */ - async difference(term: Term, opts?: OperationOptions): Promise { - const options = RequestOptionsBuilder.fromArgs({ response_format: opts?.responseFormat, execution_timeout: opts?.executionTimeout }); - return await RegexSolver.getInstance().computeDifference({ terms: [this, term], options }); - } - - /** - * Compute the intersection of this term with one or more other terms. - * - * @returns A new term representing the intersection. - */ - async intersection(t1: Term, ...rest: Term[]): Promise; - async intersection(t1: Term, ...termsAndOpts: [...terms: Term[], opts: OperationOptions]): Promise; - async intersection(...args: (Term | OperationOptions)[]): Promise { - return await RegexSolver.getInstance().computeIntersection(this.getMultiTermsRequest(args)); - } - - /** - * Computes the repetition of the term between `min` and `max` times; if `max` is `null`, the repetition is unbounded. - * - * @param min The lower bound of the repetition. - * @param max The upper bound of the repetition, if `null` the repetition is unbounded. - * @param opts Execution options. - * @returns A new term representing the repetition. - */ - async repeat(min: number, max?: number | null, opts?: OperationOptions): Promise { - const options = RequestOptionsBuilder.fromArgs({ response_format: opts.responseFormat, execution_timeout: opts.executionTimeout }); - return await RegexSolver.getInstance().computeRepeat({ term: this, min, max, options }); - } - - /** - * Compute the union of this term with one or more other terms. - * - * @returns A new term representing the union. - */ - async union(t1: Term, ...rest: Term[]): Promise; - async union(t1: Term, ...termsAndOpts: [...terms: Term[], opts: OperationOptions]): Promise; - async union(...args: (Term | OperationOptions)[]): Promise { - return await RegexSolver.getInstance().computeUnion(this.getMultiTermsRequest(args)); - } - - // Generate - - /** - * Generate up to `count` example strings that match this term. - * - * @param count Maximum number of unique strings to generate. - * @param opts Execution options. - * @returns A list of strings matched by this term. - */ - async generateStrings(count: number, opts?: OperationOptions): Promise { - const options = RequestOptionsBuilder.fromArgs({ execution_timeout: opts?.executionTimeout }); - return await RegexSolver.getInstance().generateStrings({ term: this, count, options }); - } - - // Others - - /** - * @returns a string representation of this term in the format `=`, which can later be parsed by `deserialize()`. - */ - serialize(): string { - return `${this.type}=${this.value}`; - } - - /** - * Parse a string representation produced by `serialize()`. - * - * @param input The serialized term, e.g. `"regex=abc"`. - * @returns A Term instance, or `null` if the input is empty or invalid. - */ - static deserialize(input: string | null | undefined): Term | null { - if (!input || !input.includes("=")) return null; - let pos = input.indexOf("="); - const [prefix, value] = [input.slice(0, pos), input.slice(pos + 1)]; - if (prefix === "regex") return Term.regex(value); - if (prefix === "fair") return Term.fair(value); - return null; - } - - toString(): string { - return this.serialize(); - } -} - - -export class ApiError extends Error { - constructor(message: string) { - super("The API returned the following error: " + message); - } -} - -export enum ResponseFormat { - ANY = 'any', - REGEX = 'regex', - FAIR = 'fair' -}; - -export class Cardinality { - constructor( - public type: 'infinite' | 'bigInteger' | 'integer', - public value?: number - ) { } - - isInfinite(): boolean { - return this.type == 'infinite'; - } - - toString(): string { - const cap1 = s => s ? s[0].toUpperCase() + s.slice(1) : s; - if (this.type == 'integer') { - return cap1(this.type) + '(' + this.value + ')'; - } else { - return cap1(this.type); - } - } -} - -export class Length { - constructor( - public minimum: number, - public maximum?: number - ) { } - - toString(): string { - return "Length[minimum=" + this.minimum + ", maximum=" + this.maximum + "]"; - } -} \ No newline at end of file +export * from './RegexSolverClient'; +export * from './models/Term'; +export * from './models/Cardinality'; +export * from './models/Length'; +export * from './models/ResponseFormat'; +export * from './exceptions'; diff --git a/src/models/Cardinality.ts b/src/models/Cardinality.ts new file mode 100644 index 0000000..7cec6bc --- /dev/null +++ b/src/models/Cardinality.ts @@ -0,0 +1,36 @@ +import { Cardinality as CardinalityDto } from "../generated"; + +export class Cardinality { + public readonly type: "integer" | "bigInteger" | "infinite"; + public readonly value: number | null; + + constructor( + type: "integer" | "bigInteger" | "infinite", + value: number | null = null, + ) { + this.type = type; + this.value = value; + } + + public static fromDto(dto: CardinalityDto): Cardinality { + if (dto.type === "integer") { + return new Cardinality("integer", dto.value); + } else if (dto.type === "bigInteger") { + return new Cardinality("bigInteger"); + } else { + return new Cardinality("infinite"); + } + } + + public isInfinite(): boolean { + return this.type === "infinite"; + } + + public isBigInteger(): boolean { + return this.type === "bigInteger"; + } + + public isInteger(): boolean { + return this.type === "integer"; + } +} diff --git a/src/models/Length.ts b/src/models/Length.ts new file mode 100644 index 0000000..5496ef8 --- /dev/null +++ b/src/models/Length.ts @@ -0,0 +1,23 @@ +import { Length as LengthDto } from "../generated"; + +export class Length { + public readonly min: number | null; + public readonly max: number | null; + + constructor(min: number | null, max: number | null) { + this.min = min; + this.max = max; + } + + public static fromDto(dto: LengthDto): Length { + return new Length(dto.min, dto.max); + } + + public isEmpty(): boolean { + return this.min === null; + } + + public isInfinite(): boolean { + return this.max === null; + } +} diff --git a/src/models/ResponseFormat.ts b/src/models/ResponseFormat.ts new file mode 100644 index 0000000..0eedff0 --- /dev/null +++ b/src/models/ResponseFormat.ts @@ -0,0 +1,17 @@ +/** + * Defines the format in which the engine should return computed Terms. + */ +export enum ResponseFormat { + /** + * Allows the engine to return the result in the most efficient format. + */ + ANY = 'any', + /** + * Fast Automaton Internal Representation, a stable internal format. + */ + FAIR = 'fair', + /** + * Standard regular expression pattern. + */ + REGEX = 'regex' +} diff --git a/src/models/Term.ts b/src/models/Term.ts new file mode 100644 index 0000000..1ac484f --- /dev/null +++ b/src/models/Term.ts @@ -0,0 +1,76 @@ +import { Term as TermDto, TermFair, TermRegex } from "../generated"; +import { Cardinality } from "./Cardinality"; +import { Length } from "./Length"; + +export class Term { + public readonly type: "regex" | "fair"; + public readonly value: string; + + // Cache + public cardinality: Cardinality | null = null; + public length: Length | null = null; + public empty: boolean | null = null; + public emptyString: boolean | null = null; + public total: boolean | null = null; + public pattern: string | null = null; + public dot: string | null = null; + public stableTerm: Term | null = null; + + constructor(type: "regex" | "fair", value: string) { + this.type = type; + this.value = value; + } + + public static fair(payload: string): Term { + return new Term("fair", payload); + } + + public static regex(pattern: string): Term { + return new Term("regex", pattern); + } + + public getFair(): string | null { + return this.type === "fair" ? this.value : null; + } + + public getPattern(): string | null { + return this.type === "regex" ? this.value : this.pattern; + } + + public serialize(): string { + return `${this.type}=${this.value}`; + } + + public static deserialize(serialized: string): Term { + const parts = serialized.split("="); + if (parts.length < 2) { + throw new Error("Invalid serialized term"); + } + const type = parts[0] as "regex" | "fair"; + const value = parts.slice(1).join("="); + return new Term(type, value); + } + + public isMatch(str: string): boolean | null { + const pattern = this.getPattern(); + if (pattern === null) { + return null; + } + + // Must be a "full match" (anchored). Dot (.) must match all characters including newlines. + const regex = new RegExp(`^(${pattern})$`, "s"); + return regex.test(str); + } + + public toDto(): TermDto { + if (this.type === "regex") { + return { type: "regex", value: this.value } as TermRegex; + } else { + return { type: "fair", value: this.value } as TermFair; + } + } + + public static fromDto(dto: TermDto): Term { + return new Term(dto.type, dto.value); + } +} diff --git a/tests/RateLimiter.test.ts b/tests/RateLimiter.test.ts new file mode 100644 index 0000000..6cebc06 --- /dev/null +++ b/tests/RateLimiter.test.ts @@ -0,0 +1,58 @@ +import { RateLimiter } from '../src/RateLimiter'; + +describe('RateLimiter', () => { + let rateLimiter: RateLimiter; + + beforeEach(() => { + rateLimiter = new RateLimiter(); + }); + + test('wait should resolve immediately if not blocked', async () => { + const start = Date.now(); + await rateLimiter.wait(); + const duration = Date.now() - start; + expect(duration).toBeLessThan(50); + }); + + test('trigger should block wait', async () => { + const retryAfter = 0.1; // 100ms + rateLimiter.trigger(retryAfter); + + const start = Date.now(); + await rateLimiter.wait(); + const duration = Date.now() - start; + + expect(duration).toBeGreaterThanOrEqual(100); + expect(duration).toBeLessThan(150); + }); + + test('multiple triggers should be ignored while blocked', async () => { + const retryAfter1 = 0.2; // 200ms + const retryAfter2 = 0.1; // 100ms + + rateLimiter.trigger(retryAfter1); + rateLimiter.trigger(retryAfter2); // Should be ignored + + const start = Date.now(); + await rateLimiter.wait(); + const duration = Date.now() - start; + + expect(duration).toBeGreaterThanOrEqual(200); + expect(duration).toBeLessThan(250); + }); + + test('concurrent waits should all resolve after block is lifted', async () => { + const retryAfter = 0.1; // 100ms + rateLimiter.trigger(retryAfter); + + const start = Date.now(); + const p1 = rateLimiter.wait(); + const p2 = rateLimiter.wait(); + const p3 = rateLimiter.wait(); + + await Promise.all([p1, p2, p3]); + const duration = Date.now() - start; + + expect(duration).toBeGreaterThanOrEqual(100); + }); +}); diff --git a/tests/assets/response_error.json b/tests/assets/response_error.json deleted file mode 100644 index 0faf2d7..0000000 --- a/tests/assets/response_error.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "error", - "message": "A random error." -} \ No newline at end of file diff --git a/tests/client.test.ts b/tests/client.test.ts new file mode 100644 index 0000000..44ae6dd --- /dev/null +++ b/tests/client.test.ts @@ -0,0 +1,140 @@ +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import { RegexSolverClient } from '../src/RegexSolverClient'; +import { Term } from '../src/models/Term'; +import * as Exceptions from '../src/exceptions'; + +describe('RegexSolverClient', () => { + let mock: any; + let client: RegexSolverClient; + + beforeEach(() => { + mock = new MockAdapter(axios); + client = new RegexSolverClient({ apiToken: 'test-token' }); + }); + + afterEach(() => { + mock.restore(); + }); + + test('getCardinality should work', async () => { + mock.onPost('/analyze/cardinality').reply(200, { + success: true, + data: { type: 'integer', value: 26 } + }); + + const term = Term.regex('[a-z]'); + const cardinality = await client.getCardinality(term); + + expect(cardinality.isInteger()).toBe(true); + expect(cardinality.value).toBe(26); + expect(term.cardinality).toBe(cardinality); + }); + + test('getLength should work', async () => { + mock.onPost('/analyze/length').reply(200, { + success: true, + data: { type: 'length', min: 1, max: 4 } + }); + + const term = Term.regex('(abc)?d'); + const length = await client.getLength(term); + + expect(length.min).toBe(1); + expect(length.max).toBe(4); + expect(term.length).toBe(length); + }); + + test('error mapping should work for 400 Bad Request', async () => { + mock.onPost('/analyze/cardinality').reply(400, { + success: false, + error: 'Invalid JSON body', + errorCode: 'InvalidJson' + }); + + const term = Term.regex('[a-z]'); + await expect(client.getCardinality(term)).rejects.toThrow(Exceptions.InvalidJson); + }); + + test('rate limit with retry-after should work and block concurrent requests', async () => { + // First call 429 + mock.onPost('/analyze/cardinality').replyOnce(429, { + success: false, + error: 'Too many requests', + errorCode: 'RateLimitExceeded' + }, { 'retry-after': '0.1' }); + + // Second call 200 + mock.onPost('/analyze/cardinality').reply(200, { + success: true, + data: { type: 'integer', value: 26 } + }); + + const start = Date.now(); + const term = Term.regex('[a-z]'); + + // Parallel calls + const p1 = client.getCardinality(term); + const p2 = client.getCardinality(term); + + const [c1, c2] = await Promise.all([p1, p2]); + const duration = Date.now() - start; + + expect(c1.value).toBe(26); + expect(c2.value).toBe(26); + expect(duration).toBeGreaterThanOrEqual(100); + }); + + test('error mapping for 401 Unauthorized - Invalid Token', async () => { + mock.onPost('/analyze/cardinality').reply(401, { + success: false, + error: 'Invalid token', + errorCode: 'InvalidToken' + }); + + await expect(client.getCardinality(Term.regex('abc'))).rejects.toThrow(Exceptions.InvalidToken); + }); + + test('error mapping for 403 Forbidden - Quota Exceeded', async () => { + mock.onPost('/analyze/cardinality').reply(403, { + success: false, + error: 'Quota exceeded', + errorCode: 'QuotaExceeded' + }); + + await expect(client.getCardinality(Term.regex('abc'))).rejects.toThrow(Exceptions.QuotaExceeded); + }); + + test('error mapping for 500 Internal Server Error', async () => { + mock.onPost('/analyze/cardinality').reply(500, { + success: false, + error: 'Internal server error' + }); + + await expect(client.getCardinality(Term.regex('abc'))).rejects.toThrow(Exceptions.InternalServerError); + }); + + test('isEmpty should work', async () => { + mock.onPost('/analyze/empty').reply(200, { + success: true, + data: { value: true } + }); + + const term = Term.regex('[]'); + const emptyValue = await client.isEmpty(term); + expect(emptyValue).toBe(true); + expect(term.empty).toBe(true); + }); + + test('intersection should work', async () => { + mock.onPost('/compute/intersection').reply(200, { + success: true, + data: { type: 'regex', value: 'a' } + }); + + const t1 = Term.regex('a'); + const t2 = Term.regex('ab'); + const result = await client.intersection([t1, t2]); + expect(result.value).toBe('a'); + }); +}); diff --git a/tests/integration.test.ts b/tests/integration.test.ts deleted file mode 100644 index 0d300b4..0000000 --- a/tests/integration.test.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { RegexSolver, ResponseFormat, Term } from '../src/index'; - -describe('integration test', () => { - beforeAll(() => { - require('dotenv').config(); - RegexSolver.initialize(); - }); - - // Analyze - - it('analyze cardinality', async () => { - const term = Term.regex('[0-4]'); - const c = await term.getCardinality(); - expect(c.toString()).toEqual('Integer(5)'); - }); - - it('analyze dot', async () => { - const term = Term.regex('(abc|de)'); - const dot = await term.getDot(); - expect(dot.startsWith('digraph ')).toBe(true); - }); - - it('analyze empty string', async () => { - const term = Term.regex(''); - const result = await term.isEmptyString(); - expect(result).toBe(true); - }); - - it('analyze empty', async () => { - const term = Term.regex('[]'); - const result = await term.isEmpty(); - expect(result).toBe(true); - }); - - it('analyze total', async () => { - const term = Term.regex('.*'); - const result = await term.isTotal(); - expect(result).toBe(true); - }); - - it('analyze equivalent', async () => { - const term1 = Term.regex('(abc|de)'); - const term2 = Term.fair(' { - const term = Term.regex('[]'); - const length = await term.getLength(); - expect(length.toString()).toEqual('Length[minimum=null, maximum=null]'); - }); - - it('analyze length', async () => { - const term = Term.regex('(abc)?'); - const length = await term.getLength(); - expect(length.toString()).toEqual('Length[minimum=0, maximum=3]'); - }); - - it('analyze pattern', async () => { - const term = Term.regex('abc.*'); - const pattern = await term.getPattern(); - expect(pattern).toEqual('abc.*'); - }); - - it('analyze subset', async () => { - const term1 = Term.regex('de'); - const term2 = Term.regex('(abc|de)'); - const result = await term1.subset(term2); - expect(result).toBe(true); - }); - - // Compute - - it('compute concat', async () => { - const term1 = Term.regex('abc'); - const term2 = Term.regex('de'); - const result = await term1.concat(term2, { responseFormat: ResponseFormat.REGEX }); - expect(result.toString()).toEqual('regex=abcde'); - }); - - it('compute difference', async () => { - const term1 = Term.regex('(abc|de)'); - const term2 = Term.regex('de'); - const result = await term1.difference(term2, { responseFormat: ResponseFormat.REGEX }); - expect(result.toString()).toEqual('regex=abc'); - }); - - it('compute intersection', async () => { - const term1 = Term.regex('(abc|de){2}'); - const term2 = Term.regex('de.*'); - const term3 = Term.regex('.*abc'); - const result = await term1.intersection(term2, term3, { responseFormat: ResponseFormat.REGEX }); - expect(result.toString()).toEqual('regex=deabc'); - }); - - it('compute repeat', async () => { - const term = Term.regex('abc'); - const result = await term.repeat(3, 5, { responseFormat: ResponseFormat.REGEX }); - expect(result.toString()).toEqual('regex=(abc){3,5}'); - }); - - it('compute union', async () => { - const term1 = Term.regex('abc'); - const term2 = Term.regex('de'); - const term3 = Term.regex('fghi'); - const result = await term1.union(term2, term3, { responseFormat: ResponseFormat.REGEX }); - expect(result.toString()).toEqual('regex=(abc|de|fghi)'); - }); - - // Generate - - it('generate strings', async () => { - const term = Term.regex('(abc|de){2}'); - const strings = await term.generateStrings(10); - expect(strings.length).toEqual(4); - }); - - // README examples - - it('readme quickstart', () => { - const term1 = Term.regex("(abc|de|fg){2,}"); - const term2 = Term.regex("de.*"); - const term3 = Term.regex(".*abc"); - - const term4 = Term.regex(".+(abc|de).+"); - - term1.intersection(term2, term3) - .then(result => result.difference(term4)) - .then(result => result.getPattern()) - .then(result => expect(result).toEqual('de(fg)*abc')); // de(fg)*abc - }); - - it('readme response format', () => { - const term = Term.regex('abcde'); - - term.union(Term.regex('de'), { responseFormat: ResponseFormat.REGEX }).then(result => { - expect(result.toString()).toEqual('regex=(abc)?de'); - }); - - term.intersection(Term.regex('de.*'), { responseFormat: ResponseFormat.FAIR }).then(result => { - expect(result.toString().startsWith("fair=")).toBeTruthy(); - }); - }); -}); \ No newline at end of file diff --git a/tests/serialization.test.ts b/tests/serialization.test.ts deleted file mode 100644 index 7466afc..0000000 --- a/tests/serialization.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {Term} from '../src/index'; - -describe('term serialization', () => { - function assertSerialization(term: Term) { - const serialized = term.serialize() - const deserialized = Term.deserialize(serialized) - - expect(deserialized).toEqual(term); - } - - it('serialize and deserialize term correctly', () => { - assertSerialization(Term.regex(".*")); - assertSerialization(Term.regex("")); - - assertSerialization(Term.fair("rgmsW[1g2LvP=Gr&V>sLc#w-!No&(opHq@B-9o[LpP-a#fYI+")); - assertSerialization(Term.fair("")); - }); -}); \ No newline at end of file diff --git a/tests/term-operation.test.ts b/tests/term-operation.test.ts deleted file mode 100644 index 074705a..0000000 --- a/tests/term-operation.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { RegexSolver } from '../src'; -import { Term } from '../src'; -import { promises as fs } from 'fs'; -import * as nock from 'nock'; - - -describe('term operations', () => { - beforeAll(() => { - RegexSolver.initialize("TOKEN"); - }); - - it('error response correctly handled', async () => { - const response = JSON.parse(await fs.readFile("tests/assets/response_error.json", 'utf-8')); - nock('https://api.regexsolver.com/v1/') - .post('/compute/intersection') - .reply(200, response); - - const term1 = Term.regex("abc"); - const term2 = Term.regex("de"); - - try { - await term1.intersection(term2); - } catch (error) { - expect(error.message).toEqual("The API returned the following error: A random error."); - } - }); -}); \ No newline at end of file diff --git a/tests/term.test.ts b/tests/term.test.ts new file mode 100644 index 0000000..ed02637 --- /dev/null +++ b/tests/term.test.ts @@ -0,0 +1,45 @@ +import { Term } from '../src/models/Term'; + +describe('Term', () => { + test('isMatch should work for regex terms', () => { + const term = Term.regex('[a-z]+'); + expect(term.isMatch('abc')).toBe(true); + expect(term.isMatch('123')).toBe(false); + expect(term.isMatch('ABC')).toBe(false); + }); + + test('isMatch should work with dotAll equivalent', () => { + const term = Term.regex('.+'); + expect(term.isMatch('abc\ndef')).toBe(true); + }); + + test('isMatch should be anchored', () => { + const term = Term.regex('abc'); + expect(term.isMatch('abcd')).toBe(false); + expect(term.isMatch('xabc')).toBe(false); + }); + + test('serialize/deserialize should work', () => { + const term = Term.regex('[a-z]+'); + const serialized = term.serialize(); + expect(serialized).toBe('regex=[a-z]+'); + + const deserialized = Term.deserialize(serialized); + expect(deserialized.type).toBe('regex'); + expect(deserialized.value).toBe('[a-z]+'); + }); + + test('toDto should work', () => { + const term = Term.regex('[a-z]+'); + const dto = term.toDto(); + expect(dto.type).toBe('regex'); + expect(dto.value).toBe('[a-z]+'); + }); + + test('fromDto should work', () => { + const dto = { type: 'regex' as const, value: '[a-z]+' }; + const term = Term.fromDto(dto); + expect(term.type).toBe('regex'); + expect(term.value).toBe('[a-z]+'); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index d26fa3a..14c0084 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,15 +1,19 @@ { - "version": "1.6.2", - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "outDir": "lib/", - "lib": [ - "es2015", - "dom" - ] - }, - "files": [ - "./src/index.ts" - ] -} \ No newline at end of file + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["es2020", "dom"], + "declaration": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "lib/", + "baseUrl": ".", + "paths": { + "*": ["node_modules/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "tests", "lib"] +} From ba45255ff6e6b3c05b27fb8c7eea253042cd9681 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:06:20 +0100 Subject: [PATCH 08/20] Make it more consistent with the python library Signed-off-by: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> --- src/RateLimiter.ts | 74 ++++----- src/RegexSolverClient.ts | 152 ++++++++++++------ src/exceptions.ts | 88 +++++++++++ src/exceptions/index.ts | 91 ----------- src/index.ts | 12 +- src/models/Cardinality.ts | 113 ++++++++++++-- src/models/Length.ts | 33 +++- src/models/ResponseFormat.ts | 24 +-- src/models/Term.ts | 26 ++-- src/models/TermPropertiesMixin.ts | 33 ++++ tests/RateLimiter.test.ts | 114 +++++++------- tests/client.test.ts | 250 ++++++++++++++++-------------- tests/term.test.ts | 76 ++++----- 13 files changed, 648 insertions(+), 438 deletions(-) create mode 100644 src/exceptions.ts delete mode 100644 src/exceptions/index.ts create mode 100644 src/models/TermPropertiesMixin.ts diff --git a/src/RateLimiter.ts b/src/RateLimiter.ts index 470e514..1c6280e 100644 --- a/src/RateLimiter.ts +++ b/src/RateLimiter.ts @@ -1,47 +1,47 @@ export class RateLimiter { - private isBlocked = false; - private blockPromise: Promise | null = null; - private blockTimeout: NodeJS.Timeout | null = null; - - async wait(): Promise { - if (this.isBlocked && this.blockPromise) { - await this.blockPromise; - } + private isBlocked = false; + private blockPromise: Promise | null = null; + private blockTimeout: NodeJS.Timeout | null = null; + + async wait(): Promise { + if (this.isBlocked && this.blockPromise) { + await this.blockPromise; } + } - trigger(retryAfterSeconds: number): void { - if (this.isBlocked) { - return; - } - - this.isBlocked = true; - const waitTime = retryAfterSeconds * 1000; - - let resolveBlock: () => void; - this.blockPromise = new Promise((resolve) => { - resolveBlock = resolve; - }); - - if (this.blockTimeout) { - clearTimeout(this.blockTimeout); - } - - this.blockTimeout = setTimeout(() => { - this.isBlocked = false; - this.blockPromise = null; - this.blockTimeout = null; - resolveBlock(); - }, waitTime); + trigger(retryAfterSeconds: number): void { + if (this.isBlocked) { + return; } + + this.isBlocked = true; + const waitTime = retryAfterSeconds * 1000; + + let resolveBlock: () => void; + this.blockPromise = new Promise((resolve) => { + resolveBlock = resolve; + }); + + if (this.blockTimeout) { + clearTimeout(this.blockTimeout); + } + + this.blockTimeout = setTimeout(() => { + this.isBlocked = false; + this.blockPromise = null; + this.blockTimeout = null; + resolveBlock(); + }, waitTime); + } } const rateLimiters = new Map(); export function getRateLimiter(apiToken: string): RateLimiter { - let limiter = rateLimiters.get(apiToken); - if (!limiter) { - limiter = new RateLimiter(); - rateLimiters.set(apiToken, limiter); - } - return limiter; + let limiter = rateLimiters.get(apiToken); + if (!limiter) { + limiter = new RateLimiter(); + rateLimiters.set(apiToken, limiter); + } + return limiter; } diff --git a/src/RegexSolverClient.ts b/src/RegexSolverClient.ts index 9c4df89..2dd8f75 100644 --- a/src/RegexSolverClient.ts +++ b/src/RegexSolverClient.ts @@ -12,7 +12,7 @@ import { RequestOptions, } from "./generated"; import { Term } from "./models/Term"; -import { Cardinality } from "./models/Cardinality"; +import { Cardinality, Infinite, Integer } from "./models/Cardinality"; import { Length } from "./models/Length"; import { ResponseFormat } from "./models/ResponseFormat"; import * as Exceptions from "./exceptions"; @@ -125,43 +125,93 @@ export class RegexSolverClient { const statusCode = error.response.status; const data = error.response.data; - const message = data?.error || error.message; - const errorCode = data?.errorCode; + + let message = error.message; + let errorCode = "UnknownError"; + const bodyString = typeof data === "string" ? data : JSON.stringify(data); + + if (data) { + message = data.error || message; + errorCode = data.errorCode || errorCode; + } + + message = message || "Unknown API Error"; switch (statusCode) { case 400: if (errorCode === "InvalidJson") - return new Exceptions.InvalidJson(message); + return new Exceptions.InvalidJsonError( + message, + statusCode, + bodyString, + ); if (errorCode === "TooManyTerms") - return new Exceptions.TooManyTerms(message); + return new Exceptions.TooManyTermsError( + message, + statusCode, + bodyString, + ); if (errorCode === "TimeoutTooLarge") - return new Exceptions.TimeoutTooLarge(message); + return new Exceptions.TimeoutTooLargeError( + message, + statusCode, + bodyString, + ); if (errorCode === "TimeoutExceeded") - return new Exceptions.TimeoutExceeded(message); + return new Exceptions.TimeoutExceededError( + message, + statusCode, + bodyString, + ); if (errorCode === "InvalidNumberOfStringsToGenerate") - return new Exceptions.InvalidNumberOfStringsToGenerate(message); - return new Exceptions.RegexSolverError(message, 400, errorCode); + return new Exceptions.InvalidNumberOfStringsToGenerate( + message, + statusCode, + bodyString, + ); + return new Exceptions.BadRequestError(message, statusCode, bodyString); case 401: if (errorCode === "MissingOrMalformedToken") - return new Exceptions.MissingOrMalformedToken(message); + return new Exceptions.MissingOrMalformedTokenError( + message, + statusCode, + bodyString, + ); if (errorCode === "InvalidToken") - return new Exceptions.InvalidToken(message); - return new Exceptions.RegexSolverError(message, 401, errorCode); + return new Exceptions.InvalidTokenError( + message, + statusCode, + bodyString, + ); + return new Exceptions.UnauthorizedError( + message, + statusCode, + bodyString, + ); case 403: if (errorCode === "QuotaExceeded") - return new Exceptions.QuotaExceeded(message); - return new Exceptions.RegexSolverError(message, 403, errorCode); + return new Exceptions.QuotaExceededError( + message, + statusCode, + bodyString, + ); + return new Exceptions.ForbiddenError(message, statusCode, bodyString); case 404: - return new Exceptions.NotFound(message); + return new Exceptions.NotFoundError(message, statusCode, bodyString); case 429: - const retryAfter = parseFloat( - error.response.headers["retry-after"] || "1", - ); - return new Exceptions.TooManyRequestsError(message, retryAfter); + const msg = + message === "Unknown API Error" + ? "Max retries exceeded for 429 Too Many Requests." + : message; + return new Exceptions.TooManyRequestsError(msg, statusCode, bodyString); case 500: - return new Exceptions.InternalServerError(message); + return new Exceptions.InternalServerError( + message, + statusCode, + bodyString, + ); default: - return new Exceptions.RegexSolverError(message, statusCode, errorCode); + return new Exceptions.ApiError(message, statusCode, bodyString); } } @@ -177,8 +227,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term.cardinality !== null) { - return term.cardinality; + if (term._cardinality !== null) { + return term._cardinality; } const request: TermRequest = { @@ -189,7 +239,7 @@ export class RegexSolverClient { this.analyzeApi.cardinality(request), ); const cardinality = Cardinality.fromDto(response.data.data); - term.cardinality = cardinality; + term._cardinality = cardinality; return cardinality; } @@ -203,8 +253,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term.length !== null) { - return term.length; + if (term._length !== null) { + return term._length; } const request: TermRequest = { @@ -215,7 +265,7 @@ export class RegexSolverClient { this.analyzeApi.length(request), ); const length = Length.fromDto(response.data.data); - term.length = length; + term._length = length; return length; } @@ -273,8 +323,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term.empty !== null) { - return term.empty; + if (term._empty !== null) { + return term._empty; } const request: TermRequest = { @@ -285,11 +335,11 @@ export class RegexSolverClient { this.analyzeApi.empty(request), ); const isEmpty = response.data.data.value; - term.empty = isEmpty; + term._empty = isEmpty; if (isEmpty) { - term.cardinality = new Cardinality("integer", 0); - term.length = new Length(null, null); + term._cardinality = new Integer(0); + term._length = new Length(null, null); } return isEmpty; @@ -305,8 +355,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term.emptyString !== null) { - return term.emptyString; + if (term._emptyString !== null) { + return term._emptyString; } const request: TermRequest = { @@ -317,11 +367,11 @@ export class RegexSolverClient { this.analyzeApi.emptyString(request), ); const isEmptyString = response.data.data.value; - term.emptyString = isEmptyString; + term._emptyString = isEmptyString; if (isEmptyString) { - term.cardinality = new Cardinality("integer", 1); - term.length = new Length(0, 0); + term._cardinality = new Integer(1); + term._length = new Length(0, 0); } return isEmptyString; @@ -337,8 +387,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term.total !== null) { - return term.total; + if (term._total !== null) { + return term._total; } const request: TermRequest = { @@ -349,11 +399,11 @@ export class RegexSolverClient { this.analyzeApi.total(request), ); const isTotal = response.data.data.value; - term.total = isTotal; + term._total = isTotal; if (isTotal) { - term.cardinality = new Cardinality("infinite"); - term.length = new Length(0, null); + term._cardinality = new Infinite(); + term._length = new Length(0, null); } return isTotal; @@ -369,8 +419,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term.pattern !== null) { - return term.pattern; + if (term._pattern !== null) { + return term._pattern; } const request: TermRequest = { @@ -381,7 +431,7 @@ export class RegexSolverClient { this.analyzeApi.pattern(request), ); const pattern = response.data.data.value; - term.pattern = pattern; + term._pattern = pattern; return pattern; } @@ -392,8 +442,8 @@ export class RegexSolverClient { * @returns DOT string. */ public async getDot(term: Term, executionTimeout?: number): Promise { - if (term.dot !== null) { - return term.dot; + if (term._dot !== null) { + return term._dot; } const request: TermRequest = { @@ -404,7 +454,7 @@ export class RegexSolverClient { this.analyzeApi.dot(request), ); const dot = response.data.data.value; - term.dot = dot; + term._dot = dot; return dot; } @@ -569,8 +619,8 @@ export class RegexSolverClient { let termToUse = term; let returnStableTerm = false; - if (term.stableTerm !== null) { - termToUse = term.stableTerm; + if (term._stableTerm !== null) { + termToUse = term._stableTerm; } else { returnStableTerm = true; } @@ -587,7 +637,7 @@ export class RegexSolverClient { ); const data = response.data.data; if (data.term) { - term.stableTerm = Term.fromDto(data.term); + term._stableTerm = Term.fromDto(data.term); } return data.strings.value; } diff --git a/src/exceptions.ts b/src/exceptions.ts new file mode 100644 index 0000000..904c70e --- /dev/null +++ b/src/exceptions.ts @@ -0,0 +1,88 @@ +export class RegexSolverError extends Error { + /** Base exception for all RegexSolver errors. */ + constructor(message: string) { + super(message); + this.name = this.constructor.name; + + // Maintain proper stack trace for where our error was thrown (only available on V8) + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + } +} + +export class ApiError extends RegexSolverError { + /** Base exception raised when the RegexSolver API returns an error response. */ + public readonly statusCode?: number; + public readonly body?: string; + + constructor(message: string, statusCode?: number, body?: string) { + super(message); + this.statusCode = statusCode; + this.body = body; + } +} + +export class BadRequestError extends ApiError { + /** Raised when the API returns a 400 Bad Request error. */ +} + +export class InvalidJsonError extends BadRequestError { + /** Raised when the provided JSON is invalid or cannot be parsed. */ +} + +export class TooManyTermsError extends BadRequestError { + /** Raised when the number of terms provided exceeds the maximum allowed. */ +} + +export class TimeoutTooLargeError extends BadRequestError { + /** Raised when the requested `execution_timeout` exceeds the maximum allowed for your current plan. */ +} + +export class TimeoutExceededError extends BadRequestError { + /** Raised when the execution of the request exceeds the provided `execution_timeout` or the maximum allowed for your current plan. */ +} + +export class InvalidNumberOfStringsToGenerate extends BadRequestError { + /** Raised when the requested number of strings to generate is below the minimum or exceeds the maximum allowed. */ +} + +export class UnauthorizedError extends ApiError { + /** Raised when the API returns a 401 Unauthorized error. */ +} + +export class MissingOrMalformedTokenError extends UnauthorizedError { + /** Raised when the provided authentication token is missing or malformed. */ +} + +export class InvalidTokenError extends UnauthorizedError { + /** Raised when the provided authentication token is invalid. */ +} + +export class ForbiddenError extends ApiError { + /** Raised when the API returns a 403 Forbidden error. */ +} + +export class QuotaExceededError extends ForbiddenError { + /** Raised when your account's monthly compute quota has been exceeded. */ +} + +export class NotFoundError extends ApiError { + /** * Raised when the API returns a 404 Not Found error. + * Indicates that the requested API endpoint or resource does not exist. + */ +} + +export class TooManyRequestsError extends ApiError { + /** + * Raised when the API returns a 429 Too Many Requests error and max retries are exceeded. + * Indicates that your requests-per-second (req/s) rate limit has been exceeded. + */ +} + +export class InternalServerError extends ApiError { + /** + * Raised when the API returns a 500 Internal Server Error. + * Indicates an unexpected failure or panic on the RegexSolver compute servers. + */ +} diff --git a/src/exceptions/index.ts b/src/exceptions/index.ts deleted file mode 100644 index 5e5bc3b..0000000 --- a/src/exceptions/index.ts +++ /dev/null @@ -1,91 +0,0 @@ -export class RegexSolverError extends Error { - public readonly errorCode?: string; - public readonly statusCode?: number; - - constructor(message: string, statusCode?: number, errorCode?: string) { - super(message); - this.name = 'RegexSolverError'; - this.statusCode = statusCode; - this.errorCode = errorCode; - } -} - -export class InvalidJson extends RegexSolverError { - constructor(message: string) { - super(message, 400, 'InvalidJson'); - this.name = 'InvalidJson'; - } -} - -export class TooManyTerms extends RegexSolverError { - constructor(message: string) { - super(message, 400, 'TooManyTerms'); - this.name = 'TooManyTerms'; - } -} - -export class TimeoutTooLarge extends RegexSolverError { - constructor(message: string) { - super(message, 400, 'TimeoutTooLarge'); - this.name = 'TimeoutTooLarge'; - } -} - -export class TimeoutExceeded extends RegexSolverError { - constructor(message: string) { - super(message, 400, 'TimeoutExceeded'); - this.name = 'TimeoutExceeded'; - } -} - -export class InvalidNumberOfStringsToGenerate extends RegexSolverError { - constructor(message: string) { - super(message, 400, 'InvalidNumberOfStringsToGenerate'); - this.name = 'InvalidNumberOfStringsToGenerate'; - } -} - -export class MissingOrMalformedToken extends RegexSolverError { - constructor(message: string) { - super(message, 401, 'MissingOrMalformedToken'); - this.name = 'MissingOrMalformedToken'; - } -} - -export class InvalidToken extends RegexSolverError { - constructor(message: string) { - super(message, 401, 'InvalidToken'); - this.name = 'InvalidToken'; - } -} - -export class QuotaExceeded extends RegexSolverError { - constructor(message: string) { - super(message, 403, 'QuotaExceeded'); - this.name = 'QuotaExceeded'; - } -} - -export class NotFound extends RegexSolverError { - constructor(message: string) { - super(message, 404); - this.name = 'NotFound'; - } -} - -export class TooManyRequestsError extends RegexSolverError { - public readonly retryAfter?: number; - - constructor(message: string, retryAfter?: number) { - super(message, 429); - this.name = 'TooManyRequestsError'; - this.retryAfter = retryAfter; - } -} - -export class InternalServerError extends RegexSolverError { - constructor(message: string) { - super(message, 500); - this.name = 'InternalServerError'; - } -} diff --git a/src/index.ts b/src/index.ts index 968d9b8..27dcedc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ -export * from './RegexSolverClient'; -export * from './models/Term'; -export * from './models/Cardinality'; -export * from './models/Length'; -export * from './models/ResponseFormat'; -export * from './exceptions'; +export * from "./RegexSolverClient"; +export * from "./models/Term"; +export * from "./models/Cardinality"; +export * from "./models/Length"; +export * from "./models/ResponseFormat"; +export * from "./exceptions"; diff --git a/src/models/Cardinality.ts b/src/models/Cardinality.ts index 7cec6bc..226d2a7 100644 --- a/src/models/Cardinality.ts +++ b/src/models/Cardinality.ts @@ -1,36 +1,115 @@ import { Cardinality as CardinalityDto } from "../generated"; +import { TermPropertiesMixin } from "./TermPropertiesMixin"; -export class Cardinality { - public readonly type: "integer" | "bigInteger" | "infinite"; - public readonly value: number | null; - - constructor( - type: "integer" | "bigInteger" | "infinite", - value: number | null = null, - ) { - this.type = type; - this.value = value; - } +export abstract class Cardinality extends TermPropertiesMixin { + /** Base class representing the number of unique strings matched by a term. */ + public abstract readonly type: "integer" | "bigInteger" | "infinite"; public static fromDto(dto: CardinalityDto): Cardinality { if (dto.type === "integer") { - return new Cardinality("integer", dto.value); + return new Integer(dto.value as number); } else if (dto.type === "bigInteger") { - return new Cardinality("bigInteger"); + return new BigInteger(); } else { - return new Cardinality("infinite"); + return new Infinite(); } } - public isInfinite(): boolean { + public isEmpty(): boolean | undefined { + return undefined; + } + + public isEmptyString(): boolean | undefined { + return undefined; + } + + public isTotal(): boolean | undefined { + return undefined; + } + + public isInfinite(): this is Infinite { return this.type === "infinite"; } - public isBigInteger(): boolean { + public isBigInteger(): this is BigInteger { return this.type === "bigInteger"; } - public isInteger(): boolean { + public isInteger(): this is Integer { return this.type === "integer"; } + + public toString(): string { + return ""; + } +} + +export class Infinite extends Cardinality { + /** Indicates that the set of matched strings is infinite. */ + public readonly type = "infinite"; + + public isEmpty(): boolean | undefined { + return false; + } + + public isEmptyString(): boolean | undefined { + return false; + } + + public toString(): string { + return ""; + } +} + +export class BigInteger extends Cardinality { + /** Indicates that the set of matched strings is finite but too large to be returned as a standard integer. */ + public readonly type = "bigInteger"; + + public isEmpty(): boolean | undefined { + return false; + } + + public isEmptyString(): boolean | undefined { + return false; + } + + public isTotal(): boolean | undefined { + return false; + } + + public toString(): string { + return ""; + } +} + +export class Integer extends Cardinality { + /** * Indicates that the set of matched strings is finite and exactly calculable. + * * @param value The exact count of uniquely matched strings. + */ + public readonly type = "integer"; + public readonly value: number; + + constructor(value: number) { + super(); + this.value = value; + } + + public isEmpty(): boolean | undefined { + return this.value === 0; + } + + public isEmptyString(): boolean | undefined { + if (this.value === 1) { + return undefined; + } + return false; + } + + public isTotal(): boolean | undefined { + return false; + } + + public toString(): string { + return ``; + } } diff --git a/src/models/Length.ts b/src/models/Length.ts index 5496ef8..c1e9f2e 100644 --- a/src/models/Length.ts +++ b/src/models/Length.ts @@ -1,10 +1,17 @@ import { Length as LengthDto } from "../generated"; +import { TermPropertiesMixin } from "./TermPropertiesMixin"; -export class Length { +/** + * Represents the minimum and maximum lengths of any string matched by the term. + */ +export class Length extends TermPropertiesMixin { + /** The shortest possible matched string length, or null if the language is empty. */ public readonly min: number | null; + /** The longest possible matched string length, or null if the length is unbounded. */ public readonly max: number | null; - constructor(min: number | null, max: number | null) { + constructor(min: number | null = null, max: number | null = null) { + super(); this.min = min; this.max = max; } @@ -13,11 +20,27 @@ export class Length { return new Length(dto.min, dto.max); } - public isEmpty(): boolean { - return this.min === null; + public toString(): string { + return ``; + } + + public isEmpty(): boolean | undefined { + return this.min === null && this.max === null; + } + + public isEmptyString(): boolean | undefined { + return this.min === 0 && this.max === 0; + } + + public isTotal(): boolean | undefined { + if (this.min !== 0 || this.max !== null) { + return false; + } else { + return undefined; + } } public isInfinite(): boolean { - return this.max === null; + return this.min !== null && this.max === null; } } diff --git a/src/models/ResponseFormat.ts b/src/models/ResponseFormat.ts index 0eedff0..4a8db15 100644 --- a/src/models/ResponseFormat.ts +++ b/src/models/ResponseFormat.ts @@ -2,16 +2,16 @@ * Defines the format in which the engine should return computed Terms. */ export enum ResponseFormat { - /** - * Allows the engine to return the result in the most efficient format. - */ - ANY = 'any', - /** - * Fast Automaton Internal Representation, a stable internal format. - */ - FAIR = 'fair', - /** - * Standard regular expression pattern. - */ - REGEX = 'regex' + /** + * Allows the engine to return the result in the most efficient format. + */ + ANY = "any", + /** + * Fast Automaton Internal Representation, a stable internal format. + */ + FAIR = "fair", + /** + * Standard regular expression pattern. + */ + REGEX = "regex", } diff --git a/src/models/Term.ts b/src/models/Term.ts index 1ac484f..dd4dae6 100644 --- a/src/models/Term.ts +++ b/src/models/Term.ts @@ -7,14 +7,22 @@ export class Term { public readonly value: string; // Cache - public cardinality: Cardinality | null = null; - public length: Length | null = null; - public empty: boolean | null = null; - public emptyString: boolean | null = null; - public total: boolean | null = null; - public pattern: string | null = null; - public dot: string | null = null; - public stableTerm: Term | null = null; + /** @internal */ + public _cardinality: Cardinality | null = null; + /** @internal */ + public _length: Length | null = null; + /** @internal */ + public _empty: boolean | null = null; + /** @internal */ + public _emptyString: boolean | null = null; + /** @internal */ + public _total: boolean | null = null; + /** @internal */ + public _pattern: string | null = null; + /** @internal */ + public _dot: string | null = null; + /** @internal */ + public _stableTerm: Term | null = null; constructor(type: "regex" | "fair", value: string) { this.type = type; @@ -34,7 +42,7 @@ export class Term { } public getPattern(): string | null { - return this.type === "regex" ? this.value : this.pattern; + return this.type === "regex" ? this.value : this._pattern; } public serialize(): string { diff --git a/src/models/TermPropertiesMixin.ts b/src/models/TermPropertiesMixin.ts new file mode 100644 index 0000000..f235ee6 --- /dev/null +++ b/src/models/TermPropertiesMixin.ts @@ -0,0 +1,33 @@ +/** + * A mixin providing default property inference for Term analytics. + * + * Returns `undefined` when a property cannot be strictly inferred from the current data alone. + */ +export abstract class TermPropertiesMixin { + /** + * Infers whether the term matches no strings at all. + * + * @returns {boolean | undefined} True if it definitely matches no strings, False if it matches at least one, or undefined if it cannot be inferred. + */ + public isEmpty(): boolean | undefined { + return undefined; + } + + /** + * Infers whether the term matches strictly the empty string (""). + * + * @returns {boolean | undefined} True if it definitely matches only the empty string, False if it matches other strings, or undefined if it cannot be inferred. + */ + public isEmptyString(): boolean | undefined { + return undefined; + } + + /** + * Infers whether the term matches all possible strings. + * + * @returns {boolean | undefined} True if it definitely matches all strings, False if it misses at least one string, or undefined if it cannot be inferred. + */ + public isTotal(): boolean | undefined { + return undefined; + } +} diff --git a/tests/RateLimiter.test.ts b/tests/RateLimiter.test.ts index 6cebc06..a23bd87 100644 --- a/tests/RateLimiter.test.ts +++ b/tests/RateLimiter.test.ts @@ -1,58 +1,58 @@ -import { RateLimiter } from '../src/RateLimiter'; - -describe('RateLimiter', () => { - let rateLimiter: RateLimiter; - - beforeEach(() => { - rateLimiter = new RateLimiter(); - }); - - test('wait should resolve immediately if not blocked', async () => { - const start = Date.now(); - await rateLimiter.wait(); - const duration = Date.now() - start; - expect(duration).toBeLessThan(50); - }); - - test('trigger should block wait', async () => { - const retryAfter = 0.1; // 100ms - rateLimiter.trigger(retryAfter); - - const start = Date.now(); - await rateLimiter.wait(); - const duration = Date.now() - start; - - expect(duration).toBeGreaterThanOrEqual(100); - expect(duration).toBeLessThan(150); - }); - - test('multiple triggers should be ignored while blocked', async () => { - const retryAfter1 = 0.2; // 200ms - const retryAfter2 = 0.1; // 100ms - - rateLimiter.trigger(retryAfter1); - rateLimiter.trigger(retryAfter2); // Should be ignored - - const start = Date.now(); - await rateLimiter.wait(); - const duration = Date.now() - start; - - expect(duration).toBeGreaterThanOrEqual(200); - expect(duration).toBeLessThan(250); - }); - - test('concurrent waits should all resolve after block is lifted', async () => { - const retryAfter = 0.1; // 100ms - rateLimiter.trigger(retryAfter); - - const start = Date.now(); - const p1 = rateLimiter.wait(); - const p2 = rateLimiter.wait(); - const p3 = rateLimiter.wait(); - - await Promise.all([p1, p2, p3]); - const duration = Date.now() - start; - - expect(duration).toBeGreaterThanOrEqual(100); - }); +import { RateLimiter } from "../src/RateLimiter"; + +describe("RateLimiter", () => { + let rateLimiter: RateLimiter; + + beforeEach(() => { + rateLimiter = new RateLimiter(); + }); + + test("wait should resolve immediately if not blocked", async () => { + const start = Date.now(); + await rateLimiter.wait(); + const duration = Date.now() - start; + expect(duration).toBeLessThan(50); + }); + + test("trigger should block wait", async () => { + const retryAfter = 0.1; // 100ms + rateLimiter.trigger(retryAfter); + + const start = Date.now(); + await rateLimiter.wait(); + const duration = Date.now() - start; + + expect(duration).toBeGreaterThanOrEqual(100); + expect(duration).toBeLessThan(150); + }); + + test("multiple triggers should be ignored while blocked", async () => { + const retryAfter1 = 0.2; // 200ms + const retryAfter2 = 0.1; // 100ms + + rateLimiter.trigger(retryAfter1); + rateLimiter.trigger(retryAfter2); // Should be ignored + + const start = Date.now(); + await rateLimiter.wait(); + const duration = Date.now() - start; + + expect(duration).toBeGreaterThanOrEqual(200); + expect(duration).toBeLessThan(250); + }); + + test("concurrent waits should all resolve after block is lifted", async () => { + const retryAfter = 0.1; // 100ms + rateLimiter.trigger(retryAfter); + + const start = Date.now(); + const p1 = rateLimiter.wait(); + const p2 = rateLimiter.wait(); + const p3 = rateLimiter.wait(); + + await Promise.all([p1, p2, p3]); + const duration = Date.now() - start; + + expect(duration).toBeGreaterThanOrEqual(100); + }); }); diff --git a/tests/client.test.ts b/tests/client.test.ts index 44ae6dd..c84a345 100644 --- a/tests/client.test.ts +++ b/tests/client.test.ts @@ -1,140 +1,160 @@ -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; -import { RegexSolverClient } from '../src/RegexSolverClient'; -import { Term } from '../src/models/Term'; -import * as Exceptions from '../src/exceptions'; - -describe('RegexSolverClient', () => { - let mock: any; - let client: RegexSolverClient; - - beforeEach(() => { - mock = new MockAdapter(axios); - client = new RegexSolverClient({ apiToken: 'test-token' }); +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { RegexSolverClient } from "../src/RegexSolverClient"; +import { Term } from "../src/models/Term"; +import * as Exceptions from "../src/exceptions"; +import { Integer } from "../src/models/Cardinality"; + +describe("RegexSolverClient", () => { + let mock: any; + let client: RegexSolverClient; + + beforeEach(() => { + client = new RegexSolverClient({ apiToken: "test-token" }); + + // FIX: Mock the specific Axios instance created inside the client, + // otherwise MockAdapter only intercepts the global `axios` object. + mock = new MockAdapter((client as any).axiosInstance); + }); + + afterEach(() => { + mock.restore(); + }); + + test("getCardinality should work", async () => { + mock.onPost("/analyze/cardinality").reply(200, { + success: true, + data: { type: "integer", value: 26 }, }); - afterEach(() => { - mock.restore(); - }); + const term = Term.regex("[a-z]"); + const cardinality = await client.getCardinality(term); - test('getCardinality should work', async () => { - mock.onPost('/analyze/cardinality').reply(200, { - success: true, - data: { type: 'integer', value: 26 } - }); - - const term = Term.regex('[a-z]'); - const cardinality = await client.getCardinality(term); - - expect(cardinality.isInteger()).toBe(true); - expect(cardinality.value).toBe(26); - expect(term.cardinality).toBe(cardinality); - }); + expect(cardinality.isInteger()).toBe(true); + expect(cardinality).toEqual(new Integer(26)); + expect(term._cardinality).toBe(cardinality); + }); - test('getLength should work', async () => { - mock.onPost('/analyze/length').reply(200, { - success: true, - data: { type: 'length', min: 1, max: 4 } - }); - - const term = Term.regex('(abc)?d'); - const length = await client.getLength(term); - - expect(length.min).toBe(1); - expect(length.max).toBe(4); - expect(term.length).toBe(length); + test("getLength should work", async () => { + mock.onPost("/analyze/length").reply(200, { + success: true, + data: { type: "length", min: 1, max: 4 }, }); - test('error mapping should work for 400 Bad Request', async () => { - mock.onPost('/analyze/cardinality').reply(400, { - success: false, - error: 'Invalid JSON body', - errorCode: 'InvalidJson' - }); + const term = Term.regex("(abc)?d"); + const length = await client.getLength(term); - const term = Term.regex('[a-z]'); - await expect(client.getCardinality(term)).rejects.toThrow(Exceptions.InvalidJson); + expect(length.min).toBe(1); + expect(length.max).toBe(4); + expect(term._length).toBe(length); + }); + + test("error mapping should work for 400 Bad Request", async () => { + mock.onPost("/analyze/cardinality").reply(400, { + success: false, + error: "Invalid JSON body", + errorCode: "InvalidJson", }); - test('rate limit with retry-after should work and block concurrent requests', async () => { - // First call 429 - mock.onPost('/analyze/cardinality').replyOnce(429, { - success: false, - error: 'Too many requests', - errorCode: 'RateLimitExceeded' - }, { 'retry-after': '0.1' }); - - // Second call 200 - mock.onPost('/analyze/cardinality').reply(200, { - success: true, - data: { type: 'integer', value: 26 } - }); - - const start = Date.now(); - const term = Term.regex('[a-z]'); - - // Parallel calls - const p1 = client.getCardinality(term); - const p2 = client.getCardinality(term); - - const [c1, c2] = await Promise.all([p1, p2]); - const duration = Date.now() - start; - - expect(c1.value).toBe(26); - expect(c2.value).toBe(26); - expect(duration).toBeGreaterThanOrEqual(100); + const term = Term.regex("[a-z]"); + + // FIX: Updated to InvalidJsonError + await expect(client.getCardinality(term)).rejects.toThrow( + Exceptions.InvalidJsonError, + ); + }); + + test("rate limit with retry-after should work and block concurrent requests", async () => { + // First call 429 + mock.onPost("/analyze/cardinality").replyOnce( + 429, + { + success: false, + error: "Too many requests", + errorCode: "RateLimitExceeded", + }, + { "retry-after": "0.1" }, + ); + + // Second call 200 + mock.onPost("/analyze/cardinality").reply(200, { + success: true, + data: { type: "integer", value: 26 }, }); - test('error mapping for 401 Unauthorized - Invalid Token', async () => { - mock.onPost('/analyze/cardinality').reply(401, { - success: false, - error: 'Invalid token', - errorCode: 'InvalidToken' - }); + const start = Date.now(); + const term = Term.regex("[a-z]"); - await expect(client.getCardinality(Term.regex('abc'))).rejects.toThrow(Exceptions.InvalidToken); - }); + // Parallel calls + const p1 = client.getCardinality(term); + const p2 = client.getCardinality(term); - test('error mapping for 403 Forbidden - Quota Exceeded', async () => { - mock.onPost('/analyze/cardinality').reply(403, { - success: false, - error: 'Quota exceeded', - errorCode: 'QuotaExceeded' - }); + const [c1, c2] = await Promise.all([p1, p2]); + const duration = Date.now() - start; - await expect(client.getCardinality(Term.regex('abc'))).rejects.toThrow(Exceptions.QuotaExceeded); + expect(c1).toEqual(new Integer(26)); + expect(c2).toEqual(new Integer(26)); + expect(duration).toBeGreaterThanOrEqual(100); + }); + + test("error mapping for 401 Unauthorized - Invalid Token", async () => { + mock.onPost("/analyze/cardinality").reply(401, { + success: false, + error: "Invalid token", + errorCode: "InvalidToken", + }); + + // FIX: Updated to InvalidTokenError + await expect(client.getCardinality(Term.regex("abc"))).rejects.toThrow( + Exceptions.InvalidTokenError, + ); + }); + + test("error mapping for 403 Forbidden - Quota Exceeded", async () => { + mock.onPost("/analyze/cardinality").reply(403, { + success: false, + error: "Quota exceeded", + errorCode: "QuotaExceeded", }); - test('error mapping for 500 Internal Server Error', async () => { - mock.onPost('/analyze/cardinality').reply(500, { - success: false, - error: 'Internal server error' - }); + // FIX: Updated to QuotaExceededError + await expect(client.getCardinality(Term.regex("abc"))).rejects.toThrow( + Exceptions.QuotaExceededError, + ); + }); - await expect(client.getCardinality(Term.regex('abc'))).rejects.toThrow(Exceptions.InternalServerError); + test("error mapping for 500 Internal Server Error", async () => { + mock.onPost("/analyze/cardinality").reply(500, { + success: false, + error: "Internal server error", }); - test('isEmpty should work', async () => { - mock.onPost('/analyze/empty').reply(200, { - success: true, - data: { value: true } - }); + await expect(client.getCardinality(Term.regex("abc"))).rejects.toThrow( + Exceptions.InternalServerError, + ); + }); - const term = Term.regex('[]'); - const emptyValue = await client.isEmpty(term); - expect(emptyValue).toBe(true); - expect(term.empty).toBe(true); + test("isEmpty should work", async () => { + mock.onPost("/analyze/empty").reply(200, { + success: true, + data: { value: true }, }); - test('intersection should work', async () => { - mock.onPost('/compute/intersection').reply(200, { - success: true, - data: { type: 'regex', value: 'a' } - }); + const term = Term.regex("[]"); + const emptyValue = await client.isEmpty(term); + expect(emptyValue).toBe(true); + expect(term._empty).toBe(true); + }); - const t1 = Term.regex('a'); - const t2 = Term.regex('ab'); - const result = await client.intersection([t1, t2]); - expect(result.value).toBe('a'); + test("intersection should work", async () => { + mock.onPost("/compute/intersection").reply(200, { + success: true, + data: { type: "regex", value: "a" }, }); + + const t1 = Term.regex("a"); + const t2 = Term.regex("ab"); + const result = await client.intersection([t1, t2]); + expect(result.value).toBe("a"); + }); }); diff --git a/tests/term.test.ts b/tests/term.test.ts index ed02637..b09e2e0 100644 --- a/tests/term.test.ts +++ b/tests/term.test.ts @@ -1,45 +1,45 @@ -import { Term } from '../src/models/Term'; +import { Term } from "../src/models/Term"; -describe('Term', () => { - test('isMatch should work for regex terms', () => { - const term = Term.regex('[a-z]+'); - expect(term.isMatch('abc')).toBe(true); - expect(term.isMatch('123')).toBe(false); - expect(term.isMatch('ABC')).toBe(false); - }); +describe("Term", () => { + test("isMatch should work for regex terms", () => { + const term = Term.regex("[a-z]+"); + expect(term.isMatch("abc")).toBe(true); + expect(term.isMatch("123")).toBe(false); + expect(term.isMatch("ABC")).toBe(false); + }); - test('isMatch should work with dotAll equivalent', () => { - const term = Term.regex('.+'); - expect(term.isMatch('abc\ndef')).toBe(true); - }); + test("isMatch should work with dotAll equivalent", () => { + const term = Term.regex(".+"); + expect(term.isMatch("abc\ndef")).toBe(true); + }); - test('isMatch should be anchored', () => { - const term = Term.regex('abc'); - expect(term.isMatch('abcd')).toBe(false); - expect(term.isMatch('xabc')).toBe(false); - }); + test("isMatch should be anchored", () => { + const term = Term.regex("abc"); + expect(term.isMatch("abcd")).toBe(false); + expect(term.isMatch("xabc")).toBe(false); + }); - test('serialize/deserialize should work', () => { - const term = Term.regex('[a-z]+'); - const serialized = term.serialize(); - expect(serialized).toBe('regex=[a-z]+'); - - const deserialized = Term.deserialize(serialized); - expect(deserialized.type).toBe('regex'); - expect(deserialized.value).toBe('[a-z]+'); - }); + test("serialize/deserialize should work", () => { + const term = Term.regex("[a-z]+"); + const serialized = term.serialize(); + expect(serialized).toBe("regex=[a-z]+"); - test('toDto should work', () => { - const term = Term.regex('[a-z]+'); - const dto = term.toDto(); - expect(dto.type).toBe('regex'); - expect(dto.value).toBe('[a-z]+'); - }); + const deserialized = Term.deserialize(serialized); + expect(deserialized.type).toBe("regex"); + expect(deserialized.value).toBe("[a-z]+"); + }); - test('fromDto should work', () => { - const dto = { type: 'regex' as const, value: '[a-z]+' }; - const term = Term.fromDto(dto); - expect(term.type).toBe('regex'); - expect(term.value).toBe('[a-z]+'); - }); + test("toDto should work", () => { + const term = Term.regex("[a-z]+"); + const dto = term.toDto(); + expect(dto.type).toBe("regex"); + expect(dto.value).toBe("[a-z]+"); + }); + + test("fromDto should work", () => { + const dto = { type: "regex" as const, value: "[a-z]+" }; + const term = Term.fromDto(dto); + expect(term.type).toBe("regex"); + expect(term.value).toBe("[a-z]+"); + }); }); From f9e25d120e42ddfbb540587ffc6d2ed6817f5785 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Sun, 29 Mar 2026 16:46:48 +0200 Subject: [PATCH 09/20] Update library --- src/RegexSolverClient.ts | 83 ++++----- src/exceptions.ts | 90 +++++----- src/models/Cardinality.ts | 23 +-- src/models/Term.ts | 244 ++++++++++++++++++++------ tests/client.test.ts | 348 ++++++++++++++++++++++++++++++++------ tests/models.test.ts | 67 ++++++++ tests/term.test.ts | 53 +++++- 7 files changed, 687 insertions(+), 221 deletions(-) create mode 100644 tests/models.test.ts diff --git a/src/RegexSolverClient.ts b/src/RegexSolverClient.ts index 2dd8f75..8fcc8b9 100644 --- a/src/RegexSolverClient.ts +++ b/src/RegexSolverClient.ts @@ -227,9 +227,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term._cardinality !== null) { - return term._cardinality; - } + const cached = term.getCachedCardinality(); + if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), @@ -238,8 +237,10 @@ export class RegexSolverClient { const response = await this.executeWithRetry(() => this.analyzeApi.cardinality(request), ); + const cardinality = Cardinality.fromDto(response.data.data); - term._cardinality = cardinality; + term.setCachedCardinality(cardinality); + term._setPropertiesMixin(cardinality as any); return cardinality; } @@ -253,9 +254,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term._length !== null) { - return term._length; - } + const cached = term.getCachedLength(); + if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), @@ -264,8 +264,10 @@ export class RegexSolverClient { const response = await this.executeWithRetry(() => this.analyzeApi.length(request), ); + const length = Length.fromDto(response.data.data); - term._length = length; + term.setCachedLength(length); + term._setPropertiesMixin(length as any); return length; } @@ -323,9 +325,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term._empty !== null) { - return term._empty; - } + const cached = term.getCachedEmpty(); + if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), @@ -334,14 +335,14 @@ export class RegexSolverClient { const response = await this.executeWithRetry(() => this.analyzeApi.empty(request), ); + const isEmpty = response.data.data.value; - term._empty = isEmpty; + term.setCachedEmpty(isEmpty); if (isEmpty) { - term._cardinality = new Integer(0); - term._length = new Length(null, null); + term.setCachedCardinality(new Integer(0)); + term.setCachedLength(new Length(null, null)); } - return isEmpty; } @@ -355,9 +356,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term._emptyString !== null) { - return term._emptyString; - } + const cached = term.getCachedEmptyString(); + if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), @@ -366,14 +366,14 @@ export class RegexSolverClient { const response = await this.executeWithRetry(() => this.analyzeApi.emptyString(request), ); + const isEmptyString = response.data.data.value; - term._emptyString = isEmptyString; + term.setCachedEmptyString(isEmptyString); if (isEmptyString) { - term._cardinality = new Integer(1); - term._length = new Length(0, 0); + term.setCachedCardinality(new Integer(1)); + term.setCachedLength(new Length(0, 0)); } - return isEmptyString; } @@ -387,9 +387,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term._total !== null) { - return term._total; - } + const cached = term.getCachedTotal(); + if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), @@ -398,14 +397,14 @@ export class RegexSolverClient { const response = await this.executeWithRetry(() => this.analyzeApi.total(request), ); + const isTotal = response.data.data.value; - term._total = isTotal; + term.setCachedTotal(isTotal); if (isTotal) { - term._cardinality = new Infinite(); - term._length = new Length(0, null); + term.setCachedCardinality(new Infinite()); + term.setCachedLength(new Length(0, null)); } - return isTotal; } @@ -419,9 +418,8 @@ export class RegexSolverClient { term: Term, executionTimeout?: number, ): Promise { - if (term._pattern !== null) { - return term._pattern; - } + const existingPattern = term.getPattern(); + if (existingPattern !== null) return existingPattern; const request: TermRequest = { term: term.toDto(), @@ -430,8 +428,9 @@ export class RegexSolverClient { const response = await this.executeWithRetry(() => this.analyzeApi.pattern(request), ); + const pattern = response.data.data.value; - term._pattern = pattern; + term.setCachedPattern(pattern); return pattern; } @@ -442,9 +441,8 @@ export class RegexSolverClient { * @returns DOT string. */ public async getDot(term: Term, executionTimeout?: number): Promise { - if (term._dot !== null) { - return term._dot; - } + const cached = term.getCachedDot(); + if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), @@ -453,8 +451,9 @@ export class RegexSolverClient { const response = await this.executeWithRetry(() => this.analyzeApi.dot(request), ); + const dot = response.data.data.value; - term._dot = dot; + term.setCachedDot(dot); return dot; } @@ -619,8 +618,9 @@ export class RegexSolverClient { let termToUse = term; let returnStableTerm = false; - if (term._stableTerm !== null) { - termToUse = term._stableTerm; + const stableTerm = term.getCachedStableTerm(); + if (stableTerm !== null) { + termToUse = stableTerm; } else { returnStableTerm = true; } @@ -632,13 +632,16 @@ export class RegexSolverClient { returnStableTerm, options: this.buildOptions(executionTimeout), }; + const response = await this.executeWithRetry(() => this.generateApi.strings(request), ); + const data = response.data.data; if (data.term) { - term._stableTerm = Term.fromDto(data.term); + term.setCachedStableTerm(Term.fromDto(data.term)); } + return data.strings.value; } } diff --git a/src/exceptions.ts b/src/exceptions.ts index 904c70e..d1af952 100644 --- a/src/exceptions.ts +++ b/src/exceptions.ts @@ -1,5 +1,5 @@ +/** Base exception for all RegexSolver errors. */ export class RegexSolverError extends Error { - /** Base exception for all RegexSolver errors. */ constructor(message: string) { super(message); this.name = this.constructor.name; @@ -11,8 +11,8 @@ export class RegexSolverError extends Error { } } +/** Base exception raised when the RegexSolver API returns an error response. */ export class ApiError extends RegexSolverError { - /** Base exception raised when the RegexSolver API returns an error response. */ public readonly statusCode?: number; public readonly body?: string; @@ -23,66 +23,52 @@ export class ApiError extends RegexSolverError { } } -export class BadRequestError extends ApiError { - /** Raised when the API returns a 400 Bad Request error. */ -} +/** Raised when the API returns a 400 Bad Request error. */ +export class BadRequestError extends ApiError {} -export class InvalidJsonError extends BadRequestError { - /** Raised when the provided JSON is invalid or cannot be parsed. */ -} +/** Raised when the provided JSON is invalid or cannot be parsed. */ +export class InvalidJsonError extends BadRequestError {} -export class TooManyTermsError extends BadRequestError { - /** Raised when the number of terms provided exceeds the maximum allowed. */ -} +/** Raised when the number of terms provided exceeds the maximum allowed. */ +export class TooManyTermsError extends BadRequestError {} -export class TimeoutTooLargeError extends BadRequestError { - /** Raised when the requested `execution_timeout` exceeds the maximum allowed for your current plan. */ -} +/** Raised when the requested `execution_timeout` exceeds the maximum allowed for your current plan. */ +export class TimeoutTooLargeError extends BadRequestError {} -export class TimeoutExceededError extends BadRequestError { - /** Raised when the execution of the request exceeds the provided `execution_timeout` or the maximum allowed for your current plan. */ -} +/** Raised when the execution of the request exceeds the provided `execution_timeout` or the maximum allowed for your current plan. */ +export class TimeoutExceededError extends BadRequestError {} -export class InvalidNumberOfStringsToGenerate extends BadRequestError { - /** Raised when the requested number of strings to generate is below the minimum or exceeds the maximum allowed. */ -} +/** Raised when the requested number of strings to generate is below the minimum or exceeds the maximum allowed. */ +export class InvalidNumberOfStringsToGenerate extends BadRequestError {} -export class UnauthorizedError extends ApiError { - /** Raised when the API returns a 401 Unauthorized error. */ -} +/** Raised when the API returns a 401 Unauthorized error. */ +export class UnauthorizedError extends ApiError {} -export class MissingOrMalformedTokenError extends UnauthorizedError { - /** Raised when the provided authentication token is missing or malformed. */ -} +/** Raised when the provided authentication token is missing or malformed. */ +export class MissingOrMalformedTokenError extends UnauthorizedError {} -export class InvalidTokenError extends UnauthorizedError { - /** Raised when the provided authentication token is invalid. */ -} +/** Raised when the provided authentication token is invalid. */ +export class InvalidTokenError extends UnauthorizedError {} -export class ForbiddenError extends ApiError { - /** Raised when the API returns a 403 Forbidden error. */ -} +/** Raised when the API returns a 403 Forbidden error. */ +export class ForbiddenError extends ApiError {} -export class QuotaExceededError extends ForbiddenError { - /** Raised when your account's monthly compute quota has been exceeded. */ -} +/** Raised when your account's monthly compute quota has been exceeded. */ +export class QuotaExceededError extends ForbiddenError {} -export class NotFoundError extends ApiError { - /** * Raised when the API returns a 404 Not Found error. - * Indicates that the requested API endpoint or resource does not exist. - */ -} +/** Raised when the API returns a 404 Not Found error. + * Indicates that the requested API endpoint or resource does not exist. + */ +export class NotFoundError extends ApiError {} -export class TooManyRequestsError extends ApiError { - /** - * Raised when the API returns a 429 Too Many Requests error and max retries are exceeded. - * Indicates that your requests-per-second (req/s) rate limit has been exceeded. - */ -} +/** + * Raised when the API returns a 429 Too Many Requests error and max retries are exceeded. + * Indicates that your requests-per-second (req/s) rate limit has been exceeded. + */ +export class TooManyRequestsError extends ApiError {} -export class InternalServerError extends ApiError { - /** - * Raised when the API returns a 500 Internal Server Error. - * Indicates an unexpected failure or panic on the RegexSolver compute servers. - */ -} +/** + * Raised when the API returns a 500 Internal Server Error. + * Indicates an unexpected failure or panic on the RegexSolver compute servers. + */ +export class InternalServerError extends ApiError {} diff --git a/src/models/Cardinality.ts b/src/models/Cardinality.ts index 226d2a7..6097fde 100644 --- a/src/models/Cardinality.ts +++ b/src/models/Cardinality.ts @@ -2,7 +2,6 @@ import { Cardinality as CardinalityDto } from "../generated"; import { TermPropertiesMixin } from "./TermPropertiesMixin"; export abstract class Cardinality extends TermPropertiesMixin { - /** Base class representing the number of unique strings matched by a term. */ public abstract readonly type: "integer" | "bigInteger" | "infinite"; public static fromDto(dto: CardinalityDto): Cardinality { @@ -15,18 +14,6 @@ export abstract class Cardinality extends TermPropertiesMixin { } } - public isEmpty(): boolean | undefined { - return undefined; - } - - public isEmptyString(): boolean | undefined { - return undefined; - } - - public isTotal(): boolean | undefined { - return undefined; - } - public isInfinite(): this is Infinite { return this.type === "infinite"; } @@ -44,8 +31,8 @@ export abstract class Cardinality extends TermPropertiesMixin { } } +/** Indicates that the set of matched strings is infinite. */ export class Infinite extends Cardinality { - /** Indicates that the set of matched strings is infinite. */ public readonly type = "infinite"; public isEmpty(): boolean | undefined { @@ -61,8 +48,8 @@ export class Infinite extends Cardinality { } } +/** Indicates that the set of matched strings is finite but too large to be returned as a standard integer. */ export class BigInteger extends Cardinality { - /** Indicates that the set of matched strings is finite but too large to be returned as a standard integer. */ public readonly type = "bigInteger"; public isEmpty(): boolean | undefined { @@ -82,10 +69,10 @@ export class BigInteger extends Cardinality { } } +/** Indicates that the set of matched strings is finite and exactly calculable. + * @param value The exact count of uniquely matched strings. + */ export class Integer extends Cardinality { - /** * Indicates that the set of matched strings is finite and exactly calculable. - * * @param value The exact count of uniquely matched strings. - */ public readonly type = "integer"; public readonly value: number; diff --git a/src/models/Term.ts b/src/models/Term.ts index dd4dae6..23bc0e9 100644 --- a/src/models/Term.ts +++ b/src/models/Term.ts @@ -1,84 +1,224 @@ import { Term as TermDto, TermFair, TermRegex } from "../generated"; import { Cardinality } from "./Cardinality"; import { Length } from "./Length"; +import { TermPropertiesMixin } from "./TermPropertiesMixin"; -export class Term { - public readonly type: "regex" | "fair"; - public readonly value: string; - - // Cache - /** @internal */ - public _cardinality: Cardinality | null = null; - /** @internal */ - public _length: Length | null = null; - /** @internal */ - public _empty: boolean | null = null; - /** @internal */ - public _emptyString: boolean | null = null; - /** @internal */ - public _total: boolean | null = null; - /** @internal */ - public _pattern: string | null = null; - /** @internal */ - public _dot: string | null = null; - /** @internal */ - public _stableTerm: Term | null = null; - - constructor(type: "regex" | "fair", value: string) { - this.type = type; +/** + * Represents a mathematical term (Regex or FAIR) on which operations can be performed. + */ +export abstract class Term { + private readonly value: string; + + // Shared Cache (Internal) + private _cardinality: Cardinality | null = null; + private _length: Length | null = null; + private _empty: boolean | null = null; + private _emptyString: boolean | null = null; + private _total: boolean | null = null; + protected _pattern: string | null = null; + private _dot: string | null = null; + private _stableTerm: Term | null = null; + + private _compiledRegex: RegExp | null = null; + + protected constructor(value: string) { this.value = value; } - public static fair(payload: string): Term { - return new Term("fair", payload); - } + public abstract getPattern(): string | null; + public abstract getFair(): string | null; + public abstract toDto(): TermDto; + public abstract serialize(): string; public static regex(pattern: string): Term { - return new Term("regex", pattern); + return new RegexTerm(pattern); } - public getFair(): string | null { - return this.type === "fair" ? this.value : null; + public static fair(payload: string): Term { + return new FairTerm(payload); } - public getPattern(): string | null { - return this.type === "regex" ? this.value : this._pattern; - } + // --- Shared Behavior --- - public serialize(): string { - return `${this.type}=${this.value}`; + public getValue(): string { + return this.value; } - public static deserialize(serialized: string): Term { - const parts = serialized.split("="); - if (parts.length < 2) { - throw new Error("Invalid serialized term"); + public _setPropertiesMixin(propertiesMixin: TermPropertiesMixin): void { + const empty = propertiesMixin.isEmpty(); + if (empty !== null && empty !== undefined) { + this.setCachedEmpty(empty); + } + + const emptyString = propertiesMixin.isEmptyString(); + if (emptyString !== null && emptyString !== undefined) { + this.setCachedEmptyString(emptyString); + } + + const total = propertiesMixin.isTotal(); + if (total !== null && total !== undefined) { + this.setCachedTotal(total); } - const type = parts[0] as "regex" | "fair"; - const value = parts.slice(1).join("="); - return new Term(type, value); } - public isMatch(str: string): boolean | null { + public isMatch(str: string): boolean { const pattern = this.getPattern(); if (pattern === null) { + throw new Error( + "The regex pattern of this term is not defined yet, call getPattern() on the client to set it.", + ); + } + + if (this._compiledRegex === null) { + try { + this._compiledRegex = new RegExp(`^(?:${pattern})$`, "s"); + } catch (e: any) { + throw new Error( + `Pattern '${pattern}' cannot be evaluated by JavaScript's RegExp module: ${e.message}`, + ); + } + } + + return this._compiledRegex.test(str); + } + + public static deserialize( + serialized: string | null | undefined, + ): Term | null { + if (!serialized || !serialized.includes("=")) { return null; } - // Must be a "full match" (anchored). Dot (.) must match all characters including newlines. - const regex = new RegExp(`^(${pattern})$`, "s"); - return regex.test(str); + const index = serialized.indexOf("="); + const typeStr = serialized.substring(0, index); + const val = serialized.substring(index + 1); + + if (typeStr.toLowerCase() === "regex") { + return Term.regex(val); + } else if (typeStr.toLowerCase() === "fair") { + return Term.fair(val); + } + return null; } - public toDto(): TermDto { - if (this.type === "regex") { - return { type: "regex", value: this.value } as TermRegex; + public static fromDto(dto: TermDto): Term { + if (dto.type === "regex") { + return Term.regex(dto.value); } else { - return { type: "fair", value: this.value } as TermFair; + return Term.fair(dto.value); } } - public static fromDto(dto: TermDto): Term { - return new Term(dto.type, dto.value); + // --- Cache Getters and Setters --- + + public getCachedCardinality(): Cardinality | null { + return this._cardinality; + } + public setCachedCardinality(cardinality: Cardinality | null): void { + this._cardinality = cardinality; + } + + public getCachedLength(): Length | null { + return this._length; + } + public setCachedLength(length: Length | null): void { + this._length = length; + } + + public getCachedEmpty(): boolean | null { + return this._empty; + } + public setCachedEmpty(empty: boolean | null): void { + this._empty = empty; + } + + public getCachedEmptyString(): boolean | null { + return this._emptyString; + } + public setCachedEmptyString(emptyString: boolean | null): void { + this._emptyString = emptyString; + } + + public getCachedTotal(): boolean | null { + return this._total; + } + public setCachedTotal(total: boolean | null): void { + this._total = total; + } + + public getCachedPattern(): string | null { + return this._pattern; + } + public setCachedPattern(pattern: string | null): void { + this._pattern = pattern; + } + + public getCachedDot(): string | null { + return this._dot; + } + public setCachedDot(dot: string | null): void { + this._dot = dot; + } + + public getCachedStableTerm(): Term | null { + return this._stableTerm; + } + public setCachedStableTerm(stableTerm: Term | null): void { + this._stableTerm = stableTerm; + } + + public equals(other: any): boolean { + if (this === other) return true; + if (!(other instanceof Term)) return false; + return this.serialize() === other.serialize(); + } + + public toString(): string { + return this.serialize(); + } +} + +export class RegexTerm extends Term { + constructor(value: string) { + super(value); + } + + public getPattern(): string | null { + return this.getValue(); + } + + public getFair(): string | null { + const stable = this.getCachedStableTerm(); + return stable ? stable.getFair() : null; + } + + public toDto(): TermDto { + return { type: "regex", value: this.getValue() } as TermRegex; + } + + public serialize(): string { + return "regex=" + this.getValue(); + } +} + +export class FairTerm extends Term { + constructor(value: string) { + super(value); + } + + public getPattern(): string | null { + return this._pattern; // Accesses the protected property of the abstract class + } + + public getFair(): string | null { + return this.getValue(); + } + + public toDto(): TermDto { + return { type: "fair", value: this.getValue() } as TermFair; + } + + public serialize(): string { + return "fair=" + this.getValue(); } } diff --git a/tests/client.test.ts b/tests/client.test.ts index c84a345..ca337bc 100644 --- a/tests/client.test.ts +++ b/tests/client.test.ts @@ -3,7 +3,7 @@ import MockAdapter from "axios-mock-adapter"; import { RegexSolverClient } from "../src/RegexSolverClient"; import { Term } from "../src/models/Term"; import * as Exceptions from "../src/exceptions"; -import { Integer } from "../src/models/Cardinality"; +import { BigInteger, Infinite, Integer } from "../src/models/Cardinality"; describe("RegexSolverClient", () => { let mock: any; @@ -12,8 +12,6 @@ describe("RegexSolverClient", () => { beforeEach(() => { client = new RegexSolverClient({ apiToken: "test-token" }); - // FIX: Mock the specific Axios instance created inside the client, - // otherwise MockAdapter only intercepts the global `axios` object. mock = new MockAdapter((client as any).axiosInstance); }); @@ -21,7 +19,7 @@ describe("RegexSolverClient", () => { mock.restore(); }); - test("getCardinality should work", async () => { + test("getCardinality should work for integer", async () => { mock.onPost("/analyze/cardinality").reply(200, { success: true, data: { type: "integer", value: 26 }, @@ -32,7 +30,35 @@ describe("RegexSolverClient", () => { expect(cardinality.isInteger()).toBe(true); expect(cardinality).toEqual(new Integer(26)); - expect(term._cardinality).toBe(cardinality); + expect(term.getCachedCardinality()).toBe(cardinality); + }); + + test("getCardinality should work for infinite", async () => { + mock.onPost("/analyze/cardinality").reply(200, { + success: true, + data: { type: "infinite" }, + }); + + const term = Term.regex(".*"); + const cardinality = await client.getCardinality(term); + + expect(cardinality.isInfinite()).toBe(true); + expect(cardinality).toBeInstanceOf(Infinite); + expect(term.getCachedCardinality()).toBe(cardinality); + }); + + test("getCardinality should work for bigInteger", async () => { + mock.onPost("/analyze/cardinality").reply(200, { + success: true, + data: { type: "bigInteger" }, + }); + + const term = Term.regex(".{100}"); + const cardinality = await client.getCardinality(term); + + expect(cardinality.isBigInteger()).toBe(true); + expect(cardinality).toBeInstanceOf(BigInteger); + expect(term.getCachedCardinality()).toBe(cardinality); }); test("getLength should work", async () => { @@ -46,55 +72,251 @@ describe("RegexSolverClient", () => { expect(length.min).toBe(1); expect(length.max).toBe(4); - expect(term._length).toBe(length); + expect(term.getCachedLength()).toBe(length); + }); + + test("isEmpty should work", async () => { + mock.onPost("/analyze/empty").reply(200, { + success: true, + data: { value: true }, + }); + + const term = Term.regex("[]"); + const emptyValue = await client.isEmpty(term); + expect(emptyValue).toBe(true); + expect(term.getCachedEmpty()).toBe(true); + }); + + test("isEmptyString should work", async () => { + mock.onPost("/analyze/empty_string").reply(200, { + success: true, + data: { value: true }, + }); + + const term = Term.regex(""); + const result = await client.isEmptyString(term); + expect(result).toBe(true); + expect(term.getCachedEmptyString()).toBe(true); + }); + + test("isTotal should work", async () => { + mock.onPost("/analyze/total").reply(200, { + success: true, + data: { value: true }, + }); + + const term = Term.regex(".*"); + const result = await client.isTotal(term); + expect(result).toBe(true); + expect(term.getCachedTotal()).toBe(result); + }); + + test("getPattern should work", async () => { + mock.onPost("/analyze/pattern").reply(200, { + success: true, + data: { value: "a" }, + }); + + const term = Term.fair("..."); + const result = await client.getPattern(term); + expect(result).toBe("a"); + // getCachedPattern() explicitly verifies our internal cache got updated properly + expect(term.getCachedPattern()).toBe("a"); + }); + + test("getDot should work", async () => { + mock.onPost("/analyze/dot").reply(200, { + success: true, + data: { value: "digraph {...}" }, + }); + + const term = Term.regex("a"); + const result = await client.getDot(term); + expect(result).toBe("digraph {...}"); + expect(term.getCachedDot()).toBe(result); + }); + + test("equivalent should work", async () => { + mock.onPost("/analyze/equivalent").reply(200, { + success: true, + data: { value: true }, + }); + + const t1 = Term.regex("a"); + const t2 = Term.regex("a"); + const result = await client.equivalent(t1, t2); + expect(result).toBe(true); + }); + + test("subset should work", async () => { + mock.onPost("/analyze/subset").reply(200, { + success: true, + data: { value: true }, + }); + + const t1 = Term.regex("a"); + const t2 = Term.regex("a|b"); + const result = await client.subset(t1, t2); + expect(result).toBe(true); + }); + + test("intersection should work", async () => { + mock.onPost("/compute/intersection").reply(200, { + success: true, + data: { type: "regex", value: "a" }, + }); + + const t1 = Term.regex("a"); + const t2 = Term.regex("ab"); + const result = await client.intersection([t1, t2]); + expect(result.getValue()).toBe("a"); + }); + + test("union should work", async () => { + mock.onPost("/compute/union").reply(200, { + success: true, + data: { type: "regex", value: "a|b" }, + }); + + const t1 = Term.regex("a"); + const t2 = Term.regex("b"); + const result = await client.union([t1, t2]); + expect(result.getValue()).toBe("a|b"); + }); + + test("difference should work", async () => { + mock.onPost("/compute/difference").reply(200, { + success: true, + data: { type: "regex", value: "a" }, + }); + + const t1 = Term.regex("a|b"); + const t2 = Term.regex("b"); + const result = await client.difference(t1, t2); + expect(result.getValue()).toBe("a"); + }); + + test("concat should work", async () => { + mock.onPost("/compute/concat").reply(200, { + success: true, + data: { type: "regex", value: "ab" }, + }); + + const t1 = Term.regex("a"); + const t2 = Term.regex("b"); + const result = await client.concat([t1, t2]); + expect(result.getValue()).toBe("ab"); + }); + + test("repeat should work", async () => { + mock.onPost("/compute/repeat").reply(200, { + success: true, + data: { type: "regex", value: "a{2,3}" }, + }); + + const term = Term.regex("a"); + const result = await client.repeat(term, 2, 3); + expect(result.getValue()).toBe("a{2,3}"); + }); + + test("complement should work", async () => { + mock.onPost("/compute/complement").reply(200, { + success: true, + data: { type: "regex", value: "[^a]*" }, + }); + + const term = Term.regex("a*"); + const result = await client.complement(term); + expect(result.getValue()).toBe("[^a]*"); + }); + + test("generateStrings should work", async () => { + mock.onPost("/generate/strings").reply(200, { + success: true, + data: { + type: "generatedStrings", + strings: { + type: "strings", + value: ["", "a", "aa"], + }, + }, + }); + + const term = Term.regex("a*"); + const result = await client.generateStrings(term, 3, 0); + expect(result).toEqual(["", "a", "aa"]); }); - test("error mapping should work for 400 Bad Request", async () => { + test("error mapping for 400 Bad Request - Invalid JSON", async () => { mock.onPost("/analyze/cardinality").reply(400, { success: false, error: "Invalid JSON body", errorCode: "InvalidJson", }); - const term = Term.regex("[a-z]"); - - // FIX: Updated to InvalidJsonError - await expect(client.getCardinality(term)).rejects.toThrow( + await expect(client.getCardinality(Term.regex("a"))).rejects.toThrow( Exceptions.InvalidJsonError, ); }); - test("rate limit with retry-after should work and block concurrent requests", async () => { - // First call 429 - mock.onPost("/analyze/cardinality").replyOnce( - 429, - { - success: false, - error: "Too many requests", - errorCode: "RateLimitExceeded", - }, - { "retry-after": "0.1" }, + test("error mapping for 400 Bad Request - Too Many Terms", async () => { + mock.onPost("/compute/union").reply(400, { + success: false, + error: "Too many terms", + errorCode: "TooManyTerms", + }); + + await expect(client.union([Term.regex("a")])).rejects.toThrow( + Exceptions.TooManyTermsError, ); + }); - // Second call 200 - mock.onPost("/analyze/cardinality").reply(200, { - success: true, - data: { type: "integer", value: 26 }, + test("error mapping for 400 Bad Request - Timeout Too Large", async () => { + mock.onPost("/analyze/cardinality").reply(400, { + success: false, + error: "Timeout too large", + errorCode: "TimeoutTooLarge", }); - const start = Date.now(); - const term = Term.regex("[a-z]"); + await expect(client.getCardinality(Term.regex("a"))).rejects.toThrow( + Exceptions.TimeoutTooLargeError, + ); + }); - // Parallel calls - const p1 = client.getCardinality(term); - const p2 = client.getCardinality(term); + test("error mapping for 400 Bad Request - Timeout Exceeded", async () => { + mock.onPost("/analyze/cardinality").reply(400, { + success: false, + error: "Timeout exceeded", + errorCode: "TimeoutExceeded", + }); - const [c1, c2] = await Promise.all([p1, p2]); - const duration = Date.now() - start; + await expect(client.getCardinality(Term.regex("a"))).rejects.toThrow( + Exceptions.TimeoutExceededError, + ); + }); - expect(c1).toEqual(new Integer(26)); - expect(c2).toEqual(new Integer(26)); - expect(duration).toBeGreaterThanOrEqual(100); + test("error mapping for 400 Bad Request - Invalid Number Of Strings To Generate", async () => { + mock.onPost("/generate/strings").reply(400, { + success: false, + error: "Invalid number of strings", + errorCode: "InvalidNumberOfStringsToGenerate", + }); + + await expect( + client.generateStrings(Term.regex("a"), 10, 0), + ).rejects.toThrow(Exceptions.InvalidNumberOfStringsToGenerate); + }); + + test("error mapping for 401 Unauthorized - Missing or Malformed Token", async () => { + mock.onPost("/analyze/cardinality").reply(401, { + success: false, + error: "Missing token", + errorCode: "MissingOrMalformedToken", + }); + + await expect(client.getCardinality(Term.regex("abc"))).rejects.toThrow( + Exceptions.MissingOrMalformedTokenError, + ); }); test("error mapping for 401 Unauthorized - Invalid Token", async () => { @@ -104,7 +326,6 @@ describe("RegexSolverClient", () => { errorCode: "InvalidToken", }); - // FIX: Updated to InvalidTokenError await expect(client.getCardinality(Term.regex("abc"))).rejects.toThrow( Exceptions.InvalidTokenError, ); @@ -117,12 +338,22 @@ describe("RegexSolverClient", () => { errorCode: "QuotaExceeded", }); - // FIX: Updated to QuotaExceededError await expect(client.getCardinality(Term.regex("abc"))).rejects.toThrow( Exceptions.QuotaExceededError, ); }); + test("error mapping for 404 Not Found", async () => { + mock.onPost("/analyze/cardinality").reply(404, { + success: false, + error: "Not Found", + }); + + await expect(client.getCardinality(Term.regex("abc"))).rejects.toThrow( + Exceptions.NotFoundError, + ); + }); + test("error mapping for 500 Internal Server Error", async () => { mock.onPost("/analyze/cardinality").reply(500, { success: false, @@ -134,27 +365,36 @@ describe("RegexSolverClient", () => { ); }); - test("isEmpty should work", async () => { - mock.onPost("/analyze/empty").reply(200, { + test("rate limit with retry-after should work and block concurrent requests", async () => { + // First call 429 + mock.onPost("/analyze/cardinality").replyOnce( + 429, + { + success: false, + error: "Too many requests", + errorCode: "RateLimitExceeded", + }, + { "retry-after": "0.1" }, + ); + + // Second call 200 + mock.onPost("/analyze/cardinality").reply(200, { success: true, - data: { value: true }, + data: { type: "integer", value: 26 }, }); - const term = Term.regex("[]"); - const emptyValue = await client.isEmpty(term); - expect(emptyValue).toBe(true); - expect(term._empty).toBe(true); - }); + const start = Date.now(); + const term = Term.regex("[a-z]"); - test("intersection should work", async () => { - mock.onPost("/compute/intersection").reply(200, { - success: true, - data: { type: "regex", value: "a" }, - }); + // Parallel calls + const p1 = client.getCardinality(term); + const p2 = client.getCardinality(term); - const t1 = Term.regex("a"); - const t2 = Term.regex("ab"); - const result = await client.intersection([t1, t2]); - expect(result.value).toBe("a"); + const [c1, c2] = await Promise.all([p1, p2]); + const duration = Date.now() - start; + + expect(c1).toEqual(new Integer(26)); + expect(c2).toEqual(new Integer(26)); + expect(duration).toBeGreaterThanOrEqual(100); }); }); diff --git a/tests/models.test.ts b/tests/models.test.ts new file mode 100644 index 0000000..d14bacb --- /dev/null +++ b/tests/models.test.ts @@ -0,0 +1,67 @@ +import { BigInteger, Infinite, Integer } from "../src/models/Cardinality"; +import { Length } from "../src/models/Length"; + +describe("Cardinality", () => { + test("Integer should work", () => { + const c = new Integer(10); + expect(c.value).toBe(10); + expect(c.isEmpty()).toBe(false); + expect(c.isEmptyString()).toBe(false); + expect(c.isTotal()).toBe(false); + expect(c.toString()).toBe(""); + }); + + test("Integer zero should work", () => { + const c = new Integer(0); + expect(c.isEmpty()).toBe(true); + expect(c.isEmptyString()).toBe(false); + }); + + test("Integer one should work", () => { + const c = new Integer(1); + expect(c.isEmpty()).toBe(false); + expect(c.isEmptyString()).toBe(undefined); + }); + + test("BigInteger should work", () => { + const c = new BigInteger(); + expect(c.isEmpty()).toBe(false); + expect(c.isEmptyString()).toBe(false); + expect(c.isTotal()).toBe(false); + expect(c.toString()).toBe(""); + }); + + test("Infinite should work", () => { + const c = new Infinite(); + expect(c.isEmpty()).toBe(false); + expect(c.isEmptyString()).toBe(false); + expect(c.toString()).toBe(""); + }); +}); + +describe("Length", () => { + test("Length should work", () => { + const l = new Length(1, 5); + expect(l.min).toBe(1); + expect(l.max).toBe(5); + expect(l.isEmpty()).toBe(false); + expect(l.isEmptyString()).toBe(false); + expect(l.isTotal()).toBe(false); + expect(l.toString()).toBe(""); + }); + + test("Length empty should work", () => { + const l = new Length(null, null); + expect(l.isEmpty()).toBe(true); + }); + + test("Length empty string should work", () => { + const l = new Length(0, 0); + expect(l.isEmptyString()).toBe(true); + }); + + test("Length total candidate should work", () => { + const l = new Length(0, null); + expect(l.isTotal()).toBe(undefined); + }); +}); diff --git a/tests/term.test.ts b/tests/term.test.ts index b09e2e0..55f3c3c 100644 --- a/tests/term.test.ts +++ b/tests/term.test.ts @@ -1,6 +1,30 @@ -import { Term } from "../src/models/Term"; +import { Term, RegexTerm, FairTerm } from "../src/models/Term"; +import { Integer } from "../src/models/Cardinality"; describe("Term", () => { + test("creation should work", () => { + const regexTerm = Term.regex("abc"); + expect(regexTerm).toBeInstanceOf(RegexTerm); + expect(regexTerm.getValue()).toBe("abc"); + + const fairTerm = Term.fair("payload"); + expect(fairTerm).toBeInstanceOf(FairTerm); + expect(fairTerm.getValue()).toBe("payload"); + }); + + test("getFair and getPattern should work", () => { + const regexTerm = Term.regex("abc"); + expect(regexTerm.getPattern()).toBe("abc"); + expect(regexTerm.getFair()).toBeNull(); + + const fairTerm = Term.fair("payload"); + expect(fairTerm.getFair()).toBe("payload"); + expect(fairTerm.getPattern()).toBeNull(); + + fairTerm.setCachedPattern("abc"); + expect(fairTerm.getPattern()).toBe("abc"); + }); + test("isMatch should work for regex terms", () => { const term = Term.regex("[a-z]+"); expect(term.isMatch("abc")).toBe(true); @@ -19,14 +43,33 @@ describe("Term", () => { expect(term.isMatch("xabc")).toBe(false); }); + test("isMatch should throw for fair terms without cached pattern", () => { + const term = Term.fair("payload"); + expect(() => term.isMatch("abc")).toThrow( + "The regex pattern of this term is not defined yet", + ); + }); + test("serialize/deserialize should work", () => { const term = Term.regex("[a-z]+"); const serialized = term.serialize(); expect(serialized).toBe("regex=[a-z]+"); const deserialized = Term.deserialize(serialized); - expect(deserialized.type).toBe("regex"); - expect(deserialized.value).toBe("[a-z]+"); + if (deserialized === null) { + fail("Deserialized term should not be null"); + } + expect(deserialized).toBeInstanceOf(RegexTerm); + expect(deserialized.getValue()).toBe("[a-z]+"); + + const fairTerm = Term.fair("payload"); + const deserializedFair = Term.deserialize(fairTerm.serialize()); + expect(deserializedFair).toEqual(fairTerm); + }); + + test("deserialize should return null for invalid format", () => { + expect(Term.deserialize("invalid")).toBeNull(); + expect(Term.deserialize("unknown=value")).toBeNull(); }); test("toDto should work", () => { @@ -39,7 +82,7 @@ describe("Term", () => { test("fromDto should work", () => { const dto = { type: "regex" as const, value: "[a-z]+" }; const term = Term.fromDto(dto); - expect(term.type).toBe("regex"); - expect(term.value).toBe("[a-z]+"); + expect(term).toBeInstanceOf(RegexTerm); + expect(term.getValue()).toBe("[a-z]+"); }); }); From 6f4f578af89a4b8af5f8898e99dfdd6e8d91cc9d Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Sun, 29 Mar 2026 17:19:58 +0200 Subject: [PATCH 10/20] Update README.md --- README.md | 116 ++++++++++++++++++++++-------------------------------- 1 file changed, 46 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index afecc27..ee5a766 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ # RegexSolver JS API Client - [Homepage](https://regexsolver.com) | [Online Demo](https://regexsolver.com/demo) | [Documentation](https://docs.regexsolver.com) | [Developer Console](https://console.regexsolver.com) **RegexSolver** is a powerful toolkit for building, combining, and analyzing regular expressions. It is designed for constraint solvers, test generators, and other systems that need advanced regex operations. @@ -7,38 +6,29 @@ ## Installation ```sh -npm i regexsolver -# or -yarn add regexsolver -# or -pnpm add regexsolver +npm install regexsolver ``` -## Usage +Requirements: **Node.js >= 16** + +## Quick Start 1. Create an API token in the [Developer Console](https://console.regexsolver.com/). -2. Initialize the client and start working with terms: +2. Initialize the client and start working with terms. ```javascript -import { RegexSolver, Term } from 'regexsolver'; +import { RegexSolverClient, Term } from 'regexsolver'; -// Set REGEXSOLVER_API_TOKEN in your env and call initialize(), -// or pass the token directly: -RegexSolver.initialize(); // or RegexSolver.initialize('YOUR_API_TOKEN') +const client = new RegexSolverClient({ apiToken: 'YOUR_API_TOKEN' }); const term1 = Term.regex("(abc|de|fg){2,}"); const term2 = Term.regex("de.*"); -const term3 = Term.regex(".*abc"); - -const term4 = Term.regex(".+(abc|de).+"); -term1.intersection(term2, term3) - .then(result => result.difference(term4)) - .then(result => result.getPattern()) - .then(result => console.log(result)); // de(fg)*abc +const intersection = await client.intersection([term1, term2]); +const pattern = await client.getPattern(intersection); +console.log(pattern); // de(abc|de|fg)+ ``` - ## Key Concepts & Limitations RegexSolver supports a subset of regular expressions that adhere to the principles of regular languages. Here are the key characteristics and limitations of the regular expressions supported by RegexSolver: @@ -49,7 +39,6 @@ RegexSolver supports a subset of regular expressions that adhere to the principl - **Line Feed and Dot:** RegexSolver handles all characters the same way. The dot `.` matches any Unicode character including line feed (`\n`). - **Empty Regular Expressions:** The empty language (matches no string) is represented by constructs like `[]` (empty character class). This is distinct from the empty string. - ## Response Formats The API can handle terms in two formats: @@ -61,15 +50,14 @@ By default, the engine returns whatever the operation produces, with no extra co ```javascript import { Term, ResponseFormat } from 'regexsolver'; -const term = Term.regex('abcde'); +const term1 = Term.regex('abcde'); +const term2 = Term.regex('de'); -term.union(Term.regex('de'), { responseFormat: ResponseFormat.REGEX }).then(result => { - console.log(result.toString()); // regex=(abc)?de -}); +const result1 = await client.union([term1, term2], ResponseFormat.REGEX); +console.log(result1.toString()); // regex=(abc)?de -term.union(Term.regex('de'), { responseFormat: ResponseFormat.FAIR }).then(result => { - console.log(result.toString()); // fair=... -}); +const result2 = await client.union([term1, term2], ResponseFormat.FAIR); +console.log(result2.toString()); // fair=... ``` If the format does not matter, omit `responseFormat` or set it to `ResponseFormat.ANY`. @@ -81,69 +69,57 @@ Regardless of the format, you can always call `getPattern()` to obtain the regex Set a server-side compute timeout in milliseconds with `executionTimeout`: ```javascript -import { ApiError, Term } from 'regexsolver'; - -// Limit the server-side compute time to 5 ms -Term.regex('.*ab.*c(de|fg).*dab.*c(de|fg).*ab.*c(de|fg).*dab.*c') - .difference(Term.regex('.*abc.*'), { executionTimeout: 5 }) - .then(res => {/* */}) - .catch(err => { - if (err instanceof ApiError) { - console.log(err.message); // The operation took too much time. - } else { - throw err; - } - }); +import { TimeoutExceededError, Term } from 'regexsolver'; + +// Limit the server-side compute time to 100 ms +try { + const term1 = Term.regex('.*ab.*c(de|fg).*dab.*c(de|fg).*ab.*c(de|fg).*dab.*c'); + const term2 = Term.regex('.*abc.*'); + + const res = await client.difference(term1, term2, undefined, 100); +} catch (error) { + if (error instanceof TimeoutExceededError) { + console.log(error.message); // The operation took too much time. + } +} ``` Timeout is best effort. The exact time is not guaranteed. ## API Overview -`Term` exposes the following methods. - -### Build -| Method | Return | Description | -| -------- | ------- | ------- | -| `Term.fair(fair: string)` | `Term` | Creates a term from a FAIR. | -| `Term.regex(regex: string)` | `Term` | Creates a term from a regex pattern. | +`RegexSolverClient` exposes the following methods. ### Analyze | Method | Return | Description | | -------- | ------- | ------- | -| `t.equivalent(term: Term, opts?)` | `Promise` | `true` if `t` and `term` accept exactly the same language. Supports `executionTimeout`. | -| `t.getCardinality()` | `Promise` | Returns the cardinality of the term (i.e., the number of possible matched strings). | -| `t.getDot()` | `Promise` | Returns a Graphviz DOT representation of the automaton for the term. | -| `t.getFair()` | `string` | Returns the FAIR of the term if defined. | -| `t.getLength()` | `Promise` | Returns the minimum and maximum length of matched strings. | -| `t.getPattern()` | `Promise` | Returns a regular expression pattern for the term. | -| `t.isEmpty()` | `Promise` | `true` if the term matches no string. | -| `t.isEmptyString()` | `Promise` | `true` if the term matches only the empty string. | -| `t.isTotal()` | `Promise` | `true` if the term matches all possible strings. | -| `t.subset(term: Term, opts?)` | `Promise` | `true` if every string matched by `t` is also matched by `term`. Supports `executionTimeout`. | +| `client.equivalent(term1, term2)` | `Promise` | `true` if `term1` and `term2` accept exactly the same language. | +| `client.getCardinality(term)` | `Promise` | Returns the number of possible matched strings. | +| `client.getDot(term)` | `Promise` | Returns a Graphviz DOT representation of the automaton. | +| `client.getLength(term)` | `Promise` | Returns the minimum and maximum length of matched strings. | +| `client.getPattern(term)` | `Promise` | Returns a regular expression pattern for the term. | +| `client.isEmpty(term)` | `Promise` | `true` if the term matches no string. | +| `client.isEmptyString(term)` | `Promise` | `true` if the term matches only the empty string. | +| `client.isTotal(term)` | `Promise` | `true` if the term matches all possible strings. | +| `client.subset(term1, term2)` | `Promise` | `true` if every string matched by `term1` is also matched by `term2`. | ### Compute | Method | Return | Description | | -------- | ------- | ------- | -| `t.concat(...terms: Term[], opts?)` | `Promise` | Concatenates `t` with the given terms. Supports `responseFormat` and `executionTimeout`. | -| `t.difference(term: Term, opts?)` | `Promise` | Computes the difference `t - term`. Supports `responseFormat` and `executionTimeout`. | -| `t.intersection(...terms: Term[], opts?)` | `Promise` | Computes the intersection of `t` with the given terms. Supports `responseFormat` and `executionTimeout`. | -| `t.repeat(min: number, max?: number, opts?)` | `Promise` | Computes the repetition of the term between `min` and `max` times; if `max` is `null`, the repetition is unbounded. Supports `responseFormat` and `executionTimeout`. | -| `t.union(...terms: Term[], opts?)` | `Promise` | Computes the union of `t` with the given terms. Supports `responseFormat` and `executionTimeout`. | +| `client.complement(term)` | `Promise` | Computes the complement of the given term. | +| `client.concat(terms)` | `Promise` | Concatenates multiple terms in order. | +| `client.difference(term1, term2)` | `Promise` | Computes the difference `term1 - term2`. | +| `client.intersection(terms)` | `Promise` | Computes the intersection of the given terms. | +| `client.repeat(term, min, max)` | `Promise` | Computes the repetition of the term between `min` and `max` times. | +| `client.union(terms)` | `Promise` | Computes the union of the given terms. | ### Generate | Method | Return | Description | | -------- | ------- | ------- | -| `t.generateStrings(count: number, opts?)` | `Promise` | Generates up to `count` unique example strings matched by `t`. Supports `executionTimeout`. | - -### Other -| Method | Return | Description | -| -------- | ------- | ------- | -| `t.serialize()` | `string` | Returns a serialized form of `t`. | -| `Term.deserialize(string: string)` | `Term` | Returns a deserialized term from the given `string`. | +| `client.generateStrings(term, limit, offset)` | `Promise` | Generates up to `limit` unique strings matched by `term`, skipping the first `offset` strings. | ## Cross-Language Support From 3b814efab0eadc000498b1df767595f1a85acb46 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Sun, 29 Mar 2026 19:08:46 +0200 Subject: [PATCH 11/20] Update method calls --- README.md | 16 ++-- src/RegexSolverClient.ts | 194 +++++++++++++++++++++++---------------- 2 files changed, 120 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index ee5a766..c0bd74e 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ const client = new RegexSolverClient({ apiToken: 'YOUR_API_TOKEN' }); const term1 = Term.regex("(abc|de|fg){2,}"); const term2 = Term.regex("de.*"); -const intersection = await client.intersection([term1, term2]); +const intersection = await client.intersection(term1, term2); const pattern = await client.getPattern(intersection); console.log(pattern); // de(abc|de|fg)+ ``` @@ -45,7 +45,7 @@ The API can handle terms in two formats: - `regex`: a regular expression pattern - `fair`: FAIR (Fast Automaton Internal Representation), a stable, signed format used internally by the engine -By default, the engine returns whatever the operation produces, with no extra convertion. Override with `responseFormat`: +By default, the engine returns whatever the operation produces, with no extra convertion. Override with `RequestOptions`: ```javascript import { Term, ResponseFormat } from 'regexsolver'; @@ -53,20 +53,18 @@ import { Term, ResponseFormat } from 'regexsolver'; const term1 = Term.regex('abcde'); const term2 = Term.regex('de'); -const result1 = await client.union([term1, term2], ResponseFormat.REGEX); +const result1 = await client.union(term1, term2, { responseFormat: ResponseFormat.REGEX }); console.log(result1.toString()); // regex=(abc)?de -const result2 = await client.union([term1, term2], ResponseFormat.FAIR); +const result2 = await client.union(term1, term2, { responseFormat: ResponseFormat.FAIR }); console.log(result2.toString()); // fair=... ``` -If the format does not matter, omit `responseFormat` or set it to `ResponseFormat.ANY`. - Regardless of the format, you can always call `getPattern()` to obtain the regex pattern of a term. ## Bounding execution time -Set a server-side compute timeout in milliseconds with `executionTimeout`: +Set a server-side compute timeout in milliseconds with `executionTimeout` in `RequestOptions`: ```javascript import { TimeoutExceededError, Term } from 'regexsolver'; @@ -76,7 +74,7 @@ try { const term1 = Term.regex('.*ab.*c(de|fg).*dab.*c(de|fg).*ab.*c(de|fg).*dab.*c'); const term2 = Term.regex('.*abc.*'); - const res = await client.difference(term1, term2, undefined, 100); + const res = await client.difference(term1, term2, { executionTimeout: 100 }); } catch (error) { if (error instanceof TimeoutExceededError) { console.log(error.message); // The operation took too much time. @@ -88,7 +86,7 @@ Timeout is best effort. The exact time is not guaranteed. ## API Overview -`RegexSolverClient` exposes the following methods. +`RegexSolverClient` exposes the following methods. All methods accept an optional `OperationOptions` object as the last parameter. ### Analyze diff --git a/src/RegexSolverClient.ts b/src/RegexSolverClient.ts index 8fcc8b9..e7d17ab 100644 --- a/src/RegexSolverClient.ts +++ b/src/RegexSolverClient.ts @@ -9,7 +9,7 @@ import { TwoTermsRequest, RepeatRequest, GenerateStringsRequest, - RequestOptions, + RequestOptions as RequestOptionsDto, } from "./generated"; import { Term } from "./models/Term"; import { Cardinality, Infinite, Integer } from "./models/Cardinality"; @@ -25,6 +25,20 @@ export interface RegexSolverConfig { baseUrl?: string; } +/** + * Options to customize the execution of operations. + */ +export interface OperationOptions { + /** + * Return format of the term. + */ + responseFormat?: ResponseFormat | string; + /** + * Timeout in milliseconds for the operation. + */ + executionTimeout?: number; +} + export class RegexSolverClient { private readonly apiToken: string; private readonly analyzeApi: AnalyzeApi; @@ -76,18 +90,15 @@ export class RegexSolverClient { ); } - private buildOptions( - executionTimeout?: number, - responseFormat?: ResponseFormat | string, - ): RequestOptions { - const options: RequestOptions = { schemaVersion: 1 }; - if (executionTimeout !== undefined) { - options.execution = { timeout: executionTimeout }; + private buildOptions(options?: OperationOptions): RequestOptionsDto { + const dto: RequestOptionsDto = { schemaVersion: 1 }; + if (options?.executionTimeout !== undefined) { + dto.execution = { timeout: options.executionTimeout }; } - if (responseFormat !== undefined) { - options.response = { format: responseFormat as any }; + if (options?.responseFormat !== undefined) { + dto.response = { format: options.responseFormat as any }; } - return options; + return dto; } private async executeWithRetry( @@ -220,19 +231,19 @@ export class RegexSolverClient { /** * Computes how many unique strings the term matches. * @param term Target term to analyze. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns Cardinality object. */ public async getCardinality( term: Term, - executionTimeout?: number, + options?: OperationOptions, ): Promise { const cached = term.getCachedCardinality(); if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), - options: this.buildOptions(executionTimeout), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.analyzeApi.cardinality(request), @@ -247,19 +258,19 @@ export class RegexSolverClient { /** * Compute the minimum and maximum length of strings matched by the term. * @param term Target term to analyze. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns Length object. */ public async getLength( term: Term, - executionTimeout?: number, + options?: OperationOptions, ): Promise { const cached = term.getCachedLength(); if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), - options: this.buildOptions(executionTimeout), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.analyzeApi.length(request), @@ -275,17 +286,17 @@ export class RegexSolverClient { * Check if the two terms accept exactly the same language. * @param term1 First term. * @param term2 Second term. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns True if both terms accept the same language. */ public async equivalent( term1: Term, term2: Term, - executionTimeout?: number, + options?: OperationOptions, ): Promise { const request: TwoTermsRequest = { terms: [term1.toDto(), term2.toDto()], - options: this.buildOptions(executionTimeout), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.analyzeApi.equivalent(request), @@ -297,17 +308,17 @@ export class RegexSolverClient { * Check if the first term's language is a subset of the second term's language. * @param subset Candidate subset term. * @param superset Candidate superset term. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns True if subset's language is contained within superset's language. */ public async subset( subset: Term, superset: Term, - executionTimeout?: number, + options?: OperationOptions, ): Promise { const request: TwoTermsRequest = { terms: [subset.toDto(), superset.toDto()], - options: this.buildOptions(executionTimeout), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.analyzeApi.subset(request), @@ -318,19 +329,19 @@ export class RegexSolverClient { /** * Check if the term matches no strings. * @param term Target term to analyze. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns True if language is empty. */ public async isEmpty( term: Term, - executionTimeout?: number, + options?: OperationOptions, ): Promise { const cached = term.getCachedEmpty(); if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), - options: this.buildOptions(executionTimeout), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.analyzeApi.empty(request), @@ -349,19 +360,19 @@ export class RegexSolverClient { /** * Check if the term matches only the empty string. * @param term Target term to analyze. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns True if language contains only the empty string. */ public async isEmptyString( term: Term, - executionTimeout?: number, + options?: OperationOptions, ): Promise { const cached = term.getCachedEmptyString(); if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), - options: this.buildOptions(executionTimeout), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.analyzeApi.emptyString(request), @@ -380,19 +391,19 @@ export class RegexSolverClient { /** * Check if the term matches all the possible strings. * @param term Target term to analyze. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns True if language contains all possible strings. */ public async isTotal( term: Term, - executionTimeout?: number, + options?: OperationOptions, ): Promise { const cached = term.getCachedTotal(); if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), - options: this.buildOptions(executionTimeout), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.analyzeApi.total(request), @@ -411,19 +422,19 @@ export class RegexSolverClient { /** * Return a regular expression pattern that represents the term. * @param term Target term to analyze. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns Regex pattern string. */ public async getPattern( term: Term, - executionTimeout?: number, + options?: OperationOptions, ): Promise { const existingPattern = term.getPattern(); if (existingPattern !== null) return existingPattern; const request: TermRequest = { term: term.toDto(), - options: this.buildOptions(executionTimeout), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.analyzeApi.pattern(request), @@ -437,16 +448,16 @@ export class RegexSolverClient { /** * Build a Graphviz DOT representation of the term's automaton. * @param term Target term to analyze. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns DOT string. */ - public async getDot(term: Term, executionTimeout?: number): Promise { + public async getDot(term: Term, options?: OperationOptions): Promise { const cached = term.getCachedDot(); if (cached !== null) return cached; const request: TermRequest = { term: term.toDto(), - options: this.buildOptions(executionTimeout), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.analyzeApi.dot(request), @@ -461,19 +472,17 @@ export class RegexSolverClient { /** * Concatenate the given terms in order. - * @param terms Array of terms to concatenate. - * @param responseFormat Desired format of the returned term. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param terms Array of terms or variadic terms to concatenate. * @returns New term representing the concatenation. */ - public async concat( - terms: Term[], - responseFormat?: ResponseFormat | string, - executionTimeout?: number, - ): Promise { + public async concat(terms: Term[]): Promise; + public async concat(...terms: Term[]): Promise; + public async concat(terms: Term[], options?: OperationOptions): Promise; + public async concat(...args: any[]): Promise { + const { terms, options } = this.parseArgs(args); const request: MultiTermsRequest = { terms: terms.map((t) => t.toDto()), - options: this.buildOptions(executionTimeout, responseFormat), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.computeApi.concat(request), @@ -483,19 +492,20 @@ export class RegexSolverClient { /** * Computes the intersection of the given terms. - * @param terms Array of terms to intersect. - * @param responseFormat Desired format of the returned term. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param terms Array of terms or variadic terms to intersect. * @returns New term representing the intersection. */ + public async intersection(terms: Term[]): Promise; + public async intersection(...terms: Term[]): Promise; public async intersection( terms: Term[], - responseFormat?: ResponseFormat | string, - executionTimeout?: number, - ): Promise { + options?: OperationOptions, + ): Promise; + public async intersection(...args: any[]): Promise { + const { terms, options } = this.parseArgs(args); const request: MultiTermsRequest = { terms: terms.map((t) => t.toDto()), - options: this.buildOptions(executionTimeout, responseFormat), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.computeApi.intersection(request), @@ -505,19 +515,17 @@ export class RegexSolverClient { /** * Computes the union of the given terms. - * @param terms Array of terms to unite. - * @param responseFormat Desired format of the returned term. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param terms Array of terms or variadic terms to unite. * @returns New term representing the union. */ - public async union( - terms: Term[], - responseFormat?: ResponseFormat | string, - executionTimeout?: number, - ): Promise { + public async union(terms: Term[]): Promise; + public async union(...terms: Term[]): Promise; + public async union(terms: Term[], options?: OperationOptions): Promise; + public async union(...args: any[]): Promise { + const { terms, options } = this.parseArgs(args); const request: MultiTermsRequest = { terms: terms.map((t) => t.toDto()), - options: this.buildOptions(executionTimeout, responseFormat), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.computeApi.union(request), @@ -525,23 +533,51 @@ export class RegexSolverClient { return Term.fromDto(response.data.data); } + private parseArgs(args: any[]): { + terms: Term[]; + options?: OperationOptions; + } { + if (args.length === 0) { + return { terms: [] }; + } + + if (Array.isArray(args[0])) { + return { + terms: args[0], + options: args[1], + }; + } + + // Variadic + // Check if last arg is options object + const lastArg = args[args.length - 1]; + if ( + args.length > 1 && + typeof lastArg === "object" && + lastArg !== null && + !(lastArg instanceof Term) + ) { + return { terms: args.slice(0, -1), options: lastArg }; + } + + return { terms: args }; + } + /** * Computes the difference between the two provided terms. * @param base Term to subtract from. * @param excluded Term to exclude. - * @param responseFormat Desired format of the returned term. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns New term representing the difference. */ public async difference( base: Term, excluded: Term, - responseFormat?: ResponseFormat | string, - executionTimeout?: number, + options?: OperationOptions, ): Promise { const request: TwoTermsRequest = { terms: [base.toDto(), excluded.toDto()], - options: this.buildOptions(executionTimeout, responseFormat), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.computeApi.difference(request), @@ -554,22 +590,20 @@ export class RegexSolverClient { * @param term Term to repeat. * @param min Minimum number of repetitions. * @param max Maximum number of repetitions (optional, unbounded if null). - * @param responseFormat Desired format of the returned term. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns New term representing the repetition. */ public async repeat( term: Term, min: number, max?: number | null, - responseFormat?: ResponseFormat | string, - executionTimeout?: number, + options?: OperationOptions, ): Promise { const request: RepeatRequest = { term: term.toDto(), min, max, - options: this.buildOptions(executionTimeout, responseFormat), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.computeApi.repeat(request), @@ -580,18 +614,16 @@ export class RegexSolverClient { /** * Computes the complement of the given term. * @param term Term to complement. - * @param responseFormat Desired format of the returned term. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns New term representing the complement. */ public async complement( term: Term, - responseFormat?: ResponseFormat | string, - executionTimeout?: number, + options?: OperationOptions, ): Promise { const request: TermRequest = { term: term.toDto(), - options: this.buildOptions(executionTimeout, responseFormat), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => this.computeApi.complement(request), @@ -606,14 +638,14 @@ export class RegexSolverClient { * @param term Source term to generate strings from. * @param limit Maximum number of unique strings to return. * @param offset Number of matched strings to skip before starting to collect the results. Used for pagination. - * @param executionTimeout Timeout in milliseconds for the operation. + * @param options Options object. * @returns Array of unique strings. */ public async generateStrings( term: Term, limit: number, offset: number, - executionTimeout?: number, + options?: OperationOptions, ): Promise { let termToUse = term; let returnStableTerm = false; @@ -630,7 +662,7 @@ export class RegexSolverClient { limit, offset, returnStableTerm, - options: this.buildOptions(executionTimeout), + options: this.buildOptions(options), }; const response = await this.executeWithRetry(() => From d981d7f1d221b770c91198952507fb20c38f38b2 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Mon, 6 Apr 2026 14:54:58 +0200 Subject: [PATCH 12/20] Update library --- README.md | 52 ++++++++++++++++++++++++++++------------------------ package.json | 13 ++++++++++--- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index c0bd74e..2cc1b6a 100644 --- a/README.md +++ b/README.md @@ -19,14 +19,18 @@ Requirements: **Node.js >= 16** ```javascript import { RegexSolverClient, Term } from 'regexsolver'; -const client = new RegexSolverClient({ apiToken: 'YOUR_API_TOKEN' }); +async function main() { + const client = new RegexSolverClient({ apiToken: 'YOUR_API_TOKEN' }); -const term1 = Term.regex("(abc|de|fg){2,}"); -const term2 = Term.regex("de.*"); + const term1 = Term.regex("(abc|de|fg){2,}"); + const term2 = Term.regex("de.*"); -const intersection = await client.intersection(term1, term2); -const pattern = await client.getPattern(intersection); -console.log(pattern); // de(abc|de|fg)+ + const intersection = await client.intersection(term1, term2); + const pattern = await client.getPattern(intersection); + console.log(pattern); // de(abc|de|fg)+ +} + +main(); ``` ## Key Concepts & Limitations @@ -45,7 +49,7 @@ The API can handle terms in two formats: - `regex`: a regular expression pattern - `fair`: FAIR (Fast Automaton Internal Representation), a stable, signed format used internally by the engine -By default, the engine returns whatever the operation produces, with no extra convertion. Override with `RequestOptions`: +By default, the engine returns whatever the operation produces, with no extra convertion. Override with `OperationOptions`: ```javascript import { Term, ResponseFormat } from 'regexsolver'; @@ -64,7 +68,7 @@ Regardless of the format, you can always call `getPattern()` to obtain the regex ## Bounding execution time -Set a server-side compute timeout in milliseconds with `executionTimeout` in `RequestOptions`: +Set a server-side compute timeout in milliseconds with `executionTimeout` in `OperationOptions`: ```javascript import { TimeoutExceededError, Term } from 'regexsolver'; @@ -92,32 +96,32 @@ Timeout is best effort. The exact time is not guaranteed. | Method | Return | Description | | -------- | ------- | ------- | -| `client.equivalent(term1, term2)` | `Promise` | `true` if `term1` and `term2` accept exactly the same language. | -| `client.getCardinality(term)` | `Promise` | Returns the number of possible matched strings. | -| `client.getDot(term)` | `Promise` | Returns a Graphviz DOT representation of the automaton. | -| `client.getLength(term)` | `Promise` | Returns the minimum and maximum length of matched strings. | -| `client.getPattern(term)` | `Promise` | Returns a regular expression pattern for the term. | -| `client.isEmpty(term)` | `Promise` | `true` if the term matches no string. | -| `client.isEmptyString(term)` | `Promise` | `true` if the term matches only the empty string. | -| `client.isTotal(term)` | `Promise` | `true` if the term matches all possible strings. | -| `client.subset(term1, term2)` | `Promise` | `true` if every string matched by `term1` is also matched by `term2`. | +| `client.equivalent(term1, term2, options?)` | `Promise` | `true` if `term1` and `term2` accept exactly the same language. | +| `client.getCardinality(term, options?)` | `Promise` | Returns the number of possible matched strings. | +| `client.getDot(term, options?)` | `Promise` | Returns a Graphviz DOT representation of the automaton. | +| `client.getLength(term, options?)` | `Promise` | Returns the minimum and maximum length of matched strings. | +| `client.getPattern(term, options?)` | `Promise` | Returns a regular expression pattern for the term. | +| `client.isEmpty(term, options?)` | `Promise` | `true` if the term matches no string. | +| `client.isEmptyString(term, options?)` | `Promise` | `true` if the term matches only the empty string. | +| `client.isTotal(term, options?)` | `Promise` | `true` if the term matches all possible strings. | +| `client.subset(term1, term2, options?)` | `Promise` | `true` if every string matched by `term1` is also matched by `term2`. | ### Compute | Method | Return | Description | | -------- | ------- | ------- | -| `client.complement(term)` | `Promise` | Computes the complement of the given term. | -| `client.concat(terms)` | `Promise` | Concatenates multiple terms in order. | -| `client.difference(term1, term2)` | `Promise` | Computes the difference `term1 - term2`. | -| `client.intersection(terms)` | `Promise` | Computes the intersection of the given terms. | -| `client.repeat(term, min, max)` | `Promise` | Computes the repetition of the term between `min` and `max` times. | -| `client.union(terms)` | `Promise` | Computes the union of the given terms. | +| `client.complement(term, options?)` | `Promise` | Computes the complement of the given term. | +| `client.concat(term1, term2, ..., options?)` | `Promise` | Concatenates multiple terms in order. | +| `client.difference(term1, term2, options?)` | `Promise` | Computes the difference `term1 - term2`. | +| `client.intersection(term1, term2, ..., options?)` | `Promise` | Computes the intersection of the given terms. | +| `client.repeat(term, min, max, options?)` | `Promise` | Computes the repetition of the term between `min` and `max` times. | +| `client.union(term1, term2, ..., options?)` | `Promise` | Computes the union of the given terms. | ### Generate | Method | Return | Description | | -------- | ------- | ------- | -| `client.generateStrings(term, limit, offset)` | `Promise` | Generates up to `limit` unique strings matched by `term`, skipping the first `offset` strings. | +| `client.generateStrings(term, limit, offset, options?)` | `Promise` | Generates up to `limit` unique strings matched by `term`, skipping the first `offset` strings. | ## Cross-Language Support diff --git a/package.json b/package.json index 21239fd..86f61ed 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,16 @@ { "name": "regexsolver", "version": "1.1.0", - "main": "lib/index.js", - "typings": "lib/index.d.ts", - "types": "lib/index.d.ts", + "main": "./lib/index.js", + "typings": "./lib/index.d.ts", + "types": "./lib/index.d.ts", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "import": "./lib/index.js", + "require": "./lib/index.js" + } + }, "files": [ "src/", "lib/" From 1e319e2e30edc9b4649d4e9457df2d3a2bf3dc33 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Sun, 12 Apr 2026 21:25:06 +0200 Subject: [PATCH 13/20] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2cc1b6a..05eefea 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Requirements: **Node.js >= 16** import { RegexSolverClient, Term } from 'regexsolver'; async function main() { - const client = new RegexSolverClient({ apiToken: 'YOUR_API_TOKEN' }); + const client = new RegexSolverClient({ apiToken: 'REGEXSOLVER_API_TOKEN' }); const term1 = Term.regex("(abc|de|fg){2,}"); const term2 = Term.regex("de.*"); From eda5ffb27bf419748928d6d8f317105abd61055c Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Mon, 13 Apr 2026 21:13:45 +0200 Subject: [PATCH 14/20] Update possible exceptions --- src/RegexSolverClient.ts | 8 ++++++ src/exceptions.ts | 6 +++++ src/generated/api.ts | 55 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/RegexSolverClient.ts b/src/RegexSolverClient.ts index e7d17ab..e0fb5e1 100644 --- a/src/RegexSolverClient.ts +++ b/src/RegexSolverClient.ts @@ -180,6 +180,14 @@ export class RegexSolverClient { statusCode, bodyString, ); + if (errorCode === "AutomatonTooManyStates") + return new Exceptions.AutomatonTooManyStatesError( + message, + statusCode, + bodyString, + ); + if (errorCode === "RegexSyntaxError") + return new Exceptions.RegexSyntaxError(message, statusCode, bodyString); return new Exceptions.BadRequestError(message, statusCode, bodyString); case 401: if (errorCode === "MissingOrMalformedToken") diff --git a/src/exceptions.ts b/src/exceptions.ts index d1af952..9007fb8 100644 --- a/src/exceptions.ts +++ b/src/exceptions.ts @@ -41,6 +41,12 @@ export class TimeoutExceededError extends BadRequestError {} /** Raised when the requested number of strings to generate is below the minimum or exceeds the maximum allowed. */ export class InvalidNumberOfStringsToGenerate extends BadRequestError {} +/** Raised when the NFA/DFA exceeds the maximum allowed number of states for your current plan. */ +export class AutomatonTooManyStatesError extends BadRequestError {} + +/** Raised when the provided regular expression has invalid syntax. */ +export class RegexSyntaxError extends BadRequestError {} + /** Raised when the API returns a 401 Unauthorized error. */ export class UnauthorizedError extends ApiError {} diff --git a/src/generated/api.ts b/src/generated/api.ts index 01606a0..eaad3dc 100644 --- a/src/generated/api.ts +++ b/src/generated/api.ts @@ -88,9 +88,6 @@ export interface Empty200Response { 'success': boolean; 'data': ModelBoolean; } -/** - * Standard error payload returned when success is false. - */ export interface ErrorResponse { 'success': boolean; /** @@ -102,6 +99,58 @@ export interface ErrorResponse { */ 'errorCode'?: string; } +export interface ErrorResponse400 { + 'success': boolean; + /** + * Human readable error message. + */ + 'error': string; + 'errorCode'?: ErrorResponse400ErrorCodeEnum; +} + +export const ErrorResponse400ErrorCodeEnum = { + InvalidJson: 'InvalidJson', + TooManyTerms: 'TooManyTerms', + TimeoutTooLarge: 'TimeoutTooLarge', + TimeoutExceeded: 'TimeoutExceeded', + InvalidNumberOfStringsToGenerate: 'InvalidNumberOfStringsToGenerate', + AutomatonTooManyStates: 'AutomatonTooManyStates', + RegexSyntaxError: 'RegexSyntaxError', +} as const; + +export type ErrorResponse400ErrorCodeEnum = typeof ErrorResponse400ErrorCodeEnum[keyof typeof ErrorResponse400ErrorCodeEnum]; + +export interface ErrorResponse401 { + 'success': boolean; + /** + * Human readable error message. + */ + 'error': string; + 'errorCode'?: ErrorResponse401ErrorCodeEnum; +} + +export const ErrorResponse401ErrorCodeEnum = { + MissingOrMalformedToken: 'MissingOrMalformedToken', + InvalidToken: 'InvalidToken', +} as const; + +export type ErrorResponse401ErrorCodeEnum = typeof ErrorResponse401ErrorCodeEnum[keyof typeof ErrorResponse401ErrorCodeEnum]; + +export interface ErrorResponse403 { + 'success': boolean; + /** + * Human readable error message. + */ + 'error': string; + 'errorCode'?: ErrorResponse403ErrorCodeEnum; +} + +export const ErrorResponse403ErrorCodeEnum = { + QuotaExceeded: 'QuotaExceeded', +} as const; + +export type ErrorResponse403ErrorCodeEnum = typeof ErrorResponse403ErrorCodeEnum[keyof typeof ErrorResponse403ErrorCodeEnum]; + /** * Change how the engine executes the operation. */ From 354f72d15d6c2a731c86722b867c78ed90537ede Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Sun, 14 Jun 2026 21:54:54 +0200 Subject: [PATCH 15/20] Update endpoints --- README.md | 2 + src/RegexSolverClient.ts | 122 +++++++++++++++----- src/exceptions.ts | 2 +- src/generated/api.ts | 243 ++++++++++++++++++++++++++++++++------- src/models/Term.ts | 32 +++--- tests/client.test.ts | 2 +- tests/term.test.ts | 22 ++-- 7 files changed, 330 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index 05eefea..cc1e972 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Timeout is best effort. The exact time is not guaranteed. | `client.isEmpty(term, options?)` | `Promise` | `true` if the term matches no string. | | `client.isEmptyString(term, options?)` | `Promise` | `true` if the term matches only the empty string. | | `client.isTotal(term, options?)` | `Promise` | `true` if the term matches all possible strings. | +| `client.isDeterministic(term, options?)` | `Promise` | `true` if the term's automaton is deterministic. Only a deterministic FAIR guarantees consistent string ordering across paginated `generateStrings()` calls; call `determinize()` first if this is `false`. | | `client.subset(term1, term2, options?)` | `Promise` | `true` if every string matched by `term1` is also matched by `term2`. | ### Compute @@ -112,6 +113,7 @@ Timeout is best effort. The exact time is not guaranteed. | -------- | ------- | ------- | | `client.complement(term, options?)` | `Promise` | Computes the complement of the given term. | | `client.concat(term1, term2, ..., options?)` | `Promise` | Concatenates multiple terms in order. | +| `client.determinize(term, options?)` | `Promise` | Computes a deterministic FAIR for the given term, suitable for consistent pagination with `generateStrings()`. | | `client.difference(term1, term2, options?)` | `Promise` | Computes the difference `term1 - term2`. | | `client.intersection(term1, term2, ..., options?)` | `Promise` | Computes the intersection of the given terms. | | `client.repeat(term, min, max, options?)` | `Promise` | Computes the repetition of the term between `min` and `max` times. | diff --git a/src/RegexSolverClient.ts b/src/RegexSolverClient.ts index e0fb5e1..7f10c75 100644 --- a/src/RegexSolverClient.ts +++ b/src/RegexSolverClient.ts @@ -11,7 +11,7 @@ import { GenerateStringsRequest, RequestOptions as RequestOptionsDto, } from "./generated"; -import { Term } from "./models/Term"; +import { FairTerm, Term } from "./models/Term"; import { Cardinality, Infinite, Integer } from "./models/Cardinality"; import { Length } from "./models/Length"; import { ResponseFormat } from "./models/ResponseFormat"; @@ -33,6 +33,12 @@ export interface OperationOptions { * Return format of the term. */ responseFormat?: ResponseFormat | string; + /** + * When true, guarantees the returned FAIR encodes a deterministic automaton. + * Only valid with responseFormat=ResponseFormat.FAIR or when responseFormat is + * unset (in which case it defaults to ResponseFormat.FAIR). Throws otherwise. + */ + deterministic?: boolean; /** * Timeout in milliseconds for the operation. */ @@ -95,8 +101,30 @@ export class RegexSolverClient { if (options?.executionTimeout !== undefined) { dto.execution = { timeout: options.executionTimeout }; } - if (options?.responseFormat !== undefined) { - dto.response = { format: options.responseFormat as any }; + + const { responseFormat, deterministic } = options ?? {}; + if (deterministic !== undefined && responseFormat !== undefined) { + const fmt = String(responseFormat); + if (fmt !== ResponseFormat.FAIR) { + throw new Error( + `deterministic can only be used with responseFormat=ResponseFormat.FAIR, got ${JSON.stringify(responseFormat)}`, + ); + } + } + + if (responseFormat !== undefined || deterministic !== undefined) { + dto.response = {}; + if (responseFormat !== undefined) { + dto.response.format = responseFormat as any; + } + if (deterministic !== undefined) { + dto.response.fair = { deterministic }; + if (responseFormat === undefined) { + // FairResponseOptions is only applied when the response format is + // "fair", so default to it to honor the deterministic request. + dto.response.format = ResponseFormat.FAIR as any; + } + } } return dto; } @@ -175,7 +203,7 @@ export class RegexSolverClient { bodyString, ); if (errorCode === "InvalidNumberOfStringsToGenerate") - return new Exceptions.InvalidNumberOfStringsToGenerate( + return new Exceptions.InvalidNumberOfStringsToGenerateError( message, statusCode, bodyString, @@ -187,7 +215,11 @@ export class RegexSolverClient { bodyString, ); if (errorCode === "RegexSyntaxError") - return new Exceptions.RegexSyntaxError(message, statusCode, bodyString); + return new Exceptions.RegexSyntaxError( + message, + statusCode, + bodyString, + ); return new Exceptions.BadRequestError(message, statusCode, bodyString); case 401: if (errorCode === "MissingOrMalformedToken") @@ -427,6 +459,37 @@ export class RegexSolverClient { return isTotal; } + /** + * Check if the term's automaton is deterministic. + * Only a deterministic FAIR guarantees consistent string ordering across paginated generateStrings() calls; call determinize() first if this is false. + * @param term The term to analyze. + * @param options Options object. + * @returns True if the term's automaton is deterministic. + */ + public async isDeterministic( + term: Term, + options?: OperationOptions, + ): Promise { + if (!(term instanceof FairTerm)) { + return false; + } + const cached = term.getCachedDeterministic(); + if (cached !== null) return cached; + + const request: TermRequest = { + term: term.toDto(), + options: this.buildOptions(options), + }; + const response = await this.executeWithRetry(() => + this.analyzeApi.deterministic(request), + ); + + const isDeterministic = response.data.data.value; + term.setCachedDeterministic(isDeterministic); + + return isDeterministic; + } + /** * Return a regular expression pattern that represents the term. * @param term Target term to analyze. @@ -572,7 +635,7 @@ export class RegexSolverClient { } /** - * Computes the difference between the two provided terms. + * Computes the difference between the two given terms. * @param base Term to subtract from. * @param excluded Term to exclude. * @param options Options object. @@ -594,7 +657,7 @@ export class RegexSolverClient { } /** - * Repeat a term between 'min' and 'max' times. + * Repeat a term between `min` and `max` times. * @param term Term to repeat. * @param min Minimum number of repetitions. * @param max Maximum number of repetitions (optional, unbounded if null). @@ -639,10 +702,33 @@ export class RegexSolverClient { return Term.fromDto(response.data.data); } + /** + * Computes a deterministic FAIR automaton from the given term. + * A deterministic FAIR guarantees consistent string ordering across paginated + * generateStrings() calls. Use this when isDeterministic() is false + * before calling generateStrings() with an offset. + * @param term Term to determinize. + * @param options Options object. + * @returns A deterministic FAIR. + */ + public async determinize( + term: Term, + options?: OperationOptions, + ): Promise { + const request: TermRequest = { + term: term.toDto(), + options: this.buildOptions(options), + }; + const response = await this.executeWithRetry(() => + this.computeApi.determinize(request), + ); + return Term.fromDto(response.data.data); + } + // --- GENERATE OPERATIONS --- /** - * Generates up to `limit` distinct strings matched by 'term', skipping the first 'offset' strings. + * Generates up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. * @param term Source term to generate strings from. * @param limit Maximum number of unique strings to return. * @param offset Number of matched strings to skip before starting to collect the results. Used for pagination. @@ -655,21 +741,10 @@ export class RegexSolverClient { offset: number, options?: OperationOptions, ): Promise { - let termToUse = term; - let returnStableTerm = false; - - const stableTerm = term.getCachedStableTerm(); - if (stableTerm !== null) { - termToUse = stableTerm; - } else { - returnStableTerm = true; - } - const request: GenerateStringsRequest = { - term: termToUse.toDto(), + term: term.toDto(), limit, offset, - returnStableTerm, options: this.buildOptions(options), }; @@ -677,11 +752,6 @@ export class RegexSolverClient { this.generateApi.strings(request), ); - const data = response.data.data; - if (data.term) { - term.setCachedStableTerm(Term.fromDto(data.term)); - } - - return data.strings.value; + return response.data.data.strings.value; } } diff --git a/src/exceptions.ts b/src/exceptions.ts index 9007fb8..2a94a52 100644 --- a/src/exceptions.ts +++ b/src/exceptions.ts @@ -39,7 +39,7 @@ export class TimeoutTooLargeError extends BadRequestError {} export class TimeoutExceededError extends BadRequestError {} /** Raised when the requested number of strings to generate is below the minimum or exceeds the maximum allowed. */ -export class InvalidNumberOfStringsToGenerate extends BadRequestError {} +export class InvalidNumberOfStringsToGenerateError extends BadRequestError {} /** Raised when the NFA/DFA exceeds the maximum allowed number of states for your current plan. */ export class AutomatonTooManyStatesError extends BadRequestError {} diff --git a/src/generated/api.ts b/src/generated/api.ts index eaad3dc..559181a 100644 --- a/src/generated/api.ts +++ b/src/generated/api.ts @@ -161,7 +161,16 @@ export interface ExecutionOptions { 'timeout'?: number; } /** - * Request to generate up to \'limit\' distinct strings matched by \'term\', skipping the first \'offset\' strings. + * Options controlling the FAIR output. Only applied when response format is \"fair\". + */ +export interface FairResponseOptions { + /** + * When true, the returned FAIR is guaranteed to be a deterministic automaton, suitable for consistent pagination with /generate/strings. + */ + 'deterministic'?: boolean; +} +/** + * Request to generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. For consistent pagination, `term` should be deterministic. */ export interface GenerateStringsRequest { /** @@ -176,21 +185,13 @@ export interface GenerateStringsRequest { * Number of matched strings to skip before starting to collect the results. Used for pagination. */ 'offset': number; - /** - * If set to true, a stable term is returned. This term can be reused in subsequent calls to guarantee no strings are repeated. If the provided term is already stable, it will not be returned. - */ - 'returnStableTerm'?: boolean; 'options'?: RequestOptions; } /** - * Response containing distinct strings generated from the requested \'term\'. + * Response containing distinct strings generated from the requested `term`. */ export interface GenerateStringsResponse { 'type': GenerateStringsResponseTypeEnum; - /** - * A stable term to use in subsequent calls to guarantee the uniqueness of generated strings. Omitted if \'returnStableTerm\' was false in the request, or if the provided term was already stable. - */ - 'term'?: Term; /** * The generated distinct strings. */ @@ -273,7 +274,7 @@ export interface MultiTermsRequest { 'options'?: RequestOptions; } /** - * Request to repeat a term between \'min\' and \'max\' times. + * Request to repeat a term between `min` and `max` times. */ export interface RepeatRequest { /** @@ -291,7 +292,7 @@ export interface RepeatRequest { 'options'?: RequestOptions; } /** - * Change how the engine handle the operation. + * Change how the engine handles the operation. */ export interface RequestOptions { /** @@ -309,6 +310,10 @@ export interface ResponseOptions { * Return format of the term. */ 'format'?: ResponseOptionsFormatEnum; + /** + * Options applied when format is \"fair\". Ignored otherwise. + */ + 'fair'?: FairResponseOptions; } export const ResponseOptionsFormatEnum = { @@ -355,6 +360,7 @@ export interface TermFair { * FAIR payload. */ 'value': string; + 'metadata'?: TermFairMetadata; } export const TermFairTypeEnum = { @@ -363,6 +369,15 @@ export const TermFairTypeEnum = { export type TermFairTypeEnum = typeof TermFairTypeEnum[keyof typeof TermFairTypeEnum]; +/** + * Metadata describing properties of a FAIR automaton. + */ +export interface TermFairMetadata { + /** + * Whether this FAIR encodes a deterministic automaton. Only a deterministic FAIR guarantees consistent string ordering across paginated /generate/strings requests; call /compute/determinize first if this is false. + */ + 'deterministic'?: boolean; +} /** * Term encoded as a regular expression pattern. */ @@ -381,7 +396,7 @@ export const TermRegexTypeEnum = { export type TermRegexTypeEnum = typeof TermRegexTypeEnum[keyof typeof TermRegexTypeEnum]; /** - * Request a single term. + * Request carrying a single term. */ export interface TermRequest { 'term': Term; @@ -442,9 +457,48 @@ export const AnalyzeApiAxiosParamCreator = function (configuration?: Configurati options: localVarRequestOptions, }; }, + /** + * Check if the term\'s automaton is deterministic. Only a deterministic FAIR guarantees consistent string ordering across paginated /generate/strings requests; call /compute/determinize first if this is false. + * @summary Deterministic + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deterministic: async (termRequest: TermRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'termRequest' is not null or undefined + assertParamExists('deterministic', 'termRequest', termRequest) + const localVarPath = `/analyze/deterministic`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(termRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * Build a Graphviz DOT representation of the term\'s automaton. - * @summary GraphViz Dot + * @summary Graphviz DOT * @param {TermRequest} termRequest * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -776,9 +830,22 @@ export const AnalyzeApiFp = function(configuration?: Configuration) { const localVarOperationServerBasePath = operationServerMap['AnalyzeApi.cardinality']?.[localVarOperationServerIndex]?.url; return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, + /** + * Check if the term\'s automaton is deterministic. Only a deterministic FAIR guarantees consistent string ordering across paginated /generate/strings requests; call /compute/determinize first if this is false. + * @summary Deterministic + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deterministic(termRequest: TermRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.deterministic(termRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AnalyzeApi.deterministic']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, /** * Build a Graphviz DOT representation of the term\'s automaton. - * @summary GraphViz Dot + * @summary Graphviz DOT * @param {TermRequest} termRequest * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -899,9 +966,19 @@ export const AnalyzeApiFactory = function (configuration?: Configuration, basePa cardinality(termRequest: TermRequest, options?: RawAxiosRequestConfig): AxiosPromise { return localVarFp.cardinality(termRequest, options).then((request) => request(axios, basePath)); }, + /** + * Check if the term\'s automaton is deterministic. Only a deterministic FAIR guarantees consistent string ordering across paginated /generate/strings requests; call /compute/determinize first if this is false. + * @summary Deterministic + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deterministic(termRequest: TermRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.deterministic(termRequest, options).then((request) => request(axios, basePath)); + }, /** * Build a Graphviz DOT representation of the term\'s automaton. - * @summary GraphViz Dot + * @summary Graphviz DOT * @param {TermRequest} termRequest * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -997,9 +1074,20 @@ export class AnalyzeApi extends BaseAPI { return AnalyzeApiFp(this.configuration).cardinality(termRequest, options).then((request) => request(this.axios, this.basePath)); } + /** + * Check if the term\'s automaton is deterministic. Only a deterministic FAIR guarantees consistent string ordering across paginated /generate/strings requests; call /compute/determinize first if this is false. + * @summary Deterministic + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public deterministic(termRequest: TermRequest, options?: RawAxiosRequestConfig) { + return AnalyzeApiFp(this.configuration).deterministic(termRequest, options).then((request) => request(this.axios, this.basePath)); + } + /** * Build a Graphviz DOT representation of the term\'s automaton. - * @summary GraphViz Dot + * @summary Graphviz DOT * @param {TermRequest} termRequest * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -1094,7 +1182,7 @@ export class AnalyzeApi extends BaseAPI { export const ComputeApiAxiosParamCreator = function (configuration?: Configuration) { return { /** - * Computes the complement of the given term. + * Compute the complement of the given term. * @summary Complement * @param {TermRequest} termRequest * @param {*} [options] Override http request option. @@ -1172,7 +1260,46 @@ export const ComputeApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * Computes the difference between the two provided terms. + * Compute a deterministic FAIR. + * @summary Determinize + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + determinize: async (termRequest: TermRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'termRequest' is not null or undefined + assertParamExists('determinize', 'termRequest', termRequest) + const localVarPath = `/compute/determinize`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(termRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Compute the difference between the two given terms. * @summary Difference * @param {TwoTermsRequest} twoTermsRequest * @param {*} [options] Override http request option. @@ -1211,7 +1338,7 @@ export const ComputeApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * Computes the intersection of the given terms. + * Compute the intersection of the given terms. * @summary Intersection * @param {MultiTermsRequest} multiTermsRequest * @param {*} [options] Override http request option. @@ -1250,7 +1377,7 @@ export const ComputeApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * Repeat a term between \'min\' and \'max\' times. + * Repeat a term between `min` and `max` times. * @summary Repeat * @param {RepeatRequest} repeatRequest * @param {*} [options] Override http request option. @@ -1289,7 +1416,7 @@ export const ComputeApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * Computes the union of the given terms. + * Compute the union of the given terms. * @summary Union * @param {MultiTermsRequest} multiTermsRequest * @param {*} [options] Override http request option. @@ -1337,7 +1464,7 @@ export const ComputeApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = ComputeApiAxiosParamCreator(configuration) return { /** - * Computes the complement of the given term. + * Compute the complement of the given term. * @summary Complement * @param {TermRequest} termRequest * @param {*} [options] Override http request option. @@ -1363,7 +1490,20 @@ export const ComputeApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Computes the difference between the two provided terms. + * Compute a deterministic FAIR. + * @summary Determinize + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async determinize(termRequest: TermRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.determinize(termRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ComputeApi.determinize']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Compute the difference between the two given terms. * @summary Difference * @param {TwoTermsRequest} twoTermsRequest * @param {*} [options] Override http request option. @@ -1376,7 +1516,7 @@ export const ComputeApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Computes the intersection of the given terms. + * Compute the intersection of the given terms. * @summary Intersection * @param {MultiTermsRequest} multiTermsRequest * @param {*} [options] Override http request option. @@ -1389,7 +1529,7 @@ export const ComputeApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Repeat a term between \'min\' and \'max\' times. + * Repeat a term between `min` and `max` times. * @summary Repeat * @param {RepeatRequest} repeatRequest * @param {*} [options] Override http request option. @@ -1402,7 +1542,7 @@ export const ComputeApiFp = function(configuration?: Configuration) { return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Computes the union of the given terms. + * Compute the union of the given terms. * @summary Union * @param {MultiTermsRequest} multiTermsRequest * @param {*} [options] Override http request option. @@ -1424,7 +1564,7 @@ export const ComputeApiFactory = function (configuration?: Configuration, basePa const localVarFp = ComputeApiFp(configuration) return { /** - * Computes the complement of the given term. + * Compute the complement of the given term. * @summary Complement * @param {TermRequest} termRequest * @param {*} [options] Override http request option. @@ -1444,7 +1584,17 @@ export const ComputeApiFactory = function (configuration?: Configuration, basePa return localVarFp.concat(multiTermsRequest, options).then((request) => request(axios, basePath)); }, /** - * Computes the difference between the two provided terms. + * Compute a deterministic FAIR. + * @summary Determinize + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + determinize(termRequest: TermRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.determinize(termRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Compute the difference between the two given terms. * @summary Difference * @param {TwoTermsRequest} twoTermsRequest * @param {*} [options] Override http request option. @@ -1454,7 +1604,7 @@ export const ComputeApiFactory = function (configuration?: Configuration, basePa return localVarFp.difference(twoTermsRequest, options).then((request) => request(axios, basePath)); }, /** - * Computes the intersection of the given terms. + * Compute the intersection of the given terms. * @summary Intersection * @param {MultiTermsRequest} multiTermsRequest * @param {*} [options] Override http request option. @@ -1464,7 +1614,7 @@ export const ComputeApiFactory = function (configuration?: Configuration, basePa return localVarFp.intersection(multiTermsRequest, options).then((request) => request(axios, basePath)); }, /** - * Repeat a term between \'min\' and \'max\' times. + * Repeat a term between `min` and `max` times. * @summary Repeat * @param {RepeatRequest} repeatRequest * @param {*} [options] Override http request option. @@ -1474,7 +1624,7 @@ export const ComputeApiFactory = function (configuration?: Configuration, basePa return localVarFp.repeat(repeatRequest, options).then((request) => request(axios, basePath)); }, /** - * Computes the union of the given terms. + * Compute the union of the given terms. * @summary Union * @param {MultiTermsRequest} multiTermsRequest * @param {*} [options] Override http request option. @@ -1491,7 +1641,7 @@ export const ComputeApiFactory = function (configuration?: Configuration, basePa */ export class ComputeApi extends BaseAPI { /** - * Computes the complement of the given term. + * Compute the complement of the given term. * @summary Complement * @param {TermRequest} termRequest * @param {*} [options] Override http request option. @@ -1513,7 +1663,18 @@ export class ComputeApi extends BaseAPI { } /** - * Computes the difference between the two provided terms. + * Compute a deterministic FAIR. + * @summary Determinize + * @param {TermRequest} termRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public determinize(termRequest: TermRequest, options?: RawAxiosRequestConfig) { + return ComputeApiFp(this.configuration).determinize(termRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Compute the difference between the two given terms. * @summary Difference * @param {TwoTermsRequest} twoTermsRequest * @param {*} [options] Override http request option. @@ -1524,7 +1685,7 @@ export class ComputeApi extends BaseAPI { } /** - * Computes the intersection of the given terms. + * Compute the intersection of the given terms. * @summary Intersection * @param {MultiTermsRequest} multiTermsRequest * @param {*} [options] Override http request option. @@ -1535,7 +1696,7 @@ export class ComputeApi extends BaseAPI { } /** - * Repeat a term between \'min\' and \'max\' times. + * Repeat a term between `min` and `max` times. * @summary Repeat * @param {RepeatRequest} repeatRequest * @param {*} [options] Override http request option. @@ -1546,7 +1707,7 @@ export class ComputeApi extends BaseAPI { } /** - * Computes the union of the given terms. + * Compute the union of the given terms. * @summary Union * @param {MultiTermsRequest} multiTermsRequest * @param {*} [options] Override http request option. @@ -1565,7 +1726,7 @@ export class ComputeApi extends BaseAPI { export const GenerateApiAxiosParamCreator = function (configuration?: Configuration) { return { /** - * Generates up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. + * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. * @summary Strings * @param {GenerateStringsRequest} generateStringsRequest * @param {*} [options] Override http request option. @@ -1613,7 +1774,7 @@ export const GenerateApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = GenerateApiAxiosParamCreator(configuration) return { /** - * Generates up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. + * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. * @summary Strings * @param {GenerateStringsRequest} generateStringsRequest * @param {*} [options] Override http request option. @@ -1635,7 +1796,7 @@ export const GenerateApiFactory = function (configuration?: Configuration, baseP const localVarFp = GenerateApiFp(configuration) return { /** - * Generates up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. + * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. * @summary Strings * @param {GenerateStringsRequest} generateStringsRequest * @param {*} [options] Override http request option. @@ -1652,7 +1813,7 @@ export const GenerateApiFactory = function (configuration?: Configuration, baseP */ export class GenerateApi extends BaseAPI { /** - * Generates up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. + * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. * @summary Strings * @param {GenerateStringsRequest} generateStringsRequest * @param {*} [options] Override http request option. diff --git a/src/models/Term.ts b/src/models/Term.ts index 23bc0e9..6fecde3 100644 --- a/src/models/Term.ts +++ b/src/models/Term.ts @@ -17,7 +17,6 @@ export abstract class Term { private _total: boolean | null = null; protected _pattern: string | null = null; private _dot: string | null = null; - private _stableTerm: Term | null = null; private _compiledRegex: RegExp | null = null; @@ -35,7 +34,7 @@ export abstract class Term { } public static fair(payload: string): Term { - return new FairTerm(payload); + return new FairTerm(payload, null); } // --- Shared Behavior --- @@ -61,7 +60,7 @@ export abstract class Term { } } - public isMatch(str: string): boolean { + public matches(str: string): boolean { const pattern = this.getPattern(); if (pattern === null) { throw new Error( @@ -105,7 +104,7 @@ export abstract class Term { if (dto.type === "regex") { return Term.regex(dto.value); } else { - return Term.fair(dto.value); + return new FairTerm(dto.value, dto.metadata?.deterministic ?? null); } } @@ -160,13 +159,6 @@ export abstract class Term { this._dot = dot; } - public getCachedStableTerm(): Term | null { - return this._stableTerm; - } - public setCachedStableTerm(stableTerm: Term | null): void { - this._stableTerm = stableTerm; - } - public equals(other: any): boolean { if (this === other) return true; if (!(other instanceof Term)) return false; @@ -188,8 +180,7 @@ export class RegexTerm extends Term { } public getFair(): string | null { - const stable = this.getCachedStableTerm(); - return stable ? stable.getFair() : null; + return null; } public toDto(): TermDto { @@ -202,12 +193,15 @@ export class RegexTerm extends Term { } export class FairTerm extends Term { - constructor(value: string) { + private _deterministic: boolean | null = null; + + constructor(value: string, deterministic: boolean | null) { super(value); + this._deterministic = deterministic; } public getPattern(): string | null { - return this._pattern; // Accesses the protected property of the abstract class + return this._pattern; } public getFair(): string | null { @@ -221,4 +215,12 @@ export class FairTerm extends Term { public serialize(): string { return "fair=" + this.getValue(); } + + public getCachedDeterministic(): boolean | null { + return this._deterministic; + } + + public setCachedDeterministic(deterministic: boolean | null) { + this._deterministic = deterministic; + } } diff --git a/tests/client.test.ts b/tests/client.test.ts index ca337bc..4195995 100644 --- a/tests/client.test.ts +++ b/tests/client.test.ts @@ -304,7 +304,7 @@ describe("RegexSolverClient", () => { await expect( client.generateStrings(Term.regex("a"), 10, 0), - ).rejects.toThrow(Exceptions.InvalidNumberOfStringsToGenerate); + ).rejects.toThrow(Exceptions.InvalidNumberOfStringsToGenerateError); }); test("error mapping for 401 Unauthorized - Missing or Malformed Token", async () => { diff --git a/tests/term.test.ts b/tests/term.test.ts index 55f3c3c..6032c0d 100644 --- a/tests/term.test.ts +++ b/tests/term.test.ts @@ -25,27 +25,27 @@ describe("Term", () => { expect(fairTerm.getPattern()).toBe("abc"); }); - test("isMatch should work for regex terms", () => { + test("matches should work for regex terms", () => { const term = Term.regex("[a-z]+"); - expect(term.isMatch("abc")).toBe(true); - expect(term.isMatch("123")).toBe(false); - expect(term.isMatch("ABC")).toBe(false); + expect(term.matches("abc")).toBe(true); + expect(term.matches("123")).toBe(false); + expect(term.matches("ABC")).toBe(false); }); - test("isMatch should work with dotAll equivalent", () => { + test("matches should work with dotAll equivalent", () => { const term = Term.regex(".+"); - expect(term.isMatch("abc\ndef")).toBe(true); + expect(term.matches("abc\ndef")).toBe(true); }); - test("isMatch should be anchored", () => { + test("matches should be anchored", () => { const term = Term.regex("abc"); - expect(term.isMatch("abcd")).toBe(false); - expect(term.isMatch("xabc")).toBe(false); + expect(term.matches("abcd")).toBe(false); + expect(term.matches("xabc")).toBe(false); }); - test("isMatch should throw for fair terms without cached pattern", () => { + test("matches should throw for fair terms without cached pattern", () => { const term = Term.fair("payload"); - expect(() => term.isMatch("abc")).toThrow( + expect(() => term.matches("abc")).toThrow( "The regex pattern of this term is not defined yet", ); }); From 13cbc78b9a0f3e1979cf37b966546cdb512fbff9 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Mon, 15 Jun 2026 21:10:16 +0200 Subject: [PATCH 16/20] Add new error --- src/RegexSolverClient.ts | 6 ++++++ src/exceptions.ts | 3 +++ src/generated/api.ts | 1 + 3 files changed, 10 insertions(+) diff --git a/src/RegexSolverClient.ts b/src/RegexSolverClient.ts index 7f10c75..e7801b6 100644 --- a/src/RegexSolverClient.ts +++ b/src/RegexSolverClient.ts @@ -220,6 +220,12 @@ export class RegexSolverClient { statusCode, bodyString, ); + if (errorCode === "FairSyntaxError") + return new Exceptions.FairSyntaxError( + message, + statusCode, + bodyString, + ); return new Exceptions.BadRequestError(message, statusCode, bodyString); case 401: if (errorCode === "MissingOrMalformedToken") diff --git a/src/exceptions.ts b/src/exceptions.ts index 2a94a52..c908caa 100644 --- a/src/exceptions.ts +++ b/src/exceptions.ts @@ -47,6 +47,9 @@ export class AutomatonTooManyStatesError extends BadRequestError {} /** Raised when the provided regular expression has invalid syntax. */ export class RegexSyntaxError extends BadRequestError {} +/** Raised when the provided FAIR value is malformed or cannot be decoded. */ +export class FairSyntaxError extends BadRequestError {} + /** Raised when the API returns a 401 Unauthorized error. */ export class UnauthorizedError extends ApiError {} diff --git a/src/generated/api.ts b/src/generated/api.ts index 559181a..387b756 100644 --- a/src/generated/api.ts +++ b/src/generated/api.ts @@ -116,6 +116,7 @@ export const ErrorResponse400ErrorCodeEnum = { InvalidNumberOfStringsToGenerate: 'InvalidNumberOfStringsToGenerate', AutomatonTooManyStates: 'AutomatonTooManyStates', RegexSyntaxError: 'RegexSyntaxError', + FairSyntaxError: 'FairSyntaxError', } as const; export type ErrorResponse400ErrorCodeEnum = typeof ErrorResponse400ErrorCodeEnum[keyof typeof ErrorResponse400ErrorCodeEnum]; From e1296301e44b630ae5934361373bcaf2e76d5dcd Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Sat, 25 Jul 2026 17:51:20 +0200 Subject: [PATCH 17/20] Fix some issues Signed-off-by: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> --- README.md | 4 ++-- src/RegexSolverClient.ts | 52 ++++++++++++++++++++++++++-------------- src/exceptions.ts | 3 +++ src/generated/api.ts | 1 + src/models/Term.ts | 9 +++++++ 5 files changed, 49 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index cc1e972..f3ccc3f 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ The API can handle terms in two formats: - `regex`: a regular expression pattern - `fair`: FAIR (Fast Automaton Internal Representation), a stable, signed format used internally by the engine -By default, the engine returns whatever the operation produces, with no extra convertion. Override with `OperationOptions`: +By default, the engine returns whatever the operation produces, with no extra convertion. Override with `OperationOptions`, accepted by the operations that return a term: ```javascript import { Term, ResponseFormat } from 'regexsolver'; @@ -90,7 +90,7 @@ Timeout is best effort. The exact time is not guaranteed. ## API Overview -`RegexSolverClient` exposes the following methods. All methods accept an optional `OperationOptions` object as the last parameter. +`RegexSolverClient` exposes the following methods. Every method accepts an optional options object as its last parameter: operations that return a term take `OperationOptions` (`responseFormat`, `deterministic`, `executionTimeout`), while analyze operations and `determinize()` take `ExecutionOptions` (`executionTimeout` only) — the response format is not theirs to choose. ### Analyze diff --git a/src/RegexSolverClient.ts b/src/RegexSolverClient.ts index e7801b6..d49d5a0 100644 --- a/src/RegexSolverClient.ts +++ b/src/RegexSolverClient.ts @@ -26,9 +26,23 @@ export interface RegexSolverConfig { } /** - * Options to customize the execution of operations. + * Options accepted by every operation. */ -export interface OperationOptions { +export interface ExecutionOptions { + /** + * Timeout in milliseconds for the operation. + */ + executionTimeout?: number; +} + +/** + * Options for operations that return a term. + * + * Analyze operations and determinize() take {@link ExecutionOptions} instead: + * they do not return a caller-shaped term, so responseFormat and deterministic + * would have no effect there. + */ +export interface OperationOptions extends ExecutionOptions { /** * Return format of the term. */ @@ -39,10 +53,6 @@ export interface OperationOptions { * unset (in which case it defaults to ResponseFormat.FAIR). Throws otherwise. */ deterministic?: boolean; - /** - * Timeout in milliseconds for the operation. - */ - executionTimeout?: number; } export class RegexSolverClient { @@ -190,6 +200,12 @@ export class RegexSolverClient { statusCode, bodyString, ); + if (errorCode === "TooFewTerms") + return new Exceptions.TooFewTermsError( + message, + statusCode, + bodyString, + ); if (errorCode === "TimeoutTooLarge") return new Exceptions.TimeoutTooLargeError( message, @@ -282,7 +298,7 @@ export class RegexSolverClient { */ public async getCardinality( term: Term, - options?: OperationOptions, + options?: ExecutionOptions, ): Promise { const cached = term.getCachedCardinality(); if (cached !== null) return cached; @@ -309,7 +325,7 @@ export class RegexSolverClient { */ public async getLength( term: Term, - options?: OperationOptions, + options?: ExecutionOptions, ): Promise { const cached = term.getCachedLength(); if (cached !== null) return cached; @@ -338,7 +354,7 @@ export class RegexSolverClient { public async equivalent( term1: Term, term2: Term, - options?: OperationOptions, + options?: ExecutionOptions, ): Promise { const request: TwoTermsRequest = { terms: [term1.toDto(), term2.toDto()], @@ -360,7 +376,7 @@ export class RegexSolverClient { public async subset( subset: Term, superset: Term, - options?: OperationOptions, + options?: ExecutionOptions, ): Promise { const request: TwoTermsRequest = { terms: [subset.toDto(), superset.toDto()], @@ -380,7 +396,7 @@ export class RegexSolverClient { */ public async isEmpty( term: Term, - options?: OperationOptions, + options?: ExecutionOptions, ): Promise { const cached = term.getCachedEmpty(); if (cached !== null) return cached; @@ -411,7 +427,7 @@ export class RegexSolverClient { */ public async isEmptyString( term: Term, - options?: OperationOptions, + options?: ExecutionOptions, ): Promise { const cached = term.getCachedEmptyString(); if (cached !== null) return cached; @@ -442,7 +458,7 @@ export class RegexSolverClient { */ public async isTotal( term: Term, - options?: OperationOptions, + options?: ExecutionOptions, ): Promise { const cached = term.getCachedTotal(); if (cached !== null) return cached; @@ -474,7 +490,7 @@ export class RegexSolverClient { */ public async isDeterministic( term: Term, - options?: OperationOptions, + options?: ExecutionOptions, ): Promise { if (!(term instanceof FairTerm)) { return false; @@ -504,9 +520,9 @@ export class RegexSolverClient { */ public async getPattern( term: Term, - options?: OperationOptions, + options?: ExecutionOptions, ): Promise { - const existingPattern = term.getPattern(); + const existingPattern = term.getCachedPattern(); if (existingPattern !== null) return existingPattern; const request: TermRequest = { @@ -528,7 +544,7 @@ export class RegexSolverClient { * @param options Options object. * @returns DOT string. */ - public async getDot(term: Term, options?: OperationOptions): Promise { + public async getDot(term: Term, options?: ExecutionOptions): Promise { const cached = term.getCachedDot(); if (cached !== null) return cached; @@ -719,7 +735,7 @@ export class RegexSolverClient { */ public async determinize( term: Term, - options?: OperationOptions, + options?: ExecutionOptions, ): Promise { const request: TermRequest = { term: term.toDto(), diff --git a/src/exceptions.ts b/src/exceptions.ts index c908caa..0681d9d 100644 --- a/src/exceptions.ts +++ b/src/exceptions.ts @@ -32,6 +32,9 @@ export class InvalidJsonError extends BadRequestError {} /** Raised when the number of terms provided exceeds the maximum allowed. */ export class TooManyTermsError extends BadRequestError {} +/** Raised when fewer terms are provided than the operation requires. */ +export class TooFewTermsError extends BadRequestError {} + /** Raised when the requested `execution_timeout` exceeds the maximum allowed for your current plan. */ export class TimeoutTooLargeError extends BadRequestError {} diff --git a/src/generated/api.ts b/src/generated/api.ts index 387b756..0c04769 100644 --- a/src/generated/api.ts +++ b/src/generated/api.ts @@ -111,6 +111,7 @@ export interface ErrorResponse400 { export const ErrorResponse400ErrorCodeEnum = { InvalidJson: 'InvalidJson', TooManyTerms: 'TooManyTerms', + TooFewTerms: 'TooFewTerms', TimeoutTooLarge: 'TimeoutTooLarge', TimeoutExceeded: 'TimeoutExceeded', InvalidNumberOfStringsToGenerate: 'InvalidNumberOfStringsToGenerate', diff --git a/src/models/Term.ts b/src/models/Term.ts index 6fecde3..b5d5f9d 100644 --- a/src/models/Term.ts +++ b/src/models/Term.ts @@ -3,6 +3,9 @@ import { Cardinality } from "./Cardinality"; import { Length } from "./Length"; import { TermPropertiesMixin } from "./TermPropertiesMixin"; +/** How the engine renders a language that matches no string at all. */ +const EMPTY_LANGUAGE_PATTERN = "[]"; + /** * Represents a mathematical term (Regex or FAIR) on which operations can be performed. */ @@ -68,6 +71,12 @@ export abstract class Term { ); } + // The engine renders the empty language as "[]". By definition it matches + // nothing, and other engines reject the pattern outright. + if (pattern === EMPTY_LANGUAGE_PATTERN) { + return false; + } + if (this._compiledRegex === null) { try { this._compiledRegex = new RegExp(`^(?:${pattern})$`, "s"); From 95c835ccb31370b97ad33afb9c1b6462797dce95 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:15:06 +0200 Subject: [PATCH 18/20] Align repo with the Java and Python SDKs - js.yml -> ci.yml, named CI, matrix job names and named steps matching the other two SDKs; dropped the REGEXSOLVER_API_TOKEN secret, the tests are mocked with axios-mock-adapter and never call the API - publish.yml: named jobs, npm cache on both, tests before publishing - README claimed Node.js >= 16 while CI's floor is 18; aligned both and added the matching engines field to package.json - README: added the ResponseFormat.ANY note the Python README already had, fixed a typo - .gitignore was the TypeScript compiler's boilerplate and ignored package-lock.json, which is tracked and required by npm ci - removed the stale root .openapi-generator metadata; generation writes to src/generated, which carries its own Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 34 ++++++++++++++ .github/workflows/js.yml | 27 ----------- .github/workflows/publish.yml | 58 ++++++++++++++++-------- .gitignore | 85 +++++++++-------------------------- .openapi-generator/FILES | 5 --- .openapi-generator/VERSION | 1 - README.md | 6 ++- generate-api.sh | 4 +- package.json | 3 ++ 9 files changed, 103 insertions(+), 120 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/js.yml delete mode 100644 .openapi-generator/FILES delete mode 100644 .openapi-generator/VERSION diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..50ccbf3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + name: Test (Node ${{ matrix.node-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: ["18.x", "20.x", "22.x"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Run tests with jest + run: npm test diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml deleted file mode 100644 index 16d0997..0000000 --- a/.github/workflows/js.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Node.js CI - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18.x, 20.x, 22.x] - steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - run: npm ci - - run: npm run build - - name: Run tests - env: - REGEXSOLVER_API_TOKEN: ${{ secrets.REGEXSOLVER_API_TOKEN }} - run: npm test diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b3e9512..28b66f0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,33 +1,55 @@ name: Publish to npm on: - push: - tags: - - 'v*' + push: + tags: + - "v*" jobs: - build: + test: + name: Test runs-on: ubuntu-latest + steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 with: - node-version: 20 - - run: npm ci - - run: npm run build - - run: npm test + node-version: "20" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Run tests with jest + run: npm test - publish-npm: - needs: build + publish: + name: Publish to npm + needs: test runs-on: ubuntu-latest + steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 with: - node-version: 20 - registry-url: https://registry.npmjs.org/ - - run: npm ci - - run: npm run build - - run: npm publish + node-version: "20" + cache: "npm" + registry-url: "https://registry.npmjs.org/" + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Publish distribution to npm + run: npm publish env: - NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 9c37a32..be9368e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,67 +1,22 @@ -node_modules/ -.node_modules/ -.env -built/* -tests/cases/rwc/* -tests/cases/perf/* -!tests/cases/webharness/compilerToString.js -test-args.txt -~*.docx -\#*\# -.\#* -tests/baselines/local/* -tests/baselines/local.old/* -tests/services/baselines/local/* -tests/baselines/prototyping/local/* -tests/baselines/rwc/* -tests/baselines/reference/projectOutput/* -tests/baselines/local/projectOutput/* -tests/baselines/reference/testresults.tap -tests/baselines/symlinks/* -tests/services/baselines/prototyping/local/* -tests/services/browser/typescriptServices.js -src/harness/*.js -src/compiler/diagnosticInformationMap.generated.ts -src/compiler/diagnosticMessages.generated.json -src/parser/diagnosticInformationMap.generated.ts -src/parser/diagnosticMessages.generated.json -rwc-report.html -*.swp -build.json -*.actual -tests/webTestServer.js -tests/webTestServer.js.map -tests/webhost/*.d.ts -tests/webhost/webtsc.js -tests/cases/**/*.js -tests/cases/**/*.js.map -*.config -scripts/eslint/built/ -scripts/debug.bat -scripts/run.bat -scripts/**/*.js -scripts/**/*.js.map +# Build output +lib/ coverage/ -internal/ -**/.DS_Store -.settings -**/.vs -**/.vscode/* -!**/.vscode/tasks.json -!**/.vscode/settings.template.json -!**/.vscode/launch.template.json -!**/.vscode/extensions.json -!tests/cases/projects/projectOption/**/node_modules -!tests/cases/projects/NodeModulesSearch/**/* -!tests/baselines/reference/project/nodeModules*/**/* -.idea -yarn.lock -yarn-error.log -.parallelperf.* -tests/baselines/reference/dt -.failed-tests -TEST-results.xml -package-lock.json -.eslintcache +*.tgz + +# Dependencies +node_modules/ + +# Logs +npm-debug.log* *v8.log -/lib/ + +# Environment +.env + +# IDE +.idea/ +.vscode/ +*.iml + +# OS +.DS_Store diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES deleted file mode 100644 index 53250c0..0000000 --- a/.openapi-generator/FILES +++ /dev/null @@ -1,5 +0,0 @@ -api.ts -base.ts -common.ts -configuration.ts -index.ts diff --git a/.openapi-generator/VERSION b/.openapi-generator/VERSION deleted file mode 100644 index 2540a3a..0000000 --- a/.openapi-generator/VERSION +++ /dev/null @@ -1 +0,0 @@ -7.20.0 diff --git a/README.md b/README.md index f3ccc3f..46561e0 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ npm install regexsolver ``` -Requirements: **Node.js >= 16** +Requirements: **Node.js >= 18** ## Quick Start @@ -49,7 +49,7 @@ The API can handle terms in two formats: - `regex`: a regular expression pattern - `fair`: FAIR (Fast Automaton Internal Representation), a stable, signed format used internally by the engine -By default, the engine returns whatever the operation produces, with no extra convertion. Override with `OperationOptions`, accepted by the operations that return a term: +By default, the engine returns whatever the operation produces, with no extra conversion. Override with `OperationOptions`, accepted by the operations that return a term: ```javascript import { Term, ResponseFormat } from 'regexsolver'; @@ -64,6 +64,8 @@ const result2 = await client.union(term1, term2, { responseFormat: ResponseForma console.log(result2.toString()); // fair=... ``` +If the format does not matter, omit `responseFormat` or set it to `ResponseFormat.ANY`. + Regardless of the format, you can always call `getPattern()` to obtain the regex pattern of a term. ## Bounding execution time diff --git a/generate-api.sh b/generate-api.sh index fb1d15f..0f6d649 100755 --- a/generate-api.sh +++ b/generate-api.sh @@ -3,10 +3,10 @@ SPEC_FILE="../m-lab/shared/openapi.yaml" OUT_DIR="./src/generated" -echo "Generating TypeScript Axios client..." +echo "Running openapi-generator-cli..." openapi-generator-cli generate \ -i "$SPEC_FILE" \ -g typescript-axios \ -o "$OUT_DIR" -echo "Generation complete. Metadata is at root, generated source is in src/generated." +echo "API Generation Complete." diff --git a/package.json b/package.json index 86f61ed..615bfd0 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,9 @@ "test": "jest", "build": "tsc" }, + "engines": { + "node": ">=18" + }, "keywords": [ "Regular Expression", "regex", From 6f833b2aeaaf599cb2d3be5b229e30e583f8ac84 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Tue, 4 Aug 2026 20:51:42 +0200 Subject: [PATCH 19/20] Update library Signed-off-by: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Signed-off-by: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> --- README.md | 8 +- package-lock.json | 1341 +++++++++++++++------------- src/RateLimiter.ts | 45 +- src/RegexSolverClient.ts | 285 +++++- src/generated/api.ts | 200 ++++- src/generated/base.ts | 2 +- src/generated/common.ts | 2 +- src/generated/configuration.ts | 2 +- src/generated/index.ts | 2 +- src/index.ts | 2 + src/models/AccountLimits.ts | 51 ++ src/models/GenerateStringsOrder.ts | 40 + tests/RateLimiter.test.ts | 35 +- tests/client.test.ts | 245 +++++ 14 files changed, 1552 insertions(+), 708 deletions(-) create mode 100644 src/models/AccountLimits.ts create mode 100644 src/models/GenerateStringsOrder.ts diff --git a/README.md b/README.md index 46561e0..c82b960 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ Timeout is best effort. The exact time is not guaranteed. ## API Overview -`RegexSolverClient` exposes the following methods. Every method accepts an optional options object as its last parameter: operations that return a term take `OperationOptions` (`responseFormat`, `deterministic`, `executionTimeout`), while analyze operations and `determinize()` take `ExecutionOptions` (`executionTimeout` only) — the response format is not theirs to choose. +`RegexSolverClient` exposes the following methods. Every method accepts an optional options object as its last parameter: operations that return a term take `OperationOptions` (`responseFormat`, `deterministic`, `executionTimeout`), while analyze operations and `determinize()` take `ExecutionOptions` (`executionTimeout` only); the response format is not theirs to choose. `generateStrings()` takes a `GenerateStringsOptions` carrying its ordering, seed, length and charset options. ### Analyze @@ -107,7 +107,7 @@ Timeout is best effort. The exact time is not guaranteed. | `client.isEmptyString(term, options?)` | `Promise` | `true` if the term matches only the empty string. | | `client.isTotal(term, options?)` | `Promise` | `true` if the term matches all possible strings. | | `client.isDeterministic(term, options?)` | `Promise` | `true` if the term's automaton is deterministic. Only a deterministic FAIR guarantees consistent string ordering across paginated `generateStrings()` calls; call `determinize()` first if this is `false`. | -| `client.subset(term1, term2, options?)` | `Promise` | `true` if every string matched by `term1` is also matched by `term2`. | +| `client.subset(subset, superset, options?)` | `Promise` | `true` if every string matched by `subset` is also matched by `superset`. | ### Compute @@ -116,7 +116,7 @@ Timeout is best effort. The exact time is not guaranteed. | `client.complement(term, options?)` | `Promise` | Computes the complement of the given term. | | `client.concat(term1, term2, ..., options?)` | `Promise` | Concatenates multiple terms in order. | | `client.determinize(term, options?)` | `Promise` | Computes a deterministic FAIR for the given term, suitable for consistent pagination with `generateStrings()`. | -| `client.difference(term1, term2, options?)` | `Promise` | Computes the difference `term1 - term2`. | +| `client.difference(base, excluded, options?)` | `Promise` | Computes the difference `base - excluded`. | | `client.intersection(term1, term2, ..., options?)` | `Promise` | Computes the intersection of the given terms. | | `client.repeat(term, min, max, options?)` | `Promise` | Computes the repetition of the term between `min` and `max` times. | | `client.union(term1, term2, ..., options?)` | `Promise` | Computes the union of the given terms. | @@ -125,7 +125,7 @@ Timeout is best effort. The exact time is not guaranteed. | Method | Return | Description | | -------- | ------- | ------- | -| `client.generateStrings(term, limit, offset, options?)` | `Promise` | Generates up to `limit` unique strings matched by `term`, skipping the first `offset` strings. | +| `client.generateStrings(term, limit, offset, options?)` | `Promise` | Generates up to `limit` unique strings matched by `term`, skipping the first `offset` strings. The options object controls `pathOrder`, `characterOrder`, `seed`, `minLength`, `maxLength` and `charset`. | ## Cross-Language Support diff --git a/package-lock.json b/package-lock.json index cda5800..5026b9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,16 +18,19 @@ "jest": "^30.3.0", "ts-jest": "^29.4.6", "typescript": "^5.9.3" + }, + "engines": { + "node": ">=18" } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "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.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -36,9 +39,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "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": { @@ -46,21 +49,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -77,14 +80,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "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.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -94,14 +97,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "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.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -111,9 +114,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "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": { @@ -121,29 +124,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "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.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "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.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -153,9 +156,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "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": { @@ -163,9 +166,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "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": { @@ -173,9 +176,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.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": { @@ -183,9 +186,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "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": { @@ -193,27 +196,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "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.28.6", - "@babel/types": "^7.29.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "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.0" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -278,13 +281,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -320,13 +323,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "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.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -446,13 +449,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -462,33 +465,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "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.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "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.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -496,14 +499,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "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.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -517,21 +520,21 @@ "license": "MIT" }, "node_modules/@emnapi/core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", - "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.0", + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", - "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, "license": "MIT", "optional": true, @@ -540,9 +543,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", - "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", "optional": true, @@ -586,9 +589,9 @@ } }, "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", "dev": true, "license": "MIT", "engines": { @@ -596,17 +599,17 @@ } }, "node_modules/@jest/console": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.3.0.tgz", - "integrity": "sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.4.1.tgz", + "integrity": "sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@types/node": "*", "chalk": "^4.1.2", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", "slash": "^3.0.0" }, "engines": { @@ -614,38 +617,39 @@ } }, "node_modules/@jest/core": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.3.0.tgz", - "integrity": "sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz", + "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.3.0", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", + "@jest/console": "30.4.1", + "@jest/pattern": "30.4.0", + "@jest/reporters": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", "ci-info": "^4.2.0", "exit-x": "^0.2.2", + "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.11", - "jest-changed-files": "30.3.0", - "jest-config": "30.3.0", - "jest-haste-map": "30.3.0", - "jest-message-util": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-resolve-dependencies": "30.3.0", - "jest-runner": "30.3.0", - "jest-runtime": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "jest-watcher": "30.3.0", - "pretty-format": "30.3.0", + "jest-changed-files": "30.4.1", + "jest-config": "30.4.2", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-resolve-dependencies": "30.4.2", + "jest-runner": "30.4.2", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "jest-watcher": "30.4.1", + "pretty-format": "30.4.1", "slash": "^3.0.0" }, "engines": { @@ -661,9 +665,9 @@ } }, "node_modules/@jest/diff-sequences": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", - "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", + "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", "dev": true, "license": "MIT", "engines": { @@ -671,39 +675,39 @@ } }, "node_modules/@jest/environment": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.3.0.tgz", - "integrity": "sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz", + "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.3.0", - "@jest/types": "30.3.0", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", - "jest-mock": "30.3.0" + "jest-mock": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/expect": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.3.0.tgz", - "integrity": "sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==", "dev": true, "license": "MIT", "dependencies": { - "expect": "30.3.0", - "jest-snapshot": "30.3.0" + "expect": "30.4.1", + "jest-snapshot": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", - "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", + "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", "dev": true, "license": "MIT", "dependencies": { @@ -714,18 +718,18 @@ } }, "node_modules/@jest/fake-timers": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.3.0.tgz", - "integrity": "sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz", + "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", - "@sinonjs/fake-timers": "^15.0.0", + "@jest/types": "30.4.1", + "@sinonjs/fake-timers": "^15.4.0", "@types/node": "*", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-util": "30.3.0" + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -742,47 +746,47 @@ } }, "node_modules/@jest/globals": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.3.0.tgz", - "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.4.1.tgz", + "integrity": "sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/expect": "30.3.0", - "@jest/types": "30.3.0", - "jest-mock": "30.3.0" + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/types": "30.4.1", + "jest-mock": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", - "jest-regex-util": "30.0.1" + "jest-regex-util": "30.4.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/reporters": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.3.0.tgz", - "integrity": "sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.4.1.tgz", + "integrity": "sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", + "@jest/console": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", "@jridgewell/trace-mapping": "^0.3.25", "@types/node": "*", "chalk": "^4.1.2", @@ -795,9 +799,9 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^5.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "jest-worker": "30.3.0", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", "slash": "^3.0.0", "string-length": "^4.0.2", "v8-to-istanbul": "^9.0.1" @@ -815,9 +819,9 @@ } }, "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", "dev": true, "license": "MIT", "dependencies": { @@ -828,13 +832,13 @@ } }, "node_modules/@jest/snapshot-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", - "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.4.1.tgz", + "integrity": "sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "natural-compare": "^1.4.0" @@ -859,14 +863,14 @@ } }, "node_modules/@jest/test-result": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.3.0.tgz", - "integrity": "sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.4.1.tgz", + "integrity": "sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.3.0", - "@jest/types": "30.3.0", + "@jest/console": "30.4.1", + "@jest/types": "30.4.1", "@types/istanbul-lib-coverage": "^2.0.6", "collect-v8-coverage": "^1.0.2" }, @@ -875,15 +879,15 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz", - "integrity": "sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.4.1.tgz", + "integrity": "sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.3.0", + "@jest/test-result": "30.4.1", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", + "jest-haste-map": "30.4.1", "slash": "^3.0.0" }, "engines": { @@ -891,23 +895,23 @@ } }, "node_modules/@jest/transform": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.3.0.tgz", - "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.4.1.tgz", + "integrity": "sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.27.4", - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@jridgewell/trace-mapping": "^0.3.25", "babel-plugin-istanbul": "^7.0.1", "chalk": "^4.1.2", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", + "jest-haste-map": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", "pirates": "^4.0.7", "slash": "^3.0.0", "write-file-atomic": "^5.0.1" @@ -917,14 +921,14 @@ } }, "node_modules/@jest/types": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", - "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", @@ -986,16 +990,25 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", + "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" } }, "node_modules/@pkgjs/parseargs": { @@ -1010,22 +1023,22 @@ } }, "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", + "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", "dev": true, "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": "^14.18.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/pkgr" } }, "node_modules/@sinclair/typebox": { - "version": "0.34.48", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", - "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", + "version": "0.34.52", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", + "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==", "dev": true, "license": "MIT" }, @@ -1040,9 +1053,9 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz", - "integrity": "sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==", + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1050,9 +1063,9 @@ } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -1144,13 +1157,13 @@ } }, "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "version": "25.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.5.tgz", + "integrity": "sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.18.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/stack-utils": { @@ -1178,16 +1191,16 @@ "license": "MIT" }, "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", "dev": true, "license": "ISC" }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", "cpu": [ "arm" ], @@ -1199,9 +1212,9 @@ ] }, "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", "cpu": [ "arm64" ], @@ -1213,9 +1226,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", "cpu": [ "arm64" ], @@ -1227,9 +1240,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", "cpu": [ "x64" ], @@ -1241,9 +1254,9 @@ ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", "cpu": [ "x64" ], @@ -1255,9 +1268,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", "cpu": [ "arm" ], @@ -1269,9 +1282,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", "cpu": [ "arm" ], @@ -1283,9 +1296,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", "cpu": [ "arm64" ], @@ -1300,9 +1313,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", "cpu": [ "arm64" ], @@ -1316,10 +1329,44 @@ "linux" ] }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", "cpu": [ "ppc64" ], @@ -1334,9 +1381,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", "cpu": [ "riscv64" ], @@ -1351,9 +1398,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", "cpu": [ "riscv64" ], @@ -1368,9 +1415,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", "cpu": [ "s390x" ], @@ -1385,9 +1432,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", "cpu": [ "x64" ], @@ -1402,9 +1449,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", "cpu": [ "x64" ], @@ -1418,10 +1465,24 @@ "linux" ] }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", "cpu": [ "wasm32" ], @@ -1429,16 +1490,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", "cpu": [ "arm64" ], @@ -1450,9 +1513,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", "cpu": [ "ia32" ], @@ -1464,9 +1527,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", "cpu": [ "x64" ], @@ -1477,6 +1540,18 @@ "win32" ] }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1537,9 +1612,9 @@ } }, "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -1566,14 +1641,15 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", - "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.11", - "form-data": "^4.0.5", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, "node_modules/axios-mock-adapter": { @@ -1591,16 +1667,16 @@ } }, "node_modules/babel-jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.3.0.tgz", - "integrity": "sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.4.1.tgz", + "integrity": "sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "30.3.0", + "@jest/transform": "30.4.1", "@types/babel__core": "^7.20.5", "babel-plugin-istanbul": "^7.0.1", - "babel-preset-jest": "30.3.0", + "babel-preset-jest": "30.4.0", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "slash": "^3.0.0" @@ -1633,9 +1709,9 @@ } }, "node_modules/babel-plugin-jest-hoist": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.3.0.tgz", - "integrity": "sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.4.0.tgz", + "integrity": "sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==", "dev": true, "license": "MIT", "dependencies": { @@ -1673,13 +1749,13 @@ } }, "node_modules/babel-preset-jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.3.0.tgz", - "integrity": "sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.4.0.tgz", + "integrity": "sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==", "dev": true, "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "30.3.0", + "babel-plugin-jest-hoist": "30.4.0", "babel-preset-current-node-syntax": "^1.2.0" }, "engines": { @@ -1697,9 +1773,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.10", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz", - "integrity": "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==", + "version": "2.11.12", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz", + "integrity": "sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1710,9 +1786,9 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -1720,9 +1796,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "dev": true, "funding": [ { @@ -1740,11 +1816,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -1817,9 +1893,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001780", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", - "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", "dev": true, "funding": [ { @@ -2048,7 +2124,6 @@ "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" @@ -2128,9 +2203,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.321", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz", - "integrity": "sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==", + "version": "1.5.400", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.400.tgz", + "integrity": "sha512-96EWDNjM59SYflgeV5Ylsf4EMiq1a25YjCnJH7cxn/AF2H3pILRweaUnoLax0yKHWdpOzY6JKEu45e8irqZIHA==", "dev": true, "license": "ISC" }, @@ -2183,9 +2258,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2285,18 +2360,18 @@ } }, "node_modules/expect": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", - "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/expect-utils": "30.3.0", + "@jest/expect-utils": "30.4.1", "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-util": "30.3.0" + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -2341,9 +2416,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -2378,16 +2453,16 @@ } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -2546,9 +2621,9 @@ "license": "ISC" }, "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2605,9 +2680,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "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==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2623,6 +2698,19 @@ "dev": true, "license": "MIT" }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -2781,9 +2869,9 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -2854,16 +2942,16 @@ } }, "node_modules/jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.3.0.tgz", - "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz", + "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.3.0", - "@jest/types": "30.3.0", + "@jest/core": "30.4.2", + "@jest/types": "30.4.1", "import-local": "^3.2.0", - "jest-cli": "30.3.0" + "jest-cli": "30.4.2" }, "bin": { "jest": "bin/jest.js" @@ -2881,14 +2969,14 @@ } }, "node_modules/jest-changed-files": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.3.0.tgz", - "integrity": "sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.4.1.tgz", + "integrity": "sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==", "dev": true, "license": "MIT", "dependencies": { "execa": "^5.1.1", - "jest-util": "30.3.0", + "jest-util": "30.4.1", "p-limit": "^3.1.0" }, "engines": { @@ -2896,29 +2984,29 @@ } }, "node_modules/jest-circus": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.3.0.tgz", - "integrity": "sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz", + "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/expect": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", "chalk": "^4.1.2", "co": "^4.6.0", "dedent": "^1.6.0", "is-generator-fn": "^2.1.0", - "jest-each": "30.3.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-runtime": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", + "jest-each": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", "p-limit": "^3.1.0", - "pretty-format": "30.3.0", + "pretty-format": "30.4.1", "pure-rand": "^7.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.6" @@ -2928,21 +3016,21 @@ } }, "node_modules/jest-cli": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.3.0.tgz", - "integrity": "sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz", + "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", + "@jest/core": "30.4.2", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", "chalk": "^4.1.2", "exit-x": "^0.2.2", "import-local": "^3.2.0", - "jest-config": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", + "jest-config": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", "yargs": "^17.7.2" }, "bin": { @@ -2961,33 +3049,33 @@ } }, "node_modules/jest-config": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.3.0.tgz", - "integrity": "sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz", + "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.27.4", "@jest/get-type": "30.1.0", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.3.0", - "@jest/types": "30.3.0", - "babel-jest": "30.3.0", + "@jest/pattern": "30.4.0", + "@jest/test-sequencer": "30.4.1", + "@jest/types": "30.4.1", + "babel-jest": "30.4.1", "chalk": "^4.1.2", "ci-info": "^4.2.0", "deepmerge": "^4.3.1", "glob": "^10.5.0", "graceful-fs": "^4.2.11", - "jest-circus": "30.3.0", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-runner": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", + "jest-circus": "30.4.2", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-runner": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", "parse-json": "^5.2.0", - "pretty-format": "30.3.0", + "pretty-format": "30.4.1", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -3012,25 +3100,25 @@ } }, "node_modules/jest-diff": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", - "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", + "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/diff-sequences": "30.3.0", + "@jest/diff-sequences": "30.4.0", "@jest/get-type": "30.1.0", "chalk": "^4.1.2", - "pretty-format": "30.3.0" + "pretty-format": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-docblock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", - "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.4.0.tgz", + "integrity": "sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==", "dev": true, "license": "MIT", "dependencies": { @@ -3041,56 +3129,56 @@ } }, "node_modules/jest-each": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.3.0.tgz", - "integrity": "sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.4.1.tgz", + "integrity": "sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "chalk": "^4.1.2", - "jest-util": "30.3.0", - "pretty-format": "30.3.0" + "jest-util": "30.4.1", + "pretty-format": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-environment-node": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.3.0.tgz", - "integrity": "sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz", + "integrity": "sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/fake-timers": "30.3.0", - "@jest/types": "30.3.0", + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", - "jest-mock": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0" + "jest-mock": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-haste-map": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", - "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.4.1.tgz", + "integrity": "sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@types/node": "*", "anymatch": "^3.1.3", "fb-watchman": "^2.0.2", "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", - "jest-worker": "30.3.0", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", "picomatch": "^4.0.3", "walker": "^1.0.8" }, @@ -3102,49 +3190,50 @@ } }, "node_modules/jest-leak-detector": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz", - "integrity": "sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.4.1.tgz", + "integrity": "sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", - "pretty-format": "30.3.0" + "pretty-format": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", - "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", + "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", "chalk": "^4.1.2", - "jest-diff": "30.3.0", - "pretty-format": "30.3.0" + "jest-diff": "30.4.1", + "pretty-format": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-message-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", - "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", + "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@types/stack-utils": "^2.0.3", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", + "jest-util": "30.4.1", "picomatch": "^4.0.3", - "pretty-format": "30.3.0", + "pretty-format": "30.4.1", "slash": "^3.0.0", "stack-utils": "^2.0.6" }, @@ -3153,15 +3242,15 @@ } }, "node_modules/jest-mock": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", - "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", + "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@types/node": "*", - "jest-util": "30.3.0" + "jest-util": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -3186,9 +3275,9 @@ } }, "node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", "dev": true, "license": "MIT", "engines": { @@ -3196,18 +3285,18 @@ } }, "node_modules/jest-resolve": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.3.0.tgz", - "integrity": "sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.4.1.tgz", + "integrity": "sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.2", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", + "jest-haste-map": "30.4.1", "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", "slash": "^3.0.0", "unrs-resolver": "^1.7.11" }, @@ -3216,46 +3305,46 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz", - "integrity": "sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz", + "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==", "dev": true, "license": "MIT", "dependencies": { - "jest-regex-util": "30.0.1", - "jest-snapshot": "30.3.0" + "jest-regex-util": "30.4.0", + "jest-snapshot": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-runner": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.3.0.tgz", - "integrity": "sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz", + "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.3.0", - "@jest/environment": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", + "@jest/console": "30.4.1", + "@jest/environment": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", "chalk": "^4.1.2", "emittery": "^0.13.1", "exit-x": "^0.2.2", "graceful-fs": "^4.2.11", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.3.0", - "jest-haste-map": "30.3.0", - "jest-leak-detector": "30.3.0", - "jest-message-util": "30.3.0", - "jest-resolve": "30.3.0", - "jest-runtime": "30.3.0", - "jest-util": "30.3.0", - "jest-watcher": "30.3.0", - "jest-worker": "30.3.0", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-haste-map": "30.4.1", + "jest-leak-detector": "30.4.1", + "jest-message-util": "30.4.1", + "jest-resolve": "30.4.1", + "jest-runtime": "30.4.2", + "jest-util": "30.4.1", + "jest-watcher": "30.4.1", + "jest-worker": "30.4.1", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -3264,32 +3353,32 @@ } }, "node_modules/jest-runtime": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.3.0.tgz", - "integrity": "sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", + "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/fake-timers": "30.3.0", - "@jest/globals": "30.3.0", + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/globals": "30.4.1", "@jest/source-map": "30.0.1", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", "chalk": "^4.1.2", "cjs-module-lexer": "^2.1.0", "collect-v8-coverage": "^1.0.2", "glob": "^10.5.0", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -3298,9 +3387,9 @@ } }, "node_modules/jest-snapshot": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", - "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz", + "integrity": "sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==", "dev": true, "license": "MIT", "dependencies": { @@ -3309,20 +3398,20 @@ "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.3.0", + "@jest/expect-utils": "30.4.1", "@jest/get-type": "30.1.0", - "@jest/snapshot-utils": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", + "@jest/snapshot-utils": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", "babel-preset-current-node-syntax": "^1.2.0", "chalk": "^4.1.2", - "expect": "30.3.0", + "expect": "30.4.1", "graceful-fs": "^4.2.11", - "jest-diff": "30.3.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "pretty-format": "30.3.0", + "jest-diff": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "pretty-format": "30.4.1", "semver": "^7.7.2", "synckit": "^0.11.8" }, @@ -3331,9 +3420,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -3344,13 +3433,13 @@ } }, "node_modules/jest-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", - "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", @@ -3362,18 +3451,18 @@ } }, "node_modules/jest-validate": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", - "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.4.1.tgz", + "integrity": "sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", + "@jest/types": "30.4.1", "camelcase": "^6.3.0", "chalk": "^4.1.2", "leven": "^3.1.0", - "pretty-format": "30.3.0" + "pretty-format": "30.4.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -3393,19 +3482,19 @@ } }, "node_modules/jest-watcher": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.3.0.tgz", - "integrity": "sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.4.1.tgz", + "integrity": "sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", "@types/node": "*", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", "emittery": "^0.13.1", - "jest-util": "30.3.0", + "jest-util": "30.4.1", "string-length": "^4.0.2" }, "engines": { @@ -3413,15 +3502,15 @@ } }, "node_modules/jest-worker": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", - "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz", + "integrity": "sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.3.0", + "jest-util": "30.4.1", "merge-stream": "^2.0.0", "supports-color": "^8.1.1" }, @@ -3453,9 +3542,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz", + "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", "dev": true, "license": "MIT", "dependencies": { @@ -3563,9 +3652,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -3679,7 +3768,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/napi-postinstall": { @@ -3720,11 +3808,14 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "version": "2.0.52", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.52.tgz", + "integrity": "sha512-MRlTqhAfoMx/4mhEbPo3Hi02g9LJZaJkka69V6h67Cb1gjrAG0jsTE4CZX1eptNx+VCAwJmfpnDIF4P0Nh1A7A==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -3918,9 +4009,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { @@ -3954,15 +4045,16 @@ } }, "node_modules/pretty-format": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", - "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", + "@jest/schemas": "30.4.1", "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -3982,10 +4074,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/pure-rand": { "version": "7.0.1", @@ -4004,13 +4099,22 @@ ], "license": "MIT" }, - "node_modules/react-is": { + "node_modules/react-is-18": { + "name": "react-is", "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, "license": "MIT" }, + "node_modules/react-is-19": { + "name": "react-is", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", + "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", + "dev": true, + "license": "MIT" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -4329,13 +4433,13 @@ } }, "node_modules/synckit": { - "version": "0.11.12", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", - "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", + "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==", "dev": true, "license": "MIT", "dependencies": { - "@pkgr/core": "^0.2.9" + "@pkgr/core": "^0.3.6" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -4360,9 +4464,9 @@ } }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -4413,19 +4517,19 @@ "license": "BSD-3-Clause" }, "node_modules/ts-jest": { - "version": "29.4.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", - "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", + "version": "29.4.12", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.12.tgz", + "integrity": "sha512-Ov6ClY53Fflh6BGAnY2DlTq1hYDrTycz2PVTXBWFW2CU+9zrEqAp9fWdGXl42EXO5RLSFAcAZ2JFKbP+zBTFfw==", "dev": true, "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", + "handlebars": "^4.7.9", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.7.3", + "semver": "^7.8.5", "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, @@ -4442,7 +4546,7 @@ "babel-jest": "^29.0.0 || ^30.0.0", "jest": "^29.0.0 || ^30.0.0", "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" + "typescript": ">=4.3 <7" }, "peerDependenciesMeta": { "@babel/core": { @@ -4466,9 +4570,9 @@ } }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -4551,45 +4655,48 @@ } }, "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT" }, "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "napi-postinstall": "^0.3.0" + "napi-postinstall": "^0.3.4" }, "funding": { "url": "https://opencollective.com/unrs-resolver" }, "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" } }, "node_modules/update-browserslist-db": { @@ -4805,9 +4912,9 @@ "license": "ISC" }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/RateLimiter.ts b/src/RateLimiter.ts index 1c6280e..75d0557 100644 --- a/src/RateLimiter.ts +++ b/src/RateLimiter.ts @@ -1,37 +1,28 @@ +/** + * Shared across all client instances with the same API token. + * + * Holds a single deadline timestamp. `trigger` keeps the later of the current + * and the new deadline, so a longer `Retry-After` arriving while the limiter + * is already engaged is never dropped. `wait` sleeps until the deadline and + * re-checks it after every wake, so a deadline extended by a concurrent 429 + * is honored too. + */ export class RateLimiter { - private isBlocked = false; - private blockPromise: Promise | null = null; - private blockTimeout: NodeJS.Timeout | null = null; + private deadline = 0; async wait(): Promise { - if (this.isBlocked && this.blockPromise) { - await this.blockPromise; + while (Date.now() < this.deadline) { + await new Promise((resolve) => + setTimeout(resolve, this.deadline - Date.now()), + ); } } trigger(retryAfterSeconds: number): void { - if (this.isBlocked) { - return; - } - - this.isBlocked = true; - const waitTime = retryAfterSeconds * 1000; - - let resolveBlock: () => void; - this.blockPromise = new Promise((resolve) => { - resolveBlock = resolve; - }); - - if (this.blockTimeout) { - clearTimeout(this.blockTimeout); - } - - this.blockTimeout = setTimeout(() => { - this.isBlocked = false; - this.blockPromise = null; - this.blockTimeout = null; - resolveBlock(); - }, waitTime); + this.deadline = Math.max( + this.deadline, + Date.now() + retryAfterSeconds * 1000, + ); } } diff --git a/src/RegexSolverClient.ts b/src/RegexSolverClient.ts index d49d5a0..d6ce67d 100644 --- a/src/RegexSolverClient.ts +++ b/src/RegexSolverClient.ts @@ -1,5 +1,6 @@ import axios, { AxiosError, AxiosInstance, AxiosResponse } from "axios"; import { + AccountApi, AnalyzeApi, ComputeApi, GenerateApi, @@ -12,7 +13,9 @@ import { RequestOptions as RequestOptionsDto, } from "./generated"; import { FairTerm, Term } from "./models/Term"; +import { AccountLimits } from "./models/AccountLimits"; import { Cardinality, Infinite, Integer } from "./models/Cardinality"; +import { CharacterOrder, PathOrder } from "./models/GenerateStringsOrder"; import { Length } from "./models/Length"; import { ResponseFormat } from "./models/ResponseFormat"; import * as Exceptions from "./exceptions"; @@ -20,9 +23,30 @@ import { getRateLimiter, RateLimiter } from "./RateLimiter"; const VERSION = "1.1.0"; +// Retry policy for 429 responses: retry as long as the total wait stays +// within the budget, adding full jitter on top of `Retry-After` so concurrent +// waiters do not re-collide as a single burst. The values are shared across +// all the official clients — change them together. +const RETRY_BUDGET_MS = 300_000; +const JITTER_BASE_S = 0.25; +const JITTER_CAP_S = 2.0; +const DEFAULT_RETRY_AFTER_S = 1.0; + export interface RegexSolverConfig { apiToken: string; baseUrl?: string; + /** + * When true (the default), calls to concat/intersection/union carrying more + * terms than the account's per-request limit are transparently split into + * several requests and folded back into one result. Each constituent + * request counts against the monthly quota. + */ + autoBatch?: boolean; + /** + * Upper bound (>= 2) on the number of terms sent in a single request, + * overriding the limit fetched from the API when smaller. + */ + maxTermsPerRequest?: number; } /** @@ -55,18 +79,71 @@ export interface OperationOptions extends ExecutionOptions { deterministic?: boolean; } +/** + * Options accepted by generateStrings(). + */ +export interface GenerateStringsOptions extends ExecutionOptions { + /** + * Order in which the paths (shapes) of the language are scheduled. + * Defaults to sweep. + */ + pathOrder?: PathOrder | "sweep" | "interleave" | "shuffled"; + /** + * Order in which the strings within each path are produced. Defaults to + * ascending. + */ + characterOrder?: CharacterOrder | "ascending" | "shuffled"; + /** + * Seed behind the shuffled modes. The default seed is fixed, so two calls + * sharing a seed generate the same strings and `offset` pages through them + * consistently. + */ + seed?: number; + /** + * Shortest string to generate. Shorter strings are left out of the + * enumeration entirely, `offset` never counting them. + */ + minLength?: number; + /** + * Longest string to generate. + */ + maxLength?: number; + /** + * Restricts generation to the given characters, e.g. `[a-z]`. Paths + * requiring a character outside it are dropped. + */ + charset?: string; +} + export class RegexSolverClient { private readonly apiToken: string; + private readonly accountApi: AccountApi; private readonly analyzeApi: AnalyzeApi; private readonly computeApi: ComputeApi; private readonly generateApi: GenerateApi; private readonly axiosInstance: AxiosInstance; private readonly rateLimiter: RateLimiter; + private readonly autoBatch: boolean; + private readonly maxTermsPerRequest: number | null; + private limitsPromise: Promise | null = null; + private serverMaxTerms: number | null = null; constructor(config: RegexSolverConfig) { + if (!config.apiToken) { + throw new Error("apiToken is required"); + } + if ( + config.maxTermsPerRequest !== undefined && + config.maxTermsPerRequest < 2 + ) { + throw new Error("maxTermsPerRequest must be at least 2"); + } + this.apiToken = config.apiToken; const baseUrl = config.baseUrl || "https://api.regexsolver.com/v1"; this.rateLimiter = getRateLimiter(this.apiToken); + this.autoBatch = config.autoBatch ?? true; + this.maxTermsPerRequest = config.maxTermsPerRequest ?? null; const axiosConfig = { baseURL: baseUrl, @@ -78,17 +155,16 @@ export class RegexSolverClient { this.axiosInstance = axios.create(axiosConfig); - // Add rate limit interceptor - this.axiosInstance.interceptors.request.use(async (requestConfig) => { - await this.rateLimiter.wait(); - return requestConfig; - }); - const apiConfiguration = new Configuration({ accessToken: this.apiToken, basePath: baseUrl, }); + this.accountApi = new AccountApi( + apiConfiguration, + baseUrl, + this.axiosInstance, + ); this.analyzeApi = new AnalyzeApi( apiConfiguration, baseUrl, @@ -142,29 +218,37 @@ export class RegexSolverClient { private async executeWithRetry( apiCall: () => Promise>, ): Promise> { - let retried = false; + let attempt = 0; + let firstFailureAt: number | null = null; while (true) { + await this.rateLimiter.wait(); + if (attempt > 0) { + const jitterS = + Math.random() * Math.min(JITTER_BASE_S * 2 ** attempt, JITTER_CAP_S); + await new Promise((resolve) => setTimeout(resolve, jitterS * 1000)); + } try { return await apiCall(); } catch (error) { - if (axios.isAxiosError(error) && error.response) { - const statusCode = error.response.status; - if (statusCode === 429) { - if (retried) { - throw this.mapError(error); - } - retried = true; - const retryAfter = parseFloat( - error.response.headers["retry-after"] || "1", - ); - - this.rateLimiter.trigger(retryAfter); - continue; - } + if (!(axios.isAxiosError(error) && error.response)) { + throw error; + } + if (error.response.status !== 429) { throw this.mapError(error); } - throw error; + + const retryAfter = + parseFloat(error.response.headers["retry-after"]) || + DEFAULT_RETRY_AFTER_S; + const now = Date.now(); + firstFailureAt = firstFailureAt ?? now; + if (now - firstFailureAt + retryAfter * 1000 > RETRY_BUDGET_MS) { + throw this.mapError(error); + } + + this.rateLimiter.trigger(retryAfter); + attempt += 1; } } } @@ -288,6 +372,119 @@ export class RegexSolverClient { } } + // --- ACCOUNT OPERATIONS --- + + /** + * Fetches the plan limits applying to the account. + * + * The call never consumes request quota (it is only rate-limited) and the + * result is cached on the client, so calling it again is free. The cached + * maxTermsCount also drives auto-batching. + * @returns The five plan limits. + */ + public async getAccountLimits(): Promise { + if (!this.limitsPromise) { + this.limitsPromise = this.executeWithRetry(() => + this.accountApi.limits(), + ) + .then((response) => { + const limits = AccountLimits.fromDto(response.data.data!); + this.serverMaxTerms = limits.maxTermsCount; + return limits; + }) + .catch((error) => { + this.limitsPromise = null; + throw error; + }); + } + return this.limitsPromise; + } + + // --- AUTO-BATCHING --- + + /** The largest term count to send in one request, when known. */ + private effectiveMaxTerms(): number | null { + if (this.maxTermsPerRequest !== null) { + return this.serverMaxTerms !== null + ? Math.min(this.maxTermsPerRequest, this.serverMaxTerms) + : this.maxTermsPerRequest; + } + return this.serverMaxTerms; + } + + /** + * Run an n-ary operation (concat/intersection/union), transparently + * splitting the terms into several requests when they exceed the account's + * terms-per-request limit (auto-batching). + */ + private async runNary( + terms: Term[], + options: OperationOptions | undefined, + apiCall: (request: MultiTermsRequest) => Promise>, + ): Promise { + // Intermediate results are fed straight back into the next request, so + // only the final call carries the caller's response options; + // executionTimeout bounds every constituent request. + const call = async (batch: Term[], final: boolean): Promise => { + const request: MultiTermsRequest = { + terms: batch.map((t) => t.toDto()), + options: this.buildOptions( + final ? options : { executionTimeout: options?.executionTimeout }, + ), + }; + const response = await this.executeWithRetry(() => apiCall(request)); + return Term.fromDto(response.data.data); + }; + + let maxTerms = this.autoBatch ? this.effectiveMaxTerms() : null; + if (maxTerms !== null && terms.length > maxTerms) { + return this.fold(call, terms, maxTerms); + } + + try { + return await call(terms, true); + } catch (error) { + if ( + !this.autoBatch || + maxTerms !== null || + !(error instanceof Exceptions.TooManyTermsError) + ) { + throw error; + } + try { + await this.getAccountLimits(); + } catch { + throw error; // fall back to surfacing the original TooManyTerms + } + maxTerms = this.effectiveMaxTerms(); + if (maxTerms === null || maxTerms < 2 || terms.length <= maxTerms) { + throw error; + } + return this.fold(call, terms, maxTerms); + } + } + + /** + * Left fold: combine the first `maxTerms` terms, then keep feeding the + * accumulated result back with the next `maxTerms - 1` terms. + * Left-associative, so concat order is preserved; union and intersection + * are commutative and unaffected. + */ + private async fold( + call: (batch: Term[], final: boolean) => Promise, + terms: Term[], + maxTerms: number, + ): Promise { + let acc = await call(terms.slice(0, maxTerms), false); + let index = maxTerms; + while (index < terms.length) { + const batch = [acc, ...terms.slice(index, index + maxTerms - 1)]; + index += maxTerms - 1; + acc = await call(batch, index >= terms.length); + } + return acc; + } + // --- ANALYZE OPERATIONS --- /** @@ -573,14 +770,9 @@ export class RegexSolverClient { public async concat(terms: Term[], options?: OperationOptions): Promise; public async concat(...args: any[]): Promise { const { terms, options } = this.parseArgs(args); - const request: MultiTermsRequest = { - terms: terms.map((t) => t.toDto()), - options: this.buildOptions(options), - }; - const response = await this.executeWithRetry(() => + return this.runNary(terms, options, (request) => this.computeApi.concat(request), ); - return Term.fromDto(response.data.data); } /** @@ -596,14 +788,9 @@ export class RegexSolverClient { ): Promise; public async intersection(...args: any[]): Promise { const { terms, options } = this.parseArgs(args); - const request: MultiTermsRequest = { - terms: terms.map((t) => t.toDto()), - options: this.buildOptions(options), - }; - const response = await this.executeWithRetry(() => + return this.runNary(terms, options, (request) => this.computeApi.intersection(request), ); - return Term.fromDto(response.data.data); } /** @@ -616,14 +803,9 @@ export class RegexSolverClient { public async union(terms: Term[], options?: OperationOptions): Promise; public async union(...args: any[]): Promise { const { terms, options } = this.parseArgs(args); - const request: MultiTermsRequest = { - terms: terms.map((t) => t.toDto()), - options: this.buildOptions(options), - }; - const response = await this.executeWithRetry(() => + return this.runNary(terms, options, (request) => this.computeApi.union(request), ); - return Term.fromDto(response.data.data); } private parseArgs(args: any[]): { @@ -754,14 +936,14 @@ export class RegexSolverClient { * @param term Source term to generate strings from. * @param limit Maximum number of unique strings to return. * @param offset Number of matched strings to skip before starting to collect the results. Used for pagination. - * @param options Options object. + * @param options Options object (ordering, seed, length bounds, charset, executionTimeout). * @returns Array of unique strings. */ public async generateStrings( term: Term, limit: number, offset: number, - options?: OperationOptions, + options?: GenerateStringsOptions, ): Promise { const request: GenerateStringsRequest = { term: term.toDto(), @@ -769,6 +951,25 @@ export class RegexSolverClient { offset, options: this.buildOptions(options), }; + if (options?.pathOrder !== undefined) { + request.pathOrder = options.pathOrder as GenerateStringsRequest["pathOrder"]; + } + if (options?.characterOrder !== undefined) { + request.characterOrder = + options.characterOrder as GenerateStringsRequest["characterOrder"]; + } + if (options?.seed !== undefined) { + request.seed = options.seed; + } + if (options?.minLength !== undefined) { + request.minLength = options.minLength; + } + if (options?.maxLength !== undefined) { + request.maxLength = options.maxLength; + } + if (options?.charset !== undefined) { + request.charset = options.charset; + } const response = await this.executeWithRetry(() => this.generateApi.strings(request), diff --git a/src/generated/api.ts b/src/generated/api.ts index 0c04769..ad35429 100644 --- a/src/generated/api.ts +++ b/src/generated/api.ts @@ -1,7 +1,7 @@ /* tslint:disable */ /* eslint-disable */ /** - * RegexSolver + * RegexSolver API * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.1.0 @@ -23,6 +23,39 @@ import type { RequestArgs } from './base'; // @ts-ignore import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError, operationServerMap } from './base'; +/** + * The plan limits currently applying to the account. + */ +export interface AccountLimits { + 'type': AccountLimitsTypeEnum; + /** + * Maximum number of requests allowed per billing period. + */ + 'maxRequestsCount': number; + /** + * Maximum number of requests allowed per second. `0` means no rate limit is enforced. + */ + 'maxRequestsRate': number; + /** + * Maximum number of terms accepted in a single request. + */ + 'maxTermsCount': number; + /** + * Maximum execution timeout per request, in milliseconds. + */ + 'maxTimeout': number; + /** + * Maximum number of automaton states an operation may build. + */ + 'maxStatesCount': number; +} + +export const AccountLimitsTypeEnum = { + AccountLimits: 'accountLimits', +} as const; + +export type AccountLimitsTypeEnum = typeof AccountLimitsTypeEnum[keyof typeof AccountLimitsTypeEnum]; + /** * @type Cardinality * Number of unique strings matched by a term. @@ -172,7 +205,32 @@ export interface FairResponseOptions { 'deterministic'?: boolean; } /** - * Request to generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. For consistent pagination, `term` should be deterministic. + * Order in which the strings within each path are produced. Orthogonal to `pathOrder`: it does not change *what* can be generated, only which strings are reached first. `ascending` expands each position from the low end of its character range first, so `[a-z]{8}` yields `aaaaaaaa`, `aaaaaaab`, ... — a stable, spec-defined order returning the smallest witnesses of a path first. `shuffled` applies a permutation drawn from `seed`, so `[a-z]{8}` yields something like `sjtwsive` instead: the strings look like real inputs. Random in look only — generation stays reproducible and pages with `offset`, though offsets are only consistent between calls sharing the same `seed`, and the exact sequence may change between releases. Use `charset` to restrict generation to specific characters. + */ + +export const GenerateStringsCharacterOrder = { + Ascending: 'ascending', + Shuffled: 'shuffled', +} as const; + +export type GenerateStringsCharacterOrder = typeof GenerateStringsCharacterOrder[keyof typeof GenerateStringsCharacterOrder]; + + +/** + * Order in which the paths of the language are scheduled — the *shapes* the term allows, as opposed to the characters filling them (`characterOrder`). `sweep` expands one path in full, shortest first, before moving to the next one: the cheapest way to page through a whole language with `offset`. `interleave` covers every path the term holds before any path is asked for a second string, so a `limit` smaller than the number of shapes is spent entirely on distinct shapes; slower than `sweep`, but better suited to deriving test cases. `shuffled` is `interleave` with same-length paths visited in an order drawn by `seed`. Shorter paths still come first, so the seed only draws among paths of equal length. All three are deterministic and page with `offset`; for `shuffled`, offsets are only consistent between calls sharing the same `seed`. + */ + +export const GenerateStringsPathOrder = { + Sweep: 'sweep', + Interleave: 'interleave', + Shuffled: 'shuffled', +} as const; + +export type GenerateStringsPathOrder = typeof GenerateStringsPathOrder[keyof typeof GenerateStringsPathOrder]; + + +/** + * Request to generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings and confined to lengths between `minLength` and `maxLength`. For consistent pagination, `term` should be deterministic. */ export interface GenerateStringsRequest { /** @@ -186,9 +244,35 @@ export interface GenerateStringsRequest { /** * Number of matched strings to skip before starting to collect the results. Used for pagination. */ - 'offset': number; + 'offset'?: number; + /** + * Shortest string to generate. Strings shorter than this are left out of the enumeration entirely, `offset` never counting them. + */ + 'minLength'?: number; + /** + * Longest string to generate. Strings longer than this are left out of the enumeration entirely, `offset` never counting them. A value below `minLength` leaves nothing to generate. + */ + 'maxLength'?: number; + /** + * Order in which the paths of the language are scheduled. Defaults to `sweep`. + */ + 'pathOrder'?: GenerateStringsPathOrder; + /** + * Order in which the strings within each path are produced. Defaults to `ascending`. + */ + 'characterOrder'?: GenerateStringsCharacterOrder; + /** + * Seed behind the `shuffled` modes of `pathOrder` and `characterOrder`; ignored when neither is used. The default seed is fixed rather than random, so two calls sharing a seed generate the same strings and `offset` pages through them consistently. Change it to draw a different sequence from the same term. + */ + 'seed'?: number; + /** + * Character class the generated strings are restricted to, such as `[a-z]` or `\\P{C}`. Paths needing a character outside of it are dropped entirely. If omitted, every character the term allows is used. + */ + 'charset'?: string | null; 'options'?: RequestOptions; } + + /** * Response containing distinct strings generated from the requested `term`. */ @@ -231,6 +315,10 @@ export interface Length200Response { 'success': boolean; 'data': Length; } +export interface Limits200Response { + 'success': boolean; + 'data': AccountLimits; +} /** * Wrapper for a boolean value. */ @@ -415,6 +503,104 @@ export interface TwoTermsRequest { 'options'?: RequestOptions; } +/** + * AccountApi - axios parameter creator + */ +export const AccountApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * Return the plan limits applying to the account. + * @summary Limits + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + limits: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/account/limits`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * AccountApi - functional programming interface + */ +export const AccountApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = AccountApiAxiosParamCreator(configuration) + return { + /** + * Return the plan limits applying to the account. + * @summary Limits + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async limits(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.limits(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AccountApi.limits']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * AccountApi - factory interface + */ +export const AccountApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = AccountApiFp(configuration) + return { + /** + * Return the plan limits applying to the account. + * @summary Limits + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + limits(options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.limits(options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * AccountApi - object-oriented interface + */ +export class AccountApi extends BaseAPI { + /** + * Return the plan limits applying to the account. + * @summary Limits + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public limits(options?: RawAxiosRequestConfig) { + return AccountApiFp(this.configuration).limits(options).then((request) => request(this.axios, this.basePath)); + } +} + + + /** * AnalyzeApi - axios parameter creator */ @@ -1728,7 +1914,7 @@ export class ComputeApi extends BaseAPI { export const GenerateApiAxiosParamCreator = function (configuration?: Configuration) { return { /** - * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. + * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings, scheduling the paths of the language in `pathOrder`, producing the strings within each path in `characterOrder`, confined to lengths between `minLength` and `maxLength` and to the characters of `charset`. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. * @summary Strings * @param {GenerateStringsRequest} generateStringsRequest * @param {*} [options] Override http request option. @@ -1776,7 +1962,7 @@ export const GenerateApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = GenerateApiAxiosParamCreator(configuration) return { /** - * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. + * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings, scheduling the paths of the language in `pathOrder`, producing the strings within each path in `characterOrder`, confined to lengths between `minLength` and `maxLength` and to the characters of `charset`. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. * @summary Strings * @param {GenerateStringsRequest} generateStringsRequest * @param {*} [options] Override http request option. @@ -1798,7 +1984,7 @@ export const GenerateApiFactory = function (configuration?: Configuration, baseP const localVarFp = GenerateApiFp(configuration) return { /** - * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. + * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings, scheduling the paths of the language in `pathOrder`, producing the strings within each path in `characterOrder`, confined to lengths between `minLength` and `maxLength` and to the characters of `charset`. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. * @summary Strings * @param {GenerateStringsRequest} generateStringsRequest * @param {*} [options] Override http request option. @@ -1815,7 +2001,7 @@ export const GenerateApiFactory = function (configuration?: Configuration, baseP */ export class GenerateApi extends BaseAPI { /** - * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. + * Generate up to `limit` distinct strings matched by `term`, skipping the first `offset` strings, scheduling the paths of the language in `pathOrder`, producing the strings within each path in `characterOrder`, confined to lengths between `minLength` and `maxLength` and to the characters of `charset`. Strings are only guaranteed to be distinct within a single call; pagination across calls is only consistent (no repeats or gaps) if `term` is deterministic. Call `/analyze/deterministic` to check, and `/compute/determinize` first if needed. * @summary Strings * @param {GenerateStringsRequest} generateStringsRequest * @param {*} [options] Override http request option. diff --git a/src/generated/base.ts b/src/generated/base.ts index 590d2ae..6d4a962 100644 --- a/src/generated/base.ts +++ b/src/generated/base.ts @@ -1,7 +1,7 @@ /* tslint:disable */ /* eslint-disable */ /** - * RegexSolver + * RegexSolver API * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.1.0 diff --git a/src/generated/common.ts b/src/generated/common.ts index 14d5420..6a4ad72 100644 --- a/src/generated/common.ts +++ b/src/generated/common.ts @@ -1,7 +1,7 @@ /* tslint:disable */ /* eslint-disable */ /** - * RegexSolver + * RegexSolver API * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.1.0 diff --git a/src/generated/configuration.ts b/src/generated/configuration.ts index 324f346..67f7077 100644 --- a/src/generated/configuration.ts +++ b/src/generated/configuration.ts @@ -1,6 +1,6 @@ /* tslint:disable */ /** - * RegexSolver + * RegexSolver API * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.1.0 diff --git a/src/generated/index.ts b/src/generated/index.ts index 31e5aa5..25d011c 100644 --- a/src/generated/index.ts +++ b/src/generated/index.ts @@ -1,7 +1,7 @@ /* tslint:disable */ /* eslint-disable */ /** - * RegexSolver + * RegexSolver API * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.1.0 diff --git a/src/index.ts b/src/index.ts index 27dcedc..e1588ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,8 @@ export * from "./RegexSolverClient"; export * from "./models/Term"; +export * from "./models/AccountLimits"; export * from "./models/Cardinality"; +export * from "./models/GenerateStringsOrder"; export * from "./models/Length"; export * from "./models/ResponseFormat"; export * from "./exceptions"; diff --git a/src/models/AccountLimits.ts b/src/models/AccountLimits.ts new file mode 100644 index 0000000..8a2470f --- /dev/null +++ b/src/models/AccountLimits.ts @@ -0,0 +1,51 @@ +import { AccountLimits as AccountLimitsDto } from "../generated"; + +/** + * The plan limits currently applying to the account. + */ +export class AccountLimits { + /** Maximum number of requests allowed per billing period. */ + public readonly maxRequestsCount: number; + /** Maximum number of requests allowed per second. 0 means no rate limit is enforced. */ + public readonly maxRequestsRate: number; + /** Maximum number of terms accepted in a single request. */ + public readonly maxTermsCount: number; + /** Maximum execution timeout per request, in milliseconds. */ + public readonly maxTimeout: number; + /** Maximum number of automaton states an operation may build. */ + public readonly maxStatesCount: number; + + constructor( + maxRequestsCount: number, + maxRequestsRate: number, + maxTermsCount: number, + maxTimeout: number, + maxStatesCount: number, + ) { + this.maxRequestsCount = maxRequestsCount; + this.maxRequestsRate = maxRequestsRate; + this.maxTermsCount = maxTermsCount; + this.maxTimeout = maxTimeout; + this.maxStatesCount = maxStatesCount; + } + + public static fromDto(dto: AccountLimitsDto): AccountLimits { + return new AccountLimits( + dto.maxRequestsCount, + dto.maxRequestsRate, + dto.maxTermsCount, + dto.maxTimeout, + dto.maxStatesCount, + ); + } + + public toString(): string { + return ( + `` + ); + } +} diff --git a/src/models/GenerateStringsOrder.ts b/src/models/GenerateStringsOrder.ts new file mode 100644 index 0000000..d022cb0 --- /dev/null +++ b/src/models/GenerateStringsOrder.ts @@ -0,0 +1,40 @@ +/** + * Order in which the paths of the language are scheduled when generating + * strings — the *shapes* the term allows, as opposed to the characters + * filling them. + */ +export enum PathOrder { + /** + * Expand one path in full, shortest first, before moving to the next one. + * The cheapest way to page through a whole language. + */ + SWEEP = "sweep", + /** + * Cover every path once before any path yields a second string. Best + * suited to deriving test cases. + */ + INTERLEAVE = "interleave", + /** + * Interleave with same-length paths visited in an order drawn from the + * seed. + */ + SHUFFLED = "shuffled", +} + +/** + * Order in which the strings within each path are produced when generating + * strings. Orthogonal to PathOrder: it does not change *what* can be + * generated, only which strings are reached first. + */ +export enum CharacterOrder { + /** + * Expand each position from the low end of its character range first — a + * stable order returning the smallest witnesses of a path first. + */ + ASCENDING = "ascending", + /** + * A permutation drawn from the seed, so the strings look like real inputs. + * Random in look only — generation stays reproducible. + */ + SHUFFLED = "shuffled", +} diff --git a/tests/RateLimiter.test.ts b/tests/RateLimiter.test.ts index a23bd87..4b33bb5 100644 --- a/tests/RateLimiter.test.ts +++ b/tests/RateLimiter.test.ts @@ -26,21 +26,42 @@ describe("RateLimiter", () => { expect(duration).toBeLessThan(150); }); - test("multiple triggers should be ignored while blocked", async () => { - const retryAfter1 = 0.2; // 200ms - const retryAfter2 = 0.1; // 100ms - - rateLimiter.trigger(retryAfter1); - rateLimiter.trigger(retryAfter2); // Should be ignored + test("a shorter retry-after never shrinks the pending deadline", async () => { + rateLimiter.trigger(0.2); + rateLimiter.trigger(0.1); // must not shorten the 200ms deadline const start = Date.now(); await rateLimiter.wait(); const duration = Date.now() - start; - expect(duration).toBeGreaterThanOrEqual(200); + expect(duration).toBeGreaterThanOrEqual(190); expect(duration).toBeLessThan(250); }); + test("a longer retry-after arriving while blocked extends the deadline", async () => { + rateLimiter.trigger(0.1); + rateLimiter.trigger(0.2); // the later deadline wins + + const start = Date.now(); + await rateLimiter.wait(); + const duration = Date.now() - start; + + expect(duration).toBeGreaterThanOrEqual(190); + }); + + test("a deadline extended while waiting is honored", async () => { + rateLimiter.trigger(0.1); + + const start = Date.now(); + const waiter = rateLimiter.wait(); + setTimeout(() => rateLimiter.trigger(0.2), 50); + await waiter; + const duration = Date.now() - start; + + // The waiter woke at the original deadline, re-checked, and slept again. + expect(duration).toBeGreaterThanOrEqual(240); + }); + test("concurrent waits should all resolve after block is lifted", async () => { const retryAfter = 0.1; // 100ms rateLimiter.trigger(retryAfter); diff --git a/tests/client.test.ts b/tests/client.test.ts index 4195995..45f86c9 100644 --- a/tests/client.test.ts +++ b/tests/client.test.ts @@ -365,6 +365,251 @@ describe("RegexSolverClient", () => { ); }); + test("constructor rejects an empty apiToken", () => { + expect(() => new RegexSolverClient({ apiToken: "" })).toThrow( + "apiToken is required", + ); + }); + + test("constructor rejects maxTermsPerRequest below 2", () => { + expect( + () => + new RegexSolverClient({ apiToken: "test-token", maxTermsPerRequest: 1 }), + ).toThrow("maxTermsPerRequest must be at least 2"); + }); + + test("getAccountLimits should work and be memoized", async () => { + mock.onGet("/account/limits").reply(200, { + success: true, + data: { + type: "accountLimits", + maxRequestsCount: 1000, + maxRequestsRate: 10, + maxTermsCount: 4, + maxTimeout: 60000, + maxStatesCount: 8192, + }, + }); + + const limits = await client.getAccountLimits(); + expect(limits.maxRequestsCount).toBe(1000); + expect(limits.maxRequestsRate).toBe(10); + expect(limits.maxTermsCount).toBe(4); + expect(limits.maxTimeout).toBe(60000); + expect(limits.maxStatesCount).toBe(8192); + + await client.getAccountLimits(); + expect(mock.history.get.length).toBe(1); + }); + + test("proactive batching folds oversized concat calls in order", async () => { + const batchClient = new RegexSolverClient({ + apiToken: "batch-token", + maxTermsPerRequest: 3, + }); + const batchMock = new MockAdapter((batchClient as any).axiosInstance); + for (const value of ["r0", "r1", "r2", "r3"]) { + batchMock.onPost("/compute/concat").replyOnce(200, { + success: true, + data: { type: "regex", value }, + }); + } + + const terms = Array.from({ length: 8 }, (_, i) => Term.regex(`t${i}`)); + const result = await batchClient.concat(terms, { + responseFormat: "regex", + }); + expect(result.getValue()).toBe("r3"); + + const bodies = batchMock.history.post.map((r) => JSON.parse(r.data)); + expect(bodies.length).toBe(4); + // Left fold preserves concat order: contiguous chunks, accumulator first. + expect(bodies[0].terms.map((t: any) => t.value)).toEqual([ + "t0", + "t1", + "t2", + ]); + expect(bodies[1].terms.map((t: any) => t.value)).toEqual([ + "r0", + "t3", + "t4", + ]); + expect(bodies[2].terms.map((t: any) => t.value)).toEqual([ + "r1", + "t5", + "t6", + ]); + expect(bodies[3].terms.map((t: any) => t.value)).toEqual(["r2", "t7"]); + // Only the final request carries the caller's response options. + expect(bodies[0].options.response).toBeUndefined(); + expect(bodies[1].options.response).toBeUndefined(); + expect(bodies[2].options.response).toBeUndefined(); + expect(bodies[3].options.response).toEqual({ format: "regex" }); + // The limit was known up front, so no limits fetch happened. + expect(batchMock.history.get.length).toBe(0); + batchMock.restore(); + }); + + test("reactive batching fetches the limits once and re-runs batched", async () => { + mock.onGet("/account/limits").reply(200, { + success: true, + data: { + type: "accountLimits", + maxRequestsCount: 1000, + maxRequestsRate: 10, + maxTermsCount: 4, + maxTimeout: 60000, + maxStatesCount: 8192, + }, + }); + mock.onPost("/compute/union").replyOnce(400, { + success: false, + error: "9 terms provided. Maximum allowed is 4.", + errorCode: "TooManyTerms", + }); + for (const value of ["r0", "r1", "r2"]) { + mock.onPost("/compute/union").replyOnce(200, { + success: true, + data: { type: "regex", value }, + }); + } + + const terms = Array.from({ length: 9 }, (_, i) => Term.regex(`t${i}`)); + const result = await client.union(terms); + expect(result.getValue()).toBe("r2"); + + expect(mock.history.get.length).toBe(1); + const bodies = mock.history.post.map((r: any) => JSON.parse(r.data)); + expect(bodies.length).toBe(4); + expect(bodies[1].terms.map((t: any) => t.value)).toEqual([ + "t0", + "t1", + "t2", + "t3", + ]); + expect(bodies[2].terms.map((t: any) => t.value)).toEqual([ + "r0", + "t4", + "t5", + "t6", + ]); + expect(bodies[3].terms.map((t: any) => t.value)).toEqual([ + "r1", + "t7", + "t8", + ]); + }); + + test("autoBatch: false surfaces TooManyTerms without fetching limits", async () => { + const noBatchClient = new RegexSolverClient({ + apiToken: "no-batch-token", + autoBatch: false, + }); + const noBatchMock = new MockAdapter((noBatchClient as any).axiosInstance); + noBatchMock.onPost("/compute/union").reply(400, { + success: false, + error: "9 terms provided. Maximum allowed is 4.", + errorCode: "TooManyTerms", + }); + + const terms = Array.from({ length: 9 }, (_, i) => Term.regex(`t${i}`)); + await expect(noBatchClient.union(terms)).rejects.toThrow( + Exceptions.TooManyTermsError, + ); + expect(noBatchMock.history.get.length).toBe(0); + noBatchMock.restore(); + }); + + test("a failed limits fetch rethrows the original TooManyTerms", async () => { + mock.onGet("/account/limits").reply(500, { + success: false, + error: "Internal server error", + }); + mock.onPost("/compute/union").reply(400, { + success: false, + error: "9 terms provided. Maximum allowed is 4.", + errorCode: "TooManyTerms", + }); + + const terms = Array.from({ length: 9 }, (_, i) => Term.regex(`t${i}`)); + await expect(client.union(terms)).rejects.toThrow( + Exceptions.TooManyTermsError, + ); + // The memo was cleared on rejection, so a later call fetches again. + await expect(client.getAccountLimits()).rejects.toThrow( + Exceptions.InternalServerError, + ); + expect(mock.history.get.length).toBe(2); + }); + + test("generateStrings serializes the ordering options", async () => { + mock.onPost("/generate/strings").reply(200, { + success: true, + data: { + type: "generatedStrings", + strings: { type: "strings", value: ["xy"] }, + }, + }); + + const result = await client.generateStrings(Term.regex("[a-z]{2}"), 5, 0, { + pathOrder: "interleave", + characterOrder: "shuffled", + seed: 42, + minLength: 1, + maxLength: 10, + charset: "[a-z]", + }); + expect(result).toEqual(["xy"]); + + const body = JSON.parse(mock.history.post[0].data); + expect(body.pathOrder).toBe("interleave"); + expect(body.characterOrder).toBe("shuffled"); + expect(body.seed).toBe(42); + expect(body.minLength).toBe(1); + expect(body.maxLength).toBe(10); + expect(body.charset).toBe("[a-z]"); + }); + + test("generateStrings omits unset options from the body", async () => { + mock.onPost("/generate/strings").reply(200, { + success: true, + data: { + type: "generatedStrings", + strings: { type: "strings", value: ["a"] }, + }, + }); + + await client.generateStrings(Term.regex("a"), 1, 0); + + const body = JSON.parse(mock.history.post[0].data); + expect(body.pathOrder).toBeUndefined(); + expect(body.characterOrder).toBeUndefined(); + expect(body.seed).toBeUndefined(); + expect(body.minLength).toBeUndefined(); + expect(body.maxLength).toBeUndefined(); + expect(body.charset).toBeUndefined(); + }); + + test("more consecutive 429s than the old retry cap still succeed", async () => { + jest.spyOn(Math, "random").mockReturnValue(0); + for (let i = 0; i < 3; i++) { + mock.onPost("/analyze/cardinality").replyOnce( + 429, + { success: false, error: "Too many requests" }, + { "retry-after": "0.02" }, + ); + } + mock.onPost("/analyze/cardinality").reply(200, { + success: true, + data: { type: "integer", value: 26 }, + }); + + const cardinality = await client.getCardinality(Term.regex("[a-z]")); + expect(cardinality).toEqual(new Integer(26)); + expect(mock.history.post.length).toBe(4); + (Math.random as jest.Mock).mockRestore(); + }); + test("rate limit with retry-after should work and block concurrent requests", async () => { // First call 429 mock.onPost("/analyze/cardinality").replyOnce( From ad3464648dafa46fec0ee0867cb50489750379a9 Mon Sep 17 00:00:00 2001 From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com> Date: Wed, 5 Aug 2026 22:07:52 +0200 Subject: [PATCH 20/20] Fix failing build --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5026b9f..a78b238 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.1.0", "license": "MIT", "dependencies": { - "axios": "^1.13.6" + "axios": "~1.18.1" }, "devDependencies": { "@types/jest": "^30.0.0", @@ -1641,13 +1641,13 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", - "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", - "form-data": "^4.0.6", + "form-data": "^4.0.5", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } diff --git a/package.json b/package.json index 615bfd0..4b08b7c 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "Alexandre van Beurden (https://github.com/alexvbrdn)" ], "dependencies": { - "axios": "^1.13.6" + "axios": "~1.18.1" }, "devDependencies": { "@types/jest": "^30.0.0",