diff --git a/sdk/typescript/.gitignore b/sdk/typescript/.gitignore index 6c62fd432..d39addef6 100644 --- a/sdk/typescript/.gitignore +++ b/sdk/typescript/.gitignore @@ -1,4 +1,6 @@ /dist/ /node_modules +/reports/ +/.stryker-tmp/ /private_release/dist/ /*.tsbuildinfo diff --git a/sdk/typescript/package.json b/sdk/typescript/package.json index e29413c30..82c6aa106 100644 --- a/sdk/typescript/package.json +++ b/sdk/typescript/package.json @@ -49,6 +49,7 @@ "lint": "tsc --noEmit", "prepack": "node --run build", "test": "bun test --timeout 30000 ./tests-ts", + "test:mutation": "stryker run", "test:package": "node scripts/smoke-package.mjs", "types": "pnpm run generate:models:check && tsc --noEmit" }, @@ -68,9 +69,11 @@ "smol-toml": "1.6.1" }, "devDependencies": { + "@stryker-mutator/core": "9.6.1", "@types/bun": "1.3.13", "@types/node": "22.19.17", "@types/papaparse": "5.3.15", + "fast-check": "4.9.0", "json-schema-to-typescript": "15.0.4", "prettier": "3.2.5", "typescript": "5.7.3" diff --git a/sdk/typescript/pnpm-lock.yaml b/sdk/typescript/pnpm-lock.yaml index eca8be0e5..4befc5cfb 100644 --- a/sdk/typescript/pnpm-lock.yaml +++ b/sdk/typescript/pnpm-lock.yaml @@ -48,6 +48,9 @@ importers: specifier: 1.6.1 version: 1.6.1 devDependencies: + '@stryker-mutator/core': + specifier: 9.6.1 + version: 9.6.1(@types/node@22.19.17) '@types/bun': specifier: 1.3.13 version: 1.3.13 @@ -57,6 +60,9 @@ importers: '@types/papaparse': specifier: 5.3.15 version: 5.3.15 + fast-check: + specifier: 4.9.0 + version: 4.9.0 json-schema-to-typescript: specifier: 15.0.4 version: 15.0.4 @@ -73,6 +79,159 @@ packages: resolution: {integrity: sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==} engines: {node: '>= 16'} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-proposal-decorators@7.29.7': + resolution: {integrity: sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.29.7': + resolution: {integrity: sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.29.7': + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.29.7': + resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.29.7': + resolution: {integrity: sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.29.7': + resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.29.7': + resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@cfworker/json-schema@4.1.1': resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} @@ -215,6 +374,22 @@ packages: '@types/node': optional: true + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jsdevtools/ono@7.1.3': resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} @@ -384,6 +559,29 @@ packages: resolution: {integrity: sha512-WmaxVSfvY5K/TwcG2B2TU1WOe1As1uc2s7myswtP6dBlcjU3hM08SApxv/jmyGaCE8t4gO5BBhmHY4pDUfmr2g==} engines: {node: '>=22'} + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@stryker-mutator/api@9.6.1': + resolution: {integrity: sha512-g8VNoFWQWbx0pdal3Vt8jVCZW+v3sc3gi94iI0GVtVgUGTqphAjJF6EAruPTx0lqvtonsaAxn5TD36hcG1d6Wg==} + engines: {node: '>=20.0.0'} + + '@stryker-mutator/core@9.6.1': + resolution: {integrity: sha512-WMgnvf+Wyh/yiruhNZwc8w8DlzmmjXhPjSn5MR8RhAXzlnWji8TQrUYgBUkHk9bEgSaIlB3KZHm37iiU5Q2cLQ==} + engines: {node: '>=20.0.0'} + hasBin: true + + '@stryker-mutator/instrumenter@9.6.1': + resolution: {integrity: sha512-5K8wH4Pthly25c2uKKik4Dfcoeou7sbJdFS6u3QIYHlulgFVDJwtEMWTZGkZfs7IiUEXIDNa0keRACq5jn5AvA==} + engines: {node: '>=20.0.0'} + + '@stryker-mutator/util@9.6.1': + resolution: {integrity: sha512-Lk/ALVctJjFv1vvwR+CFoKzDCWvsBlq7flDUnmnpuwTrGbm156EdZD1Jjq4o8KdOap0ezUZqQNE9OAI1m2+pUQ==} + '@toon-format/toon@2.3.0': resolution: {integrity: sha512-/Ew9etdRQKVMnm9fDaCG0JjyAOK/O7T0M97oum1aW4W+UR8ZhVVPBanIV7oWgHBiGlnVxV9M55PWQCHofDV07w==} @@ -405,21 +603,61 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + angular-html-parser@10.4.0: + resolution: {integrity: sha512-++nLNyZwRfHqFh7akH5Gw/JYizoFlMRz0KRigfwfsLqV8ZqlcVRb1LkPEWdYvEKDnbktknM2J4BXaYUGrQZPww==} + engines: {node: '>= 14'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.11.13: + resolution: {integrity: sha512-k9HNuUVMlqVjQ9UHzfPjIqiDbWw7WqT1AoT7GL8VwvF3r0ZfArtgiSPAlmupyNquNgOJHTuH4CKYf8ttMTWBTQ==} + engines: {node: '>=6.0.0'} + hasBin: true + before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} + + browserslist@4.28.8: + resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} bun-types@1.3.13: resolution: {integrity: sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA==} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + caniuse-lite@1.0.30001809: + resolution: {integrity: sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chardet@2.2.0: resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} @@ -427,10 +665,21 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + content-type@2.0.0: resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} engines: {node: '>=18'} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -440,14 +689,54 @@ packages: supports-color: optional: true + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.403: + resolution: {integrity: sha512-MQsYmdaLzvaCX5j+ZZBr5Fm6uCCnPQcRtlvmvRlWqrXy+BH2O4ffXIAScF+JQznQWB9brWp4lSD9Z4yNmaf2BA==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} + engines: {node: ^18.19.0 || >=20.5.0} + extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} hasBin: true + fast-check@4.9.0: + resolution: {integrity: sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==} + engines: {node: '>=12.17.0'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -478,14 +767,53 @@ packages: fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + graphql@17.0.2: resolution: {integrity: sha512-FRWbddMxfkjiB7z+aQDWIR+E34xo9I8c9mtK2RPv8PmMzKRvrdsreHL/Ui/TmwHJfhHChEtsFPyMHKI+xuarQQ==} engines: {node: ^22.0.0 || ^24.0.0 || ^25.0.0 || >=26.0.0} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + iconv-lite@0.7.3: resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} @@ -495,6 +823,9 @@ packages: engines: {node: '>=22'} hasBin: true + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -503,10 +834,39 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-md4@0.3.2: + resolution: {integrity: sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-yaml@4.3.1: resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} hasBin: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-rpc-2.0@1.7.1: + resolution: {integrity: sha512-JqZjhjAanbpkXIzFE7u8mE/iFblawwlXtONaCvRqI+pyABVz7B4M1EUNpyVW+dZjqgQ2L5HFmZCmOCgUKm00hg==} + json-schema-to-typescript@15.0.4: resolution: {integrity: sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ==} engines: {node: '>=16.0.0'} @@ -518,25 +878,84 @@ packages: json-with-bigint@3.5.10: resolution: {integrity: sha512-Vcx+JVNEBts/xfcoCS69sKrOhOk/3TVlvlT+XzUOefVKnnrbYSCKpDCm10pohsJFtsJVYnwa/cXRZ4eElzaM6w==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + lodash.groupby@4.6.0: + resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==} + lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mutation-server-protocol@0.4.1: + resolution: {integrity: sha512-SBGK0j8hLDne7bktgThKI8kGvGTx3rY3LAeQTmOKZ5bVnL/7TorLMvcVF7dIPJCu5RNUWhkkuF53kurygYVt3g==} + engines: {node: '>=18'} + + mutation-testing-elements@3.7.3: + resolution: {integrity: sha512-SMeIPxngJpfjfNYctFpYQQtlBlZaVO0aoB3FKdwrI8Ee/2bkyUuCZzAOCLv1U9fnmfA37dPFq0Owduoxs2XgGQ==} + + mutation-testing-metrics@3.7.3: + resolution: {integrity: sha512-B8QrP0ZomErzTPNlhrzKWPNBln+3afwBZPHv0Q7N8wZZTYxMptzb/Gdm3ExXVmioVYrtZAtsDs7W/T/b2AixOQ==} + + mutation-testing-report-schema@3.7.3: + resolution: {integrity: sha512-BHm3MYq+ckO+t5CtlG8zpqxc75rdJCkxVlE+fGuGJM3F7tNCQ/OW2N+TQVHN3BHsYa84+BFc6g3AwDYkUsw2MA==} + mute-stream@3.0.0: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} + node-releases@2.0.53: + resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} + engines: {node: '>=18'} + + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} papaparse@5.5.3: resolution: {integrity: sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + pdfjs-dist@6.2.108: resolution: {integrity: sha512-YxFb+SQcodN2rnX9Tn3dHYlqfb7NjlzzfONPpJd+AKoKtUjEdevTfbC07d5TcczzOK6261auRkP/M8OBHs9vFQ==} engines: {node: '>=22.13.0 || >=24'} @@ -544,6 +963,9 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@4.0.5: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} @@ -553,16 +975,72 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + engines: {node: '>=18'} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + pure-rand@8.4.2: + resolution: {integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==} + + qs@6.15.1: + resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} + engines: {node: '>=0.6'} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -571,6 +1049,14 @@ packages: resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} @@ -578,20 +1064,63 @@ packages: tokenx@1.3.0: resolution: {integrity: sha512-NLdXTEZkKiO0gZuLtMoZKjCXTREXeZZt8nnnNeyoXtNZAfG/GKGSbQtLU5STspc0rMSwcA+UJfWZkbNU01iKmQ==} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tunnel@0.0.6: + resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} + engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + + typed-inject@5.0.0: + resolution: {integrity: sha512-0Ql2ORqBORLMdAW89TQKZsb1PQkFGImFfVmncXWe7a+AA3+7dh7Se9exxZowH4kbnlvKEFkMxUYdHUpjYWFJaA==} + engines: {node: '>=18'} + + typed-rest-client@2.3.1: + resolution: {integrity: sha512-k4kX5Up6qA68D0Cby2AK+6+vM5k3qTxe+/3FqhnHRExjY5cfbOnzjQZbP/LXleF8hVoDvDqxlgk9KK83HoBZlQ==} + engines: {node: '>= 16.0.0'} + typescript@5.7.3: resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} engines: {node: '>=14.17'} hasBin: true + underscore@1.13.8: + resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + universal-user-agent@7.0.3: resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + update-browserslist-db@1.3.1: + resolution: {integrity: sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + weapon-regex@1.3.6: + resolution: {integrity: sha512-wsf1m1jmMrso5nhwVFJJHSubEBf3+pereGd7+nBKtYJ18KoB/PWJOHS3WRkwS04VrOU0iJr2bZU+l1QaTJ+9nA==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@2.9.0: resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} @@ -600,6 +1129,10 @@ packages: yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yoctocolors@2.2.0: + resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==} + engines: {node: '>=18'} + zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -611,6 +1144,222 @@ snapshots: '@types/json-schema': 7.0.15 js-yaml: 4.3.1 + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.29.7': + dependencies: + '@babel/types': 7.29.8 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.8 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/traverse': 7.29.8 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-member-expression-to-functions@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.29.7': + dependencies: + '@babel/types': 7.29.8 + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/plugin-proposal-decorators@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-decorators': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-syntax-decorators@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-explicit-resource-management@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.28.5(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@cfworker/json-schema@4.1.1': {} '@graphql-typed-document-node/core@3.2.0(graphql@17.0.2)': @@ -736,6 +1485,25 @@ snapshots: optionalDependencies: '@types/node': 22.19.17 + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jsdevtools/ono@7.1.3': {} '@linear/sdk@89.0.0(graphql@17.0.2)': @@ -876,6 +1644,68 @@ snapshots: '@scalar/openapi-types@0.8.0': {} + '@sec-ant/readable-stream@0.4.1': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@stryker-mutator/api@9.6.1': + dependencies: + mutation-testing-metrics: 3.7.3 + mutation-testing-report-schema: 3.7.3 + tslib: 2.8.1 + typed-inject: 5.0.0 + + '@stryker-mutator/core@9.6.1(@types/node@22.19.17)': + dependencies: + '@inquirer/prompts': 8.3.0(@types/node@22.19.17) + '@stryker-mutator/api': 9.6.1 + '@stryker-mutator/instrumenter': 9.6.1 + '@stryker-mutator/util': 9.6.1 + ajv: 8.18.0 + chalk: 5.6.2 + commander: 14.0.3 + diff-match-patch: 1.0.5 + emoji-regex: 10.6.0 + execa: 9.6.1 + json-rpc-2.0: 1.7.1 + lodash.groupby: 4.6.0 + minimatch: 10.2.6 + mutation-server-protocol: 0.4.1 + mutation-testing-elements: 3.7.3 + mutation-testing-metrics: 3.7.3 + mutation-testing-report-schema: 3.7.3 + npm-run-path: 6.0.0 + progress: 2.0.3 + rxjs: 7.8.2 + semver: 7.8.5 + source-map: 0.7.6 + tree-kill: 1.2.2 + tslib: 2.8.1 + typed-inject: 5.0.0 + typed-rest-client: 2.3.1 + transitivePeerDependencies: + - '@types/node' + - supports-color + + '@stryker-mutator/instrumenter@9.6.1': + dependencies: + '@babel/core': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/parser': 7.29.8 + '@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-explicit-resource-management': 7.29.7(@babel/core@7.29.7) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.7) + '@stryker-mutator/api': 9.6.1 + '@stryker-mutator/util': 9.6.1 + angular-html-parser: 10.4.0 + semver: 7.7.4 + tslib: 2.8.1 + weapon-regex: 1.3.6 + transitivePeerDependencies: + - supports-color + + '@stryker-mutator/util@9.6.1': {} + '@toon-format/toon@2.3.0': {} '@types/bun@1.3.13': @@ -899,6 +1729,13 @@ snapshots: '@types/node': 22.19.17 optional: true + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.5 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 @@ -906,30 +1743,114 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + angular-html-parser@10.4.0: {} + argparse@2.0.1: {} + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.11.13: {} + before-after-hook@4.0.0: {} + brace-expansion@5.0.9: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.28.8: + dependencies: + baseline-browser-mapping: 2.11.13 + caniuse-lite: 1.0.30001809 + electron-to-chromium: 1.5.403 + node-releases: 2.0.53 + update-browserslist-db: 1.3.1(browserslist@4.28.8) + buffer-crc32@0.2.13: {} bun-types@1.3.13: dependencies: '@types/node': 22.19.17 + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + caniuse-lite@1.0.30001809: {} + + chalk@5.6.2: {} + chardet@2.2.0: {} cli-width@4.1.0: {} + commander@14.0.3: {} + content-type@2.0.0: {} + convert-source-map@2.0.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + debug@4.4.3: dependencies: ms: 2.1.3 + des.js@1.1.0: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + diff-match-patch@1.0.5: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.403: {} + + emoji-regex@10.6.0: {} + end-of-stream@1.4.5: dependencies: once: 1.4.0 + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + escalade@3.2.0: {} + + execa@9.6.1: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.3.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.2.0 + extract-zip@2.0.1: dependencies: debug: 4.4.3 @@ -940,6 +1861,10 @@ snapshots: transitivePeerDependencies: - supports-color + fast-check@4.9.0: + dependencies: + pure-rand: 8.4.2 + fast-deep-equal@3.1.3: {} fast-string-truncated-width@3.0.3: {} @@ -964,12 +1889,53 @@ snapshots: fflate@0.8.2: {} + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + get-stream@5.2.0: dependencies: pump: 3.0.4 + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + + gopd@1.2.0: {} + graphql@17.0.2: {} + has-symbols@1.1.0: {} + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + human-signals@8.0.1: {} + iconv-lite@0.7.3: dependencies: safer-buffer: 2.1.2 @@ -984,16 +1950,34 @@ snapshots: yaml: 2.9.0 zod: 4.4.3 + inherits@2.0.4: {} + is-extglob@2.1.1: {} is-glob@4.0.3: dependencies: is-extglob: 2.1.1 + is-plain-obj@4.1.0: {} + + is-stream@4.0.1: {} + + is-unicode-supported@2.1.0: {} + + isexe@2.0.0: {} + + js-md4@0.3.2: {} + + js-tokens@4.0.0: {} + js-yaml@4.3.1: dependencies: argparse: 2.0.1 + jsesc@3.1.0: {} + + json-rpc-2.0@1.7.1: {} + json-schema-to-typescript@15.0.4: dependencies: '@apidevtools/json-schema-ref-parser': 11.9.3 @@ -1010,43 +1994,148 @@ snapshots: json-with-bigint@3.5.10: {} + json5@2.2.3: {} + + lodash.groupby@4.6.0: {} + lodash@4.18.1: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + math-intrinsics@1.1.0: {} + + minimalistic-assert@1.0.1: {} + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.9 + minimist@1.2.8: {} ms@2.1.3: {} + mutation-server-protocol@0.4.1: + dependencies: + zod: 4.4.3 + + mutation-testing-elements@3.7.3: {} + + mutation-testing-metrics@3.7.3: + dependencies: + mutation-testing-report-schema: 3.7.3 + + mutation-testing-report-schema@3.7.3: {} + mute-stream@3.0.0: {} + node-releases@2.0.53: {} + + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + + object-inspect@1.13.4: {} + once@1.4.0: dependencies: wrappy: 1.0.2 papaparse@5.5.3: {} + parse-ms@4.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + pdfjs-dist@6.2.108: optionalDependencies: '@napi-rs/canvas': 1.0.3 pend@1.2.0: {} + picocolors@1.1.1: {} + picomatch@4.0.5: {} prettier@3.2.5: {} + pretty-ms@9.3.0: + dependencies: + parse-ms: 4.0.0 + + progress@2.0.3: {} + pump@3.0.4: dependencies: end-of-stream: 1.4.5 once: 1.4.0 + pure-rand@8.4.2: {} + + qs@6.15.1: + dependencies: + side-channel: 1.1.1 + require-from-string@2.0.2: {} + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + safer-buffer@2.1.2: {} + semver@6.3.1: {} + + semver@7.7.4: {} + + semver@7.8.5: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + signal-exit@4.1.0: {} smol-toml@1.6.1: {} + source-map@0.7.6: {} + + strip-final-newline@4.0.0: {} + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.5) @@ -1054,14 +2143,48 @@ snapshots: tokenx@1.3.0: {} + tree-kill@1.2.2: {} + + tslib@2.8.1: {} + + tunnel@0.0.6: {} + + typed-inject@5.0.0: {} + + typed-rest-client@2.3.1: + dependencies: + des.js: 1.1.0 + js-md4: 0.3.2 + qs: 6.15.1 + tunnel: 0.0.6 + underscore: 1.13.8 + typescript@5.7.3: {} + underscore@1.13.8: {} + undici-types@6.21.0: {} + unicorn-magic@0.3.0: {} + universal-user-agent@7.0.3: {} + update-browserslist-db@1.3.1(browserslist@4.28.8): + dependencies: + browserslist: 4.28.8 + escalade: 3.2.0 + picocolors: 1.1.1 + + weapon-regex@1.3.6: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + wrappy@1.0.2: {} + yallist@3.1.1: {} + yaml@2.9.0: {} yauzl@2.10.0: @@ -1069,4 +2192,6 @@ snapshots: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 + yoctocolors@2.2.0: {} + zod@4.4.3: {} diff --git a/sdk/typescript/scripts/check-package.mjs b/sdk/typescript/scripts/check-package.mjs index 8abdb37b5..72f87261f 100644 --- a/sdk/typescript/scripts/check-package.mjs +++ b/sdk/typescript/scripts/check-package.mjs @@ -168,6 +168,7 @@ const distFiles = new Set( "config", "contract", "cost", + "cost-model", "errors", "index", "knowledge-base", diff --git a/sdk/typescript/src/cost-model.ts b/sdk/typescript/src/cost-model.ts new file mode 100644 index 000000000..eb36e4055 --- /dev/null +++ b/sdk/typescript/src/cost-model.ts @@ -0,0 +1,122 @@ +export interface ScanCost { + model: string; + inputTokens: number; + cachedInputTokens: number; + cacheWriteInputTokens: number; + outputTokens: number; + estimatedUsd: number; +} + +type ModelPricing = readonly [ + input: number, + cachedInput: number, + cacheWriteInput: number, + output: number, +]; + +export interface ScanTokenUsage { + input_tokens: number; + cached_input_tokens: number; + cache_write_input_tokens: number; + output_tokens: number; + reasoning_output_tokens: number; + total_tokens: number; +} + +const MODEL_PRICING_NANODOLLARS: Readonly> = { + "gpt-5.6": [5_000, 500, 6_250, 30_000], + "gpt-5.6-sol": [5_000, 500, 6_250, 30_000], + "gpt-5.6-terra": [2_000, 200, 2_500, 12_000], + "gpt-5.6-luna": [200, 20, 250, 1_200], +}; + +export function tokenUsage(value: unknown): ScanTokenUsage | null { + if (!isRecord(value)) return null; + const input = value["input_tokens"]; + const cached = value["cached_input_tokens"] ?? 0; + const canonicalCacheWrite = value["cache_write_input_tokens"]; + const legacyCacheWrite = value["cache_write_tokens"]; + const cacheWrite = + canonicalCacheWrite === 0 && + isTokenCount(input) && + isTokenCount(cached) && + isTokenCount(legacyCacheWrite) && + legacyCacheWrite > 0 && + cached + legacyCacheWrite <= input + ? legacyCacheWrite + : canonicalCacheWrite ?? legacyCacheWrite ?? 0; + const output = value["output_tokens"]; + const reasoning = value["reasoning_output_tokens"] ?? 0; + if ( + !isTokenCount(input) || + !isTokenCount(cached) || + !isTokenCount(cacheWrite) || + !isTokenCount(output) || + !isTokenCount(reasoning) || + cached + cacheWrite > input || + reasoning > output + ) { + return null; + } + return { + input_tokens: input, + cached_input_tokens: cached, + cache_write_input_tokens: cacheWrite, + output_tokens: output, + reasoning_output_tokens: reasoning, + total_tokens: input + output, + }; +} + +export function estimateScanCost( + model: string | undefined, + usage: unknown, +): ScanCost | null { + if (model === undefined) return null; + const pricingModel = model.startsWith("openai.") + ? model.slice("openai.".length) + : model; + const pricing = MODEL_PRICING_NANODOLLARS[pricingModel]; + const normalized = tokenUsage(usage); + if (pricing === undefined || normalized === null) return null; + const [inputRate, cachedInputRate, cacheWriteInputRate, outputRate] = pricing; + const { + input_tokens: inputTokens, + cached_input_tokens: cachedInputTokens, + cache_write_input_tokens: cacheWriteInputTokens, + output_tokens: outputTokens, + } = normalized; + + const nanodollars = + (inputTokens - cachedInputTokens - cacheWriteInputTokens) * inputRate + + cachedInputTokens * cachedInputRate + + cacheWriteInputTokens * cacheWriteInputRate + + outputTokens * outputRate; + if (!Number.isSafeInteger(nanodollars)) return null; + + return { + model, + inputTokens, + cachedInputTokens, + cacheWriteInputTokens, + outputTokens, + estimatedUsd: nanodollars / 1_000_000_000, + }; +} + +export function formatUsd(value: number): string { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + minimumFractionDigits: 2, + maximumFractionDigits: 9, + }).format(value); +} + +function isTokenCount(value: unknown): value is number { + return typeof value === "number" && Number.isSafeInteger(value) && value >= 0; +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} diff --git a/sdk/typescript/src/cost.ts b/sdk/typescript/src/cost.ts index 764a1e759..fd5778162 100644 --- a/sdk/typescript/src/cost.ts +++ b/sdk/typescript/src/cost.ts @@ -1,3 +1,10 @@ +import { + estimateScanCost, + tokenUsage, + type ScanCost, + type ScanTokenUsage, +} from "./cost-model.js"; +export { estimateScanCost, formatUsd, type ScanCost } from "./cost-model.js"; import { open, readdir } from "node:fs/promises"; import { join, relative, sep } from "node:path"; import { @@ -9,15 +16,6 @@ import { type ScanProgress, } from "./worker-progress.js"; -export interface ScanCost { - model: string; - inputTokens: number; - cachedInputTokens: number; - cacheWriteInputTokens: number; - outputTokens: number; - estimatedUsd: number; -} - export interface ScanSessionEvent { threadId: string; parentThreadId: string | null; @@ -25,22 +23,6 @@ export interface ScanSessionEvent { event: Record; } -type ModelPricing = readonly [ - input: number, - cachedInput: number, - cacheWriteInput: number, - output: number, -]; - -interface ScanTokenUsage { - input_tokens: number; - cached_input_tokens: number; - cache_write_input_tokens: number; - output_tokens: number; - reasoning_output_tokens: number; - total_tokens: number; -} - interface SessionReasoning { id: string; text: string; @@ -90,13 +72,6 @@ interface ScanCostSnapshot { cost: ScanCost | null; } -const MODEL_PRICING_NANODOLLARS: Readonly> = { - "gpt-5.6": [5_000, 500, 6_250, 30_000], - "gpt-5.6-sol": [5_000, 500, 6_250, 30_000], - "gpt-5.6-terra": [2_000, 200, 2_500, 12_000], - "gpt-5.6-luna": [200, 20, 250, 1_200], -}; - const COST_POLL_INTERVAL_MS = 100; const SESSION_READ_SIZE = 64 * 1_024; @@ -781,44 +756,6 @@ function sessionContentText( .join("\n"); } -function tokenUsage(value: unknown): ScanTokenUsage | null { - if (!isRecord(value)) return null; - const input = value["input_tokens"]; - const cached = value["cached_input_tokens"] ?? 0; - const canonicalCacheWrite = value["cache_write_input_tokens"]; - const legacyCacheWrite = value["cache_write_tokens"]; - const cacheWrite = - canonicalCacheWrite === 0 && - isTokenCount(input) && - isTokenCount(cached) && - isTokenCount(legacyCacheWrite) && - legacyCacheWrite > 0 && - cached + legacyCacheWrite <= input - ? legacyCacheWrite - : canonicalCacheWrite ?? legacyCacheWrite ?? 0; - const output = value["output_tokens"]; - const reasoning = value["reasoning_output_tokens"] ?? 0; - if ( - !isTokenCount(input) || - !isTokenCount(cached) || - !isTokenCount(cacheWrite) || - !isTokenCount(output) || - !isTokenCount(reasoning) || - cached + cacheWrite > input || - reasoning > output - ) { - return null; - } - return { - input_tokens: input, - cached_input_tokens: cached, - cache_write_input_tokens: cacheWrite, - output_tokens: output, - reasoning_output_tokens: reasoning, - total_tokens: input + output, - }; -} - function addTokenUsage( previous: ScanTokenUsage | null, next: ScanTokenUsage, @@ -860,52 +797,3 @@ function isRecord(value: unknown): value is Record { function isMissingFile(error: unknown): boolean { return isRecord(error) && error["code"] === "ENOENT"; } - -export function estimateScanCost( - model: string | undefined, - usage: unknown, -): ScanCost | null { - if (model === undefined) return null; - const pricingModel = model.startsWith("openai.") - ? model.slice("openai.".length) - : model; - const pricing = MODEL_PRICING_NANODOLLARS[pricingModel]; - const normalized = tokenUsage(usage); - if (pricing === undefined || normalized === null) return null; - const [inputRate, cachedInputRate, cacheWriteInputRate, outputRate] = pricing; - const { - input_tokens: inputTokens, - cached_input_tokens: cachedInputTokens, - cache_write_input_tokens: cacheWriteInputTokens, - output_tokens: outputTokens, - } = normalized; - - const nanodollars = - (inputTokens - cachedInputTokens - cacheWriteInputTokens) * inputRate + - cachedInputTokens * cachedInputRate + - cacheWriteInputTokens * cacheWriteInputRate + - outputTokens * outputRate; - if (!Number.isSafeInteger(nanodollars)) return null; - - return { - model, - inputTokens, - cachedInputTokens, - cacheWriteInputTokens, - outputTokens, - estimatedUsd: nanodollars / 1_000_000_000, - }; -} - -export function formatUsd(value: number): string { - return new Intl.NumberFormat("en-US", { - style: "currency", - currency: "USD", - minimumFractionDigits: 2, - maximumFractionDigits: 9, - }).format(value); -} - -function isTokenCount(value: unknown): value is number { - return typeof value === "number" && Number.isSafeInteger(value) && value >= 0; -} diff --git a/sdk/typescript/src/errors.ts b/sdk/typescript/src/errors.ts index 7a1988166..01a3ce941 100644 --- a/sdk/typescript/src/errors.ts +++ b/sdk/typescript/src/errors.ts @@ -1,4 +1,4 @@ -import { formatUsd, type ScanCost } from "./cost.js"; +import { formatUsd, type ScanCost } from "./cost-model.js"; /** Returns the original error message without altering its contents. */ export function errorMessage(error: unknown): string { diff --git a/sdk/typescript/stryker.config.json b/sdk/typescript/stryker.config.json new file mode 100644 index 000000000..452fbc763 --- /dev/null +++ b/sdk/typescript/stryker.config.json @@ -0,0 +1,12 @@ +{ + "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json", + "testRunner": "command", + "commandRunner": { + "command": "bun test --timeout 30000 ./tests-ts/cost-model.property.test.ts ./tests-ts/errors.test.ts ./tests-ts/errors.property.test.ts ./tests-ts/worker-progress.test.ts ./tests-ts/worker-progress.property.test.ts" + }, + "mutate": ["src/cost-model.ts", "src/errors.ts", "src/worker-progress.ts"], + "coverageAnalysis": "off", + "reporters": ["clear-text", "progress", "json", "html"], + "concurrency": 2, + "thresholds": { "break": 0 } +} diff --git a/sdk/typescript/tests-ts/contract.test.ts b/sdk/typescript/tests-ts/contract.test.ts index 4fdfcd05f..17f9b7ddf 100644 --- a/sdk/typescript/tests-ts/contract.test.ts +++ b/sdk/typescript/tests-ts/contract.test.ts @@ -15,10 +15,12 @@ import { import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { afterEach, describe, expect, test } from "bun:test"; +import fc from "fast-check"; import { ContractValidationError, loadContract } from "../src/index.js"; import { sameCheckedFileDevice } from "../src/contract.js"; import type { NormalizedTarget, ScanExpectation } from "../src/index.js"; import { PLUGIN_ROOT } from "./plugin-root.js"; +import { propertyOptions } from "./support/property.js"; const EXAMPLE = join(PLUGIN_ROOT, "examples", "completed-scan"); const temporaryDirectories: string[] = []; @@ -105,6 +107,98 @@ function expectation( } describe("canonical scan contract", () => { + test("rejects byte changes to any sealed binary artifact", async () => { + const scanDir = await copyExample(); + const artifactPath = join(scanDir, "artifacts", "synthetic.bin"); + const manifestPath = join(scanDir, "scan-manifest.json"); + const manifest = await readJson(manifestPath); + await mkdir(dirname(artifactPath), { recursive: true }); + + await fc.assert( + fc.asyncProperty( + fc.uint8Array({ minLength: 1, maxLength: 256 }), + fc.nat(), + fc.integer({ min: 1, max: 255 }), + async (bytes, offset, difference) => { + await writeFile(artifactPath, bytes); + await writeJson(manifestPath, { + ...manifest, + scan: { + ...manifest["scan"], + artifacts: [ + ...manifest["scan"]["artifacts"], + { + path: "artifacts/synthetic.bin", + sha256: createHash("sha256").update(bytes).digest("hex"), + mediaType: "application/octet-stream", + }, + ], + }, + }); + await loadContract(scanDir, { pluginRoot: PLUGIN_ROOT }); + const changed = Uint8Array.from(bytes); + changed[offset % bytes.length]! ^= difference; + await writeFile(artifactPath, changed); + await expect( + loadContract(scanDir, { pluginRoot: PLUGIN_ROOT }), + ).rejects.toBeInstanceOf(ContractValidationError); + }, + ), + { + ...propertyOptions, + numRuns: Number(process.env["CODEX_SECURITY_PROPERTY_RUNS"] ?? "20"), + }, + ); + }); + + test("rejects non-relative artifact paths before accepting a seal", async () => { + const scanDir = await copyExample(); + const manifestPath = join(scanDir, "scan-manifest.json"); + const manifest = await readJson(manifestPath); + const prefixes = ["../", "/", "C:/", "artifacts/../", "artifacts\\"]; + await mkdir(join(scanDir, "artifacts"), { recursive: true }); + await fc.assert( + fc.asyncProperty( + fc.stringMatching(/^[a-z]{1,16}$/u), + fc.constantFrom(...prefixes), + async (name, prefix) => { + const filename = `artifact-${name}`; + const bytes = Buffer.from(name); + const artifact = { + path: `artifacts/${filename}`, + sha256: createHash("sha256").update(bytes).digest("hex"), + mediaType: "application/octet-stream", + }; + const scan = { + ...manifest["scan"], + artifacts: [...manifest["scan"]["artifacts"], artifact], + }; + await writeFile(join(scanDir, "artifacts", filename), bytes); + await writeJson(manifestPath, { ...manifest, scan }); + await loadContract(scanDir, { pluginRoot: PLUGIN_ROOT }); + + artifact.path = `${prefix}${filename}`; + await writeJson(manifestPath, { ...manifest, scan }); + const rejected = loadContract(scanDir, { pluginRoot: PLUGIN_ROOT }); + await expect(rejected).rejects.toBeInstanceOf( + ContractValidationError, + ); + await expect(rejected).rejects.toThrow( + /safe scan-relative POSIX path|schema validation failed \(pattern/u, + ); + }, + ), + { + ...propertyOptions, + numRuns: Number(process.env["CODEX_SECURITY_PROPERTY_RUNS"] ?? "20"), + examples: [ + ...prefixes.map((prefix): [string, string] => ["synthetic", prefix]), + ["con", "C:/"], + ], + }, + ); + }); + test("compares exact Windows volume serials without rounding file identity", async () => { const scanDir = await copyExample(); const path = join(scanDir, "scan-manifest.json"); diff --git a/sdk/typescript/tests-ts/cost-model.property.test.ts b/sdk/typescript/tests-ts/cost-model.property.test.ts new file mode 100644 index 000000000..65703a80e --- /dev/null +++ b/sdk/typescript/tests-ts/cost-model.property.test.ts @@ -0,0 +1,184 @@ +import { describe, expect, test } from "bun:test"; +import fc from "fast-check"; +import { estimateScanCost, formatUsd, tokenUsage } from "../src/cost-model.js"; +import { propertyOptions } from "./support/property.js"; + +const rates = [ + ["gpt-5.6", 5000n, 500n, 6250n, 30000n], + ["gpt-5.6-sol", 5000n, 500n, 6250n, 30000n], + ["gpt-5.6-terra", 2000n, 200n, 2500n, 12000n], + ["gpt-5.6-luna", 200n, 20n, 250n, 1200n], +] as const; +const count = fc.integer({ min: 0, max: 1_000_000_000 }); +const usageParts = fc.record({ + uncached: count, + cached: count, + written: count, + output: count, +}); + +function usage(parts: { + uncached: number; + cached: number; + written: number; + output: number; +}) { + return { + input_tokens: parts.uncached + parts.cached + parts.written, + cached_input_tokens: parts.cached, + cache_write_input_tokens: parts.written, + output_tokens: parts.output, + reasoning_output_tokens: parts.output, + }; +} + +describe("cost-model invariants", () => { + test("rejects non-object usage and formats small costs without rounding them away", () => { + for (const value of [undefined, null, [], "usage", 1, true]) { + expect(tokenUsage(value)).toBeNull(); + expect(estimateScanCost("gpt-5.6-sol", value)).toBeNull(); + } + expect(formatUsd(0)).toBe("$0.00"); + expect(formatUsd(0.000000001)).toBe("$0.000000001"); + expect(formatUsd(1234.5)).toBe("$1,234.50"); + }); + + test("matches an integer nanodollar oracle for every priced model", () => { + fc.assert( + fc.property( + usageParts, + fc.constantFrom(...rates), + fc.boolean(), + ( + parts, + [model, inputRate, cachedRate, writeRate, outputRate], + prefixed, + ) => { + const selected = prefixed ? `openai.${model}` : model; + const tokens = usage(parts); + const nanos = + BigInt(parts.uncached) * inputRate + + BigInt(parts.cached) * cachedRate + + BigInt(parts.written) * writeRate + + BigInt(parts.output) * outputRate; + expect(estimateScanCost(selected, tokens)).toEqual({ + model: selected, + inputTokens: tokens.input_tokens, + cachedInputTokens: parts.cached, + cacheWriteInputTokens: parts.written, + outputTokens: parts.output, + estimatedUsd: Number(nanos) / 1_000_000_000, + }); + expect(tokenUsage(tokens)).toEqual({ + ...tokens, + total_tokens: tokens.input_tokens + tokens.output_tokens, + }); + }, + ), + propertyOptions, + ); + }); + + test("normalizes legacy cache writes without double charging", () => { + fc.assert( + fc.property(usageParts, (parts) => { + const canonical = usage(parts); + const { cache_write_input_tokens: written, ...rest } = canonical; + const expected = estimateScanCost("gpt-5.6-sol", canonical); + expect( + estimateScanCost("gpt-5.6-sol", { + ...rest, + cache_write_tokens: written, + }), + ).toEqual(expected); + expect( + estimateScanCost("gpt-5.6-sol", { + ...canonical, + cache_write_tokens: canonical.input_tokens + 1, + }), + ).toEqual(expected); + if (parts.written > 0) { + expect( + estimateScanCost("gpt-5.6-sol", { + ...canonical, + cache_write_tokens: parts.written - 1, + }), + ).toEqual(expected); + } + expect( + estimateScanCost("gpt-5.6-sol", { + ...rest, + cache_write_input_tokens: 0, + cache_write_tokens: written, + }), + ).toEqual(expected); + }), + propertyOptions, + ); + }); + + test("rejects inconsistent or non-integer token accounting", () => { + fc.assert( + fc.property( + usageParts, + fc.constantFrom( + "input_tokens", + "cached_input_tokens", + "cache_write_input_tokens", + "output_tokens", + "reasoning_output_tokens", + ), + fc.constantFrom( + -1, + 0.5, + Number.MAX_SAFE_INTEGER + 1, + NaN, + Infinity, + "1", + ), + (parts, field, invalid) => { + const valid = usage(parts); + expect( + estimateScanCost("gpt-5.6-sol", { ...valid, [field]: invalid }), + ).toBeNull(); + expect( + estimateScanCost("gpt-5.6-sol", { + ...valid, + cached_input_tokens: valid.input_tokens + 1, + }), + ).toBeNull(); + expect( + estimateScanCost("gpt-5.6-sol", { + ...valid, + reasoning_output_tokens: valid.output_tokens + 1, + }), + ).toBeNull(); + expect( + estimateScanCost("unpriced-synthetic-model", valid), + ).toBeNull(); + expect(estimateScanCost(undefined, valid)).toBeNull(); + }, + ), + propertyOptions, + ); + }); + + test("returns no estimate when nanodollar arithmetic would lose precision", () => { + fc.assert( + fc.property( + fc.integer({ min: 0, max: Number.MAX_SAFE_INTEGER }), + (input) => { + const nanos = BigInt(input) * 5000n; + const result = estimateScanCost("gpt-5.6-sol", { + input_tokens: input, + output_tokens: 0, + }); + if (nanos > BigInt(Number.MAX_SAFE_INTEGER)) + expect(result).toBeNull(); + else expect(result?.estimatedUsd).toBe(Number(nanos) / 1_000_000_000); + }, + ), + propertyOptions, + ); + }); +}); diff --git a/sdk/typescript/tests-ts/errors.property.test.ts b/sdk/typescript/tests-ts/errors.property.test.ts new file mode 100644 index 000000000..ced620dc7 --- /dev/null +++ b/sdk/typescript/tests-ts/errors.property.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, test } from "bun:test"; +import fc from "fast-check"; +import { errorMessage, safeErrorMessage } from "../src/errors.js"; +import { propertyOptions } from "./support/property.js"; + +const word = fc.stringMatching(/^[a-zA-Z0-9]{1,40}$/u); +const credential = word.map((value) => `SYNTHETIC_${value}`); + +describe("error-message invariants", () => { + test("omits recognized credentials in plain, JSON, and URL-encoded errors", () => { + fc.assert( + fc.property( + credential, + fc.constantFrom( + "api_key", + "apikey", + "accesskey", + "access-key", + "privatekey", + "private-key", + "sig", + "access_token", + "clientSecret", + "password", + "authorization", + ), + (secret, field) => { + const json = JSON.stringify({ [field]: secret }); + for (const message of [ + `${field}=${secret}`, + json, + JSON.stringify(json), + encodeURIComponent(json), + `Bearer ${secret}`, + `Basic ${secret}`, + `token%20${secret}`, + `sk-${secret}`, + `sk-proj-${secret}`, + `github_pat_${secret}`, + ...["ghp_", "gho_", "ghu_", "ghs_", "ghr_", "npm_"].map( + (prefix) => `${prefix}${secret}`, + ), + `https://synthetic:${secret}@example.test/`, + ]) { + expect(safeErrorMessage(message)).toBe("[redacted]"); + expect(safeErrorMessage(new Error(message))).toBe("[redacted]"); + expect(errorMessage(new Error(message))).toBe(message); + } + }, + ), + propertyOptions, + ); + }); + + test("preserves ordinary diagnostic text exactly", () => { + fc.assert( + fc.property(fc.nat(), word, (index, detail) => { + const message = `operation failed for item ${index} (${detail})`; + expect(safeErrorMessage(message)).toBe(message); + expect(safeErrorMessage(new Error(message))).toBe(message); + }), + propertyOptions, + ); + }); +}); diff --git a/sdk/typescript/tests-ts/errors.test.ts b/sdk/typescript/tests-ts/errors.test.ts index 35be8c961..c710d5223 100644 --- a/sdk/typescript/tests-ts/errors.test.ts +++ b/sdk/typescript/tests-ts/errors.test.ts @@ -1,7 +1,63 @@ import { describe, expect, test } from "bun:test"; -import { errorMessage, safeErrorMessage } from "../src/errors.js"; +import { + CodexSecurityError, + OutputInsideProtectedRootError, + ScanCostLimitExceededError, + ScanInterruptedError, + errorMessage, + safeErrorMessage, +} from "../src/errors.js"; describe("error messages", () => { + test("preserves public error names, causes, and recovery details", () => { + const cause = new Error("synthetic cause"); + const base = new CodexSecurityError("synthetic failure", { cause }); + expect(base).toMatchObject({ + name: "CodexSecurityError", + message: "synthetic failure", + cause, + }); + const output = new OutputInsideProtectedRootError("/scan", "/repository"); + expect(output).toMatchObject({ + name: "OutputInsideProtectedRootError", + outputDirectory: "/scan", + protectedRoot: "/repository", + pathKind: "output", + }); + expect(output.message).toContain("/scan"); + expect( + new OutputInsideProtectedRootError("/runtime", "/repository", "runtime") + .pathKind, + ).toBe("runtime"); + expect( + new ScanInterruptedError("stopped", "/scan", { cause }), + ).toMatchObject({ + name: "ScanInterruptedError", + message: "stopped", + scanDir: "/scan", + cause, + }); + const cost = { + model: "synthetic", + inputTokens: 1, + cachedInputTokens: 0, + cacheWriteInputTokens: 0, + outputTokens: 1, + estimatedUsd: 2, + }; + const limit = new ScanCostLimitExceededError(1, cost, "/scan"); + expect(limit).toBeInstanceOf(ScanInterruptedError); + expect(limit).toMatchObject({ + name: "ScanCostLimitExceededError", + maxCostUsd: 1, + cost, + scanDir: "/scan", + }); + expect(limit.message).toContain("$2.00"); + expect(limit.message).toContain("$1.00"); + expect(limit.message).toContain("/scan"); + }); + test("preserves error messages exactly", () => { const message = "request failed: token=SYNTHETIC_TOKEN"; expect(errorMessage(new Error(message))).toBe(message); diff --git a/sdk/typescript/tests-ts/publication-store.test.ts b/sdk/typescript/tests-ts/publication-store.test.ts index 6fb9762ca..194230bfb 100644 --- a/sdk/typescript/tests-ts/publication-store.test.ts +++ b/sdk/typescript/tests-ts/publication-store.test.ts @@ -165,6 +165,72 @@ function publishedIssue( } describe("persisted finding publication associations", () => { + test("rolls back failed migrations without losing populated scan history", async () => { + const fixture = await publicationFixture({ count: 1 }); + await recordPublishedIssues( + fixture.publication, + [publishedIssue(fixture.publication, 0)], + fixture.environment, + ); + databaseRows( + fixture, + "INSERT INTO finding_triage (occurrence_id, status, close_reason, note, updated_at) VALUES (?, 'closed', 'false_positive', 'synthetic triage note', '2026-08-01T00:00:00Z')", + [fixture.publication.issues[0]!.occurrenceId], + ); + + const probe = spawnSync( + fixture.python, + [ + "-I", + "-B", + "-c", + `import json, sqlite3, sys +sys.path.insert(0, sys.argv[1]) +import workbench_db as db +import workbench_schema as schema + +connection = sqlite3.connect(sys.argv[2]) +connection.row_factory = sqlite3.Row +connection.execute("PRAGMA foreign_keys = ON") +db.apply_migrations(connection) +before = list(connection.iterdump()) +next_version = max(version for version, _, _ in schema.MIGRATIONS) + 1 +failing = (next_version, "synthetic failing migration", """ +CREATE TABLE synthetic_migration_probe (value TEXT); +INSERT INTO synthetic_migration_probe VALUES ('synthetic'); +DELETE FROM finding_triage; +INSERT INTO synthetic_missing_table VALUES (1); +""") +try: + schema.apply_migrations(connection, (*schema.MIGRATIONS, failing), db.now, db.backfill_security_targets) +except sqlite3.OperationalError as error: + assert "synthetic_missing_table" in str(error), error +else: + raise AssertionError("The injected migration must fail") +assert not connection.in_transaction +assert list(connection.iterdump()) == before, "Failed migration changed committed data or schema" +db.apply_migrations(connection) +db.apply_migrations(connection) +assert list(connection.iterdump()) == before, "Reapplying migrations changed existing history" +assert list(connection.execute("PRAGMA foreign_key_check")) == [] +assert connection.execute("PRAGMA integrity_check").fetchone()[0] == "ok" +print(json.dumps({table: connection.execute("SELECT COUNT(*) FROM " + table).fetchone()[0] for table in ("scans", "finding_occurrences", "finding_triage", "finding_publications")})) +connection.close() +`, + join(PLUGIN_ROOT, "scripts"), + join(fixture.stateDirectory, "workbench.sqlite3"), + ], + { encoding: "utf8", env: fixture.environment }, + ); + expect(probe.status, probe.stderr).toBe(0); + expect(JSON.parse(probe.stdout)).toEqual({ + scans: 1, + finding_occurrences: 1, + finding_triage: 1, + finding_publications: 1, + }); + }); + test("upgrades existing scan history and verifies every completed finding before publication", async () => { const fixture = await publicationFixture(); databaseRows(fixture, "DROP TABLE finding_publications"); diff --git a/sdk/typescript/tests-ts/scan-comparison.property.test.ts b/sdk/typescript/tests-ts/scan-comparison.property.test.ts new file mode 100644 index 000000000..d6b27bd90 --- /dev/null +++ b/sdk/typescript/tests-ts/scan-comparison.property.test.ts @@ -0,0 +1,142 @@ +import { describe, expect, test } from "bun:test"; +import fc from "fast-check"; +import { + matchScanFindings, + type ScanComparisonInput, + type ScanComparisonOptions, + type ScanComparisonResult, +} from "../src/scan-comparison.js"; +import { propertyOptions } from "./support/property.js"; + +const identities = fc.uniqueArray(fc.uuid(), { minLength: 2, maxLength: 8 }); + +function fixture(ids: string[]) { + const input: ScanComparisonInput = { + before: ids.map((id) => ({ occurrenceId: `before-${id}` })), + after: ids.map((id) => ({ occurrenceId: `after-${id}` })), + }; + const result: ScanComparisonResult = { + matches: ids.slice(1).map((id) => ({ + beforeOccurrenceIds: [`before-${id}`], + afterOccurrenceIds: [`after-${id}`], + confidence: "high", + reason: "same synthetic control", + })), + uncertain: [ + { + beforeOccurrenceId: `before-${ids[0]}`, + afterOccurrenceId: `after-${ids[0]}`, + reason: "needs more evidence", + }, + ], + }; + return { input, result }; +} + +function compare( + input: ScanComparisonInput, + response: unknown, + options: Pick = {}, +) { + return matchScanFindings(input, { + ...options, + codex: { + startThread: () => ({ + run: async () => ({ finalResponse: JSON.stringify(response) }), + }), + }, + }); +} + +describe("finding-comparison invariants", () => { + test("preserves valid identities regardless of finding order", async () => { + await fc.assert( + fc.asyncProperty(identities, async (ids) => { + const { input, result } = fixture(ids); + await expect(compare(input, result)).resolves.toEqual(result); + await expect( + compare( + { + before: [...input.before].reverse(), + after: [...input.after].reverse(), + }, + result, + ), + ).resolves.toEqual(result); + }), + propertyOptions, + ); + }); + + test("rejects invented identities and duplicate confirmed assignments", async () => { + await fc.assert( + fc.asyncProperty(identities, async (ids) => { + const { input, result } = fixture(ids); + const first = result.matches[0]!; + for (const side of [ + "beforeOccurrenceIds", + "afterOccurrenceIds", + ] as const) { + await expect( + compare(input, { + ...result, + matches: [{ ...first, [side]: ["unknown-synthetic-occurrence"] }], + }), + ).rejects.toThrow(/unknown .* occurrence/u); + await expect( + compare(input, { + ...result, + matches: [ + { ...first, [side]: [...first[side], first[side][0]!] }, + ], + }), + ).rejects.toThrow(/more than once/u); + } + await expect( + compare(input, { ...result, matches: [...result.matches, first] }), + ).rejects.toThrow(/more than once/u); + }), + propertyOptions, + ); + }); + + test("keeps uncertain pairs unique and separate from confirmed matches", async () => { + await fc.assert( + fc.asyncProperty(identities, async (ids) => { + const { input, result } = fixture(ids); + const uncertain = result.uncertain[0]!; + await expect( + compare(input, { ...result, uncertain: [uncertain, uncertain] }), + ).rejects.toThrow(/duplicate uncertain pair/u); + const overlapsAfter = { + ...uncertain, + afterOccurrenceId: result.matches[0]!.afterOccurrenceIds[0]!, + }; + const historical = { ...result, uncertain: [overlapsAfter] }; + await expect(compare(input, historical)).rejects.toThrow( + /invalid uncertain pair/u, + ); + await expect( + compare(input, historical, { allowHistoricalUncertainty: true }), + ).resolves.toEqual(historical); + await expect( + compare( + input, + { + ...result, + uncertain: [ + { + ...uncertain, + beforeOccurrenceId: + result.matches[0]!.beforeOccurrenceIds[0]!, + }, + ], + }, + { allowHistoricalUncertainty: true }, + ), + ).rejects.toThrow(/invalid uncertain pair/u); + }), + propertyOptions, + ); + }); +}); diff --git a/sdk/typescript/tests-ts/support/property.ts b/sdk/typescript/tests-ts/support/property.ts new file mode 100644 index 000000000..cc5abbd9f --- /dev/null +++ b/sdk/typescript/tests-ts/support/property.ts @@ -0,0 +1,7 @@ +export const propertyOptions = { + seed: Number(process.env["CODEX_SECURITY_PROPERTY_SEED"] ?? "20260817"), + numRuns: Number(process.env["CODEX_SECURITY_PROPERTY_RUNS"] ?? "100"), + ...(process.env["CODEX_SECURITY_PROPERTY_PATH"] === undefined + ? {} + : { path: process.env["CODEX_SECURITY_PROPERTY_PATH"] }), +}; diff --git a/sdk/typescript/tests-ts/worker-progress.property.test.ts b/sdk/typescript/tests-ts/worker-progress.property.test.ts new file mode 100644 index 000000000..f36f13a2c --- /dev/null +++ b/sdk/typescript/tests-ts/worker-progress.property.test.ts @@ -0,0 +1,147 @@ +import { describe, expect, test } from "bun:test"; +import fc from "fast-check"; +import { + scanProgressUpdatesFromEvent, + workerStatusFromEvent, +} from "../src/worker-progress.js"; +import { propertyOptions } from "./support/property.js"; + +const count = fc.integer({ min: 0, max: Number.MAX_SAFE_INTEGER }); +const countInput = fc.oneof( + count, + fc.constantFrom(-1, 0.5, Number.MAX_SAFE_INTEGER + 1, null, "1"), +); +const phases = [ + "preflight", + "threat_model", + "discovery", + "validation", + "attack_path", + "reporting", +] as const; +const phase = fc.constantFrom(...phases); + +function message(text: string) { + return { + type: "item.completed", + item: { type: "agent_message", text }, + }; +} + +function validCounts(completed: unknown, total: unknown): boolean { + return ( + typeof completed === "number" && + typeof total === "number" && + Number.isSafeInteger(completed) && + Number.isSafeInteger(total) && + completed >= 0 && + completed <= total + ); +} + +describe("progress invariants", () => { + test("accepts exactly the valid progress count pairs", () => { + fc.assert( + fc.property( + phase, + countInput, + countInput, + (phase, filesCompleted, filesTotal) => { + const progress = { phase, filesCompleted, filesTotal }; + const marker = `CODEX_SECURITY_SCAN_PROGRESS ${JSON.stringify(progress)}`; + const expected = validCounts(filesCompleted, filesTotal) + ? [ + { + phase, + filesCompleted: Number(filesCompleted), + filesTotal: Number(filesTotal), + }, + ] + : []; + expect(scanProgressUpdatesFromEvent(message(marker))).toEqual( + expected, + ); + expect( + scanProgressUpdatesFromEvent({ + type: "item.completed", + item: { type: "command_execution", aggregated_output: marker }, + }), + ).toEqual(expected); + expect( + scanProgressUpdatesFromEvent( + message(`\`\`\`json\n${marker}\n\`\`\``), + ), + ).toEqual([]); + }, + ), + propertyOptions, + ); + }); + + test("never reports more started workers than were planned", () => { + fc.assert( + fc.property( + fc.constantFrom("ranking", "file_review", "validation", "attack_path"), + countInput, + countInput, + (phase, started, planned) => { + const payload = { phase, planned, started }; + const marker = `CODEX_SECURITY_WORKER_STATUS ${JSON.stringify(payload)}`; + expect(workerStatusFromEvent(message(marker))).toEqual( + validCounts(started, planned) + ? { + kind: "dispatch", + phase, + started: Number(started), + planned: Number(planned), + } + : null, + ); + expect( + workerStatusFromEvent(message(`${marker}\n${marker}`)), + ).toBeNull(); + }, + ), + propertyOptions, + ); + }); + + test("preserves zero, complete, and maximum-safe counts", () => { + fc.assert( + fc.property(count, (total) => { + for (const completed of [0, total]) { + for (const phase of phases) { + const progress = { + phase, + filesCompleted: completed, + filesTotal: total, + }; + expect( + scanProgressUpdatesFromEvent( + message( + `CODEX_SECURITY_SCAN_PROGRESS ${JSON.stringify(progress)}`, + ), + ), + ).toEqual([progress]); + } + const dispatch = { + phase: "ranking" as const, + planned: total, + started: completed, + }; + expect( + workerStatusFromEvent( + message( + `CODEX_SECURITY_WORKER_STATUS ${JSON.stringify(dispatch)}`, + ), + ), + ).toEqual({ kind: "dispatch", ...dispatch }); + } + }), + { + ...propertyOptions, + examples: [[0], [Number.MAX_SAFE_INTEGER]], + }, + ); + }); +}); diff --git a/sdk/typescript/tests-ts/worker-progress.test.ts b/sdk/typescript/tests-ts/worker-progress.test.ts index 36b95b86b..557559fa0 100644 --- a/sdk/typescript/tests-ts/worker-progress.test.ts +++ b/sdk/typescript/tests-ts/worker-progress.test.ts @@ -29,6 +29,85 @@ function messageEvent(text: string): Record { } describe("worker progress events", () => { + test("ignores incomplete and malformed event envelopes", () => { + const progress = + 'CODEX_SECURITY_SCAN_PROGRESS {"phase":"reporting","filesCompleted":1,"filesTotal":1}'; + const dispatch = + 'CODEX_SECURITY_WORKER_STATUS {"phase":"ranking","planned":1,"started":1}'; + const preflight = JSON.stringify({ + profile: "security_scan", + results: [{ capability: "delegated_workers", status: "pass" }], + }); + for (const event of [ + {}, + { type: "item.completed", item: null }, + { type: "item.completed", item: [] }, + { ...messageEvent(progress), type: "item.started" }, + { ...messageEvent(dispatch), type: "item.updated" }, + { + type: "item.completed", + item: { type: "reasoning", text: `${progress}\n${dispatch}` }, + }, + { + type: "item.completed", + item: { type: "reasoning", aggregated_output: progress }, + }, + { type: "item.completed", item: { type: "agent_message", text: null } }, + { + type: "item.completed", + item: { + type: "command_execution", + command: ["config_preflight.py"], + aggregated_output: preflight, + }, + }, + { + type: "item.completed", + item: { + type: "command_execution", + command: "config_preflight.py", + aggregated_output: [preflight], + }, + }, + { + type: "item.completed", + item: { + type: "command_execution", + command: null, + aggregated_output: 1, + }, + }, + messageEvent( + "CODEX_SECURITY_SCAN_PROGRESS not-json\nCODEX_SECURITY_WORKER_STATUS null", + ), + ]) { + expect(workerStatusFromEvent(event)).toBeNull(); + expect(scanProgressUpdatesFromEvent(event)).toEqual([]); + } + }); + + test("rejects conflicting preflight capacity and ignores unrelated results", () => { + const delegated = { capability: "delegated_workers", status: "pass" }; + const capacity = { capability: "usable_worker_slots_2", actual: 2 }; + const preflight = (results: unknown[]) => + workerStatusFromEvent( + commandEvent( + "config_preflight.py", + JSON.stringify({ profile: "security_scan", results }), + ), + ); + expect( + preflight([null, {}, { capability: 42 }, delegated, capacity]), + ).toEqual({ + kind: "preflight", + delegation: "available", + configuredSlots: 2, + }); + expect(preflight([delegated, capacity, capacity])).toBeNull(); + expect(preflight([{ ...delegated, status: "invalid" }])).toBeNull(); + expect(preflight([])).toBeNull(); + }); + test("reads configured worker capacity from a completed preflight", () => { const output = JSON.stringify({ profile: "security_scan", @@ -233,19 +312,33 @@ describe("worker progress events", () => { }); test("does not mistake documented examples for real scan progress", () => { + const progress = { + phase: "discovery" as const, + filesCompleted: 3, + filesTotal: 8, + }; + const marker = `CODEX_SECURITY_SCAN_PROGRESS ${JSON.stringify(progress)}`; + for (const indent of ["", " ", " ", " "]) { + expect( + scanProgressUpdatesFromEvent( + commandEvent( + "read the scan workflow", + [ + "Example progress:", + `${indent}\`\`\`text`, + marker, + `${indent}\`\`\``, + marker, + ].join("\n"), + ), + ), + ).toEqual([progress]); + } expect( scanProgressUpdatesFromEvent( - commandEvent( - "read the scan workflow", - [ - "Example progress:", - "```text", - 'CODEX_SECURITY_SCAN_PROGRESS {"phase":"discovery","filesCompleted":3,"filesTotal":8}', - "```", - ].join("\n"), - ), + messageEvent(`Inline \`\`\` is not a fence.\n${marker}`), ), - ).toEqual([]); + ).toEqual([progress]); }); test("rejects malformed or overstated file progress", () => {