From 932a8868f64d6b08c51e54b8b2d4c6630c4a6753 Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Tue, 31 Mar 2026 15:37:25 +0100 Subject: [PATCH 1/5] docs: align default webhook signing with v0.12+ behavior Document v0= signing over raw body, x-outpost-timestamp in a separate header, and link to legacy template env vars. Update migration guide snippet to SDK tenants/destinations calls. Made-with: Cursor --- docs/pages/destinations/webhook.mdx | 22 ++++++++++++++-------- docs/pages/guides/migrate-to-outpost.mdx | 13 ++++++------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/docs/pages/destinations/webhook.mdx b/docs/pages/destinations/webhook.mdx index e719cec6e..8e826ddc2 100644 --- a/docs/pages/destinations/webhook.mdx +++ b/docs/pages/destinations/webhook.mdx @@ -38,7 +38,7 @@ Content-Type: application/json x-outpost-id: evt_123 x-outpost-topic: orders x-outpost-timestamp: 1704067200 -x-outpost-signature: t=1704067200,v0=abc123... +x-outpost-signature: v0=abc123... x-outpost-source: checkout-service {"order_id": "123", "status": "created"} @@ -135,22 +135,28 @@ Outpost adds headers using the configured prefix (default `x-outpost-`): ### Signature -The signature is computed over the timestamp and request body: +By default the signed payload is the **raw request body** (see `DESTINATIONS_WEBHOOK_SIGNATURE_CONTENT_TEMPLATE`). ``` -signature = HMAC-SHA256(secret, "${timestamp}.${body}") +signature = HMAC-SHA256(secret, body) ``` -The signature header value follows the format: `t=${timestamp},v0=${signature}` +The signature header value follows the format: `v0=${signature}`. During secret rotation, Outpost may send several comma-separated digests: `v0=${sig1},${sig2}`. Compare your computed digest to any of the values using a constant-time check. + +The event time is in the `x-outpost-timestamp` header (Unix seconds), not embedded in the signature header. + +:::note[Upgrading from v0.11] +The previous defaults signed `${timestamp}.${body}` and used `t=${timestamp},v0=${signature}`. See the [v0.12 upgrade guide](/docs/guides/upgrade-v0.12#webhook-signature-defaults) to align receivers or restore the old templates. +::: ### Verifying Signatures To verify webhook requests: -1. Extract the timestamp and signature from the `x-outpost-signature` header -2. Compute the expected signature using your secret -3. Compare signatures using a constant-time comparison -4. Optionally, reject requests with old timestamps to prevent replay attacks +1. Read the raw body **before** JSON parsing (the digest is over exact bytes). +2. Parse `v0=` from the `x-outpost-signature` header; split any comma-separated digests if present. +3. Compute `HMAC-SHA256(secret, rawBody)` with the configured encoding (default hex) and compare. +4. Optionally, use `x-outpost-timestamp` and reject stale requests to limit replay attacks. ### Operator Configuration diff --git a/docs/pages/guides/migrate-to-outpost.mdx b/docs/pages/guides/migrate-to-outpost.mdx index cdd2b4a80..c12e7ee77 100644 --- a/docs/pages/guides/migrate-to-outpost.mdx +++ b/docs/pages/guides/migrate-to-outpost.mdx @@ -59,7 +59,7 @@ Webhooks are delivered via HTTP `POST` requests. ### Webhook Event HTTP Headers - `x-outpost-event-id`: A unique identifier for the event within the Outpost installation -- `x-outpost-signature`: The signature generated using the webhook secret (e.g., `t=,v0=`). +- `x-outpost-signature`: HMAC of the raw body (default `v0=`; operators may customize templates — see [Webhook destination](/docs/destinations/webhook#signature)). - `x-outpost-timestamp`: A Unix timestamp representing the time the event was generated - `x-outpost-topic`: The topic of the event. For example, `user.created`. @@ -140,7 +140,7 @@ The following diagram shows the Outpost schema. You can connect to the database A migration script may look something like this: ```ts -// Outpost API wrapper +// Outpost TypeScript SDK (see examples/demos/nodejs) import outpost from "./outpost"; // Database wrapper @@ -163,7 +163,7 @@ const migrateOrganizations = async () => { const migratedOrgIds: string[] = []; const organizations = db.getOrganizations(); for (const organization of organizations) { - await outpost.registerTenant(organization.id); + await outpost.tenants.upsert(organization.id); migratedOrgIds.push(organization.id); } return migratedOrgIds; @@ -172,12 +172,11 @@ const migrateOrganizations = async () => { const migrateSubscriptions = async (organizationId: string) => { const subscriptions = db.getSubscriptions(organizationId); for (const subscription of subscriptions) { - await outpost.createDestination({ - tenant_id: organizationId, + await outpost.destinations.create(organizationId, { type: "webhook", - url: subscription.url, + config: { url: subscription.url }, topics: subscription.topics, - secret: subscription.secret, + credentials: { secret: subscription.secret }, }); } }; From 9b8cbb00ce9a32f3f2cce3173bf2ad3b36f05a19 Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Tue, 31 Mar 2026 15:37:48 +0100 Subject: [PATCH 2/5] examples: refresh webhook verify demo and overview README Default Outpost signs raw body with v0=; keep optional legacy verifier for v0.11-style templates. Modernize overview curl samples and remove dated portal copy. Made-with: Cursor --- examples/demos/nodejs/src/verify-signature.ts | 140 +++++++++--------- examples/demos/overview/README.md | 12 +- 2 files changed, 79 insertions(+), 73 deletions(-) diff --git a/examples/demos/nodejs/src/verify-signature.ts b/examples/demos/nodejs/src/verify-signature.ts index f83a24e63..f01d6a0e0 100644 --- a/examples/demos/nodejs/src/verify-signature.ts +++ b/examples/demos/nodejs/src/verify-signature.ts @@ -1,84 +1,92 @@ import * as crypto from "crypto"; +/** + * Verify Outpost default-mode webhooks (v0.12+ defaults). + * Signed content: raw request body. Header value: v0=[,...] during secret rotation. + * Timestamp for replay checks lives in x-outpost-timestamp, not in the signature header. + */ function verifyWebhookSignature( - requestBody: string, + rawBody: string, signatureHeader: string, secret: string ): boolean { - // Parse the signature header (assumed format: t=,v0=) - const parts = signatureHeader.split(","); - const timestampPart = parts.find((part) => part.startsWith("t=")); - const signaturePart = parts.find((part) => part.startsWith("v0=")); - - console.log(`signaturePart: ${signaturePart}`); - console.log(`timestampPart: ${timestampPart}`); - - if (!timestampPart || !signaturePart) { - return false; // Malformed signature header + const trimmed = signatureHeader.trim(); + if (!trimmed.startsWith("v0=")) { + return false; } - const timestamp = timestampPart.split("=")[1]; - const expectedSignature = signaturePart.split("=")[1]; - - // Create the string to sign - const data = `${timestamp}.${requestBody}`; - - // Compute the HMAC SHA-256 signature - const hmac = crypto.createHmac("sha256", secret); - hmac.update(data); - const computedSignature = hmac.digest("hex"); - - // Compare the computed signature with the expected one - console.log(`comparing: \n${computedSignature}\n${expectedSignature}`); - - return crypto.timingSafeEqual( - Buffer.from(computedSignature, "utf8"), - Buffer.from(expectedSignature, "utf8") + const listed = trimmed.slice("v0=".length).split(","); + const expected = crypto + .createHmac("sha256", secret) + .update(rawBody) + .digest("hex"); + + return listed.some( + (sig) => + sig.length === expected.length && + crypto.timingSafeEqual( + Buffer.from(sig, "utf8"), + Buffer.from(expected, "utf8") + ) ); } -// Example usage -const requestBody = '{"test":"data"}'; // The actual webhook payload -const signatureHeader = - "t=1741797142,v0=ec25087a0b05b76fd057f61af808778b2b0e3b4c9f0dfc80f4cdc5cecdd1f325"; -const secret = "some_secret_value"; - -const isValid = verifyWebhookSignature(requestBody, signatureHeader, secret); -console.log(`Signature valid: ${isValid}`); - -// Helper function to assert valid signature -function assertValidSignature( - secret: string, - rawBody: Uint8Array, - signatureHeader: string -) { - // Parse "t={timestamp},v0={signature1,signature2}" format - const parts = signatureHeader.split(",", 2); // Split only on first comma - - const timestampStr = parts[0].replace("t=", ""); - const signatures = parts[1].replace("v0=", "").split(","); - - const timestamp = parseInt(timestampStr, 10); - if (isNaN(timestamp)) { - throw new Error("timestamp should be a valid integer"); +/** + * Legacy format when the operator sets DESTINATIONS_WEBHOOK_SIGNATURE_* to v0.11-style templates. + * Header: t=,v0=[,...] Signed content: "." + */ +function verifyWebhookSignatureLegacy( + rawBody: string, + signatureHeader: string, + secret: string +): boolean { + const comma = signatureHeader.indexOf(","); + if (comma === -1) { + return false; + } + const tsPart = signatureHeader.slice(0, comma); + const sigPart = signatureHeader.slice(comma + 1); + if (!tsPart.startsWith("t=") || !sigPart.startsWith("v0=")) { + return false; } - // Reconstruct the signed content + const timestamp = tsPart.slice("t=".length); + const listed = sigPart.slice("v0=".length).split(","); const signedContent = `${timestamp}.${rawBody}`; + const expected = crypto + .createHmac("sha256", secret) + .update(signedContent) + .digest("hex"); + + return listed.some( + (sig) => + sig.length === expected.length && + crypto.timingSafeEqual( + Buffer.from(sig, "utf8"), + Buffer.from(expected, "utf8") + ) + ); +} - // Generate HMAC-SHA256 - const hmac = crypto.createHmac("sha256", secret); - hmac.update(signedContent); - const expectedSignature = hmac.digest("hex"); +// --- Default (current) Outpost behavior --- +const requestBody = '{"test":"data"}'; +const signatureHeader = + "v0=5920020651a5934e394f95a7e79a85400ba12318c11f330b9ca30c7f064318d1"; +const secret = "some_secret_value"; - // Check if any of the signatures match - const found = signatures.some((sig) => sig === expectedSignature); - return found; -} +const isValidDefault = verifyWebhookSignature( + requestBody, + signatureHeader, + secret +); +console.log(`Default format signature valid: ${isValidDefault}`); -const assertResult = assertValidSignature( - secret, - Buffer.from(requestBody), - signatureHeader +// --- Legacy (optional operator override) --- +const legacyHeader = + "t=1741797142,v0=ec25087a0b05b76fd057f61af808778b2b0e3b4c9f0dfc80f4cdc5cecdd1f325"; +const isValidLegacy = verifyWebhookSignatureLegacy( + requestBody, + legacyHeader, + secret ); -console.log(`Signature valid: ${assertResult}`); +console.log(`Legacy format signature valid: ${isValidLegacy}`); diff --git a/examples/demos/overview/README.md b/examples/demos/overview/README.md index e8741fa68..a2eeb7c28 100644 --- a/examples/demos/overview/README.md +++ b/examples/demos/overview/README.md @@ -11,7 +11,7 @@ URL=your_webhook_url You'd do this whenever a new organization signups up. ```sh -curl --location --request PUT "localhost:3333/api/v1/tenants/$TENANT_ID" \ +curl --location --request PUT "http://localhost:3333/api/v1/tenants/$TENANT_ID" \ --header "Authorization: Bearer $API_KEY" ``` @@ -20,7 +20,7 @@ curl --location --request PUT "localhost:3333/api/v1/tenants/$TENANT_ID" \ When someone within an org wants to subscribe to an event, create a Destination: ```sh -curl --location "localhost:3333/api/v1/tenants/$TENANT_ID/destinations" \ +curl --location "http://localhost:3333/api/v1/tenants/$TENANT_ID/destinations" \ --header "Content-Type: application/json" \ --header "Authorization: Bearer $API_KEY" \ --data '{ @@ -52,7 +52,7 @@ Outpost supports two ways of publishing: Publish via the API: ```sh -curl --location "localhost:3333/api/v1/publish" \ +curl --location "http://localhost:3333/api/v1/publish" \ --header "Content-Type: application/json" \ --header "Authorization: Bearer $API_KEY" \ --data '{ @@ -72,12 +72,10 @@ Check the webhook was delivered and ingested. ## Open the Outpost Portal -Outpost comes with a pre-built portal that supports event destination management, event inspection, and metrics. - -Event Inspection will be available next week (mid-March). Metric will be part of the BETA release. +Outpost includes a portal for event destination management, logs, and metrics (features depend on your Outpost version and configuration). ```sh -curl "localhost:3333/api/v1/tenants/$TENANT_ID/portal" \ +curl "http://localhost:3333/api/v1/tenants/$TENANT_ID/portal" \ --header "Authorization: Bearer $API_KEY" ``` From 3ac4fd2c6b1747e3cd9ac6bd0fdb8d2c1be7045f Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Tue, 31 Mar 2026 15:38:01 +0100 Subject: [PATCH 3/5] examples(azure): list topics via GET /api/v1/topics Tenant-scoped topics endpoint was removed (v0.13); use global topics list. Made-with: Cursor --- examples/azure/diagnostics.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/azure/diagnostics.sh b/examples/azure/diagnostics.sh index 64394fb7f..a57e88dc9 100755 --- a/examples/azure/diagnostics.sh +++ b/examples/azure/diagnostics.sh @@ -195,7 +195,7 @@ run_api_tests() { echo " -> ✅ Tenant created." echo " (Checking configured topics...)" - topics_response=$(curl -s -w "\n%{http_code}" -X GET "$base_url/api/v1/tenants/$TENANT_ID/topics" \ + topics_response=$(curl -s -w "\n%{http_code}" -X GET "$base_url/api/v1/topics" \ -H "Authorization: Bearer $API_KEY") topics_http_code=$(echo "$topics_response" | tail -n1) From e975dae4e61ddf9f10520b588ca53abac232bdef Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Tue, 31 Mar 2026 15:38:10 +0100 Subject: [PATCH 4/5] examples(node): use pinned @hookdeck/outpost-sdk, drop custom client Replace axios OutspotClient with the official SDK (exact 0.9.2) so routes and types track releases deliberately. Add npm run typecheck and tsconfig bundler resolution for tsc --noEmit. Made-with: Cursor --- examples/demos/nodejs/README.md | 2 + examples/demos/nodejs/package-lock.json | 1068 ++++++++++++++++- examples/demos/nodejs/package.json | 5 +- examples/demos/nodejs/src/create-tenant.ts | 9 +- examples/demos/nodejs/src/healthz.ts | 19 +- .../demos/nodejs/src/lib/outpost-client.ts | 133 -- examples/demos/nodejs/src/lib/outpost.ts | 11 +- examples/demos/nodejs/src/migrate.ts | 26 +- examples/demos/nodejs/src/portal-urls.ts | 8 +- examples/demos/nodejs/src/publish-api.ts | 28 +- examples/demos/nodejs/tsconfig.json | 4 +- 11 files changed, 1085 insertions(+), 228 deletions(-) delete mode 100644 examples/demos/nodejs/src/lib/outpost-client.ts diff --git a/examples/demos/nodejs/README.md b/examples/demos/nodejs/README.md index 60e668794..6ea4ae108 100644 --- a/examples/demos/nodejs/README.md +++ b/examples/demos/nodejs/README.md @@ -1,5 +1,7 @@ # Examples for Node.js / TypeScript +Admin API calls use the official [`@hookdeck/outpost-sdk`](https://www.npmjs.com/package/@hookdeck/outpost-sdk). The version is **pinned exactly** in `package.json` so new SDK releases do not change this demo unexpectedly. Bump the pin when you intentionally adopt a newer SDK (run `npm install` and fix any API drift). + Create a `.env`: ``` diff --git a/examples/demos/nodejs/package-lock.json b/examples/demos/nodejs/package-lock.json index fd9a3c367..2a86c62c7 100644 --- a/examples/demos/nodejs/package-lock.json +++ b/examples/demos/nodejs/package-lock.json @@ -11,8 +11,8 @@ "dependencies": { "@aws-sdk/client-sqs": "^3.774.0", "@google-cloud/pubsub": "^4.11.0", + "@hookdeck/outpost-sdk": "0.9.2", "amqplib": "^0.10.3", - "axios": "^1.8.2", "dotenv": "^16.4.7" }, "devDependencies": { @@ -1139,6 +1139,30 @@ "node": ">=6" } }, + "node_modules/@hono/node-server": { + "version": "1.19.12", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.12.tgz", + "integrity": "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@hookdeck/outpost-sdk": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@hookdeck/outpost-sdk/-/outpost-sdk-0.9.2.tgz", + "integrity": "sha512-S394MDF9nrzb6Hw/DN9wWaA/7K+48rXsJT8RE//ZhfDh6muoGj55pwMhNC8w5yma80sI6KHSY1GoO8SMlIvypA==", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.26.0", + "zod": "^3.25.0 || ^4.0.0" + }, + "bin": { + "mcp": "bin/mcp-server.js" + } + }, "node_modules/@js-sdsl/ordered-map": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", @@ -1149,6 +1173,46 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, "node_modules/@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", @@ -1916,6 +1980,44 @@ "node": ">=6.5" } }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/agent-base": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", @@ -1925,6 +2027,39 @@ "node": ">= 14" } }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/amqplib": { "version": "0.10.5", "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.5.tgz", @@ -1992,17 +2127,6 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, - "node_modules/axios": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz", - "integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -2045,6 +2169,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", @@ -2076,6 +2224,15 @@ "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==", "license": "MIT" }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -2089,6 +2246,22 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -2158,10 +2331,81 @@ "node": ">= 0.8" } }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2184,6 +2428,15 @@ "node": ">=0.4.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", @@ -2231,12 +2484,27 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -2338,6 +2606,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/esrun": { "version": "3.2.26", "resolved": "https://registry.npmjs.org/esrun/-/esrun-3.2.26.tgz", @@ -2356,6 +2630,15 @@ "node": ">=14.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -2365,12 +2648,141 @@ "node": ">=6" } }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz", + "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", + "license": "MIT", + "dependencies": { + "ip-address": "10.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fast-xml-parser": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", @@ -2406,39 +2818,43 @@ "node": ">=8" } }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, "engines": { - "node": ">=4.0" + "node": ">= 18.0.0" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, "engines": { - "node": ">= 6" + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, "node_modules/fsevents": { @@ -2676,6 +3092,36 @@ "node": ">=10.0.0" } }, + "node_modules/hono": { + "version": "4.12.9", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.9.tgz", + "integrity": "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -2715,12 +3161,46 @@ "node": ">= 14" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2776,6 +3256,12 @@ "node": ">=0.12.0" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -2794,6 +3280,21 @@ "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", "license": "MIT" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/json-bigint": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", @@ -2803,6 +3304,18 @@ "bignumber.js": "^9.0.0" } }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -2851,6 +3364,27 @@ "node": ">= 0.4" } }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -2878,6 +3412,15 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -2908,6 +3451,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-hash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", @@ -2917,6 +3469,30 @@ "node": ">= 6" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2935,6 +3511,34 @@ "node": ">=8" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.1.tgz", + "integrity": "sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -2948,6 +3552,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/proto3-json-serializer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", @@ -2984,11 +3597,33 @@ "node": ">=12.0.0" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/querystringify": { "version": "2.2.0", @@ -2996,6 +3631,30 @@ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "license": "MIT" }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -3032,6 +3691,15 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -3052,12 +3720,212 @@ "node": ">=14" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/send/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stream-events": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", @@ -3200,6 +4068,15 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -3212,6 +4089,45 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/typescript": { "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", @@ -3232,6 +4148,15 @@ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", @@ -3261,6 +4186,15 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -3277,6 +4211,21 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -3335,6 +4284,25 @@ "engines": { "node": ">=12" } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } } } } diff --git a/examples/demos/nodejs/package.json b/examples/demos/nodejs/package.json index 69e44077b..7d0accf86 100644 --- a/examples/demos/nodejs/package.json +++ b/examples/demos/nodejs/package.json @@ -15,7 +15,8 @@ "verify": "esrun src/verify-signature.ts", "portal-urls": "esrun src/portal-urls.ts", "create-tenant": "esrun src/create-tenant.ts", - "healthz": "esrun src/healthz.ts" + "healthz": "esrun src/healthz.ts", + "typecheck": "tsc --noEmit" }, "engines": { "node": ">=20.6.0" @@ -29,8 +30,8 @@ "dependencies": { "@aws-sdk/client-sqs": "^3.774.0", "@google-cloud/pubsub": "^4.11.0", + "@hookdeck/outpost-sdk": "0.9.2", "amqplib": "^0.10.3", - "axios": "^1.8.2", "dotenv": "^16.4.7" } } diff --git a/examples/demos/nodejs/src/create-tenant.ts b/examples/demos/nodejs/src/create-tenant.ts index e52e77c2f..503585b65 100644 --- a/examples/demos/nodejs/src/create-tenant.ts +++ b/examples/demos/nodejs/src/create-tenant.ts @@ -1,7 +1,6 @@ import db from "./lib/db"; import outpost from "./lib/outpost"; -// Node.js arguments: [0] = node, [1] = script path, [2] = first user argument const tenantId = process.argv[2]; if (!tenantId) { console.error("Please provide an tenant ID"); @@ -9,12 +8,12 @@ if (!tenantId) { } const main = async () => { - const response = await outpost.registerTenant(tenantId); + const tenant = await outpost.tenants.upsert(tenantId); console.log(`Tenant ${tenantId} created`); - console.log(response); + console.log(tenant); - const portalUrl = await outpost.getPortalURL(tenantId); - console.log(`Portal URL for ${tenantId}:`, portalUrl); + const portal = await outpost.tenants.getPortalUrl(tenantId); + console.log(`Portal URL for ${tenantId}:`, portal.redirectUrl ?? ""); }; main() diff --git a/examples/demos/nodejs/src/healthz.ts b/examples/demos/nodejs/src/healthz.ts index bd33e9edf..c9626630e 100644 --- a/examples/demos/nodejs/src/healthz.ts +++ b/examples/demos/nodejs/src/healthz.ts @@ -1,8 +1,23 @@ import outpost from "./lib/outpost"; const main = async () => { - const response = await outpost.healthz(); - console.log(`Health check: ${response ? "OK" : "FAIL"}`); + let ok = false; + try { + await outpost.health.check(); + ok = true; + } catch (err: unknown) { + const status = + err && typeof err === "object" && "statusCode" in err + ? (err as { statusCode: number }).statusCode + : undefined; + if (status === 404) { + console.log("Health endpoint not available (e.g. managed Outpost). Skipping."); + ok = true; + } else { + console.error(err); + } + } + console.log(`Health check: ${ok ? "OK" : "FAIL"}`); }; main() diff --git a/examples/demos/nodejs/src/lib/outpost-client.ts b/examples/demos/nodejs/src/lib/outpost-client.ts deleted file mode 100644 index 93b9f4e81..000000000 --- a/examples/demos/nodejs/src/lib/outpost-client.ts +++ /dev/null @@ -1,133 +0,0 @@ -import axios from "axios"; - -interface Tenant { - id: string; -} - -interface Destination { - id: string; - topics: string[]; -} - -class OutpostClient { - outpostApiUrl: string; - outpostApiKey: string; - - constructor(outpostApiUrl: string, outpostApiKey: string) { - this.outpostApiUrl = outpostApiUrl; - this.outpostApiKey = outpostApiKey; - } - - async request(path: string, method: string, data: any): Promise { - const response = await axios.request({ - url: `${this.outpostApiUrl}${path}`, - method, - data, - headers: { - Authorization: `Bearer ${this.outpostApiKey}`, - }, - }); - return response.data; - } - - async healthz(): Promise { - try { - await this.request("/healthz", "GET", {}); - return true; - } catch (error) { - return false; - } - } - - async publishEvent({ - tenant_id, - topic, - eligible_for_retry, - data, - metadata, - }: { - tenant_id: string; - topic: string; - eligible_for_retry: boolean; - data: any; - metadata: any; - }): Promise { - const response = await this.request("/publish", "POST", { - tenant_id, - topic, - eligible_for_retry, - data, - metadata, - }); - return !!response; - } - - async registerTenant(tenant_id: string): Promise { - const response = await this.request(`/${tenant_id}`, "PUT", {}); - return response; - } - - async deleteTenant(tenant_id: string) { - const response = await this.request(`/${tenant_id}`, "DELETE", {}); - return response; - } - - async getDestinations(tenant_id: string) { - const response = await this.request( - `/${tenant_id}/destinations`, - "GET", - {} - ); - return response; - } - - async createDestination({ - tenant_id, - type, - url, - topics, - secret, - }: { - tenant_id: any; - type: string; - url: string; - topics: string[]; - secret: string; - }) { - const response = await this.request( - `/${tenant_id}/destinations`, - "POST", - { - type, - config: { - url, - }, - topics, - credentials: { - secret, - }, - } - ); - return response; - } - - async deleteDestination(tenant_id: string, destination_id: string) { - const response = await this.request( - `/${tenant_id}/destinations/${destination_id}`, - "DELETE", - {} - ); - return response; - } - - async getPortalURL(tenant_id: string): Promise { - const response = await this.request<{ redirect_url: string }>( - `/${tenant_id}/portal`, - "GET", - {} - ); - return response.redirect_url; - } -} - -export default OutpostClient; diff --git a/examples/demos/nodejs/src/lib/outpost.ts b/examples/demos/nodejs/src/lib/outpost.ts index 62cbe6651..3d1889291 100644 --- a/examples/demos/nodejs/src/lib/outpost.ts +++ b/examples/demos/nodejs/src/lib/outpost.ts @@ -1,6 +1,7 @@ import * as process from "process"; import * as dotenv from "dotenv"; +import { Outpost } from "@hookdeck/outpost-sdk"; dotenv.config(); if (!process.env.OUTPOST_API_BASE_URL || !process.env.OUTPOST_API_KEY) { @@ -8,11 +9,9 @@ if (!process.env.OUTPOST_API_BASE_URL || !process.env.OUTPOST_API_KEY) { process.exit(1); } -import OutpostClient from "./outpost-client"; - -const outpost = new OutpostClient( - process.env.OUTPOST_API_BASE_URL, - process.env.OUTPOST_API_KEY -); +const outpost = new Outpost({ + serverURL: process.env.OUTPOST_API_BASE_URL, + apiKey: process.env.OUTPOST_API_KEY, +}); export default outpost; diff --git a/examples/demos/nodejs/src/migrate.ts b/examples/demos/nodejs/src/migrate.ts index c5e377a05..669254e66 100644 --- a/examples/demos/nodejs/src/migrate.ts +++ b/examples/demos/nodejs/src/migrate.ts @@ -1,10 +1,7 @@ import * as process from "process"; import { askQuestion } from "./lib/utils"; -// Outpost API wrapper import outpost from "./lib/outpost"; - -// Database wrapper import { default as db } from "./lib/db"; const cleanup = async () => { @@ -20,12 +17,12 @@ const cleanup = async () => { const organizations = db.getOrganizations(); for (const organization of organizations) { - const destinations = await outpost.getDestinations(organization.id); + const destinations = await outpost.destinations.list(organization.id); for (const destination of destinations) { console.log(`Deleting destination:`, destination); - await outpost.deleteDestination(organization.id, destination.id); + await outpost.destinations.delete(organization.id, destination.id); } - await outpost.deleteTenant(organization.id); + await outpost.tenants.delete(organization.id); } }; @@ -46,7 +43,7 @@ const migrateOrganizations = async () => { const migratedOrgIds: string[] = []; const organizations = db.getOrganizations(); for (const organization of organizations) { - await outpost.registerTenant(organization.id); + await outpost.tenants.upsert(organization.id); migratedOrgIds.push(organization.id); } return migratedOrgIds; @@ -55,12 +52,15 @@ const migrateOrganizations = async () => { const migrateSubscriptions = async (organizationId: string) => { const subscriptions = db.getSubscriptions(organizationId); for (const subscription of subscriptions) { - await outpost.createDestination({ - tenant_id: organizationId, + await outpost.destinations.create(organizationId, { type: "webhook", - url: subscription.url, + config: { + url: subscription.url, + }, topics: subscription.topics, - secret: subscription.secret, + credentials: { + secret: subscription.secret, + }, }); } }; @@ -76,8 +76,8 @@ const main = async () => { for (const organizationId of migratedOrgIds) { await migrateSubscriptions(organizationId); - const portalUrl = await outpost.getPortalURL(organizationId); - console.log(`Portal URL for ${organizationId}:`, portalUrl); + const portal = await outpost.tenants.getPortalUrl(organizationId); + console.log(`Portal URL for ${organizationId}:`, portal.redirectUrl ?? ""); } }; diff --git a/examples/demos/nodejs/src/portal-urls.ts b/examples/demos/nodejs/src/portal-urls.ts index 4bccb2dd0..4a1057d23 100644 --- a/examples/demos/nodejs/src/portal-urls.ts +++ b/examples/demos/nodejs/src/portal-urls.ts @@ -5,13 +5,13 @@ const main = async () => { const organizations = db.getOrganizations(); for (const org of organizations) { - const portalUrl = await outpost.getPortalURL(org.id); - console.log(`Portal URL for ${org.id}:`, portalUrl); + const portal = await outpost.tenants.getPortalUrl(org.id); + console.log(`Portal URL for ${org.id}:`, portal.redirectUrl ?? ""); } try { - const portalUrl = await outpost.getPortalURL("test-tenant"); - console.log(`Portal URL for test-tenant:`, portalUrl); + const portal = await outpost.tenants.getPortalUrl("test-tenant"); + console.log(`Portal URL for test-tenant:`, portal.redirectUrl ?? ""); } catch (error) { console.error(`Failed to create portal for test-tenant:`, error); } diff --git a/examples/demos/nodejs/src/publish-api.ts b/examples/demos/nodejs/src/publish-api.ts index cd4ca5e85..eeedbc2f5 100644 --- a/examples/demos/nodejs/src/publish-api.ts +++ b/examples/demos/nodejs/src/publish-api.ts @@ -7,25 +7,31 @@ const main = async () => { const organizations = db.getOrganizations(); for (const organization of organizations) { - const destinations = await outpost.getDestinations(organization.id); + const destinations = await outpost.destinations.list(organization.id); for (const destination of destinations) { + const topics = destination.topics; + const topic = + Array.isArray(topics) && topics.length > 0 + ? topics[0] + : "test.event"; + const data = { + test: "data", + from_organization_id: organization.id, + from_destination_id: destination.id, + timestamp: new Date().toISOString(), + }; const event = { - tenant_id: organization.id, - topic: destination.topics[0], - eligible_for_retry: true, - data: { - test: "data", - from_organization_id: organization.id, - from_destination_id: destination.id, - timestamp: new Date().toISOString(), - }, + tenantId: organization.id, + topic, + eligibleForRetry: true, + data, metadata: { some: "metadata", }, }; console.log("Publishing event"); console.log(event); - await outpost.publishEvent(event); + await outpost.publish.event(event); } } }; diff --git a/examples/demos/nodejs/tsconfig.json b/examples/demos/nodejs/tsconfig.json index a29104562..f75c72796 100644 --- a/examples/demos/nodejs/tsconfig.json +++ b/examples/demos/nodejs/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "node", + "module": "ESNext", + "moduleResolution": "bundler", "outDir": "./dist", "rootDir": "./src", "strict": true, From 7084d62d5bf1eb39109344f7ead6d6eab8a21ecd Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Tue, 31 Mar 2026 15:38:16 +0100 Subject: [PATCH 5/5] examples: fix paginated SDK list types (result.models) events.list and tenants.list return PageIterator payloads shaped as { result: { models } }; access result.models for TypeScript. Made-with: Cursor --- examples/demos/dashboard-integration/package-lock.json | 4 ++-- examples/demos/dashboard-integration/src/lib/outpost.ts | 6 +++--- examples/sdk-typescript/auth.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/demos/dashboard-integration/package-lock.json b/examples/demos/dashboard-integration/package-lock.json index 202ecc014..466dcbbc5 100644 --- a/examples/demos/dashboard-integration/package-lock.json +++ b/examples/demos/dashboard-integration/package-lock.json @@ -38,7 +38,7 @@ }, "../../../sdks/outpost-typescript": { "name": "@hookdeck/outpost-sdk", - "version": "0.7.3", + "version": "0.9.2", "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", "zod": "^3.25.0 || ^4.0.0" @@ -55,7 +55,7 @@ "eslint": "^9.26.0", "express": "^4.21.2", "globals": "^15.14.0", - "tshy": "^2.0.0", + "tshy": "^3.3.2", "typescript": "~5.8.3", "typescript-eslint": "^8.26.0" } diff --git a/examples/demos/dashboard-integration/src/lib/outpost.ts b/examples/demos/dashboard-integration/src/lib/outpost.ts index 953ed94a1..3ca43e814 100644 --- a/examples/demos/dashboard-integration/src/lib/outpost.ts +++ b/examples/demos/dashboard-integration/src/lib/outpost.ts @@ -69,11 +69,11 @@ export async function getTenantOverview(tenantId: string) { : []; logger.debug(`Destinations found`, { tenantId, count: destinations.length }); - // Get recent events (SDK v0.13: list returns { models, pagination }) + // Get recent events (PageIterator; items live under result.models) let recentEvents: any[] = []; try { - const eventsResponse = await outpost.events.list({ tenantId }); - const models = eventsResponse?.models ?? []; + const eventsPage = await outpost.events.list({ tenantId }); + const models = eventsPage.result?.models ?? []; recentEvents = models.slice(0, 10); logger.debug(`Events found`, { tenantId, count: recentEvents.length }); } catch (error) { diff --git a/examples/sdk-typescript/auth.ts b/examples/sdk-typescript/auth.ts index f322ffc37..d28f4c794 100644 --- a/examples/sdk-typescript/auth.ts +++ b/examples/sdk-typescript/auth.ts @@ -98,7 +98,7 @@ const withAdminApiKey = async () => { // List tenants (v0.14+: tenants.list(request) with a single request object) const tenantsPage = await outpost.tenants.list({ limit: 5 }); - console.log("Tenants (first page):", tenantsPage?.models ?? []); + console.log("Tenants (first page):", tenantsPage.result?.models ?? []); // Get portal URL (Admin API Key required) try {