Merge branch 'master' into feature/zuc-cipher-3656552422777355745

This commit is contained in:
Kyi Wong 2026-07-30 17:57:56 +08:00 committed by GitHub
commit 9ca4ebee0a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
53 changed files with 5398 additions and 3147 deletions

View File

@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Close stale unsigned-CLA PRs
uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 #v10.3.0
uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 #v10.4.0
with:
# ---- Guards: only act on PRs carrying the CLA label ----
only-labels: 'awaiting cla'

View File

@ -16,10 +16,10 @@ jobs:
pages: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set node version
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
registry-url: "https://registry.npmjs.org"

View File

@ -12,10 +12,10 @@ jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set node version
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
registry-url: "https://registry.npmjs.org"
@ -61,14 +61,14 @@ jobs:
- name: Set up Docker Buildx
if: success()
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- name: Set up QEMU
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
- name: Production Image Build
if: success()
id: build-image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7

View File

@ -22,10 +22,10 @@ jobs:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set node version
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
registry-url: "https://registry.npmjs.org"
@ -61,14 +61,14 @@ jobs:
xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- name: Set up QEMU
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
- name: Image Metadata
id: image-metadata
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
@ -77,14 +77,14 @@ jobs:
type=semver,pattern={{version}}
- name: Log in to GHCR
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.REGISTRY_USER }}
password: ${{ env.REGISTRY_PASSWORD }}
- name: Publish to GHCR
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
push: true
@ -110,10 +110,10 @@ jobs:
needs: main
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set node version
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
registry-url: "https://registry.npmjs.org"

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ build
src/core/config/modules/*
src/core/config/OperationConfig.json
src/core/operations/index.mjs
src/core/lib/HTMLEntities.mjs
src/node/config/OperationConfig.json
src/node/index.mjs
tests/operations/index.mjs

View File

@ -13,6 +13,75 @@ All major and minor version changes will be documented in this file. Details of
## Details
### [11.3.0] - 2026-07-24
This release includes a security fix ([#2687])
- Security: Fix pretty recipe parser ReDoS [@zainnadeem786] | [#2687]
- feat: add modulo operation [@thomasnemer] [@GCHQDeveloper581] | [#2103]
- Add HMAC regression tests for Decimal key parsing [@alleria173] | [#2680]
- fix: await Node API operations whose run() returns a non-async Promise [@roberson-io] | [#2659]
- chore (deps): bump morgan from 1.10.1 to 1.11.0 | [#2676]
- fix: fromDecimal Auto delimiter now correctly parses multiple numbers [@min23asdw] | [#2270]
- Add Generate Prime Number operation [@p-leriche] | [#2212]
- Add Modular Inverse operation [@p-leriche] | [#2207]
- Consolidate HTML entity tables into a single spec-generated source (#2645) [@roberson-io] | [#2671]
- Add Extended GCD operation [@p-leriche] | [#2206]
- Add COBS encoding/decoding operations [@giesmininkas] | [#2185]
- chore (deps): bump websocket-driver from 0.7.4 to 0.7.5 | [#2673]
- fix: remove stray punctuation from malformed To HTML Entity table values [@roberson-io] | [#2660]
- chore (deps): bump the actions-dependencies group across 1 directory with 6 updates | [#2668]
- chore (deps): bump the minor-updates group across 1 directory with 3 updates | [#2669]
- chore (deps): bump the patch-updates group with 5 updates | [#2654]
- feat: add TEA and XTEA block ciphers [@thomasxm] | [#2225]
- feat: add PRESENT and Twofish ciphers [@thomasxm] | [#2157]
- fix: support constructor and __proto__ parameters in Parse URI (#2578) [@mansiverma897993] | [#2581]
- feat: Implement automated option-type ingredient validation [@mansiverma897993] | [#2625]
- Add Ascon (NIST SP 800-232) operations: Hash, MAC, Encrypt, Decrypt [@thomasxm] | [#2155]
- chore (deps): bump the patch-updates group across 1 directory with 6 updates | [#2638]
- chore (deps): bump webpack from 5.107.2 to 5.108.3 in the minor-updates group | [#2635]
- chore (deps): bump nginxinc/nginx-unprivileged from `458ecbe` to `fd3314e` in the docker-dependencies group | [#2633]
- Feature: automatically expire PRs if CLA remains unsigned for an extended period [@GCHQDeveloper581] | [#2636]
- fix/2445 HOTP (and 2426 TOTP) type errors [@alleria173] | [#2620]
- Fix base32 unicode alphabet [@loki1205] | [#2380]
- Add a workflow to automatically flag PRs without a signed CLA [@GCHQDeveloper581] | [#2627]
- fix/2444 TOTP input validation for correct otpauth uri generation [@alleria173] | [#2621]
- Validate Wrap line width [@vetrovk] [@GCHQDeveloper581] [@C85297] | [#2606]
- Handle malformed image parser errors in View Bit Plane [@zainnadeem786] | [#2612]
- Fixes #2446 hotp otpauth uri validation [@alleria173] | [#2614]
- Handle invalid bcrypt salt errors in Bcrypt compare [@zainnadeem786] | [#2615]
- Validate empty Show On Map options [@vetrovk] | [#2631]
- Create AGENTS.md file [@C85297] | [#2619]
- Set parameter validation Metadata for GenerateImage operations [@GCHQDeveloper581] | [#2611]
- Update 4 vulnerable dependencies [@GCHQDeveloper581] | [#2616]
- Fix BigNumber deserialisation in Dish, and add tests [@GCHQDeveloper581] | [#2607]
- chore (deps): bump the docker-dependencies group with 2 updates | [#2600]
- chore (deps): bump the patch-updates group with 8 updates | [#2602]
- chore (deps): bump actions/checkout from 6.0.3 to 7.0.0 in the actions-dependencies group | [#2601]
- chore (deps): bump the minor-updates group with 2 updates | [#2603]
- Handle empty Generate Image mode [@vetrovk] | [#2598]
- Fix stale presenter after expected operation errors [@zainnadeem786] [@GCHQDeveloper581] | [#2589]
- Clean up/rationalise webpack paths and thereby increase compatibility for Win… [@GCHQDeveloper581] | [#2585]
- Improve parameter validation for a number of operations where exceptions otherwise caused. [@GCHQDeveloper581] | [#2586]
- Fix uncaught TypeError in "Show on map" operation. [@lzandman] | [#2453]
- fix: jsonata $base64decode/$base64encode in Web Worker [@min23asdw] | [#2275]
- fix Dechunk HTTP Response leaks terminating chunk and trailers into output [@williballenthin] | [#2290]
- fix: MIME Decoding corrupts non-ASCII characters in Base64-encoded words [@williballenthin] | [#2291]
- fix: Gzip comment with header checksum produces corrupt streams [@williballenthin] | [#2288]
- fix TLV Parser BER long-form length parsing [@williballenthin] | [#2289]
- fix: Unescape Unicode Characters accepts 4-6 hex digits for U+ prefix [@williballenthin] | [#2287]
- fix Set Difference and Set Intersection preserve duplicates from first sample [@williballenthin] | [#2286]
- fix: From Base operation produces wrong results for fractional inputs [@williballenthin] | [#2285]
- fix Median operation returns incorrect result for unsorted odd-length inputs [@williballenthin] | [#2284]
- Added RenderPDF functionality [@Shailendra1703] [@GCHQDeveloper581] | [#2105]
- Fix URL encoding incorrectly converting input to UTF-8 [@C85297] | [#2340]
- feat: Add automated parameter validation framework [@mansiverma897993] | [#2561]
- Fix: added viewport styles to img tag in RenderImage Dish [@Shailendra1703] [@C85297] | [#2109]
- chore (deps): bump form-data from 4.0.5 to 4.0.6 | [#2572]
- chore (deps): bump the patch-updates group with 5 updates [@GCHQDeveloper581] | [#2580]
- chore (deps): bump the docker-dependencies group with 2 updates | [#2579]
- Fix operation description rendering [@C85297] | [#2577]
- chore (deps): bump launch-editor from 2.13.1 to 2.14.1 | [#2574]
- chore (deps): bump dompurify from 3.4.8 to 3.4.9 | [#2573]
### [11.2.0] - 2026-06-17
This release includes a security fix ([#2569])
- Security: Chart operation prototype protection [@C85297] | [#2569]
@ -718,6 +787,7 @@ Breaking changes:
## [4.0.0] - 2016-11-28
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
[11.3.0]: https://github.com/gchq/CyberChef/releases/tag/v11.3.0
[11.2.0]: https://github.com/gchq/CyberChef/releases/tag/v11.2.0
[11.1.0]: https://github.com/gchq/CyberChef/releases/tag/v11.1.0
[11.0.0]: https://github.com/gchq/CyberChef/releases/tag/v11.0.0
@ -1018,6 +1088,17 @@ Breaking changes:
[@qa2me]: https://github.com/qa2me
[@heapframe]: https://github.com/heapframe
[@skyswordw]: https://github.com/skyswordw
[@thomasnemer]: https://github.com/thomasnemer
[@alleria173]: https://github.com/alleria173
[@roberson-io]: https://github.com/roberson-io
[@min23asdw]: https://github.com/min23asdw
[@giesmininkas]: https://github.com/giesmininkas
[@mansiverma897993]: https://github.com/mansiverma897993
[@loki1205]: https://github.com/loki1205
[@vetrovk]: https://github.com/vetrovk
[@zainnadeem786]: https://github.com/zainnadeem786
[@williballenthin]: https://github.com/williballenthin
[@Shailendra1703]: https://github.com/Shailendra1703
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
@ -1389,4 +1470,69 @@ Breaking changes:
[#2404]: https://github.com/gchq/CyberChef/pull/2404
[#2458]: https://github.com/gchq/CyberChef/pull/2458
[#2514]: https://github.com/gchq/CyberChef/pull/2514
[#2687]: https://github.com/gchq/CyberChef/pull/2687
[#2103]: https://github.com/gchq/CyberChef/pull/2103
[#2680]: https://github.com/gchq/CyberChef/pull/2680
[#2659]: https://github.com/gchq/CyberChef/pull/2659
[#2676]: https://github.com/gchq/CyberChef/pull/2676
[#2270]: https://github.com/gchq/CyberChef/pull/2270
[#2212]: https://github.com/gchq/CyberChef/pull/2212
[#2207]: https://github.com/gchq/CyberChef/pull/2207
[#2671]: https://github.com/gchq/CyberChef/pull/2671
[#2206]: https://github.com/gchq/CyberChef/pull/2206
[#2185]: https://github.com/gchq/CyberChef/pull/2185
[#2673]: https://github.com/gchq/CyberChef/pull/2673
[#2660]: https://github.com/gchq/CyberChef/pull/2660
[#2668]: https://github.com/gchq/CyberChef/pull/2668
[#2669]: https://github.com/gchq/CyberChef/pull/2669
[#2654]: https://github.com/gchq/CyberChef/pull/2654
[#2225]: https://github.com/gchq/CyberChef/pull/2225
[#2157]: https://github.com/gchq/CyberChef/pull/2157
[#2581]: https://github.com/gchq/CyberChef/pull/2581
[#2625]: https://github.com/gchq/CyberChef/pull/2625
[#2155]: https://github.com/gchq/CyberChef/pull/2155
[#2638]: https://github.com/gchq/CyberChef/pull/2638
[#2635]: https://github.com/gchq/CyberChef/pull/2635
[#2633]: https://github.com/gchq/CyberChef/pull/2633
[#2636]: https://github.com/gchq/CyberChef/pull/2636
[#2620]: https://github.com/gchq/CyberChef/pull/2620
[#2380]: https://github.com/gchq/CyberChef/pull/2380
[#2627]: https://github.com/gchq/CyberChef/pull/2627
[#2621]: https://github.com/gchq/CyberChef/pull/2621
[#2606]: https://github.com/gchq/CyberChef/pull/2606
[#2612]: https://github.com/gchq/CyberChef/pull/2612
[#2614]: https://github.com/gchq/CyberChef/pull/2614
[#2615]: https://github.com/gchq/CyberChef/pull/2615
[#2631]: https://github.com/gchq/CyberChef/pull/2631
[#2619]: https://github.com/gchq/CyberChef/pull/2619
[#2611]: https://github.com/gchq/CyberChef/pull/2611
[#2616]: https://github.com/gchq/CyberChef/pull/2616
[#2607]: https://github.com/gchq/CyberChef/pull/2607
[#2600]: https://github.com/gchq/CyberChef/pull/2600
[#2602]: https://github.com/gchq/CyberChef/pull/2602
[#2601]: https://github.com/gchq/CyberChef/pull/2601
[#2603]: https://github.com/gchq/CyberChef/pull/2603
[#2598]: https://github.com/gchq/CyberChef/pull/2598
[#2589]: https://github.com/gchq/CyberChef/pull/2589
[#2585]: https://github.com/gchq/CyberChef/pull/2585
[#2586]: https://github.com/gchq/CyberChef/pull/2586
[#2453]: https://github.com/gchq/CyberChef/pull/2453
[#2275]: https://github.com/gchq/CyberChef/pull/2275
[#2290]: https://github.com/gchq/CyberChef/pull/2290
[#2291]: https://github.com/gchq/CyberChef/pull/2291
[#2288]: https://github.com/gchq/CyberChef/pull/2288
[#2289]: https://github.com/gchq/CyberChef/pull/2289
[#2287]: https://github.com/gchq/CyberChef/pull/2287
[#2286]: https://github.com/gchq/CyberChef/pull/2286
[#2285]: https://github.com/gchq/CyberChef/pull/2285
[#2284]: https://github.com/gchq/CyberChef/pull/2284
[#2105]: https://github.com/gchq/CyberChef/pull/2105
[#2340]: https://github.com/gchq/CyberChef/pull/2340
[#2561]: https://github.com/gchq/CyberChef/pull/2561
[#2109]: https://github.com/gchq/CyberChef/pull/2109
[#2572]: https://github.com/gchq/CyberChef/pull/2572
[#2580]: https://github.com/gchq/CyberChef/pull/2580
[#2579]: https://github.com/gchq/CyberChef/pull/2579
[#2577]: https://github.com/gchq/CyberChef/pull/2577
[#2574]: https://github.com/gchq/CyberChef/pull/2574
[#2573]: https://github.com/gchq/CyberChef/pull/2573

View File

@ -27,7 +27,7 @@ RUN npm run build
#########################################
# Package static build files into nginx #
#########################################
FROM nginxinc/nginx-unprivileged:stable-alpine@sha256:fd3314e343bad2de4e1127ef58be122abbfa7e09572fa46ae62fcddb6b3f21c5 AS cyberchef
FROM nginxinc/nginx-unprivileged:stable-alpine@sha256:44e36330f74d4f3a1d4e222acca9e23b401fb87811a7597024502bb759c4dd49 AS cyberchef
LABEL maintainer="GCHQ <oss@gchq.gov.uk>"

View File

@ -367,6 +367,7 @@ module.exports = function (grunt) {
command: chainCommands([
"echo '\n--- Regenerating config files. ---'",
"echo [] > src/core/config/OperationConfig.json",
`node ${nodeFlags} src/core/config/scripts/generateHTMLEntities.mjs`,
`node ${nodeFlags} src/core/config/scripts/generateOpsIndex.mjs`,
`node ${nodeFlags} src/core/config/scripts/generateConfig.mjs`,
"echo '--- Config scripts finished. ---\n'"

459
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "cyberchef",
"version": "11.2.0",
"version": "11.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cyberchef",
"version": "11.2.0",
"version": "11.3.0",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@ -21,7 +21,7 @@
"assert": "^2.1.0",
"avsc": "^5.7.9",
"bcryptjs": "^3.0.3",
"bignumber.js": "^11.1.4",
"bignumber.js": "^11.1.5",
"blakejs": "^1.2.1",
"bootstrap": "4.6.2",
"bootstrap-colorpicker": "^3.4.0",
@ -39,7 +39,7 @@
"d3": "7.9.0",
"d3-hexbin": "^0.2.2",
"diff": "^9.0.0",
"dompurify": "^3.4.11",
"dompurify": "^3.4.12",
"es6-promisify": "^7.0.0",
"escodegen": "^2.1.0",
"esprima": "^4.0.1",
@ -59,7 +59,7 @@
"js-sha3": "^0.9.3",
"jsesc": "^3.1.0",
"json5": "^2.2.3",
"jsonata": "^2.2.1",
"jsonata": "^2.2.2",
"jsonpath-plus": "^10.4.0",
"jsonwebtoken": "9.0.3",
"jsqr": "^1.4.0",
@ -72,9 +72,9 @@
"loglevel-message-prefix": "^3.0.0",
"lz-string": "^1.5.0",
"lz4js": "^0.2.0",
"markdown-it": "^14.2.0",
"markdown-it": "^14.3.0",
"moment": "^2.30.1",
"moment-timezone": "^0.6.2",
"moment-timezone": "^0.6.3",
"ngeohash": "^0.6.3",
"node-forge": "^1.4.0",
"node-md6": "^0.1.0",
@ -86,7 +86,7 @@
"path": "^0.12.7",
"popper.js": "^1.16.1",
"process": "^0.11.10",
"protobufjs": "^8.6.5",
"protobufjs": "^8.7.1",
"punycode.js": "^2.3.1",
"qr-image": "^3.2.0",
"reflect-metadata": "^0.2.2",
@ -118,13 +118,13 @@
"@codemirror/commands": "^6.10.4",
"@codemirror/language": "^6.12.4",
"@codemirror/search": "^6.7.1",
"@codemirror/state": "^6.5.4",
"@codemirror/view": "^6.43.4",
"@codemirror/state": "^6.7.1",
"@codemirror/view": "^6.43.6",
"@puppeteer/browsers": "3.0.6",
"autoprefixer": "^10.5.2",
"autoprefixer": "^10.5.4",
"babel-loader": "^10.1.1",
"base64-loader": "^1.0.0",
"chromedriver": "^148.0.4",
"chromedriver": "^150.0.4",
"cli-progress": "^3.12.0",
"colors": "^1.4.0",
"compression-webpack-plugin": "^12.0.0",
@ -132,7 +132,7 @@
"core-js": "^3.49.0",
"cspell": "^10.0.1",
"css-loader": "^7.1.4",
"eslint": "^9.39.4",
"eslint": "^9.39.5",
"eslint-plugin-jsdoc": "^50.8.0",
"globals": "^17.7.0",
"grunt": "^1.6.2",
@ -151,16 +151,16 @@
"mini-css-extract-plugin": "2.10.2",
"modify-source-webpack-plugin": "^4.1.0",
"nightwatch": "^3.16.0",
"postcss": "^8.5.16",
"postcss": "^8.5.21",
"postcss-css-variables": "^0.19.0",
"postcss-import": "^16.1.1",
"postcss-loader": "^8.2.1",
"prompt": "^1.3.0",
"sitemap": "^9.0.1",
"terser": "^5.48.0",
"webpack": "^5.108.3",
"webpack-bundle-analyzer": "^5.3.0",
"webpack-dev-server": "^5.2.5",
"terser": "^5.49.0",
"webpack": "^5.108.4",
"webpack-bundle-analyzer": "^5.3.1",
"webpack-dev-server": "^5.2.6",
"webpack-node-externals": "^3.0.0",
"worker-loader": "^3.0.8"
},
@ -1889,9 +1889,9 @@
}
},
"node_modules/@codemirror/state": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.0.tgz",
"integrity": "sha512-Zbl9NyscLMZkfXPQnNAIIAFftidrA1UbcJEIMp24C0Bukc2I5T8wJS0wsXYsnDOqCFJUeJ1BITGNs5CqPDSmSg==",
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.1.tgz",
"integrity": "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1899,9 +1899,9 @@
}
},
"node_modules/@codemirror/view": {
"version": "6.43.4",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.4.tgz",
"integrity": "sha512-YImu23iyKfncJzT7sRy+rEqEhSc8RhOHqDxwy4WzXRKJwYm6iwf/9OJk5ctCAdZ6yi2ZqaGEvmf55fSVqMDrgg==",
"version": "6.43.6",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.6.tgz",
"integrity": "sha512-EVunGSYN1wz1p75WY1s3Xg7t3i8Yol0kGZGizNdX9BUFgMFILYVe8/u6EVpo7Ff5PwbZuILb4QAq7IZoKzIEQA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2776,9 +2776,9 @@
}
},
"node_modules/@eslint/eslintrc": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
"integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz",
"integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2788,7 +2788,7 @@
"globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.1",
"js-yaml": "^4.3.0",
"minimatch": "^3.1.5",
"strip-json-comments": "^3.1.1"
},
@ -2813,9 +2813,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.39.4",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
"integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
"version": "9.39.5",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz",
"integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==",
"dev": true,
"license": "MIT",
"engines": {
@ -5129,9 +5129,9 @@
}
},
"node_modules/adm-zip": {
"version": "0.5.17",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.17.tgz",
"integrity": "sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ==",
"version": "0.5.18",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz",
"integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==",
"dev": true,
"license": "MIT",
"engines": {
@ -5620,9 +5620,9 @@
"license": "MIT"
},
"node_modules/autoprefixer": {
"version": "10.5.2",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.2.tgz",
"integrity": "sha512-rD5t5DwOjJdmSORcTq64j8MawTC+tbQ+HHqjR4NDumamy/ambn1UJrlKL+KdwujWxMkFjPM3pPHOEA9tl4767Q==",
"version": "10.5.4",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz",
"integrity": "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==",
"dev": true,
"funding": [
{
@ -5640,8 +5640,8 @@
],
"license": "MIT",
"dependencies": {
"browserslist": "^4.28.4",
"caniuse-lite": "^1.0.30001799",
"browserslist": "^4.28.6",
"caniuse-lite": "^1.0.30001806",
"fraction.js": "^5.3.4",
"picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0"
@ -5700,17 +5700,45 @@
}
},
"node_modules/axios": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz",
"integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==",
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz",
"integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==",
"dev": true,
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.16.0",
"form-data": "^4.0.5",
"https-proxy-agent": "^5.0.1",
"proxy-from-env": "^2.1.0"
}
},
"node_modules/axios/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==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/axios/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==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/babel-loader": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.1.1.tgz",
@ -5814,9 +5842,9 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
"version": "2.10.40",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.40.tgz",
"integrity": "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==",
"version": "2.11.1",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.1.tgz",
"integrity": "sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@ -5883,9 +5911,9 @@
}
},
"node_modules/bignumber.js": {
"version": "11.1.4",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-11.1.4.tgz",
"integrity": "sha512-AJ9dSeaUGj2xu7tEwmdqb51dqdb633xo4njI9K8ZFfcLrNr0XN8/EPkkZUNaF9fkCblGt2zVwZymesUdGynEkQ==",
"version": "11.1.5",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-11.1.5.tgz",
"integrity": "sha512-6WmzCNtUnfKpbozq+hOgWaZMMzORmYBwF1xZScyoIX3QRYWeKTtxxwDOW5tIz7C9BdjkIYHGTcelCLkXg0mndw==",
"license": "MIT"
},
"node_modules/binary-extensions": {
@ -5996,9 +6024,9 @@
}
},
"node_modules/body-parser": {
"version": "1.20.5",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
"integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
"version": "1.20.6",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz",
"integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -6231,9 +6259,9 @@
}
},
"node_modules/brace-expansion": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
"integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"version": "1.1.16",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
"integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -6348,9 +6376,9 @@
}
},
"node_modules/browserslist": {
"version": "4.28.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz",
"integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==",
"version": "4.28.7",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz",
"integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==",
"dev": true,
"funding": [
{
@ -6368,10 +6396,10 @@
],
"license": "MIT",
"dependencies": {
"baseline-browser-mapping": "^2.10.38",
"caniuse-lite": "^1.0.30001799",
"electron-to-chromium": "^1.5.376",
"node-releases": "^2.0.48",
"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": {
@ -6563,9 +6591,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001799",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
"integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
"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": [
{
@ -6705,20 +6733,20 @@
}
},
"node_modules/chromedriver": {
"version": "148.0.4",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-148.0.4.tgz",
"integrity": "sha512-3UyptFDG4YF1Pyv3fzn95s1CN4K3zCpHSmE6g+6J4f2u9KxxOYzrwN2GApVyM2z02hlbSqzo9Ajn2hMi7LnvCw==",
"version": "150.0.4",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-150.0.4.tgz",
"integrity": "sha512-Yg2oXBONi1mqAMOG2ybwUneSvxIn04NF4qNAJ+yGO2H7WwX1WYLGQRHVHsbySEjIuqIyDmS1ZfAw6p0wtl43zQ==",
"dev": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@testim/chrome-version": "^1.1.4",
"adm-zip": "^0.5.17",
"axios": "^1.16.0",
"compare-versions": "^6.1.0",
"proxy-agent": "^8.0.1",
"proxy-from-env": "^2.0.0",
"tcp-port-used": "^1.0.2"
"adm-zip": "^0.5.18",
"axios": "^1.18.1",
"compare-versions": "^6.1.1",
"proxy-agent": "^8.0.2",
"proxy-from-env": "^2.1.0",
"tcp-port-used": "^1.0.3"
},
"bin": {
"chromedriver": "bin/chromedriver"
@ -8674,9 +8702,9 @@
}
},
"node_modules/dompurify": {
"version": "3.4.11",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz",
"integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==",
"version": "3.4.12",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz",
"integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
@ -8781,9 +8809,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.379",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.379.tgz",
"integrity": "sha512-v/qV5aV5EUA2pGilzUCq5/eyOloZAqDZBu9UMBIzgPpLlprjSR6zswsWBTv0KpqxLGUAZEwhO95ZCt7srymNVA==",
"version": "1.5.396",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.396.tgz",
"integrity": "sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==",
"dev": true,
"license": "ISC"
},
@ -9084,9 +9112,9 @@
}
},
"node_modules/eslint": {
"version": "9.39.4",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
"version": "9.39.5",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz",
"integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -9095,8 +9123,8 @@
"@eslint/config-array": "^0.21.2",
"@eslint/config-helpers": "^0.4.2",
"@eslint/core": "^0.17.0",
"@eslint/eslintrc": "^3.3.5",
"@eslint/js": "9.39.4",
"@eslint/eslintrc": "^3.3.6",
"@eslint/js": "9.39.5",
"@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
@ -9664,9 +9692,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
"integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz",
"integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==",
"dev": true,
"funding": [
{
@ -9744,9 +9772,9 @@
}
},
"node_modules/filelist/node_modules/brace-expansion": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
"integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -10177,13 +10205,13 @@
}
},
"node_modules/get-uri": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-8.0.0.tgz",
"integrity": "sha512-CqtZlMKvfJeY0Zxv8wazDwXmSKmnMnsmNy8j8+wudi8EyG/pMUB1NqHc+Tv1QaNtpYsK9nOYjb7r7Ufu32RPSw==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-8.0.1.tgz",
"integrity": "sha512-/5N/P4Lrh0p/mDwlDRi7Y1+P2o/OyzZI3l6Iz1Ov6XXwwm1y3RlZLuo3gVgML99djrEDtV980bBxSuOeHLk8ww==",
"dev": true,
"license": "MIT",
"dependencies": {
"basic-ftp": "^5.2.0",
"basic-ftp": "^5.3.1",
"data-uri-to-buffer": "8.0.0",
"debug": "^4.3.4"
},
@ -11589,13 +11617,13 @@
}
},
"node_modules/ip-regex": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz",
"integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
"integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
"node": ">=4"
}
},
"node_modules/ipaddr.js": {
@ -12207,15 +12235,15 @@
}
},
"node_modules/is2": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/is2/-/is2-2.0.9.tgz",
"integrity": "sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is2/-/is2-2.0.1.tgz",
"integrity": "sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA==",
"dev": true,
"license": "MIT",
"dependencies": {
"deep-is": "^0.1.3",
"ip-regex": "^4.1.0",
"is-url": "^1.2.4"
"ip-regex": "^2.1.0",
"is-url": "^1.2.2"
},
"engines": {
"node": ">=v0.10.0"
@ -12507,9 +12535,9 @@
}
},
"node_modules/jsonata": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.2.1.tgz",
"integrity": "sha512-xd1uwUrKeIcJbsWhaoS3qAX4Ea8m0Mw0G5nlnAQvPT7TbZ5qaPdzBVTQia9KfyuyQm+nenfyjvzUDTRYHsC2sw==",
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.2.2.tgz",
"integrity": "sha512-XDFH2PuaAXv0AXJEWwElXbqADmKFGKMLMkFb+qOz0EhjQW2EIJ6pgtordLU+4u3IVERyBfqy6bi6kZKEncvRqA==",
"license": "MIT",
"engines": {
"node": ">= 8"
@ -12777,9 +12805,9 @@
"license": "MIT"
},
"node_modules/linkify-it": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz",
"integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==",
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz",
"integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==",
"funding": [
{
"type": "github",
@ -13106,9 +13134,9 @@
}
},
"node_modules/markdown-it": {
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz",
"integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==",
"version": "14.3.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz",
"integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==",
"funding": [
{
"type": "github",
@ -13122,8 +13150,8 @@
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1",
"entities": "^4.4.0",
"linkify-it": "^5.0.1",
"entities": "^4.5.0",
"linkify-it": "^5.0.2",
"mdurl": "^2.0.0",
"punycode.js": "^2.3.1",
"uc.micro": "^2.1.0"
@ -13469,9 +13497,9 @@
}
},
"node_modules/mocha/node_modules/brace-expansion": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
"integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -13556,9 +13584,9 @@
}
},
"node_modules/moment-timezone": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.6.2.tgz",
"integrity": "sha512-lDsQv8FoGdBUdf0+TjGsq2orxKuXdwFlQ6Zw6TX3xIcTwTfEpCLyKqvEauvCHJ8iu3KBV8+uPhlv70YsNGdUBQ==",
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.6.3.tgz",
"integrity": "sha512-pVEPA/HCFHHbwJ130ywnzYuZpkEGcP6Daa/OwNebpA18MybeFHmQilAGGovXgWijQ8vQtmud9jZrziUBgsykfg==",
"license": "MIT",
"dependencies": {
"moment": "^2.29.4"
@ -13582,20 +13610,24 @@
}
},
"node_modules/morgan": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
"integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.11.0.tgz",
"integrity": "sha512-zSkVu3t18r39pw4ixfBKvfZi3y2UOqr7d4WYwcj3m8nXpEQK4rPO6GLzs/CExoRgmX3y9EjmmcXqv6jq0SK46g==",
"dev": true,
"license": "MIT",
"dependencies": {
"basic-auth": "~2.0.1",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-finished": "~2.3.0",
"on-finished": "~2.4.1",
"on-headers": "~1.1.0"
},
"engines": {
"node": ">= 0.8.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/morgan/node_modules/debug": {
@ -13615,6 +13647,19 @@
"dev": true,
"license": "MIT"
},
"node_modules/morgan/node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dev": true,
"license": "MIT",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
@ -13653,9 +13698,9 @@
"license": "ISC"
},
"node_modules/nanoid": {
"version": "3.3.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
"version": "3.3.16",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
"dev": true,
"funding": [
{
@ -13988,9 +14033,9 @@
"license": "CC0-1.0"
},
"node_modules/node-releases": {
"version": "2.0.50",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz",
"integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==",
"version": "2.0.51",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
"integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==",
"dev": true,
"license": "MIT",
"engines": {
@ -14454,20 +14499,20 @@
}
},
"node_modules/pac-proxy-agent": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-9.0.1.tgz",
"integrity": "sha512-3ZOSpLboOlpW4yp8Cuv21KlTULRqyJ5Uuad3wXpSKFrxdNgcHEyoa22GRaZ2UlgCVuR6z+5BiavtYVvbajL/Yw==",
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-9.1.0.tgz",
"integrity": "sha512-1aU+1mpj3DrQPfo3gh+3Gap3G5x+axnMx1P/y0ZF2ch7kb2meyOCAH8K2k9d27ROsTE7TnAerzxqF9aon2jqnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "9.0.0",
"debug": "^4.3.4",
"get-uri": "8.0.0",
"http-proxy-agent": "9.0.0",
"https-proxy-agent": "9.0.0",
"get-uri": "8.0.1",
"http-proxy-agent": "9.1.0",
"https-proxy-agent": "9.1.0",
"pac-resolver": "9.0.1",
"quickjs-wasi": "^2.2.0",
"socks-proxy-agent": "10.0.0"
"socks-proxy-agent": "10.1.0"
},
"engines": {
"node": ">= 20"
@ -14484,28 +14529,30 @@
}
},
"node_modules/pac-proxy-agent/node_modules/http-proxy-agent": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.0.0.tgz",
"integrity": "sha512-FcF8VhXYLQcxWCnt/cCpT2apKsRDUGeVEeMqGu4HSTu29U8Yw0TLOjdYIlDsYk3IkUh+taX4IDWpPcCqKDhCjA==",
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz",
"integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "9.0.0",
"debug": "^4.3.4"
"debug": "^4.3.4",
"proxy-agent-negotiate": "1.1.0"
},
"engines": {
"node": ">= 20"
}
},
"node_modules/pac-proxy-agent/node_modules/https-proxy-agent": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.0.0.tgz",
"integrity": "sha512-/MVmHp58WkOypgFhCLk4fzpPcFQvTJ/e6LBI7irpIO2HfxUbpmYoHF+KzipzJpxxzJu7aJNWQ0xojJ/dzV2G5g==",
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz",
"integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "9.0.0",
"debug": "^4.3.4"
"debug": "^4.3.4",
"proxy-agent-negotiate": "1.1.0"
},
"engines": {
"node": ">= 20"
@ -14994,9 +15041,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.16",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
"integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
"version": "8.5.21",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.21.tgz",
"integrity": "sha512-v4sDNP3fdNiWMfabO7OwOQdOX8TiQSztKyT1Wj0w+j7LDallJThJRBBBmzVGyYj0crMh7jlV4zepPkiNu9UwDQ==",
"dev": true,
"funding": [
{
@ -15014,7 +15061,7 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.12",
"nanoid": "^3.3.16",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@ -15263,9 +15310,9 @@
"license": "MIT"
},
"node_modules/protobufjs": {
"version": "8.6.5",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-8.6.5.tgz",
"integrity": "sha512-zeE5LPpencAGXvsxyOYmEgJhxzHY8IsmPAFzstZVhDSVT8QH03q6gMZwZRaQGApevZbAL6u28ugs4CC+YKB2jQ==",
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-8.7.1.tgz",
"integrity": "sha512-agdGHrXNTv0IrYscJPDou/PlEJk1c/hBZ9o/B5NH2i/nSPtPqacNxzgwf1CebXxFMjMrZH5sqv9uQuw96aGt/A==",
"license": "BSD-3-Clause",
"dependencies": {
"long": "^5.3.2"
@ -15299,25 +15346,43 @@
}
},
"node_modules/proxy-agent": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-8.0.1.tgz",
"integrity": "sha512-kccqGBqHZXR8onQhY/ganJjoO8QIKKRiFBhPOzbTZK16attzSZ/0XSmp9H7jrRxPKHjhGyx1q32lMPrJ3uLFgA==",
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-8.0.2.tgz",
"integrity": "sha512-idLLRewuemWd7GH/BDJzGiB0dWGfT2SQs3jy6NtZtGWU9uPTTSdeC1/cdbqLwgzhfv027daGFuXX426e2Eg20A==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "9.0.0",
"debug": "^4.3.4",
"http-proxy-agent": "9.0.0",
"https-proxy-agent": "9.0.0",
"http-proxy-agent": "9.1.0",
"https-proxy-agent": "9.1.0",
"lru-cache": "^7.14.1",
"pac-proxy-agent": "9.0.1",
"pac-proxy-agent": "9.1.0",
"proxy-from-env": "^2.0.0",
"socks-proxy-agent": "10.0.0"
"socks-proxy-agent": "10.1.0"
},
"engines": {
"node": ">= 20"
}
},
"node_modules/proxy-agent-negotiate": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz",
"integrity": "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 20"
},
"peerDependencies": {
"kerberos": "^2.0.0"
},
"peerDependenciesMeta": {
"kerberos": {
"optional": true
}
}
},
"node_modules/proxy-agent/node_modules/agent-base": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz",
@ -15329,28 +15394,30 @@
}
},
"node_modules/proxy-agent/node_modules/http-proxy-agent": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.0.0.tgz",
"integrity": "sha512-FcF8VhXYLQcxWCnt/cCpT2apKsRDUGeVEeMqGu4HSTu29U8Yw0TLOjdYIlDsYk3IkUh+taX4IDWpPcCqKDhCjA==",
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz",
"integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "9.0.0",
"debug": "^4.3.4"
"debug": "^4.3.4",
"proxy-agent-negotiate": "1.1.0"
},
"engines": {
"node": ">= 20"
}
},
"node_modules/proxy-agent/node_modules/https-proxy-agent": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.0.0.tgz",
"integrity": "sha512-/MVmHp58WkOypgFhCLk4fzpPcFQvTJ/e6LBI7irpIO2HfxUbpmYoHF+KzipzJpxxzJu7aJNWQ0xojJ/dzV2G5g==",
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz",
"integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "9.0.0",
"debug": "^4.3.4"
"debug": "^4.3.4",
"proxy-agent-negotiate": "1.1.0"
},
"engines": {
"node": ">= 20"
@ -15693,9 +15760,9 @@
}
},
"node_modules/readdir-glob/node_modules/brace-expansion": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
"integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -16549,9 +16616,9 @@
}
},
"node_modules/shell-quote": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz",
"integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==",
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz",
"integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==",
"dev": true,
"license": "MIT",
"engines": {
@ -16795,9 +16862,9 @@
}
},
"node_modules/socks-proxy-agent": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-10.0.0.tgz",
"integrity": "sha512-pyp2YR3mNxAMu0mGLtzs4g7O3uT4/9sQOLAKcViAkaS9fJWkud7nmaf6ZREFqQEi24IPkBcjfHjXhPTUWjo3uA==",
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-10.1.0.tgz",
"integrity": "sha512-WlMj/67cEJ6MDI1OcsnjuYKDNDoyPCCYZ249kuuXPiMDw9F8PXkVaQ7YWu3siTydfQ/4BEZcvGzu+aYvz7dDCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -17235,14 +17302,14 @@
}
},
"node_modules/tcp-port-used": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz",
"integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.3.tgz",
"integrity": "sha512-4CEQ3qRJYo+mtEbJ+OoQu3dF4TDkwaO3RDVC4UzP5cpAOIUWwuwPjD7sdxDFFqsMUjsXVVYBMlg/boAaloThMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "4.3.1",
"is2": "^2.0.6"
"is2": "2.0.1"
}
},
"node_modules/tcp-port-used/node_modules/debug": {
@ -17271,9 +17338,9 @@
"license": "MIT"
},
"node_modules/terser": {
"version": "5.48.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz",
"integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.49.0.tgz",
"integrity": "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@ -18099,9 +18166,9 @@
}
},
"node_modules/webpack": {
"version": "5.108.3",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.108.3.tgz",
"integrity": "sha512-hOpaCHmQVVY66IVTjofnH14IgSdmod2aquSGHGuYig/OIdWge01Hk2Wt988DZcwXumFUT4+FvJY5N+ikl8o/ww==",
"version": "5.108.4",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.108.4.tgz",
"integrity": "sha512-yur8LyJoeiWh47dErD+Ok7vlbmDsJ3UbbRPAoxbGJ54WpE2y5yVo5G/inUzujnYgw3tPmBRdn+G7PoxXaYC33w==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -18145,9 +18212,9 @@
}
},
"node_modules/webpack-bundle-analyzer": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-5.3.0.tgz",
"integrity": "sha512-PEhAoqiJ+47d0uLMx/+zo5XOvaU+Vk6N2ZLht7H3n09QLy/fhyvqGNwjdRUHJDgMN8crBR2ZwVHkIswT3Xuawg==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-5.3.1.tgz",
"integrity": "sha512-wP2EusncRGL1tZyMHC/umLkjPdYMkTL9nPEKh8G8dkYCJ9TyF6xnXFqjhdqmv5J900irN/g0P5jMvLT22krEXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -18226,9 +18293,9 @@
}
},
"node_modules/webpack-dev-server": {
"version": "5.2.5",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.5.tgz",
"integrity": "sha512-4wZtCquSuv9CKX8oybo+mqxtxZqWz47uM1Ch94lxowBztOhWCbhqvRbfC/mODOwxgV2brY+JGZpHq58/SuVFYg==",
"version": "5.2.6",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.6.tgz",
"integrity": "sha512-HNLRmamRvVavZQ+avceZifmv8hmdUjg43t6MI4SqJDwFdW7RPQwH5vzGhDRZSX59SgfbeHhLnq3g+uooWo7pVw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -18250,7 +18317,7 @@
"graceful-fs": "^4.2.6",
"http-proxy-middleware": "^2.0.9",
"ipaddr.js": "^2.1.0",
"launch-editor": "^2.6.1",
"launch-editor": "^2.14.1",
"open": "^10.0.3",
"p-retry": "^6.2.0",
"schema-utils": "^4.2.0",
@ -18384,9 +18451,9 @@
}
},
"node_modules/websocket-driver": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
"integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.5.tgz",
"integrity": "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "11.2.0",
"version": "11.3.0",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "GCHQ <CyberChef@gchq.gov.uk>",
"homepage": "https://gchq.github.io/CyberChef",
@ -47,13 +47,13 @@
"@codemirror/commands": "^6.10.4",
"@codemirror/language": "^6.12.4",
"@codemirror/search": "^6.7.1",
"@codemirror/state": "^6.5.4",
"@codemirror/view": "^6.43.4",
"@codemirror/state": "^6.7.1",
"@codemirror/view": "^6.43.6",
"@puppeteer/browsers": "3.0.6",
"autoprefixer": "^10.5.2",
"autoprefixer": "^10.5.4",
"babel-loader": "^10.1.1",
"base64-loader": "^1.0.0",
"chromedriver": "^148.0.4",
"chromedriver": "^150.0.4",
"cli-progress": "^3.12.0",
"colors": "^1.4.0",
"compression-webpack-plugin": "^12.0.0",
@ -61,7 +61,7 @@
"core-js": "^3.49.0",
"cspell": "^10.0.1",
"css-loader": "^7.1.4",
"eslint": "^9.39.4",
"eslint": "^9.39.5",
"eslint-plugin-jsdoc": "^50.8.0",
"globals": "^17.7.0",
"grunt": "^1.6.2",
@ -80,16 +80,16 @@
"mini-css-extract-plugin": "2.10.2",
"modify-source-webpack-plugin": "^4.1.0",
"nightwatch": "^3.16.0",
"postcss": "^8.5.16",
"postcss": "^8.5.21",
"postcss-css-variables": "^0.19.0",
"postcss-import": "^16.1.1",
"postcss-loader": "^8.2.1",
"prompt": "^1.3.0",
"sitemap": "^9.0.1",
"terser": "^5.48.0",
"webpack": "^5.108.3",
"webpack-bundle-analyzer": "^5.3.0",
"webpack-dev-server": "^5.2.5",
"terser": "^5.49.0",
"webpack": "^5.108.4",
"webpack-bundle-analyzer": "^5.3.1",
"webpack-dev-server": "^5.2.6",
"webpack-node-externals": "^3.0.0",
"worker-loader": "^3.0.8"
},
@ -105,7 +105,7 @@
"assert": "^2.1.0",
"avsc": "^5.7.9",
"bcryptjs": "^3.0.3",
"bignumber.js": "^11.1.4",
"bignumber.js": "^11.1.5",
"blakejs": "^1.2.1",
"bootstrap": "4.6.2",
"bootstrap-colorpicker": "^3.4.0",
@ -123,7 +123,7 @@
"d3": "7.9.0",
"d3-hexbin": "^0.2.2",
"diff": "^9.0.0",
"dompurify": "^3.4.11",
"dompurify": "^3.4.12",
"es6-promisify": "^7.0.0",
"escodegen": "^2.1.0",
"esprima": "^4.0.1",
@ -143,7 +143,7 @@
"js-sha3": "^0.9.3",
"jsesc": "^3.1.0",
"json5": "^2.2.3",
"jsonata": "^2.2.1",
"jsonata": "^2.2.2",
"jsonpath-plus": "^10.4.0",
"jsonwebtoken": "9.0.3",
"jsqr": "^1.4.0",
@ -156,9 +156,9 @@
"loglevel-message-prefix": "^3.0.0",
"lz-string": "^1.5.0",
"lz4js": "^0.2.0",
"markdown-it": "^14.2.0",
"markdown-it": "^14.3.0",
"moment": "^2.30.1",
"moment-timezone": "^0.6.2",
"moment-timezone": "^0.6.3",
"ngeohash": "^0.6.3",
"node-forge": "^1.4.0",
"node-md6": "^0.1.0",
@ -170,7 +170,7 @@
"path": "^0.12.7",
"popper.js": "^1.16.1",
"process": "^0.11.10",
"protobufjs": "^8.6.5",
"protobufjs": "^8.7.1",
"punycode.js": "^2.3.1",
"qr-image": "^3.2.0",
"reflect-metadata": "^0.2.2",
@ -193,6 +193,14 @@
"xregexp": "^5.1.2",
"zlibjs": "^0.3.1"
},
"allowScripts": {
"@nightwatch/nightwatch-inspector@1.0.1": true,
"chromedriver@150.0.3": true,
"core-js": false,
"core-js-pure": false,
"fsevents": false,
"tesseract.js": false
},
"scripts": {
"start": "npx grunt dev",
"build": "npx grunt prod",

View File

@ -86,6 +86,9 @@ class Ingredient {
if (this.type === "option" && Array.isArray(checkVal)) {
checkVal = checkVal[this.defaultIndex ?? 0];
}
if (this.type === "argSelector" && Array.isArray(checkVal)) {
checkVal = checkVal[this.defaultIndex ?? 0]?.name || "";
}
// 1. check if empty
let isEmpty = false;
@ -99,8 +102,10 @@ class Ingredient {
let isAllowedOptionEmpty = false;
if (this.type === "option" && Array.isArray(this.defaultValue)) {
isAllowedOptionEmpty = this.defaultValue.includes("");
} else if (this.type === "argSelector" && Array.isArray(this.defaultValue)) {
isAllowedOptionEmpty = this.defaultValue.some(opt => opt.name === "");
}
if (this.allowEmpty === false || (this.type === "option" && !isAllowedOptionEmpty)) {
if (this.allowEmpty === false || ((this.type === "option" || this.type === "argSelector") && !isAllowedOptionEmpty)) {
throw new OperationError(`${this.name} cannot be empty.`);
}
return true;
@ -150,6 +155,23 @@ class Ingredient {
}
}
// 5. argSelector checks
if (this.type === "argSelector") {
if (Array.isArray(this.defaultValue)) {
const permittedOptions = this.defaultValue
.map(opt => opt.name)
.filter(optName => {
if (typeof optName !== "string") return false;
return !optName.match(/^\[\/?[a-z0-9 -()^]+\]$/i);
});
const valStr = (checkVal !== null && checkVal !== undefined) ? String(checkVal).toLowerCase() : "";
const matchedOption = permittedOptions.find(opt => opt.toLowerCase() === valStr);
if (!matchedOption) {
throw new OperationError(`${this.name} must be one of the following: ${permittedOptions.join(", ")}.`);
}
}
}
return true;
}

View File

@ -1015,6 +1015,7 @@ class Utils {
// Parse bespoke recipe format
recipe = recipe.replace(/\n/g, "");
Utils._validatePrettyRecipe(recipe);
let m, args;
const recipeRegex = /([^(]+)\(((?:'[^'\\]*(?:\\.[^'\\]*)*'|[^)/'])*)(\/[^)]+)?\)/g,
recipeConfig = [];
@ -1040,6 +1041,53 @@ class Utils {
}
/**
* Performs a linear structural validation pass over pretty recipe syntax.
*
* @param {string} recipe
* @throws {Error} if the recipe is structurally invalid
*/
static _validatePrettyRecipe(recipe) {
let i = 0;
while (i < recipe.length) {
const openParen = recipe.indexOf("(", i);
if (openParen === -1 || openParen === i) {
throw new Error("Invalid recipe");
}
i = openParen + 1;
let inString = false,
escaped = false,
foundCloseParen = false;
for (; i < recipe.length; i++) {
const c = recipe[i];
if (inString) {
if (escaped) {
escaped = false;
} else if (c === "\\") {
escaped = true;
} else if (c === "'") {
inString = false;
}
} else if (c === "'") {
inString = true;
} else if (c === ")") {
foundCloseParen = true;
i++;
break;
}
}
if (!foundCloseParen || inString || escaped) {
throw new Error("Invalid recipe");
}
}
}
/**
* Formats a list of files or directories.
*

View File

@ -83,7 +83,9 @@
"Rison Decode",
"To Modhex",
"From Modhex",
"MIME Decoding"
"MIME Decoding",
"To COBS",
"From COBS"
]
},
{
@ -174,6 +176,7 @@
"AES Key Wrap",
"AES Key Unwrap",
"Pseudo-Random Number Generator",
"Pseudo-Random Prime Generator",
"Enigma",
"Bombe",
"Multiple Bombe",
@ -242,9 +245,10 @@
"Subtract",
"Multiply",
"Divide",
"MOD",
"Extended GCD",
"Modular Exponentiation",
"Modular Inverse",
"Extended GCD",
"Mean",
"Median",
"Standard Deviation",

View File

@ -0,0 +1,139 @@
/**
* This script automatically generates src/core/lib/HTMLEntities.mjs, the shared
* HTML entity lookup tables used by the "To HTML Entity" and "From HTML Entity"
* operations.
*
* The data is derived from the vendored WHATWG named character reference set
* (src/core/vendor/htmlEntities/entity.json, from
* https://html.spec.whatwg.org/entities.json) so the
* two operations cannot drift apart and every entity is spec-conformant:
*
* - HTML_ENTITY_REVERSE_LOOKUP (decode) is every single-code-point spec name.
* - HTML_ENTITY_LOOKUP (encode) picks one canonical name per code point via a
* deterministic tiebreak, overridden by htmlEntityOverrides.mjs where a
* specific historical name is preferred.
*
* @author roberson-io [michaelroberson@gmail.com]
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
/* eslint no-console: ["off"] */
import path from "path";
import fs from "fs";
import process from "process";
import { fileURLToPath } from "url";
import { HTML_ENTITY_CANONICAL_OVERRIDES } from "./htmlEntityOverrides.mjs";
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
if (!fs.existsSync(path.join(process.cwd(), "src/core/lib"))) {
console.log("\nCWD: " + process.cwd());
console.log("Error: generateHTMLEntities.mjs should be run from the project root");
console.log("Example> node src/core/config/scripts/generateHTMLEntities.mjs");
process.exit(1);
}
const SPEC = JSON.parse(fs.readFileSync(
path.join(scriptDir, "..", "..", "vendor", "htmlEntities", "entity.json"), "utf8"));
// Build code point -> [spec names] for single-code-point, semicolon-terminated
// references (the representable subset; multi-code-point entities are skipped).
const codePointNames = {};
for (const [key, val] of Object.entries(SPEC)) {
if (!key.endsWith(";") || val.codepoints.length !== 1) continue;
const name = key.slice(1, -1); // strip leading "&" and trailing ";"
(codePointNames[val.codepoints[0]] ??= []).push(name);
}
// Deterministic canonical-name tiebreak: prefer a lower-case name, then the
// shortest, then alphabetical. Overridden per code point where a specific
// historical name is preferred.
const isAllUpper = s => s === s.toUpperCase() && s !== s.toLowerCase();
/**
* Choose the single canonical entity name for a code point.
*
* @param {number} codePoint - the Unicode code point being encoded
* @param {string[]} names - all valid WHATWG names for that code point
* @returns {string} the canonical name to emit when encoding
*/
function canonicalName(codePoint, names) {
const override = HTML_ENTITY_CANONICAL_OVERRIDES[codePoint];
if (override !== undefined) {
if (!names.includes(override))
throw new Error(`Override &${override}; is not a spec name for code point ${codePoint} (spec: ${names})`);
return override;
}
return [...names].sort((a, b) =>
(isAllUpper(a) - isAllUpper(b)) ||
(a.length - b.length) ||
(a < b ? -1 : a > b ? 1 : 0)
)[0];
}
const forward = {}; // code point -> canonical name (encode)
const reverse = {}; // name -> code point (decode, every spec name)
for (const [cpStr, names] of Object.entries(codePointNames)) {
const cp = Number(cpStr);
forward[cp] = canonicalName(cp, names);
for (const name of names) reverse[name] = cp;
}
// Decode-only aliases = spec names that are not the canonical encode name.
const aliases = {};
for (const [name, cp] of Object.entries(reverse)) {
if (forward[cp] !== name) aliases[name] = cp;
}
// --- emit -----------------------------------------------------------------
const fwdEntries = Object.keys(forward).map(Number).sort((a, b) => a - b)
.map(cp => ` ${cp}: "${forward[cp]}",`).join("\n").replace(/,$/, "");
const aliasEntries = Object.keys(aliases).sort()
.map(n => ` ${JSON.stringify(n)}: ${aliases[n]},`).join("\n").replace(/,$/, "");
const code = `/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/scripts/generateHTMLEntities.mjs
*
* HTML entity lookup tables shared by the "To HTML Entity" and "From HTML Entity"
* operations, derived from the WHATWG named character reference set
* (https://html.spec.whatwg.org/entities.json). Do not edit by hand — change the
* vendored src/core/vendor/htmlEntities/entity.json or htmlEntityOverrides.mjs
* and regenerate.
*
* @author roberson-io [michaelroberson@gmail.com]
* @copyright Crown Copyright ${new Date().getUTCFullYear()}
* @license Apache-2.0
*/
/**
* Canonical lookup: Unicode code point -> entity name (without "&" and ";"),
* used for ENCODING. One canonical name per code point.
*/
export const HTML_ENTITY_LOOKUP = {
${fwdEntries}
};
/**
* Legacy / alias names that only DECODE (spelling variants, deprecated names,
* box-drawing aliases, etc.); not used for encoding.
*/
export const HTML_ENTITY_DECODE_ALIASES = {
${aliasEntries}
};
/**
* Derived reverse lookup: entity name -> code point, used for DECODING. Built
* from the canonical lookup plus the legacy aliases.
*/
export const HTML_ENTITY_REVERSE_LOOKUP = {...HTML_ENTITY_DECODE_ALIASES};
for (const codePoint in HTML_ENTITY_LOOKUP) {
HTML_ENTITY_REVERSE_LOOKUP[HTML_ENTITY_LOOKUP[codePoint]] = Number(codePoint);
}
`;
fs.writeFileSync(path.join(process.cwd(), "src/core/lib/HTMLEntities.mjs"), code);
console.log(`generateHTMLEntities: ${Object.keys(forward).length} encode names, ` +
`${Object.keys(reverse).length} decode names, ${Object.keys(aliases).length} aliases, ` +
`${Object.keys(HTML_ENTITY_CANONICAL_OVERRIDES).length} overrides applied.`);

View File

@ -0,0 +1,86 @@
/**
* Canonical entity-name overrides for generateHTMLEntities.mjs.
*
* The WHATWG named character reference set assigns MANY names to some code
* points (e.g. U+2211 is both &sum; and &Sum;), but does not designate a
* canonical one. The generator therefore needs a rule to pick a single name per
* code point for ENCODING. It uses a deterministic tiebreak (prefer a
* lower-case name, then the shortest, then alphabetical), which reproduces the
* historically-emitted name for ~1355 of the ~1414 encodable code points.
*
* This file pins the canonical name for the code points where the tiebreak
* would otherwise change the emitted entity. Every value here is still a valid
* WHATWG name for that code point (the generator asserts this) these are
* editorial choices, not correctness fixes, kept so "To HTML Entity" output
* stays stable. Trim an entry to let the tiebreak decide instead.
*
* @author roberson-io [michaelroberson@gmail.com]
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
/**
* @constant
* @type {Object.<number, string>}
*/
export const HTML_ENTITY_CANONICAL_OVERRIDES = {
124: "verbar",
168: "uml",
177: "plusmn",
189: "frac12",
247: "divide",
711: "caron",
728: "breve",
937: "Omega",
949: "epsilon",
965: "upsilon",
977: "thetasym",
978: "upsih",
981: "straightphi",
8208: "hyphen",
8214: "Verbar",
8230: "hellip",
8289: "ApplyFunction",
8290: "InvisibleTimes",
8291: "InvisibleComma",
8459: "hamilt",
8461: "quaternions",
8463: "planck",
8465: "image",
8472: "weierp",
8474: "rationals",
8476: "real",
8477: "reals",
8484: "integers",
8492: "bernou",
8499: "phmmat",
8500: "order",
8501: "alefsym",
8518: "DifferentialD",
8519: "ExponentialE",
8520: "ImaginaryI",
8612: "LeftTeeArrow",
8613: "UpTeeArrow",
8615: "DownTeeArrow",
8624: "lsh",
8625: "rsh",
8660: "hArr",
8704: "forall",
8711: "nabla",
8712: "isin",
8721: "sum",
8723: "mnplus",
8730: "radic",
8750: "conint",
8768: "wreath",
8776: "asymp",
8781: "asympeq",
8784: "esdot",
8788: "colone",
8869: "perp",
8896: "xwedge",
8897: "xvee",
8902: "sstarf",
10536: "nesear",
10537: "seswar"
};

View File

@ -124,6 +124,19 @@ export function median(data) {
}
/**
* Computes modulo of two numbers and returns the value.
*
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
export function mod(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.mod(curr));
}
}
/**
* Computes standard deviation of a number array and returns the value.
*

View File

@ -70,4 +70,3 @@ export function modPow(base, exponent, modulus) {
return result;
}

86
src/core/lib/COBS.mjs Normal file
View File

@ -0,0 +1,86 @@
/**
* @author Imantas Lukenskas [imantas@lukenskas.dev]
* @copyright Imantas Lukenskas 2026
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
/**
* COBS-encode a byte array
* @param {Uint8Array} data
* @return {Uint8Array}
*/
export function toCobs(data) {
if (!data || data.length === 0) {
return new Uint8Array();
}
const output = [];
data = [0, ...data];
while (data.length > 0) {
const endIndex = data.findIndex((value, index) => value === 0 && index > 0);
if ((endIndex < 0 || endIndex > 254) && data.length > 254) {
output.push(255);
output.push(...data.slice(1, 255));
data = data.slice(255);
if (data.length !== 0) {
data = [0, ...data];
}
} else if (endIndex < 0) {
output.push(data.length);
output.push(...data.slice(1));
data = [];
} else {
output.push(endIndex);
output.push(...data.slice(1, endIndex));
data = data.slice(endIndex);
}
}
return output;
}
/**
* COBS-decode a byte array
* @param {Uint8Array} data
* @return {Uint8Array}
*/
export function fromCobs(data) {
if (!data || data.length === 0) {
return new Uint8Array();
}
if (data.findIndex((value) => value === 0) >= 0) {
throw new OperationError("Could not decode from COBS: payload must not contain a 0x00 byte");
}
const output = [];
while (data.length > 0) {
if (data[0] === 0xFF) {
output.push(...data.slice(1, 255));
data = data.slice(255);
} else {
const nextZeroIndex = data[0];
output.push(...data.slice(1, nextZeroIndex));
data = data.slice(nextZeroIndex);
let blockSize = data[0];
while (data.length > 0) {
output.push(0, ...data.slice(1, blockSize));
data = data.slice(blockSize);
if (blockSize === 0xFF) {
break;
}
blockSize = data[0];
}
}
}
return output;
}

View File

@ -24,12 +24,12 @@ import Utils from "../Utils.mjs";
* fromDecimal("10:20:30", "Colon");
*/
export function fromDecimal(data, delim="Auto") {
delim = Utils.charRep(delim);
const output = [];
let byteStr = data.split(delim);
if (byteStr[byteStr.length-1] === "")
byteStr = byteStr.slice(0, byteStr.length-1);
const delimRegex = delim === "Auto" ? /[^\d-]+/ : Utils.regexRep(delim);
let byteStr = data.split(delimRegex);
byteStr = byteStr.filter(str => str !== "");
const output = [];
for (let i = 0; i < byteStr.length; i++) {
output[i] = parseInt(byteStr[i], 10);
}

View File

@ -35,32 +35,32 @@ class A1Z26CipherDecode extends Operation {
];
this.checks = [
{
pattern: "^\\s*([12]?[0-9] )+[12]?[0-9]\\s*$",
pattern: "^\\s*((?:0?[1-9]|1[0-9]|2[0-6]) )+(?:0?[1-9]|1[0-9]|2[0-6])\\s*$",
flags: "",
args: ["Space"]
},
{
pattern: "^\\s*([12]?[0-9],)+[12]?[0-9]\\s*$",
pattern: "^\\s*((?:0?[1-9]|1[0-9]|2[0-6]),)+(?:0?[1-9]|1[0-9]|2[0-6])\\s*$",
flags: "",
args: ["Comma"]
},
{
pattern: "^\\s*([12]?[0-9];)+[12]?[0-9]\\s*$",
pattern: "^\\s*((?:0?[1-9]|1[0-9]|2[0-6]);)+(?:0?[1-9]|1[0-9]|2[0-6])\\s*$",
flags: "",
args: ["Semi-colon"]
},
{
pattern: "^\\s*([12]?[0-9]:)+[12]?[0-9]\\s*$",
pattern: "^\\s*((?:0?[1-9]|1[0-9]|2[0-6]):)+(?:0?[1-9]|1[0-9]|2[0-6])\\s*$",
flags: "",
args: ["Colon"]
},
{
pattern: "^\\s*([12]?[0-9]\\n)+[12]?[0-9]\\s*$",
pattern: "^\\s*((?:0?[1-9]|1[0-9]|2[0-6])\\n)+(?:0?[1-9]|1[0-9]|2[0-6])\\s*$",
flags: "",
args: ["Line feed"]
},
{
pattern: "^\\s*([12]?[0-9]\\r\\n)+[12]?[0-9]\\s*$",
pattern: "^\\s*((?:0?[1-9]|1[0-9]|2[0-6])\\r\\n)+(?:0?[1-9]|1[0-9]|2[0-6])\\s*$",
flags: "",
args: ["CRLF"]
}

View File

@ -66,6 +66,23 @@ class AutomatedValidationTestOp extends Operation {
"type": "option",
"value": ["[Group 1]", "Option 1", "Option 2", "[/Group 1]", "[Group 2]", "Option 3", "[/Group 2]"],
"allowEmpty": false
},
{
"name": "Arg Selector Ingredient",
"type": "argSelector",
"value": [
{
name: "Option 1",
on: [0],
off: [1]
},
{
name: "Option 2",
on: [1],
off: [0]
}
],
"allowEmpty": false
}
];
}

View File

@ -39,7 +39,7 @@ class AvroToJSON extends Operation {
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
async run(input, args) {
if (input.byteLength <= 0) {
throw new OperationError("Please provide an input.");
}

View File

@ -47,7 +47,7 @@ class Bzip2Compress extends Operation {
* @param {Object[]} args
* @returns {File}
*/
run(input, args) {
async run(input, args) {
const [blockSize, workFactor] = args;
if (input.byteLength <= 0) {
throw new OperationError("Please provide an input.");

View File

@ -0,0 +1,101 @@
/**
* @author p-leriche [philip.leriche@cantab.net]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { parseBigInt, egcd } from "../lib/BigIntUtils.mjs";
/* ---------- operation class ---------- */
/**
* Extended GCD operation
*/
class ExtendedGCD extends Operation {
/**
* ExtendedGCD constructor
*/
constructor() {
super();
this.name = "Extended GCD";
this.module = "Crypto";
this.description =
"Computes the Extended Euclidean Algorithm for integers <i>a</i> and <i>b</i>.<br><br>" +
"Finds integers <i>x</i> and <i>y</i> (Bezout coefficients) such that:<br>" +
"a*x + b*y = gcd(a, b)<br><br>" +
"This is fundamental to many number theory algorithms including modular inverse, " +
"solving linear Diophantine equations, and cryptographic operations.<br><br>" +
"<b>Input handling:</b> If either <i>a</i> or <i>b</i> is left blank, " +
"its value is taken from the Input field.";
this.infoURL = "https://wikipedia.org/wiki/Extended_Euclidean_algorithm";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Value a",
type: "string",
value: ""
},
{
name: "Value b",
type: "string",
value: ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [aStr, bStr] = args;
// Trim everything so "" and " " count as empty
const aParam = aStr?.trim();
const bParam = bStr?.trim();
const inputVal = input?.trim();
let a, b;
if (aParam && bParam) {
// Case 1: both values given as parameters
a = aParam;
b = bParam;
} else if (!aParam && bParam) {
// Case 2: a missing - take from input
a = inputVal;
b = bParam;
if (!a) throw new OperationError("Value a must be defined");
} else if (aParam && !bParam) {
// Case 3: b missing - take from input
a = aParam;
b = inputVal;
if (!b) throw new OperationError("Value b must be defined");
} else if (!aParam && !bParam) {
// Case 4: both values missing
throw new OperationError("Values a and b must be defined");
}
const aBI = parseBigInt(a, "Value a");
const bBI = parseBigInt(b, "Value b");
const [g, x, y] = egcd(aBI, bBI);
const gcd = g < 0n ? -g : g;
// Format output string bearing in mind that crypto-grade numbers
// may greatly exceed the line length.
let output = "gcd: " + gcd.toString() + "\n\n";
output += "Bezout coefficients:\n";
output += "x = " + x.toString() + "\n";
output += "y = " + y.toString() + "\n\n";
return output;
}
}
export default ExtendedGCD;

View File

@ -0,0 +1,38 @@
/**
* @author Imantas Lukenskas [imantas@lukenskas.dev]
* @copyright Imantas Lukenskas 2026
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import {fromCobs} from "../lib/COBS.mjs";
/**
* From COBS operation
*/
class FromCOBS extends Operation {
/**
* FromCOBS constructor
*/
constructor() {
super();
this.name = "From COBS";
this.module = "Default";
this.description = "Decodes COBS encoded bytes";
this.infoURL = "https://wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing";
this.inputType = "byteArray";
this.outputType = "byteArray";
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
return fromCobs(input);
}
}
export default FromCOBS;

View File

@ -8,6 +8,11 @@ import Operation from "../Operation.mjs";
import {DELIM_OPTIONS} from "../lib/Delim.mjs";
import {fromDecimal} from "../lib/Decimal.mjs";
/**
* From Decimal delimiters, plus auto-detection.
*/
const FROM_DECIMAL_DELIM_OPTIONS = [...DELIM_OPTIONS, "Auto"];
/**
* From Decimal operation
*/
@ -28,7 +33,7 @@ class FromDecimal extends Operation {
{
"name": "Delimiter",
"type": "option",
"value": DELIM_OPTIONS
"value": FROM_DECIMAL_DELIM_OPTIONS
},
{
"name": "Support signed values",

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,154 @@
/**
* @author p-leriche [philip.leriche@cantab.net]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { modPow } from "../lib/BigIntUtils.mjs";
/* ---------- helper functions ---------- */
/**
* Generate random BigInt with specified bit length
*/
function randBigInt(bits) {
const bytes = Math.ceil(bits / 8);
const a = new Uint8Array(bytes);
crypto.getRandomValues(a);
// Set high bit to ensure correct bit length
a[0] |= 1 << (7 - ((8 * bytes - bits)));
// Set low bit to ensure odd (primes > 2 are odd)
a[bytes - 1] |= 1;
let h = "";
for (const b of a) h += b.toString(16).padStart(2, "0");
return BigInt("0x" + h);
}
/**
* Miller-Rabin primality test
*/
function isProbablePrime(n, rounds) {
if (n < 2n) return false;
if (n === 2n || n === 3n) return true;
if (n % 2n === 0n) return false;
// Write n-1 as 2^r * d
let d = n - 1n;
let r = 0n;
while (d % 2n === 0n) {
d /= 2n;
r++;
}
// Witness loop
for (let i = 0; i < rounds; i++) {
const a = randBigInt(n.toString(2).length - 1) % (n - 3n) + 2n;
let x = modPow(a, d, n);
if (x === 1n || x === n - 1n) continue;
let composite = true;
for (let j = 0n; j < r - 1n; j++) {
x = modPow(x, 2n, n);
if (x === n - 1n) {
composite = false;
break;
}
}
if (composite) return false;
}
return true;
}
/* ---------- operation class ---------- */
/**
* Generate Prime Number operation
*/
class GeneratePrime extends Operation {
/**
* GeneratePrime constructor
*/
constructor() {
super();
this.name = "Pseudo-Random Prime Generator";
this.module = "Crypto";
this.description =
"Generates a random probable prime number of specified bit length using the Miller-Rabin primality test.<br><br>" +
"<b>Primality guarantee:</b><br>" +
"For numbers . 3,317, the result is guaranteed prime (deterministic test).<br>" +
"For larger numbers, uses probabilistic testing:<br>" +
"- <b>Standard (7 rounds):</b> Probability of composite approx 1 in 16,000)<br><br>" +
"- <b>Crypto grade (40 rounds):</b> Probability of composite(approx 1 in 10^24)<br>" +
"Crypto grade is recommended for cryptographic applications (RSA, Diffie-Hellman, etc.).<br><br>" ;
this.infoURL = "https://wikipedia.org/wiki/Miller-Rabin_primality_test";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Bit length",
type: "number",
value: 512,
min: 2
},
{
name: "Crypto grade",
type: "boolean",
value: false
},
{
name: "Output format",
type: "option",
value: ["Decimal", "Hexadecimal"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [bits, cryptoGrade, outputFormat] = args;
if (bits < 2) {
throw new OperationError("Bit length must be at least 2");
}
if (bits > 4096) {
throw new OperationError("Bit length limited to 4096 bits for performance reasons");
}
const rounds = cryptoGrade ? 40 : 7;
let attempts = 0;
const maxAttempts = 10000;
let n = randBigInt(bits);
while (!isProbablePrime(n, rounds)) {
n = randBigInt(bits);
attempts++;
if (attempts > maxAttempts) {
throw new OperationError(`Failed to generate prime after ${maxAttempts} attempts. Try a different bit length.`);
}
}
// Return only the prime for pipeability
if (outputFormat === "Hexadecimal") {
return "0x" + n.toString(16);
} else {
return n.toString();
}
}
}
export default GeneratePrime;

View File

@ -0,0 +1,62 @@
/**
* @license Apache-2.0
*/
import BigNumber from "bignumber.js";
import Operation from "../Operation.mjs";
import { createNumArray } from "../lib/Arithmetic.mjs";
import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim.mjs";
/**
* MOD operation
*/
class MOD extends Operation {
/**
* MOD constructor
*/
constructor() {
super();
this.name = "MOD";
this.module = "Default";
this.description = "Computes the modulo of each number in a list with a given modulus value. Numbers are extracted from the input based on the delimiter, and non-numeric values are ignored.<br><br>e.g. <code>15 4 7</code> with modulus <code>3</code> becomes <code>0 1 1</code>";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Modulus",
"type": "number",
"value": 2
},
{
"name": "Delimiter",
"type": "option",
"value": ARITHMETIC_DELIM_OPTIONS,
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const modulus = new BigNumber(args[0]);
const delimiter = args[1];
if (modulus.isZero()) {
throw new Error("Modulus cannot be zero");
}
const numbers = createNumArray(input, delimiter);
const results = numbers.map(num => num.mod(modulus));
return results.join(" ");
}
}
export default MOD;

View File

@ -0,0 +1,111 @@
/**
* @author p-leriche [philip.leriche@cantab.net]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { parseBigInt, modPow } from "../lib/BigIntUtils.mjs";
/* ---------- operation class ---------- */
/**
* Modular Exponentiation operation
*/
class ModularExponentiation extends Operation {
/**
* ModularExponentiation constructor
*/
constructor() {
super();
this.name = "Modular Exponentiation";
this.module = "Crypto";
this.description = "Performs modular exponentiation, as used in Diffie-Hellman and RSA.<br><br>" +
"Computes Base ^ Exponent mod Modulus.<br><br>" +
"<b>Input handling:</b> If <i>either</i> Base <i>or</i> Exponent is left blank, " +
"its value is taken from the Input field.";
this.infoURL = "https://wikipedia.org/wiki/Modular_exponentiation";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Base",
type: "string",
value: ""
},
{
name: "Modulus",
type: "string",
value: "1"
},
{
name: "Exponent",
type: "string",
value: ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [baseStr, modStr, expStr] = args;
// Trim everything so "" and " " count as empty
const baseParam = baseStr?.trim();
const expParam = expStr?.trim();
const modParam = modStr?.trim();
const inputVal = input?.trim();
const mod = modParam;
if (!mod) {
throw new OperationError("Modulus must be defined");
}
// Base *or* Exponent (but not both) are taken from the Input
// if their boxes are empty.
let base, exp;
if (baseParam && expParam) {
// Case 1: base and exponent both given as parameters
base = baseParam;
exp = expParam;
} else if (!baseParam && expParam) {
// Case 2: base missing - take from input
base = inputVal;
exp = expParam;
if (!base) {
throw new OperationError("Base must be defined");
}
} else if (baseParam && !expParam) {
// Case 3: exponent missing - take from input
base = baseParam;
exp = inputVal;
if (!exp) {
throw new OperationError("Exponent must be defined");
}
} else if (!inputVal) {
// Case 4: base and exponent both missing
throw new OperationError("Base and Exponent must be defined");
} else throw new OperationError("Ambiguous input: specify either Base or Exponent when using Input");
// Parse numbers
const baseBI = parseBigInt(base, "Base");
const expBI = parseBigInt(exp, "Exponent");
const modBI = parseBigInt(mod, "Modulus");
// Check for invalid modulus (parseBigInt eliminates negatives)
if (modBI === 0n) {
throw new OperationError("Modulus must be greater than zero");
}
return modPow(baseBI, expBI, modBI).toString();
}
}
export default ModularExponentiation;

View File

@ -0,0 +1,107 @@
/**
* @author p-leriche [philip.leriche@cantab.net]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { parseBigInt, egcd } from "../lib/BigIntUtils.mjs";
/* ---------- operation class ---------- */
/**
* Modular Inverse operation
*/
class ModularInverse extends Operation {
/**
* ModularInverse constructor
*/
constructor() {
super();
this.name = "Modular Inverse";
this.module = "Crypto";
this.description =
"Computes the modular multiplicative inverse of <i>a</i> modulo <i>m</i>.<br><br>" +
"Finds <i>x</i> such that a*x = 1 (mod m).<br><br>" +
"<b>Input handling:</b> If either <i>a</i> or <i>m</i> is left blank, " +
"its value is taken from the Input field.";
this.infoURL = "https://wikipedia.org/wiki/Modular_multiplicative_inverse";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Value (a)",
type: "string",
value: ""
},
{
name: "Modulus (m)",
type: "string",
value: ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [aStr, mStr] = args;
// Trim everything so "" and " " count as empty
const aParam = aStr?.trim();
const mParam = mStr?.trim();
const inputVal = input?.trim();
let a, m;
if (aParam && mParam) {
// Case 1: value and modulus both given as parameters
a = aParam;
m = mParam;
} else if (!aParam && mParam) {
// Case 2: value missing - take from input
a = inputVal;
m = mParam;
if (!a) throw new OperationError("Value (a) must be defined");
} else if (aParam && !mParam) {
// Case 3: modulus missing - take from input
a = aParam;
m = inputVal;
if (!m) throw new OperationError("Modulus (m) must be defined");
} else if (!aParam && !mParam) {
// Case 4: value and modulus both missing
throw new OperationError("Value (a) and Modulus (m) must be defined");
}
const aBI = parseBigInt(a, "Value (a)");
const mBI = parseBigInt(m, "Modulus (m)");
if (mBI <= 0n) {
throw new OperationError("Modulus must be greater than zero");
}
const aNorm = ((aBI % mBI) + mBI) % mBI;
const [g, x] = egcd(aNorm, mBI);
if (g !== 1n && g !== -1n) {
throw new OperationError("Inverse does not exist because gcd(a, m) ≠ 1");
}
let inv = x;
if (g === -1n) inv = -inv;
inv = ((inv % mBI) + mBI) % mBI;
return inv.toString();
}
}
export default ModularInverse;

View File

@ -33,15 +33,11 @@ class ParseQRCode extends Operation {
value: false,
},
];
this.checks = [
{
pattern:
"^(?:\\xff\\xd8\\xff|\\x89\\x50\\x4e\\x47|\\x47\\x49\\x46|.{8}\\x57\\x45\\x42\\x50|\\x42\\x4d)",
flags: "",
args: [false],
useful: true,
},
];
// No Magic checks: detecting a QR code in arbitrary image data requires
// actually attempting to parse one, which is expensive and produces
// spurious "Could not read a QR code from the image" log messages for
// any image input via Magic. Users can add Parse QR Code manually when
// they know the image contains a QR code. See issue #2610.
}
/**

View File

@ -0,0 +1,154 @@
/**
* @author p-leriche [philip.leriche@cantab.net]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { modPow } from "../lib/BigIntUtils.mjs";
/* ---------- helper functions ---------- */
/**
* Generate random BigInt with specified bit length
*/
function randBigInt(bits) {
const bytes = Math.ceil(bits / 8);
const a = new Uint8Array(bytes);
crypto.getRandomValues(a);
// Set high bit to ensure correct bit length
a[0] |= 1 << (7 - ((8 * bytes - bits)));
// Set low bit to ensure odd (primes > 2 are odd)
a[bytes - 1] |= 1;
let h = "";
for (const b of a) h += b.toString(16).padStart(2, "0");
return BigInt("0x" + h);
}
/**
* Miller-Rabin primality test
*/
function isProbablePrime(n, rounds) {
if (n < 2n) return false;
if (n === 2n || n === 3n) return true;
if (n % 2n === 0n) return false;
// Write n-1 as 2^r * d
let d = n - 1n;
let r = 0n;
while (d % 2n === 0n) {
d /= 2n;
r++;
}
// Witness loop
for (let i = 0; i < rounds; i++) {
const a = randBigInt(n.toString(2).length - 1) % (n - 3n) + 2n;
let x = modPow(a, d, n);
if (x === 1n || x === n - 1n) continue;
let composite = true;
for (let j = 0n; j < r - 1n; j++) {
x = modPow(x, 2n, n);
if (x === n - 1n) {
composite = false;
break;
}
}
if (composite) return false;
}
return true;
}
/* ---------- operation class ---------- */
/**
* Generate Prime Number operation
*/
class GeneratePrime extends Operation {
/**
* GeneratePrime constructor
*/
constructor() {
super();
this.name = "Pseudo-Random Prime Generator";
this.module = "Crypto";
this.description =
"Generates a random probable prime number of specified bit length using the Miller-Rabin primality test.<br><br>" +
"<b>Primality guarantee:</b><br>" +
"For numbers . 3,317, the result is guaranteed prime (deterministic test).<br>" +
"For larger numbers, uses probabilistic testing:<br>" +
"- <b>Standard (7 rounds):</b> Probability of composite approx 1 in 16,000)<br><br>" +
"- <b>Crypto grade (40 rounds):</b> Probability of composite(approx 1 in 10^24)<br>" +
"Crypto grade is recommended for cryptographic applications (RSA, Diffie-Hellman, etc.).<br><br>" ;
this.infoURL = "https://wikipedia.org/wiki/Miller-Rabin_primality_test";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Bit length",
type: "number",
value: 512,
min: 2
},
{
name: "Crypto grade",
type: "boolean",
value: false
},
{
name: "Output format",
type: "option",
value: ["Decimal", "Hexadecimal"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [bits, cryptoGrade, outputFormat] = args;
if (bits < 2) {
throw new OperationError("Bit length must be at least 2");
}
if (bits > 4096) {
throw new OperationError("Bit length limited to 4096 bits for performance reasons");
}
const rounds = cryptoGrade ? 40 : 7;
let attempts = 0;
const maxAttempts = 10000;
let n = randBigInt(bits);
while (!isProbablePrime(n, rounds)) {
n = randBigInt(bits);
attempts++;
if (attempts > maxAttempts) {
throw new OperationError(`Failed to generate prime after ${maxAttempts} attempts. Try a different bit length.`);
}
}
// Return only the prime for pipeability
if (outputFormat === "Hexadecimal") {
return "0x" + n.toString(16);
} else {
return n.toString();
}
}
}
export default GeneratePrime;

View File

@ -0,0 +1,38 @@
/**
* @author Imantas Lukenskas [imantas@lukenskas.dev]
* @copyright Imantas Lukenskas 2026
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import {toCobs} from "../lib/COBS.mjs";
/**
* To COBS operation
*/
class ToCOBS extends Operation {
/**
* ToCOBS constructor
*/
constructor() {
super();
this.name = "To COBS";
this.module = "Default";
this.description = "Encodes bytes in COBS format";
this.infoURL = "https://wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing";
this.inputType = "byteArray";
this.outputType = "byteArray";
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
return toCobs(input);
}
}
export default ToCOBS;

File diff suppressed because it is too large Load Diff

2233
src/core/vendor/htmlEntities/entity.json vendored Normal file

File diff suppressed because it is too large Load Diff

14
src/core/vendor/htmlEntities/entity.txt vendored Normal file
View File

@ -0,0 +1,14 @@
entity.json is the WHATWG named character reference set, retrieved verbatim from:
https://html.spec.whatwg.org/entities.json
It is used by src/core/config/scripts/generateHTMLEntities.mjs to generate the
shared HTML entity lookup tables (src/core/lib/HTMLEntities.mjs) consumed by the
"To HTML Entity" and "From HTML Entity" operations.
Source: HTML Standard — 13.5 Named character references
https://html.spec.whatwg.org/multipage/named-characters.html
Copyright and licence
---------------------
Copyright © WHATWG (Apple, Google, Mozilla, Microsoft). This work is licensed under a Creative Commons Attribution 4.0 International License. To the extent portions of it are incorporated into source code, such portions in the source code are licensed under the BSD 3-Clause License instead.

View File

@ -77,6 +77,10 @@ function transformArgs(opArgsList, newArgs) {
return !Array.isArray(arg.value) ? arg.value : arg.value[arg.defaultIndex ?? 0];
}
if (arg.type === "argSelector") {
return !Array.isArray(arg.value) ? arg.value : (arg.value[arg.defaultIndex ?? 0]?.name || "");
}
if (arg.type === "editableOption") {
return typeof arg.value === "string" ? arg.value : arg.value[arg.defaultIndex ?? 0].value;
}

View File

@ -24,8 +24,10 @@ import "./tests/Dish.mjs";
import "./tests/NodeDish.mjs";
import "./tests/Utils.mjs";
import "./tests/Categories.mjs";
import "./tests/ToHTMLEntity.mjs";
import "./tests/lib/BigIntUtils.mjs";
import "./tests/lib/ChartsProtocolPrototypePollution.mjs";
import "./tests/ParseQRCode.mjs";
const testStatus = {
allTestsPassing: true,

View File

@ -0,0 +1,35 @@
/**
* ParseQRCode API tests.
*
* @author Sanjays2402
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
import OperationConfig from "../../../src/core/config/OperationConfig.json" with { type: "json" };
import it from "../assertionHandler.mjs";
import assert from "assert";
TestRegister.addApiTests([
/*
* Regression test for #2610.
*
* Parse QR Code used to declare a `checks` regex that matched any JPEG,
* PNG, GIF, WEBP or BMP magic bytes. Magic aggregates every operation
* with a `checks` property, so any image input ran through a full QR
* parse attempt, which in turn emitted a "Could not read a QR code from
* the image" warning to the browser console for every image. There is
* no cheap way to detect a QR code without attempting a full parse, so
* Parse QR Code must not participate in Magic; users can add it
* manually when they know an image contains a QR code.
*/
it("Parse QR Code: must not participate in Magic (#2610)", () => {
const op = OperationConfig["Parse QR Code"];
assert(op, "Parse QR Code operation is missing from OperationConfig");
assert(
!op.checks || op.checks.length === 0,
"Parse QR Code must not declare `checks`; otherwise Magic will run a " +
"QR parse on every image and spam the console (see issue #2610)."
);
}),
]);

View File

@ -0,0 +1,82 @@
import TestRegister from "../../lib/TestRegister.mjs";
import ToHTMLEntity from "../../../src/core/operations/ToHTMLEntity.mjs";
import FromHTMLEntity from "../../../src/core/operations/FromHTMLEntity.mjs";
import { HTML_ENTITY_LOOKUP, HTML_ENTITY_REVERSE_LOOKUP } from "../../../src/core/lib/HTMLEntities.mjs";
import it from "../assertionHandler.mjs";
import assert from "assert";
import { readFileSync } from "fs";
import { fileURLToPath } from "url";
import path from "path";
// Vendored WHATWG named character reference set (https://html.spec.whatwg.org/entities.json).
const SPEC = JSON.parse(readFileSync(path.join(
path.dirname(fileURLToPath(import.meta.url)),
"../../../src/core/vendor/htmlEntities/entity.json"), "utf8"));
const specByName = {};
for (const [key, val] of Object.entries(SPEC))
if (key.endsWith(";")) specByName[key.slice(1, -1)] = val.codepoints;
TestRegister.addApiTests([
it("To HTML Entity: every named entity in the table is well-formed", () => {
// "Convert all characters" emits an entity for every code point, so a
// correct table yields an unbroken stream of entity tokens. A malformed
// value such as "&nge;;" or "&epsi;," leaves stray characters between
// tokens, which the walk below flags and reports with surrounding context.
let input = "";
for (let cp = 0; cp <= 0xFFFF; cp++) {
if (cp >= 0xD800 && cp <= 0xDFFF) continue; // skip surrogate range
input += String.fromCodePoint(cp);
}
const output = new ToHTMLEntity().run(input, [true, "Named entities"]);
const tokenRe = /&#[0-9]+;|&#x[0-9a-fA-F]+;|&[A-Za-z][A-Za-z0-9]*;/y;
const malformed = [];
let pos = 0;
while (pos < output.length) {
tokenRe.lastIndex = pos;
if (tokenRe.exec(output)) {
pos = tokenRe.lastIndex;
} else {
malformed.push(output.slice(Math.max(0, pos - 12), pos + 12));
pos++;
}
}
assert.deepStrictEqual(malformed, [], `Malformed entity value(s) near: ${JSON.stringify(malformed)}`);
}),
it("HTML Entity: named encoding round-trips through From HTML Entity", () => {
// Because both operations share one lookup table, encoding a character to
// a named entity and decoding it must return the original character for
// every BMP code point: FromHTMLEntity(ToHTMLEntity(x)) === x.
const toOp = new ToHTMLEntity(),
fromOp = new FromHTMLEntity();
const mismatches = [];
for (let cp = 0; cp <= 0xFFFF; cp++) {
if (cp >= 0xD800 && cp <= 0xDFFF) continue; // skip surrogate range
const char = String.fromCodePoint(cp);
const encoded = toOp.run(char, [true, "Named entities"]);
const decoded = fromOp.run(encoded, []);
if (decoded !== char)
mismatches.push(`U+${cp.toString(16).toUpperCase().padStart(4, "0")} -> ${encoded} -> U+${decoded.codePointAt(0).toString(16).toUpperCase()}`);
}
assert.deepStrictEqual(mismatches, [], `Round-trip failed for: ${JSON.stringify(mismatches.slice(0, 20))}`);
}),
it("HTML Entity: every table entry is conformant with the WHATWG spec", () => {
// Both lookup tables must agree with entities.json: every encode name is a
// real spec name mapping to exactly that code point, and every decode name
// maps to the spec code point.
const violations = [];
for (const [cp, name] of Object.entries(HTML_ENTITY_LOOKUP)) {
const spec = specByName[name];
if (!spec || spec.length !== 1 || spec[0] !== Number(cp))
violations.push(`encode ${cp} -> &${name}; (spec: ${spec ? JSON.stringify(spec) : "none"})`);
}
for (const [name, cp] of Object.entries(HTML_ENTITY_REVERSE_LOOKUP)) {
const spec = specByName[name];
if (!spec || spec.length !== 1 || spec[0] !== cp)
violations.push(`decode &${name}; -> ${cp} (spec: ${spec ? JSON.stringify(spec) : "none"})`);
}
assert.deepStrictEqual(violations, [], `Spec violations: ${JSON.stringify(violations.slice(0, 20))}`);
}),
]);

View File

@ -26,4 +26,81 @@ TestRegister.addApiTests([
"\x7e...",
);
}),
it("Utils: should parse normal pretty recipes", () => {
assert.deepStrictEqual(
Utils.parseRecipeConfig("From_Base64('A-Za-z0-9+/=',true)To_Hex('Space')"),
[
{
op: "From Base64",
args: ["A-Za-z0-9+/=", true],
},
{
op: "To Hex",
args: ["Space"],
},
],
);
}),
it("Utils: should parse pretty recipe options", () => {
assert.deepStrictEqual(
Utils.parseRecipeConfig("A(/disabled/breakpoint)"),
[
{
op: "A",
args: [],
disabled: true,
breakpoint: true,
},
],
);
}),
it("Utils: should parse escaped quotes and backslashes in pretty recipes", () => {
assert.deepStrictEqual(
Utils.parseRecipeConfig("A('\\'\\\\')"),
[
{
op: "A",
args: ["'\\"],
},
],
);
}),
it("Utils: should parse large valid quoted pretty recipe arguments", () => {
const value = "x".repeat(10000);
assert.deepStrictEqual(
Utils.parseRecipeConfig(`A('${value}')`),
[
{
op: "A",
args: [value],
},
],
);
}),
it("Utils: should reject malformed pretty recipes with unmatched quotes", () => {
assert.throws(
() => Utils.parseRecipeConfig("A(" + "'".repeat(10000)),
/Invalid recipe/,
);
}),
it("Utils: should reject malformed pretty recipes with malformed parentheses", () => {
assert.throws(
() => Utils.parseRecipeConfig("A("),
/Invalid recipe/,
);
}),
it("Utils: should reject malformed pretty recipes with malformed escapes", () => {
assert.throws(
() => Utils.parseRecipeConfig("A('" + "\\".repeat(10000)),
/Invalid recipe/,
);
}),
]);

View File

@ -264,6 +264,18 @@ Full hash: $2a$10$ODeP1.6fMsb.ENk2ngPUCO7qTGVPyHA9TqDVcyupyed8FjsiF65L6`;
assert.strictEqual(result.toString(), "Fit as a Fiddle");
}),
it("Bzip2 Compress: round-trips through the Node API", async () => {
const compressed = await chef.bzip2Compress("The quick brown fox.");
const result = await chef.bzip2Decompress(compressed);
assert.strictEqual(result.toString(), "The quick brown fox.");
}),
it("Avro to JSON: decodes an object container file through the Node API", async () => {
const avro = chef.fromHex("4f626a0104166176726f2e736368656d6196017b2274797065223a227265636f7264222c226e616d65223a22736d616c6c222c226669656c6473223a5b7b226e616d65223a226e616d65222c2274797065223a22737472696e67227d5d7d146176726f2e636f646563086e756c6c004e0247632e3702e5b75cdab9a62f1541020e0c6d796e616d654e0247632e3702e5b75cdab9a62f1541");
const result = await chef.avroToJSON(avro);
assert.strictEqual(result.toString(), "{\n \"name\": \"myname\"\n}");
}),
it("cartesianProduct: binary string", () => {
const result = cartesianProduct("1:2\\n\\n3:4", {
itemDelimiter: ":",

View File

@ -150,5 +150,38 @@ TestRegister.addTests([
args: [5, 1.5, "hello", "", { "option": "Option A", "string": "test" }, ""]
}
]
},
{
name: "Automated Validation: Valid Arg Selector value",
input: "test",
expectedOutput: "Success",
recipeConfig: [
{
op: "Automated Validation Test Op",
args: [5, 1.5, "hello", "", { "option": "Option A", "string": "test" }, "Option 1", "Option 2"]
}
]
},
{
name: "Automated Validation: Invalid Arg Selector value",
input: "test",
expectedOutput: "Arg Selector Ingredient must be one of the following: Option 1, Option 2.",
recipeConfig: [
{
op: "Automated Validation Test Op",
args: [5, 1.5, "hello", "", { "option": "Option A", "string": "test" }, "Option 1", "Option 3"]
}
]
},
{
name: "Automated Validation: Arg Selector value empty (invalid)",
input: "test",
expectedOutput: "Arg Selector Ingredient cannot be empty.",
recipeConfig: [
{
op: "Automated Validation Test Op",
args: [5, 1.5, "hello", "", { "option": "Option A", "string": "test" }, "Option 1", ""]
}
]
}
]);

View File

@ -0,0 +1,476 @@
/**
* @author Imantas Lukenskas [imantas@lukenskas.dev]
* @copyright Imantas Lukenskas 2026
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
// values in "COBS" testcases are taken from here:
// https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
TestRegister.addTests([
{
"name": "To COBS Example #1",
"input": "00",
"expectedOutput": "01 01",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "To COBS Example #2",
"input": "00 00",
"expectedOutput": "01 01 01",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "To COBS Example #3",
"input": "00 11 00",
"expectedOutput": "01 02 11 01",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "To COBS Example #4",
"input": "11 22 00 33",
"expectedOutput": "03 11 22 02 33",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "To COBS Example #5",
"input": "11 22 33 44",
"expectedOutput": "05 11 22 33 44",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "To COBS Example #6",
"input": "11 00 00 00",
"expectedOutput": "02 11 01 01 01",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "To COBS Example #7",
"input": "01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe",
"expectedOutput": "ff 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "To COBS Example #8",
"input": "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe",
"expectedOutput": "01 ff 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "To COBS Example #9",
"input": "01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff",
"expectedOutput": "ff 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe 02 ff",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "To COBS Example #10",
"input": "02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00",
"expectedOutput": "ff 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 01 01",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "To COBS Example #11",
"input": "03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01",
"expectedOutput": "fe 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 02 01",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "To COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Example #1",
"input": "01 01",
"expectedOutput": "00",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Example #2",
"input": "01 01 01",
"expectedOutput": "00 00",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Example #3",
"input": "01 02 11 01",
"expectedOutput": "00 11 00",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Example #4",
"input": "03 11 22 02 33",
"expectedOutput": "11 22 00 33",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Example #5",
"input": "05 11 22 33 44",
"expectedOutput": "11 22 33 44",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Example #6",
"input": "02 11 01 01 01",
"expectedOutput": "11 00 00 00",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Example #7",
"input": "ff 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe",
"expectedOutput": "01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Example #8",
"input": "01 ff 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe",
"expectedOutput": "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Example #9",
"input": "ff 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe 02 ff",
"expectedOutput": "01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Example #10",
"input": "ff 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 01 01",
"expectedOutput": "02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Example #11",
"input": "fe 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 02 01",
"expectedOutput": "03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
},
{
"op": "To Hex",
"args": ["Space"]
}
]
},
{
"name": "From COBS Invalid input #1",
"input": "00 48 45 4C 4C 4F",
"expectedOutput": "Could not decode from COBS: payload must not contain a 0x00 byte",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
}
],
},
{
"name": "From COBS Invalid input #2",
"input": "48 45 00 4C 4C 4F",
"expectedOutput": "Could not decode from COBS: payload must not contain a 0x00 byte",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
}
],
},
{
"name": "From COBS Invalid input #3",
"input": "48 45 4C 4C 4F 00",
"expectedOutput": "Could not decode from COBS: payload must not contain a 0x00 byte",
"recipeConfig": [
{
"op": "From Hex",
"args": ["Auto"]
},
{
"op": "From COBS",
"args": []
}
],
},
]);

View File

@ -0,0 +1,78 @@
/**
* Extended GCD tests.
*
* @author p-leriche [philip.leriche@cantab.net]
*
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Extended GCD: coprime numbers (3, 11)",
input: "",
expectedOutput: "gcd: 1\n\nBezout coefficients:\nx = 4\ny = -1\n\n",
recipeConfig: [
{
op: "Extended GCD",
args: ["3", "11"],
},
],
},
{
name: "Extended GCD: non-coprime numbers (240, 46)",
input: "",
expectedOutput: "gcd: 2\n\nBezout coefficients:\nx = -9\ny = 47\n\n",
recipeConfig: [
{
op: "Extended GCD",
args: ["240", "46"],
},
],
},
{
name: "Extended GCD: with zero (17, 0)",
input: "",
expectedOutput: "gcd: 17\n\nBezout coefficients:\nx = 1\ny = 0\n\n",
recipeConfig: [
{
op: "Extended GCD",
args: ["17", "0"],
},
],
},
{
name: "Extended GCD: hexadecimal input (0xFF, 0x11)",
input: "",
expectedOutput: "gcd: 17\n\nBezout coefficients:\nx = 0\ny = 1\n\n",
recipeConfig: [
{
op: "Extended GCD",
args: ["0xFF", "0x11"],
},
],
},
{
name: "Extended GCD: using input field for value a",
input: "42",
expectedOutput: "gcd: 7\n\nBezout coefficients:\nx = 1\ny = -1\n\n",
recipeConfig: [
{
op: "Extended GCD",
args: ["", "35"],
},
],
},
{
name: "Extended GCD: large numbers",
input: "",
expectedOutput: "gcd: 2\n\nBezout coefficients:\nx = 12703973750415151\ny = -1577756566311408967124629843\n\n",
recipeConfig: [
{
op: "Extended GCD",
args: ["123456789012345678901234567890", "994064509324197316"],
},
],
},
]);

View File

@ -30,4 +30,59 @@ TestRegister.addTests([
},
],
},
{
name: "From Decimal with Auto delimiter (space)",
input: "72 101 108 108 111",
expectedOutput: "Hello",
recipeConfig: [
{
op: "From Decimal",
args: ["Auto", false]
},
],
},
{
name: "From Decimal with Auto delimiter (comma)",
input: "72,101,108,108,111",
expectedOutput: "Hello",
recipeConfig: [
{
op: "From Decimal",
args: ["Auto", false]
},
],
},
{
name: "From Decimal with Auto delimiter (mixed)",
input: "72, 101 : 108; 108\t111",
expectedOutput: "Hello",
recipeConfig: [
{
op: "From Decimal",
args: ["Auto", false]
},
],
},
{
name: "From Decimal with Auto delimiter (newline)",
input: "72\n101\n108\n108\n111",
expectedOutput: "Hello",
recipeConfig: [
{
op: "From Decimal",
args: ["Auto", false]
},
],
},
{
name: "From Decimal with Auto delimiter and signed values",
input: "-130 -140 -152 -151 115 33 0 -1",
expectedOutput: "~this!\u0000\u00ff",
recipeConfig: [
{
op: "From Decimal",
args: ["Auto", true]
},
],
},
]);

View File

@ -0,0 +1,126 @@
/**
* To/From HTML Entity tests.
*
* @author roberson-io [michaelroberson@gmail.com]
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "To HTML Entity: named",
input: "<a href='#'>\"&\"</a>",
expectedOutput: "&lt;a href&equals;&apos;&num;&apos;&gt;&quot;&amp;&quot;&lt;&sol;a&gt;",
recipeConfig: [
{ op: "To HTML Entity", args: [false, "Named entities"] },
],
},
{
// Regression: this value was "&rlhar;;" (stray trailing semicolon) in the
// original byteToEntity table. See issue #2645.
name: "To HTML Entity: malformed value fixed (U+21CC)",
input: "⇌",
expectedOutput: "&rlhar;",
recipeConfig: [
{ op: "To HTML Entity", args: [false, "Named entities"] },
],
},
{
// Regression: U+03F5 emitted "&epsi;," before; the spec name is &epsiv;
// (&epsi; is U+03B5). See issue #2645.
name: "To HTML Entity: spec-conformant name (U+03F5)",
input: "ϵ",
expectedOutput: "&epsiv;",
recipeConfig: [
{ op: "To HTML Entity", args: [false, "Named entities"] },
],
},
{
// Regression: the OHM SIGN previously emitted the non-conformant &ohm;
// (spec &ohm; is U+03A9). It now encodes numerically.
name: "To HTML Entity: non-conformant name dropped (U+2126 ohm sign)",
input: "Ω",
expectedOutput: "&#8486;",
recipeConfig: [
{ op: "To HTML Entity", args: [false, "Named entities"] },
],
},
{
// The FULL BLOCK previously decoded to &block; but never encoded to it.
// Generating both tables from the spec fixes this asymmetry.
name: "To HTML Entity: encode/decode symmetry (U+2588 block)",
input: "█",
expectedOutput: "&block;",
recipeConfig: [
{ op: "To HTML Entity", args: [false, "Named entities"] },
],
},
{
name: "To HTML Entity: numeric, convert all",
input: "A<",
expectedOutput: "&#65;&#60;",
recipeConfig: [
{ op: "To HTML Entity", args: [true, "Numeric entities"] },
],
},
{
name: "To HTML Entity: hex, convert all",
input: "A<",
expectedOutput: "&#x41;&#x3c;",
recipeConfig: [
{ op: "To HTML Entity", args: [true, "Hex entities"] },
],
},
{
name: "From HTML Entity: named",
input: "&lt;a href=&apos;#&apos;&gt;&quot;&amp;&quot;&lt;/a&gt;",
expectedOutput: "<a href='#'>\"&\"</a>",
recipeConfig: [
{ op: "From HTML Entity", args: [] },
],
},
{
// Legacy/alias names still decode, now to the spec code point.
name: "From HTML Entity: alias to spec code point (&ohm;)",
input: "&ohm;",
expectedOutput: "Ω",
recipeConfig: [
{ op: "From HTML Entity", args: [] },
],
},
{
name: "From HTML Entity: decimal numeric entity",
input: "&#65;",
expectedOutput: "A",
recipeConfig: [
{ op: "From HTML Entity", args: [] },
],
},
{
name: "From HTML Entity: hex numeric entity",
input: "&#x41;",
expectedOutput: "A",
recipeConfig: [
{ op: "From HTML Entity", args: [] },
],
},
{
// Unknown entities are passed through unchanged.
name: "From HTML Entity: invalid entity passed through",
input: "a &notreal; b",
expectedOutput: "a &notreal; b",
recipeConfig: [
{ op: "From HTML Entity", args: [] },
],
},
{
name: "HTML Entity: round-trips through both operations",
input: "Hello <World> & \"friends\" — ⇌ █",
expectedOutput: "Hello <World> & \"friends\" — ⇌ █",
recipeConfig: [
{ op: "To HTML Entity", args: [true, "Named entities"] },
{ op: "From HTML Entity", args: [] },
],
},
]);

View File

@ -722,6 +722,39 @@ TestRegister.addTests([
}
]
},
{
name: "HMAC: Decimal key space-delimited matches Latin1 key (fromDecimal Auto delimiter regression)",
input: "Hello, World!",
expectedOutput: "9b641a008211bb0464a10899d5b37a24ab08ffcf839f5e74d17d99f856530957",
recipeConfig: [
{
"op": "HMAC",
"args": [{"option": "Decimal", "string": "65 65"}, "SHA256"]
}
]
},
{
name: "HMAC: Latin1 key matches space-delimited Decimal key (fromDecimal Auto delimiter regression)",
input: "Hello, World!",
expectedOutput: "9b641a008211bb0464a10899d5b37a24ab08ffcf839f5e74d17d99f856530957",
recipeConfig: [
{
"op": "HMAC",
"args": [{"option": "Latin1", "string": "AA"}, "SHA256"]
}
]
},
{
name: "HMAC: Decimal key comma-delimited matches Latin1 key (fromDecimal Auto delimiter regression)",
input: "Hello, World!",
expectedOutput: "9b641a008211bb0464a10899d5b37a24ab08ffcf839f5e74d17d99f856530957",
recipeConfig: [
{
"op": "HMAC",
"args": [{"option": "Decimal", "string": "65,65"}, "SHA256"]
}
]
},
{
name: "MD5: Complex bytes",
input: "10dc10e32010de10d010dc10d810d910d010e12e",

View File

@ -0,0 +1,208 @@
/**
* MOD tests
*
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "MOD: Basic modulo operation",
input: "15 4 7",
expectedOutput: "0 1 1",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Single number",
input: "10",
expectedOutput: "1",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Comma-separated numbers",
input: "15,8,23,16,5",
expectedOutput: "1 1 2 2 5",
recipeConfig: [
{
"op": "MOD",
"args": [7, "Comma"]
}
],
},
{
name: "MOD: Line feed separated numbers",
input: "25\n13\n44\n7",
expectedOutput: "0 3 4 2",
recipeConfig: [
{
"op": "MOD",
"args": [5, "Line feed"]
}
],
},
{
name: "MOD: Large numbers",
input: "123456789012345 987654321098765",
expectedOutput: "123456789012345 987654321098765",
recipeConfig: [
{
"op": "MOD",
"args": [1234567890123456, "Space"]
}
],
},
{
name: "MOD: Mixed with non-numeric values",
input: "15 abc 4 def 7 xyz 23",
expectedOutput: "0 1 1 2",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Decimal numbers",
input: "10.5 15.7 8.2",
expectedOutput: "1.5 0.7 2.2",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Negative numbers",
input: "-15 -8 25 -10",
expectedOutput: "0 -2 1 -1",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Zero in input",
input: "0 5 10 15 20",
expectedOutput: "0 2 1 0 2",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Modulus of 2 (even/odd check)",
input: "1 2 3 4 5 6 7 8 9 10",
expectedOutput: "1 0 1 0 1 0 1 0 1 0",
recipeConfig: [
{
"op": "MOD",
"args": [2, "Space"]
}
],
},
{
name: "MOD: Numbers with extra whitespace",
input: " 15 4 7 ",
expectedOutput: "0 1 1",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Empty input",
input: "",
expectedOutput: "",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Scientific notation",
input: "1e3 2e2 5e1",
expectedOutput: "1 2 2",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Floating point precision",
input: "10.123456789 20.987654321",
expectedOutput: "1.123456789 2.987654321",
recipeConfig: [
{
"op": "MOD",
"args": [3, "Space"]
}
],
},
{
name: "MOD: Zero modulus error",
input: "15 4 7",
expectedError: true,
expectedOutput: "MOD - Modulus cannot be zero",
recipeConfig: [
{
"op": "MOD",
"args": [0, "Space"]
}
],
},
{
name: "MOD: Semi-colon separated numbers",
input: "17;5;8;13",
expectedOutput: "2 0 3 3",
recipeConfig: [
{
"op": "MOD",
"args": [5, "Semi-colon"]
}
],
},
{
name: "MOD: Colon separated numbers",
input: "25:9:14:7",
expectedOutput: "1 1 2 3",
recipeConfig: [
{
"op": "MOD",
"args": [4, "Colon"]
}
],
},
{
name: "MOD: CRLF separated numbers",
input: "30\r\n18\r\n22\r\n11",
expectedOutput: "0 0 4 5",
recipeConfig: [
{
"op": "MOD",
"args": [6, "CRLF"]
}
],
},
]);

View File

@ -152,5 +152,49 @@ TestRegister.addTests([
args: [1, false, false]
}
]
},
{
name: "Magic: invalid zero A1Z26 values do not match",
input: "0,0",
unexpectedMatch: /A1Z26 Cipher Decode/,
recipeConfig: [
{
op: "Magic",
args: [0, false, false]
}
]
},
{
name: "Magic: invalid high A1Z26 values do not match",
input: "27,1",
unexpectedMatch: /A1Z26 Cipher Decode/,
recipeConfig: [
{
op: "Magic",
args: [0, false, false]
}
]
},
{
name: "Magic: valid leading-zero A1Z26 values match",
input: "26,01",
expectedMatch: /A1Z26 Cipher Decode/,
recipeConfig: [
{
op: "Magic",
args: [0, false, false]
}
]
},
{
name: "Magic: valid leading-zero A1Z26 values match with two digits",
input: "09,10",
expectedMatch: /A1Z26 Cipher Decode/,
recipeConfig: [
{
op: "Magic",
args: [0, false, false]
}
]
}
]);

View File

@ -0,0 +1,137 @@
/**
* Modular Exponentiation tests.
*
* @author p-leriche [philip.leriche@cantab.net]
*
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Modular Exponentiation: basic example (2^10 mod 1000)",
input: "",
expectedOutput: "24",
recipeConfig: [
{
op: "Modular Exponentiation",
args: ["2", "1000", "10"],
},
],
},
{
name: "Modular Exponentiation: small values (3^5 mod 7)",
input: "",
expectedOutput: "5",
recipeConfig: [
{
op: "Modular Exponentiation",
args: ["3", "7", "5"],
},
],
},
{
name: "Modular Exponentiation: exponent zero",
input: "",
expectedOutput: "1",
recipeConfig: [
{
op: "Modular Exponentiation",
args: ["999", "100", "0"],
},
],
},
{
name: "Modular Exponentiation: base one",
input: "",
expectedOutput: "1",
recipeConfig: [
{
op: "Modular Exponentiation",
args: ["1", "1000", "999"],
},
],
},
{
name: "Modular Exponentiation: hexadecimal input",
input: "",
expectedOutput: "256",
recipeConfig: [
{
op: "Modular Exponentiation",
args: ["0x10", "1000", "0x2"],
},
],
},
{
name: "Modular Exponentiation: using input field for base",
input: "5",
expectedOutput: "6",
recipeConfig: [
{
op: "Modular Exponentiation",
args: ["", "7", "3"],
},
],
},
{
name: "Modular Exponentiation: using input field for exponent",
input: "4",
expectedOutput: "5",
recipeConfig: [
{
op: "Modular Exponentiation",
args: ["2", "11", ""],
},
],
},
{
name: "Modular Exponentiation: RSA-like example (small)",
input: "",
expectedOutput: "561",
recipeConfig: [
{
op: "Modular Exponentiation",
args: ["123", "1000", "456"],
},
],
},
{
name: "Modular Exponentiation: large base and exponent",
input: "",
expectedOutput: "560583526",
recipeConfig: [
{
op: "Modular Exponentiation",
args: ["123456789", "1000000007", "65537"],
},
],
},
{
name: "Modular Exponentiation: crypto-sized numbers (RSA-2048 simulation)",
input: "",
expectedOutput: "1",
recipeConfig: [
{
op: "Modular Exponentiation",
args: [
"12345678901234567890123456789012345678901234567890",
"99999999999999999999999999999999999999999999999999",
"0"
],
},
],
},
{
name: "Modular Exponentiation: Fermat's Little Theorem (a^(p-1) mod p = 1)",
input: "",
expectedOutput: "1",
recipeConfig: [
{
op: "Modular Exponentiation",
args: ["3", "11", "10"],
},
],
},
]);

View File

@ -0,0 +1,78 @@
/**
* Modular Inverse tests.
*
* @author p-leriche [philip.leriche@cantab.net]
*
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Modular Inverse: basic example (3 mod 11)",
input: "",
expectedOutput: "4",
recipeConfig: [
{
op: "Modular Inverse",
args: ["3", "11"],
},
],
},
{
name: "Modular Inverse: another coprime pair (7 mod 26)",
input: "",
expectedOutput: "15",
recipeConfig: [
{
op: "Modular Inverse",
args: ["7", "26"],
},
],
},
{
name: "Modular Inverse: hexadecimal input (0x10 mod 0x11)",
input: "",
expectedOutput: "16",
recipeConfig: [
{
op: "Modular Inverse",
args: ["0x10", "0x11"],
},
],
},
{
name: "Modular Inverse: using input field for value",
input: "5",
expectedOutput: "21",
recipeConfig: [
{
op: "Modular Inverse",
args: ["", "26"],
},
],
},
{
name: "Modular Inverse: using input field for modulus",
input: "17",
expectedOutput: "7",
recipeConfig: [
{
op: "Modular Inverse",
args: ["5", ""],
},
],
},
{
name: "Modular Inverse: large number (RSA-like)",
input: "",
expectedOutput: "934281398294",
recipeConfig: [
{
op: "Modular Inverse",
args: ["65537", "9999999999999"],
},
],
},
]);