From 21fc5fac71cd342d29be7dd29bd51aefc7f479e3 Mon Sep 17 00:00:00 2001 From: karan acharya Date: Mon, 21 Sep 2026 16:54:56 -0400 Subject: [PATCH 1/4] fix(sdk-typescript): send a literal HTTP body on authenticated requests (PREDICT-9072) HttpTransport signed every authenticated request's payload into the X-GEMINI-PAYLOAD header but never attached it as an actual HTTP request body - fetchImpl was always called with no body field. Endpoints whose server handler reads the real HTTP body directly (confirmed: createCombo) rejected the empty body with 400 InvalidInput, while endpoints that read only from the header worked fine. This affects every authenticated + requestBody:true operation across every domain (52 total), not just combos. Fix: requestWithResponse now also serializes the operation's own fields (never the signed envelope's request/nonce) as a literal body whenever the operation has one, with Content-Type/Content-Length adjusted accordingly. Query-only authenticated calls and all public calls are unaffected - body stays undefined exactly as before. Updated the one existing test that asserted the bug as spec, relaxed a shared test helper that hardcoded the no-body header shape across five other domain test suites, and added new coverage: a no-body regression guard, a bigint-in-body precision test, and a dedicated createCombo integration test. Verified live against production: with this fix packed into a local tarball and installed into mcp-server, the SDK's createCombo call that previously failed with 400 InvalidInput now succeeds identically to the legacy client, with the existing mcp-server test suite unaffected. Version bumped 0.1.0 -> 0.1.1 (backward-compatible bugfix). --- packages/sdk-typescript/package-lock.json | 4 +- packages/sdk-typescript/package.json | 2 +- .../generated/rest-surfaces.test.ts | 14 +++++-- .../rest/prediction-markets.test.ts | 33 +++++++++++++++ .../sdk-typescript/src/transport/http.test.ts | 41 +++++++++++++++++-- packages/sdk-typescript/src/transport/http.ts | 22 ++++++---- 6 files changed, 99 insertions(+), 17 deletions(-) diff --git a/packages/sdk-typescript/package-lock.json b/packages/sdk-typescript/package-lock.json index 4ccefb7a..5f398d53 100644 --- a/packages/sdk-typescript/package-lock.json +++ b/packages/sdk-typescript/package-lock.json @@ -1,12 +1,12 @@ { "name": "@gemini-markets/sdk", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@gemini-markets/sdk", - "version": "0.1.0", + "version": "0.1.1", "license": "Apache-2.0", "devDependencies": { "@asyncapi/modelina": "5.10.1", diff --git a/packages/sdk-typescript/package.json b/packages/sdk-typescript/package.json index 4baf6469..be275df1 100644 --- a/packages/sdk-typescript/package.json +++ b/packages/sdk-typescript/package.json @@ -1,6 +1,6 @@ { "name": "@gemini-markets/sdk", - "version": "0.1.0", + "version": "0.1.1", "description": "Gemini exchange TypeScript SDK — browser and server entry points with HMAC, OAuth (PKCE), REST, and WebSocket support.", "type": "module", "license": "Apache-2.0", diff --git a/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts b/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts index 27bb7be7..dcc42d80 100644 --- a/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts +++ b/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts @@ -248,9 +248,17 @@ async function assertSigned(request: Request): Promise { request.init.headers["X-GEMINI-SIGNATURE"], await hmacSha384Hex("secret", encoded), ); - assert.equal(request.init.headers["Content-Length"], "0"); - assert.equal(request.init.headers["Content-Type"], "text/plain"); - assert.equal(request.init.body, undefined); + // Operations with a real request body (requestBody: true) now send it as a literal + // HTTP body too, not just signed into X-GEMINI-PAYLOAD (PREDICT-9072) — assert + // internal consistency between the content headers and body presence, since this + // generic helper covers both body-bearing and bodyless operations across every domain. + if (request.init.body !== undefined) { + assert.equal(request.init.headers["Content-Type"], "application/json"); + assert.equal(request.init.headers["Content-Length"], undefined); + } else { + assert.equal(request.init.headers["Content-Length"], "0"); + assert.equal(request.init.headers["Content-Type"], "text/plain"); + } } test("generated REST operation metadata covers the new module surfaces", () => { diff --git a/packages/sdk-typescript/src/tests/integration/rest/prediction-markets.test.ts b/packages/sdk-typescript/src/tests/integration/rest/prediction-markets.test.ts index a5d0ef42..676058f8 100644 --- a/packages/sdk-typescript/src/tests/integration/rest/prediction-markets.test.ts +++ b/packages/sdk-typescript/src/tests/integration/rest/prediction-markets.test.ts @@ -431,6 +431,39 @@ void test("generated body fields cannot replace authentication headers", async ( ); }); +void test("createCombo sends legs as a literal HTTP body, not just signed into the payload (PREDICT-9072)", async () => { + const requests: Request[] = []; + const auth = new HmacAuth({ apiKey: "key", apiSecret: "secret", now: () => 1000 }); + const rest = new PredictionMarketsRest(new HttpTransport({ + env: "sandbox", + auth, + fetchImpl: async (url, init) => { + requests.push({ url, init }); + return jsonResponse( + 200, + '{"alreadyExisted":false,"combo":{"id":1,"canonicalLegKey":"k","instrumentRegistered":false,"legCount":2,"legs":[]}}', + ); + }, + })); + + const legs = [ + { contractId: "111", requiredOutcome: "Yes" as const }, + { contractId: "222", requiredOutcome: "No" as const }, + ]; + await rest.createCombo({ legs }); + + const { init } = requests[0]!; + assert.equal(init.headers["Content-Type"], "application/json"); + assert.equal(init.headers["Content-Length"], undefined); + assert.deepEqual(init.body ? JSON.parse(init.body) : undefined, { legs }); + + // The signed payload still carries request/nonce alongside legs — the literal + // body above is a narrower, separate copy of just the operation's own fields. + const payload = parseBoundaryRecord(fromBase64(init.headers["X-GEMINI-PAYLOAD"]!)); + assert.deepEqual(Object.keys(payload).sort(), ["legs", "nonce", "request"]); + assert.equal(payload.request, "/v1/prediction-markets/combos"); +}); + void test("T5e wrappers keep position filters in the query and volume fields in the signed body", async () => { const requests: Request[] = []; const auth = new HmacAuth({ apiKey: "key", apiSecret: "secret", now: () => 1000 }); diff --git a/packages/sdk-typescript/src/transport/http.test.ts b/packages/sdk-typescript/src/transport/http.test.ts index bfbe3012..175f8b2f 100644 --- a/packages/sdk-typescript/src/transport/http.test.ts +++ b/packages/sdk-typescript/src/transport/http.test.ts @@ -133,11 +133,15 @@ test("private request shapes the Gemini payload envelope", async () => { assert.equal(url, "https://api.sandbox.gemini.com/v1/prediction-markets/order"); assert.equal(init.method, "POST"); - // Fixed private-REST headers. - assert.equal(init.headers["Content-Length"], "0"); - assert.equal(init.headers["Content-Type"], "text/plain"); + // A request with real params now also gets them as a literal HTTP body — not just + // signed into the payload header — so servers that do a real json.Decode(r.Body) + // (e.g. combos) don't reject an empty body with a 400 (PREDICT-9072). The literal + // body carries only the operation's own fields, never the signed envelope's + // request/nonce. + assert.equal(init.headers["Content-Type"], "application/json"); + assert.equal(init.headers["Content-Length"], undefined); assert.equal(init.headers["Cache-Control"], "no-cache"); - assert.equal(init.body, undefined, "private REST parameters belong only in the signed payload"); + assert.deepEqual(init.body ? JSON.parse(init.body) : undefined, { symbol: "BTCUSD", amount: "1.5" }); // The payload is base64(JSON) with request + nonce + params. const b64 = init.headers["X-GEMINI-PAYLOAD"]; @@ -154,6 +158,35 @@ test("private request shapes the Gemini payload envelope", async () => { assert.equal(init.headers["X-GEMINI-SIGNATURE"], `sig(${b64})`); }); +test("private request with no params sends no body, keeping the fixed no-body headers (PREDICT-9072 no-regression guard)", async () => { + const { fetchImpl, last } = recordingFetch({ status: 200, body: '{"result":"ok"}' }); + const client = new HttpTransport({ env: "sandbox", auth: stubAuth, fetchImpl }); + + await client.request({ + method: "POST", + path: "/v1/positions", + }); + + const { init } = last(); + assert.equal(init.headers["Content-Length"], "0"); + assert.equal(init.headers["Content-Type"], "text/plain"); + assert.equal(init.body, undefined, "an operation with no params must not send a literal body"); +}); + +test("private request body serializes a bigint param losslessly, via the same stringifyJson used for the signed payload", async () => { + const { fetchImpl, last } = recordingFetch({ status: 200, body: '{"result":"ok"}' }); + const client = new HttpTransport({ env: "sandbox", auth: stubAuth, fetchImpl }); + + await client.request({ + method: "POST", + path: "/v1/prediction-markets/combos", + params: { contractId: 123456789012345678n }, + }); + + const { init } = last(); + assert.equal(init.body, '{"contractId":123456789012345678}'); +}); + test("declared query serialization preserves array and object wire formats", async () => { let requestedUrl = ""; const client = new HttpTransport({ diff --git a/packages/sdk-typescript/src/transport/http.ts b/packages/sdk-typescript/src/transport/http.ts index 51773e67..3d6698eb 100644 --- a/packages/sdk-typescript/src/transport/http.ts +++ b/packages/sdk-typescript/src/transport/http.ts @@ -1046,20 +1046,27 @@ export class HttpTransport { if (reservedHeader) { throw new SdkError(`AuthStrategy returned reserved header ${reservedHeader}`); } + // Some newer server-side handlers (e.g. combos) do a real `json.Decode(r.Body)` + // and reject an empty body with a 400, unlike older private endpoints that read + // exclusively from the signed X-GEMINI-PAYLOAD header. Send the operation's own + // fields (never the signed envelope's `request`/`nonce`) as a second, literal + // copy whenever there's an actual body to send, so both endpoint styles work. + const body = stableParams !== undefined ? stringifyJson(stableParams) : undefined; // Add auth headers first so the fixed envelope headers always win. // This prevents an auth strategy from replacing the payload or content headers. const headers = { ...stableHeaders, ...credentials, - "Content-Length": "0", - "Content-Type": "text/plain", + ...(body !== undefined + ? { "Content-Type": "application/json" } + : { "Content-Length": "0", "Content-Type": "text/plain" }), "Cache-Control": "no-cache", "X-GEMINI-PAYLOAD": b64, ...(options.responseContract ? { Accept: options.responseContract.responseContentTypes.join(", ") } : null), } satisfies RequestHeaders; - return headers; + return { headers, body }; }; return this.send( @@ -1104,7 +1111,7 @@ export class HttpTransport { return this.send( options.method, withQuery(options.path, options.query, options.queryParameters), - async () => stableHeaders, + async () => ({ headers: stableHeaders }), options.responseInt64Paths, options.responseMode, options.responseContract, @@ -1136,7 +1143,7 @@ export class HttpTransport { private async send( method: HttpMethod, path: string, - buildHeaders: (signal?: AbortSignal) => Promise>, + buildRequest: (signal?: AbortSignal) => Promise<{ headers: Record; body?: string }>, responseInt64Paths: readonly Int64Path[] = [], responseMode: RestResponseMode = "json", responseContract?: RestResponseContract, @@ -1189,8 +1196,9 @@ export class HttpTransport { const execution = deadline(requestOptions, this.timeoutMs); try { for (let attempt = 0; ; attempt++) { let headers: Record; + let requestBody: string | undefined; try { - headers = await withSignal(buildHeaders(execution.signal), execution.signal); + ({ headers, body: requestBody } = await withSignal(buildRequest(execution.signal), execution.signal)); } catch (cause) { emit("error", "request.failure", responseMetadata(undefined, attempt), undefined, cause); throw cause; @@ -1214,7 +1222,7 @@ export class HttpTransport { const requestStartTime = Date.now(); try { response = await withSignal( - this.fetchImpl(requestUrl, { method, headers, signal: execution.signal, redirect: "manual" }), + this.fetchImpl(requestUrl, { method, headers, body: requestBody, signal: execution.signal, redirect: "manual" }), execution.signal, ); } catch (cause) { From 7f3c80ed99c609ff97f5f251f1e602bc034d77c8 Mon Sep 17 00:00:00 2001 From: karan acharya Date: Tue, 22 Sep 2026 14:42:30 -0400 Subject: [PATCH 2/4] fix(sdk-typescript): resolve high-severity audit findings blocking CI npm audit --audit-level=high found 4 pre-existing high-severity vulnerabilities in devDependencies, unrelated to the HttpTransport fix but blocking the validate check on this PR (and would have blocked the real publish pipeline too, since publish-typescript-sdk.yml runs the identical audit command before publishing). - js-yaml (via @redocly/openapi-core): resolved transitively by npm audit fix, no direct dependency change needed. - sharp (via miniflare): required npm audit fix --force, bumping miniflare to a newer alpha (5.20260825.0-alpha -> 5.20260921.0-alpha). Verified this is a real breaking change, not just a version bump: the new major version removed the `type` field from its worker config schema (confirmed against its own generated type definitions), which broke verify:runtimes' Cloudflare Workers check. Fixed by removing that one now-rejected field from scripts/verify-multi-runtime.mjs's Miniflare config. Confirmed via --omit=dev that none of these ever affected the published package - devDependencies only, zero risk to consumers. Verified: npm audit --audit-level=high and --omit=dev both report 0 vulnerabilities, full build/typecheck/test (640/640) passes, and verify:package/verify:runtimes both pass with the new miniflare version. --- packages/sdk-typescript/package-lock.json | 323 +++++++++--------- packages/sdk-typescript/package.json | 2 +- .../scripts/verify-multi-runtime.mjs | 1 - 3 files changed, 165 insertions(+), 161 deletions(-) diff --git a/packages/sdk-typescript/package-lock.json b/packages/sdk-typescript/package-lock.json index 5f398d53..e8ad186a 100644 --- a/packages/sdk-typescript/package-lock.json +++ b/packages/sdk-typescript/package-lock.json @@ -14,7 +14,7 @@ "@types/node": "^22", "@types/ws": "^8.18.1", "esbuild": "^0.28.2", - "miniflare": "5.20260825.0-alpha", + "miniflare": "^5.20260921.0-alpha", "openapi-typescript": "7.13.0", "tsx": "^4", "typescript": "^5.7", @@ -289,9 +289,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20260825.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260825.1.tgz", - "integrity": "sha512-oHu38dwaUuzAilyTb0QkQ1YxU/kzqzIQybCvQKAhiK1CGtQS9h0MmjIZYogv3g8cFGGY2k+Wxs0wV9hHK8z78g==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260921.1.tgz", + "integrity": "sha512-3iB2WnYOlZ29T+1zhCwbHFExCBp6E9bgmDUMryATYwrIGEQ1YbvR78m4ydm56XKN/d/yF3803ivMGfZMYDtiMg==", "cpu": [ "x64" ], @@ -306,9 +306,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20260825.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260825.1.tgz", - "integrity": "sha512-ak5zh8YGEjxQQ78bVo7gzU+tcg2fSFxMIjOPZtWk56a/rIYLbGu6ECcliqnYfMUlragg68H0JuVpfdr3BR5Alw==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260921.1.tgz", + "integrity": "sha512-FpqVR7IQXVBmGtajyonEmhmb5UAsmV7dTaIkpemmHZXHEw7uYpkhkzKPjc4BOPhNQy8iwt2p+RZBPMY3Y7/bvQ==", "cpu": [ "arm64" ], @@ -323,9 +323,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20260825.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260825.1.tgz", - "integrity": "sha512-bNQvzz6NemWAwixDRz1fQa5T+E5lS4xpB7A/H/72ULxrjVpHmq8CGFPSbdmRp3dvgBjZTgp7wHdGLISLSVd9Gg==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260921.1.tgz", + "integrity": "sha512-riAJIohaVp5A8Sqy4yKlzHOaLPOICMf5oey+jC2rm45RVT+wK8+7UU0d31Dy/02Nc8YUkobAFwNVjX06P8WQ5g==", "cpu": [ "x64" ], @@ -340,9 +340,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20260825.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260825.1.tgz", - "integrity": "sha512-a5E61YsnNCHHQMnmYsbVXInzeYqFqAMwm/wo16dWW4klXDr6T1bm7u1h5G7ZkxVM7+rtc69oe0yVHjDEqzpYVg==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260921.1.tgz", + "integrity": "sha512-tnJu08tT7s0XWDqp3O0H/vCp0voy9OqVAzspb89biMo1dh8IiEpnyXnoPmdJ7H4qBnXCmXgy0kuEphuvpDPj9w==", "cpu": [ "arm64" ], @@ -357,9 +357,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20260825.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260825.1.tgz", - "integrity": "sha512-EokzVY2suzRSzeURi2HpHnySR5mo6aF5V1klFIqFOZp2YJyXXTI8AvQgYzhlmGTZY3LNL41jjkUQunOM2OErgQ==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260921.1.tgz", + "integrity": "sha512-VgNcRPstoZMb1G94JTrx+jU24GtkkazNfox0gnF/2fkuXpcfW/M0e0xvdMovYfwt8ZxG5AB2ZNvanD6ufBwiuQ==", "cpu": [ "x64" ], @@ -949,9 +949,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.2.tgz", - "integrity": "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", + "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", "cpu": [ "arm64" ], @@ -968,13 +968,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.3.1" + "@img/sharp-libvips-darwin-arm64": "1.3.3" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.2.tgz", - "integrity": "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", + "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", "cpu": [ "x64" ], @@ -991,13 +991,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.3.1" + "@img/sharp-libvips-darwin-x64": "1.3.3" } }, "node_modules/@img/sharp-freebsd-wasm32": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.2.tgz", - "integrity": "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", + "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -1005,7 +1005,7 @@ "freebsd" ], "dependencies": { - "@img/sharp-wasm32": "0.35.2" + "@img/sharp-wasm32": "0.35.4" }, "engines": { "node": ">=20.9.0" @@ -1015,9 +1015,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.1.tgz", - "integrity": "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", + "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", "cpu": [ "arm64" ], @@ -1032,9 +1032,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.1.tgz", - "integrity": "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", + "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", "cpu": [ "x64" ], @@ -1049,9 +1049,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.1.tgz", - "integrity": "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", + "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", "cpu": [ "arm" ], @@ -1069,9 +1069,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.1.tgz", - "integrity": "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", + "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", "cpu": [ "arm64" ], @@ -1089,9 +1089,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.1.tgz", - "integrity": "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", + "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", "cpu": [ "ppc64" ], @@ -1109,9 +1109,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.1.tgz", - "integrity": "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", + "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", "cpu": [ "riscv64" ], @@ -1129,9 +1129,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.1.tgz", - "integrity": "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", + "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", "cpu": [ "s390x" ], @@ -1149,9 +1149,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.1.tgz", - "integrity": "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", + "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", "cpu": [ "x64" ], @@ -1169,9 +1169,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.1.tgz", - "integrity": "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", + "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", "cpu": [ "arm64" ], @@ -1189,9 +1189,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.1.tgz", - "integrity": "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", + "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", "cpu": [ "x64" ], @@ -1209,9 +1209,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.2.tgz", - "integrity": "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", + "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", "cpu": [ "arm" ], @@ -1231,13 +1231,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.3.1" + "@img/sharp-libvips-linux-arm": "1.3.3" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.2.tgz", - "integrity": "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", + "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", "cpu": [ "arm64" ], @@ -1257,13 +1257,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.3.1" + "@img/sharp-libvips-linux-arm64": "1.3.3" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.2.tgz", - "integrity": "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", + "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", "cpu": [ "ppc64" ], @@ -1283,13 +1283,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.3.1" + "@img/sharp-libvips-linux-ppc64": "1.3.3" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.2.tgz", - "integrity": "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", + "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", "cpu": [ "riscv64" ], @@ -1309,13 +1309,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.3.1" + "@img/sharp-libvips-linux-riscv64": "1.3.3" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.2.tgz", - "integrity": "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", + "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", "cpu": [ "s390x" ], @@ -1335,13 +1335,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.3.1" + "@img/sharp-libvips-linux-s390x": "1.3.3" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.2.tgz", - "integrity": "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", + "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", "cpu": [ "x64" ], @@ -1361,13 +1361,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.3.1" + "@img/sharp-libvips-linux-x64": "1.3.3" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.2.tgz", - "integrity": "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", + "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", "cpu": [ "arm64" ], @@ -1387,13 +1387,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.2.tgz", - "integrity": "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", + "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", "cpu": [ "x64" ], @@ -1413,18 +1413,18 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.3.1" + "@img/sharp-libvips-linuxmusl-x64": "1.3.3" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz", - "integrity": "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", + "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.11.1" + "@emnapi/runtime": "^1.11.3" }, "engines": { "node": ">=20.9.0" @@ -1434,9 +1434,9 @@ } }, "node_modules/@img/sharp-webcontainers-wasm32": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.2.tgz", - "integrity": "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", + "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", "cpu": [ "wasm32" ], @@ -1444,7 +1444,7 @@ "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/sharp-wasm32": "0.35.2" + "@img/sharp-wasm32": "0.35.4" }, "engines": { "node": ">=20.9.0" @@ -1454,9 +1454,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.2.tgz", - "integrity": "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", + "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", "cpu": [ "arm64" ], @@ -1474,9 +1474,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.2.tgz", - "integrity": "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", + "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", "cpu": [ "ia32" ], @@ -1494,9 +1494,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.2.tgz", - "integrity": "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", + "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", "cpu": [ "x64" ], @@ -1674,9 +1674,9 @@ "license": "MIT" }, "node_modules/@redocly/openapi-core": { - "version": "1.34.19", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.19.tgz", - "integrity": "sha512-o/0VgsBXgwcY1lyeqcVtSGdTQAPnVggo0fbFVPlxl5XVDKUcVH0OLRqt3CbkwByT5FU305E0iE0O7MzThjDblw==", + "version": "1.34.20", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.20.tgz", + "integrity": "sha512-ypeBZ/6BKXR9+7/TtbKhbl4UgD7raHhPS12oknlKno2A8+lnFkxIwiE/Aklu6L2cd/ioH+fCWuMxi9/p3EyAPw==", "dev": true, "license": "MIT", "dependencies": { @@ -1685,7 +1685,7 @@ "colorette": "1.4.0", "https-proxy-agent": "7.0.6", "js-levenshtein": "1.1.6", - "js-yaml": "4.3.1", + "js-yaml": "4.3.2", "minimatch": "5.1.9", "pluralize": "8.0.0", "yaml-ast-parser": "0.0.43" @@ -4033,9 +4033,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", - "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "funding": [ { @@ -4245,16 +4245,16 @@ } }, "node_modules/miniflare": { - "version": "5.20260825.0-alpha", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20260825.0-alpha.tgz", - "integrity": "sha512-ZwlF6LuX43ilx9EwMRDKHenoGXiNdcKSyGx5aPaJhuozujVZasb2lRR7t3ojJZSnVzwDPsBBYCu8lLnc+/KIgQ==", + "version": "5.20260921.0-alpha", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20260921.0-alpha.tgz", + "integrity": "sha512-vHH/unOYvV2jA1Q9SdkmzrQhhMoksdwg5jegu6ZeKaaRzgxZhVbt1NdTpQjHF2VTgiBjgP8SiUlUMfruB3N3SQ==", "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "0.8.1", - "sharp": "0.35.2", + "sharp": "0.35.4", "undici": "7.29.0", - "workerd": "1.20260825.1", + "workerd": "1.20260921.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, @@ -4747,9 +4747,9 @@ } }, "node_modules/ramldt2jsonschema/node_modules/js-yaml": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz", - "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", "dev": true, "license": "MIT", "dependencies": { @@ -4968,15 +4968,15 @@ } }, "node_modules/sharp": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.2.tgz", - "integrity": "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", + "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.8.4" + "semver": "^7.8.5" }, "engines": { "node": ">=20.9.0" @@ -4985,31 +4985,36 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.35.2", - "@img/sharp-darwin-x64": "0.35.2", - "@img/sharp-freebsd-wasm32": "0.35.2", - "@img/sharp-libvips-darwin-arm64": "1.3.1", - "@img/sharp-libvips-darwin-x64": "1.3.1", - "@img/sharp-libvips-linux-arm": "1.3.1", - "@img/sharp-libvips-linux-arm64": "1.3.1", - "@img/sharp-libvips-linux-ppc64": "1.3.1", - "@img/sharp-libvips-linux-riscv64": "1.3.1", - "@img/sharp-libvips-linux-s390x": "1.3.1", - "@img/sharp-libvips-linux-x64": "1.3.1", - "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", - "@img/sharp-libvips-linuxmusl-x64": "1.3.1", - "@img/sharp-linux-arm": "0.35.2", - "@img/sharp-linux-arm64": "0.35.2", - "@img/sharp-linux-ppc64": "0.35.2", - "@img/sharp-linux-riscv64": "0.35.2", - "@img/sharp-linux-s390x": "0.35.2", - "@img/sharp-linux-x64": "0.35.2", - "@img/sharp-linuxmusl-arm64": "0.35.2", - "@img/sharp-linuxmusl-x64": "0.35.2", - "@img/sharp-webcontainers-wasm32": "0.35.2", - "@img/sharp-win32-arm64": "0.35.2", - "@img/sharp-win32-ia32": "0.35.2", - "@img/sharp-win32-x64": "0.35.2" + "@img/sharp-darwin-arm64": "0.35.4", + "@img/sharp-darwin-x64": "0.35.4", + "@img/sharp-freebsd-wasm32": "0.35.4", + "@img/sharp-libvips-darwin-arm64": "1.3.3", + "@img/sharp-libvips-darwin-x64": "1.3.3", + "@img/sharp-libvips-linux-arm": "1.3.3", + "@img/sharp-libvips-linux-arm64": "1.3.3", + "@img/sharp-libvips-linux-ppc64": "1.3.3", + "@img/sharp-libvips-linux-riscv64": "1.3.3", + "@img/sharp-libvips-linux-s390x": "1.3.3", + "@img/sharp-libvips-linux-x64": "1.3.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", + "@img/sharp-libvips-linuxmusl-x64": "1.3.3", + "@img/sharp-linux-arm": "0.35.4", + "@img/sharp-linux-arm64": "0.35.4", + "@img/sharp-linux-ppc64": "0.35.4", + "@img/sharp-linux-riscv64": "0.35.4", + "@img/sharp-linux-s390x": "0.35.4", + "@img/sharp-linux-x64": "0.35.4", + "@img/sharp-linuxmusl-arm64": "0.35.4", + "@img/sharp-linuxmusl-x64": "0.35.4", + "@img/sharp-webcontainers-wasm32": "0.35.4", + "@img/sharp-win32-arm64": "0.35.4", + "@img/sharp-win32-ia32": "0.35.4", + "@img/sharp-win32-x64": "0.35.4" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/side-channel": { @@ -5730,9 +5735,9 @@ } }, "node_modules/workerd": { - "version": "1.20260825.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260825.1.tgz", - "integrity": "sha512-ccS6TEaaRxgONKawiYGnFYUVGfr2pmN1b7mrNtw0ADVhFaYsEIVoCHnQ4UjhM9EJDzuaNgAWFY82nzVrjWpuOA==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260921.1.tgz", + "integrity": "sha512-4HyG7G1W4ksa6tUZ8bV2jxDRWuL5PXnHm9+Z1sjFPb9OZNoYtXz4y7QQRh4ibi0BF/lOmlAVjbhkUqsAVZuUKA==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -5743,11 +5748,11 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20260825.1", - "@cloudflare/workerd-darwin-arm64": "1.20260825.1", - "@cloudflare/workerd-linux-64": "1.20260825.1", - "@cloudflare/workerd-linux-arm64": "1.20260825.1", - "@cloudflare/workerd-windows-64": "1.20260825.1" + "@cloudflare/workerd-darwin-64": "1.20260921.1", + "@cloudflare/workerd-darwin-arm64": "1.20260921.1", + "@cloudflare/workerd-linux-64": "1.20260921.1", + "@cloudflare/workerd-linux-arm64": "1.20260921.1", + "@cloudflare/workerd-windows-64": "1.20260921.1" } }, "node_modules/wrap-ansi": { diff --git a/packages/sdk-typescript/package.json b/packages/sdk-typescript/package.json index be275df1..5d447d81 100644 --- a/packages/sdk-typescript/package.json +++ b/packages/sdk-typescript/package.json @@ -89,7 +89,7 @@ "@types/node": "^22", "@types/ws": "^8.18.1", "esbuild": "^0.28.2", - "miniflare": "5.20260825.0-alpha", + "miniflare": "^5.20260921.0-alpha", "openapi-typescript": "7.13.0", "tsx": "^4", "typescript": "^5.7", diff --git a/packages/sdk-typescript/scripts/verify-multi-runtime.mjs b/packages/sdk-typescript/scripts/verify-multi-runtime.mjs index 18188220..c45daa01 100644 --- a/packages/sdk-typescript/scripts/verify-multi-runtime.mjs +++ b/packages/sdk-typescript/scripts/verify-multi-runtime.mjs @@ -211,7 +211,6 @@ try { workers: [{ config: { name: "sdk-browser", - type: "worker", compatibilityDate: "2024-01-01", }, legacy: { From a34bfb0aaf22440592b78f22933065c2d85b47e9 Mon Sep 17 00:00:00 2001 From: karan acharya Date: Tue, 22 Sep 2026 15:31:01 -0400 Subject: [PATCH 3/4] docs(sdk-typescript): explain why assertSigned stays lenient (PREDICT-9072) Grace's review flagged (across two passes) that assertSigned accepts either the body-present or no-body shape rather than asserting one specific expectation per request, so a regression wouldn't be caught by these particular tests. Investigated tightening it by deriving the expectation from the signed payload's own keys (extra fields beyond request/nonce implies a body). That broke on real cases: some requestBody:true operations (getRoles, oauth revoke) take zero fields, so their signed payload is indistinguishable from a query-only operation's by content alone. Resolving that correctly needs each operation's own requestBody metadata cross-referenced per call site across 30+ calls in this file - real scope, not a quick tightening. Left the lenient check as-is and documented why, so this doesn't get re-flagged as an oversight. The actual regression protection for this bug already lives in transport/http.ts's own test file: a dedicated no-body guard and a createCombo integration test, both asserting one specific expected shape per case. --- .../tests/integration/generated/rest-surfaces.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts b/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts index dcc42d80..2ecfff3e 100644 --- a/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts +++ b/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts @@ -252,6 +252,15 @@ async function assertSigned(request: Request): Promise { // HTTP body too, not just signed into X-GEMINI-PAYLOAD (PREDICT-9072) — assert // internal consistency between the content headers and body presence, since this // generic helper covers both body-bearing and bodyless operations across every domain. + // + // Deliberately not derived from the signed payload's own keys: a requestBody:true + // operation called with zero actual fields (e.g. getRoles, oauth revoke) signs + // exactly {request, nonce} — indistinguishable, by payload content alone, from a + // requestBody:false query-only operation. Resolving that needs the operation's own + // metadata (requestBody flag) cross-referenced per call site, which is real scope + // beyond this helper — precise, per-operation regression coverage for the fix + // itself already lives in transport/http.ts's own test file (a dedicated no-body + // guard and a createCombo integration test), not here. if (request.init.body !== undefined) { assert.equal(request.init.headers["Content-Type"], "application/json"); assert.equal(request.init.headers["Content-Length"], undefined); From 53610f33378828b1de7e470cb826f0937baf96ce Mon Sep 17 00:00:00 2001 From: karan acharya Date: Wed, 23 Sep 2026 10:34:15 -0400 Subject: [PATCH 4/4] fix(sdk-typescript): don't attach a literal body to GET requests (PREDICT-9072) - Only send the literal JSON body for non-GET requests; native fetch rejects a body on GET/HEAD. Signed GET params (e.g. perpetuals.getFundingPaymentReportFile) stay in the signed payload only, as before. - Add native-fetch regression tests against a local server: getFundingPaymentReportFile sends no body and succeeds, and a signed POST delivers its JSON body. - Assert the perpetuals file-report GET has no body in the rest-surfaces test. --- .../generated/rest-surfaces.test.ts | 4 + .../sdk-typescript/src/transport/http.test.ts | 86 +++++++++++++++++++ packages/sdk-typescript/src/transport/http.ts | 5 +- 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts b/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts index 2ecfff3e..b83ed610 100644 --- a/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts +++ b/packages/sdk-typescript/src/tests/integration/generated/rest-surfaces.test.ts @@ -561,6 +561,10 @@ test("Perpetuals wrappers shape public, authenticated JSON, and file requests", account: "primary", nonce: 1004, }); + // The signed GET keeps its params in the payload only — native fetch rejects a + // GET with a body — while the signed POSTs also send them as a literal body. + assert.equal(requests[5]?.init.body, undefined); + assert.deepEqual(JSON.parse(requests[4]!.init.body!), { account: "primary" }); assert.deepEqual(file.bytes, fileBytes); assert.equal( file.contentType, diff --git a/packages/sdk-typescript/src/transport/http.test.ts b/packages/sdk-typescript/src/transport/http.test.ts index 175f8b2f..20e3f0fa 100644 --- a/packages/sdk-typescript/src/transport/http.test.ts +++ b/packages/sdk-typescript/src/transport/http.test.ts @@ -1,5 +1,7 @@ import assert from "node:assert/strict"; import { execFileSync } from "node:child_process"; +import { createServer, type IncomingMessage, type ServerResponse } from "node:http"; +import type { AddressInfo } from "node:net"; import { test } from "node:test"; import { fileURLToPath } from "node:url"; @@ -29,6 +31,7 @@ import { serializeError, } from "../errors.js"; import { fromBase64 } from "../utils/encoding.js"; +import { PerpetualsRest } from "../generated/perpetuals/rest.js"; import type { BoundaryRecord, BoundaryValue } from "../utils/boundary-value.js"; import { parseBoundaryRecord } from "../tests/support/http-fixtures.js"; @@ -187,6 +190,89 @@ test("private request body serializes a bigint param losslessly, via the same st assert.equal(init.body, '{"contractId":123456789012345678}'); }); +// Fake fetchImpls don't enforce fetch's rule that GET/HEAD requests cannot carry a +// body, so these go through the runtime's native fetch against a local server. +async function withLocalServer( + respond: (req: IncomingMessage, res: ServerResponse) => void, + run: (baseUrl: string, received: () => { method?: string; url?: string; headers: IncomingMessage["headers"]; body: string }) => Promise, +): Promise { + let received: { method?: string; url?: string; headers: IncomingMessage["headers"]; body: string } | undefined; + const server = createServer((req, res) => { + const chunks: Buffer[] = []; + req.on("data", (chunk: Buffer) => chunks.push(chunk)); + req.on("end", () => { + received = { method: req.method, url: req.url, headers: req.headers, body: Buffer.concat(chunks).toString("utf8") }; + respond(req, res); + }); + }); + await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); + try { + const { port } = server.address() as AddressInfo; + await run(`http://127.0.0.1:${port}`, () => { + if (!received) throw new Error("server never received a request"); + return received; + }); + } finally { + await new Promise((resolve) => server.close(() => resolve())); + } +} + +test("native fetch: signed GET file report (perpetuals.getFundingPaymentReportFile) sends no body and succeeds", async () => { + const fileBytes = new Uint8Array([1, 2, 3, 4]); + await withLocalServer( + (_req, res) => { + res.writeHead(200, { + "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "Content-Disposition": "attachment; filename=funding-payment-report.xlsx", + }); + res.end(Buffer.from(fileBytes)); + }, + async (baseUrl, received) => { + const perpetuals = new PerpetualsRest(new HttpTransport({ env: "sandbox", baseUrl, auth: stubAuth })); + + const file = await perpetuals.getFundingPaymentReportFile({ + fromDate: "2026-01-01", + toDate: "2026-01-31", + numRows: 10, + account: "primary", + }); + + assert.deepEqual(file.bytes, fileBytes); + const req = received(); + assert.equal(req.method, "GET"); + assert.equal(req.url, "/v1/perpetuals/fundingpaymentreport/records.xlsx?fromDate=2026-01-01&toDate=2026-01-31&numRows=10"); + assert.equal(req.body, "", "a GET must never carry a literal body"); + assert.equal(req.headers["content-type"], "text/plain"); + const signed = JSON.parse(fromBase64(String(req.headers["x-gemini-payload"]))); + assert.equal(signed.account, "primary", "GET params stay in the signed payload"); + }, + ); +}); + +test("native fetch: signed POST with params delivers the literal JSON body", async () => { + await withLocalServer( + (_req, res) => { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end('{"result":"ok"}'); + }, + async (baseUrl, received) => { + const client = new HttpTransport({ env: "sandbox", baseUrl, auth: stubAuth }); + + await client.request({ + method: "POST", + path: "/v1/prediction-markets/combos", + params: { legs: [{ symbol: "GEMI-A", side: "yes" }] }, + }); + + const req = received(); + assert.equal(req.method, "POST"); + assert.equal(req.headers["content-type"], "application/json"); + assert.deepEqual(JSON.parse(req.body), { legs: [{ symbol: "GEMI-A", side: "yes" }] }); + assert.equal(req.headers["content-length"], String(Buffer.byteLength(req.body))); + }, + ); +}); + test("declared query serialization preserves array and object wire formats", async () => { let requestedUrl = ""; const client = new HttpTransport({ diff --git a/packages/sdk-typescript/src/transport/http.ts b/packages/sdk-typescript/src/transport/http.ts index 3d6698eb..5ef36b76 100644 --- a/packages/sdk-typescript/src/transport/http.ts +++ b/packages/sdk-typescript/src/transport/http.ts @@ -1051,7 +1051,10 @@ export class HttpTransport { // exclusively from the signed X-GEMINI-PAYLOAD header. Send the operation's own // fields (never the signed envelope's `request`/`nonce`) as a second, literal // copy whenever there's an actual body to send, so both endpoint styles work. - const body = stableParams !== undefined ? stringifyJson(stableParams) : undefined; + // GET is excluded: native fetch rejects a body on GET/HEAD, and signed GET + // operations (e.g. perpetuals.getFundingPaymentReportFile) carry their params + // in the signed payload only, exactly as before. + const body = stableParams !== undefined && method !== "GET" ? stringifyJson(stableParams) : undefined; // Add auth headers first so the fixed envelope headers always win. // This prevents an auth strategy from replacing the payload or content headers. const headers = {