@zuplo/cli 7.9.17 → 7.9.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/source/ai-gateway/catalog.json +170 -1
  2. package/dist/source/ai-gateway/planner.test.js +49 -1
  3. package/dist/source/ai-gateway/planner.test.js.map +1 -1
  4. package/node_modules/@posthog/core/dist/error-tracking/chunk-ids.d.ts.map +1 -1
  5. package/node_modules/@posthog/core/dist/error-tracking/chunk-ids.js +36 -22
  6. package/node_modules/@posthog/core/dist/error-tracking/chunk-ids.mjs +36 -22
  7. package/node_modules/@posthog/core/dist/error-tracking/release.d.ts +3 -5
  8. package/node_modules/@posthog/core/dist/error-tracking/release.d.ts.map +1 -1
  9. package/node_modules/@posthog/core/dist/error-tracking/release.js +4 -0
  10. package/node_modules/@posthog/core/dist/error-tracking/release.mjs +4 -0
  11. package/node_modules/@posthog/core/dist/featureFlagUtils.d.ts.map +1 -1
  12. package/node_modules/@posthog/core/dist/featureFlagUtils.js +1 -2
  13. package/node_modules/@posthog/core/dist/featureFlagUtils.mjs +1 -2
  14. package/node_modules/@posthog/core/package.json +2 -2
  15. package/node_modules/@posthog/core/src/error-tracking/chunk-ids.spec.ts +77 -0
  16. package/node_modules/@posthog/core/src/error-tracking/chunk-ids.ts +50 -25
  17. package/node_modules/@posthog/core/src/error-tracking/release.spec.ts +34 -0
  18. package/node_modules/@posthog/core/src/error-tracking/release.ts +10 -5
  19. package/node_modules/@posthog/core/src/featureFlagUtils.ts +0 -2
  20. package/node_modules/@zuplo/core/customer.cli.minified.js +1 -1
  21. package/node_modules/@zuplo/core/index.minified.js +1 -1
  22. package/node_modules/@zuplo/core/package.json +1 -1
  23. package/node_modules/@zuplo/graphql/package.json +1 -1
  24. package/node_modules/@zuplo/openapi-tools/package.json +1 -1
  25. package/node_modules/@zuplo/otel/package.json +1 -1
  26. package/node_modules/@zuplo/runtime/out/esm/{chunk-UJ56MYSD.js → chunk-6YPRGG62.js} +108 -107
  27. package/node_modules/@zuplo/runtime/out/esm/chunk-6YPRGG62.js.map +1 -0
  28. package/node_modules/@zuplo/runtime/out/esm/index.js +1 -1
  29. package/node_modules/@zuplo/runtime/out/esm/index.js.map +1 -1
  30. package/node_modules/@zuplo/runtime/out/esm/mcp-gateway/index.js +1 -1
  31. package/node_modules/@zuplo/runtime/out/esm/mocks/index.js +1 -1
  32. package/node_modules/@zuplo/runtime/out/types/index.d.ts +122 -10
  33. package/node_modules/@zuplo/runtime/package.json +1 -1
  34. package/node_modules/content-disposition/README.md +105 -35
  35. package/node_modules/content-disposition/dist/index.d.ts +72 -0
  36. package/node_modules/content-disposition/dist/index.js +388 -0
  37. package/node_modules/content-disposition/dist/index.js.map +1 -0
  38. package/node_modules/content-disposition/package.json +31 -19
  39. package/node_modules/express/Readme.md +15 -9
  40. package/node_modules/express/lib/application.js +2 -2
  41. package/node_modules/express/lib/request.js +32 -19
  42. package/node_modules/express/lib/response.js +27 -30
  43. package/node_modules/express/node_modules/content-type/LICENSE +22 -0
  44. package/node_modules/express/node_modules/content-type/README.md +71 -0
  45. package/node_modules/express/node_modules/content-type/dist/index.d.ts +46 -0
  46. package/node_modules/express/node_modules/content-type/dist/index.js +176 -0
  47. package/node_modules/express/node_modules/content-type/dist/index.js.map +1 -0
  48. package/node_modules/express/node_modules/content-type/package.json +52 -0
  49. package/node_modules/express/package.json +10 -10
  50. package/package.json +11 -11
  51. package/node_modules/@zuplo/runtime/out/esm/chunk-UJ56MYSD.js.map +0 -1
  52. package/node_modules/content-disposition/index.js +0 -536
  53. /package/node_modules/@zuplo/runtime/out/esm/{chunk-UJ56MYSD.js.LEGAL.txt → chunk-6YPRGG62.js.LEGAL.txt} +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "formatVersion": 1,
3
- "target": "256d7e12d04fba5317a0a6277edf7eb7f1e798385af6f75aed34426aa5b771f4",
3
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
4
4
  "snapshots": [
5
5
  {
6
6
  "revision": "f9f94ab6646b82abc66a167c95669a50b3e20a7dea224cac0d3893d4ddde6620",
@@ -76,6 +76,18 @@
76
76
  "tsconfig.json": "{\n \"include\": [\"modules/**/*\", \".zuplo/**/*\"],\n \"exclude\": [\"./node_modules\", \"./dist\", \"./docs\", \"./examples\"],\n \"compilerOptions\": {\n \"module\": \"ESNext\",\n \"target\": \"ES2022\",\n \"lib\": [\"ESNext\", \"WebWorker\", \"Webworker.Iterable\"],\n \"preserveConstEnums\": true,\n \"moduleResolution\": \"Bundler\",\n \"useUnknownInCatchVariables\": false,\n \"forceConsistentCasingInFileNames\": true,\n \"importHelpers\": true,\n \"removeComments\": true,\n \"esModuleInterop\": true,\n \"noEmit\": true,\n \"skipLibCheck\": true,\n \"strictNullChecks\": true,\n \"experimentalDecorators\": true\n }\n}\n",
77
77
  "zuplo.jsonc": "{\n \"version\": 1,\n \"compatibilityDate\": \"2026-03-01\"\n}\n"
78
78
  }
79
+ },
80
+ {
81
+ "revision": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
82
+ "sourceCommit": "8ada0f8048db911f5150c75dee5d450e3cd7b6c8",
83
+ "files": {
84
+ "README-template.md": "## Zuplo AI Gateway\n\nThis is a Zuplo AI Gateway that was created with\n[`create-zuplo-api`](https://zuplo.com/docs). It gives your applications one\nOpenAI-compatible API in front of many AI providers, with per-app keys, model\ncontrols, budgets, caching, and guardrails.\n\n## Getting Started\n\nThe gateway loads each app's configuration (providers, models, and policies)\nfrom your Zuplo account at request time, so the project must be linked to a\nZuplo project before it can serve requests.\n\n1. Link the project. This writes your project's settings to `.env.zuplo`, which\n is gitignored:\n\n ```bash\n npx zuplo link\n ```\n\n2. In the [Zuplo Portal](https://portal.zuplo.com), add a provider and create an\n app. Each app gets its own API URL and API key. See\n [Apps](https://zuplo.com/docs/ai-gateway/apps).\n\n3. Start the development server:\n\n ```bash\n npm run dev\n # or\n yarn dev\n # or\n pnpm dev\n ```\n\n4. Send a request to the local gateway. Replace `<app_id>` with the ID from your\n app's API URL and set `ZUPLO_APP_API_KEY` to the app's API key:\n\n ```bash\n curl http://localhost:9000/<app_id>/v1/chat/completions \\\n -H \"Authorization: Bearer $ZUPLO_APP_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"model\": \"openai/gpt-4o-mini\",\n \"messages\": [{ \"role\": \"user\", \"content\": \"Say hi\" }]\n }'\n ```\n\n## Endpoints\n\nEvery request is scoped to an app by the first path segment, `/{app_id}`. For\nthe supported endpoints and which providers serve each one, see the\n[AI Gateway documentation](https://zuplo.com/docs/ai-gateway/overview).\n\n## Project Structure\n\n| Path | What it does |\n| -------------------------- | ---------------------------------------------------------------------- |\n| `config/ai.oas.json` | The catch-all route that sends `/{app_id}/*` to the AI Gateway handler |\n| `config/policies.json` | The policies an app can select for its policy chain |\n| `modules/zuplo.runtime.ts` | Runtime plugins, such as tracing and logging |\n| `.env.example` | Sample environment variables. Copy it to `.env` for local development |\n\nAn app runs only the policies listed in its policy chain, and it can select only\npolicies declared in `config/policies.json`. To add your own policy, write it in\n`modules/` and declare it in `config/policies.json`.\n\n## Debugging\n\nIn VS Code, open **Run and Debug**, select **Launch & Attach Zuplo**, and click\nthe green play button.\n\nFor other editors and more details, see the\n[debugging guide](https://zuplo.com/docs/articles/local-development-debugging).\n\n## Deploying\n\nConnect the project to source control in the Zuplo Portal. Pushes to your\ndefault branch deploy the gateway to production.\n\n## Learn More\n\nTo learn more about the AI Gateway, visit the\n[AI Gateway documentation](https://zuplo.com/docs/ai-gateway/overview).\n\nTo connect with the community join [Discord](https://discord.zuplo.com).\n",
85
+ "config/ai.oas.json": "{\n \"openapi\": \"3.1.0\",\n \"info\": {\n \"version\": \"1.0.0\",\n \"title\": \"AI Gateway API\",\n \"description\": \"A unified API gateway for multiple AI providers. OpenAI-compatible ops are served under /{app_id}/v1/*, AWS Bedrock Runtime ops under /{app_id}/model/{modelId}/{operation}; other paths under /{app_id} are rejected.\"\n },\n \"paths\": {\n \"/u/:app_id/(.*)\": {\n \"x-zuplo-path\": {\n \"pathMode\": \"url-pattern\"\n },\n \"get,post,put,patch,delete,head\": {\n \"summary\": \"AI Gateway User App catch-all\",\n \"description\": \"Routes User App requests under /u/{app_id}/* to the AI Gateway handler. The configuration kind must be ai-user-app.\",\n \"operationId\": \"aiGatewayUserAppCatchAll\",\n \"responses\": {\n \"200\": {\n \"description\": \"Successful AI Gateway response\"\n },\n \"400\": {\n \"description\": \"Bad request\"\n },\n \"403\": {\n \"description\": \"Forbidden \\u2014 User App routes require a personal API key issued for this User App; application API keys cannot authenticate them\"\n },\n \"404\": {\n \"description\": \"Unsupported AI Gateway endpoint\"\n },\n \"500\": {\n \"description\": \"Internal server error\"\n }\n },\n \"x-zuplo-route\": {\n \"corsPolicy\": \"none\",\n \"handler\": {\n \"export\": \"aiGatewayHandlerV2\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {}\n },\n \"policies\": {\n \"inbound\": [\n \"ai-gateway-configuration-loader-inbound\",\n \"ai-gateway-configuration-executor-inbound\"\n ]\n }\n }\n }\n },\n \"/a/:app_id/(.*)\": {\n \"x-zuplo-path\": {\n \"pathMode\": \"url-pattern\"\n },\n \"get,post,put,patch,delete,head\": {\n \"summary\": \"AI Gateway AI App catch-all\",\n \"description\": \"Routes AI App requests under /a/{app_id}/* to the AI Gateway handler. The configuration kind must be ai-app.\",\n \"operationId\": \"aiGatewayAIAppCatchAll\",\n \"responses\": {\n \"200\": {\n \"description\": \"Successful AI Gateway response\"\n },\n \"400\": {\n \"description\": \"Bad request\"\n },\n \"401\": {\n \"description\": \"Unauthorized \\u2014 returned only when the application's inboundPolicyChain includes ai-gateway-auth-inbound, or authentication runs before the configuration loader\"\n },\n \"403\": {\n \"description\": \"Forbidden \\u2014 returned by per-app policies, such as an API key issued for a different app_id or a model the app does not allow\"\n },\n \"404\": {\n \"description\": \"Unsupported AI Gateway endpoint\"\n },\n \"500\": {\n \"description\": \"Internal server error\"\n }\n },\n \"x-zuplo-route\": {\n \"corsPolicy\": \"none\",\n \"handler\": {\n \"export\": \"aiGatewayHandlerV2\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {}\n },\n \"policies\": {\n \"inbound\": [\n \"ai-gateway-configuration-loader-inbound\",\n \"ai-gateway-configuration-executor-inbound\"\n ]\n }\n }\n }\n },\n \"/:app_id/(.*)\": {\n \"x-zuplo-path\": {\n \"pathMode\": \"url-pattern\"\n },\n \"get,post,put,patch,delete,head\": {\n \"summary\": \"AI Gateway catch-all\",\n \"description\": \"Routes AI requests under /{app_id}/* to the AI Gateway handler. The configuration loader loads the application configuration from the app_id path segment; the configuration executor then runs the application's inboundPolicyChain. Two path shapes succeed: a supported /v1/... operation (see resolveBasePath in packages/runtime/src/llm-translation/index.ts for the current set) and a Bedrock Runtime operation addressed AWS's way as /model/{modelId}/{operation} (see matchBedrockRuntimeRoute in packages/runtime/src/ai-gateway/bedrock-runtime-route.ts). Anything else under /{app_id} returns 404.\",\n \"operationId\": \"aiGatewayCatchAll\",\n \"responses\": {\n \"200\": {\n \"description\": \"Successful AI Gateway response\"\n },\n \"400\": {\n \"description\": \"Bad request\"\n },\n \"401\": {\n \"description\": \"Unauthorized \\u2014 returned only when the application's inboundPolicyChain includes ai-gateway-auth-inbound, or authentication runs before the configuration loader\"\n },\n \"403\": {\n \"description\": \"Forbidden \\u2014 returned by per-app policies, such as an API key issued for a different app_id or a model the app does not allow\"\n },\n \"404\": {\n \"description\": \"Unsupported AI Gateway endpoint\"\n },\n \"500\": {\n \"description\": \"Internal server error\"\n }\n },\n \"x-zuplo-route\": {\n \"corsPolicy\": \"none\",\n \"handler\": {\n \"export\": \"aiGatewayHandlerV2\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {}\n },\n \"policies\": {\n \"inbound\": [\n \"ai-gateway-configuration-loader-inbound\",\n \"ai-gateway-configuration-executor-inbound\"\n ]\n }\n }\n }\n }\n }\n}\n",
86
+ "config/policies.json": "{\n \"policies\": [\n {\n \"handler\": {\n \"export\": \"AIGatewayAuthInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {}\n },\n \"name\": \"ai-gateway-auth-inbound\",\n \"policyType\": \"ai-gateway-auth\"\n },\n {\n \"handler\": {\n \"export\": \"InternalOnlyInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {}\n },\n \"name\": \"internal-only-inbound\",\n \"policyType\": \"internal-only\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayConfigurationLoaderInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"cacheTtlSeconds\": 5\n }\n },\n \"name\": \"ai-gateway-configuration-loader-inbound\",\n \"policyType\": \"ai-gateway-configuration-loader\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayConfigurationExecutorInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {}\n },\n \"name\": \"ai-gateway-configuration-executor-inbound\",\n \"policyType\": \"ai-gateway-configuration-executor\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayMeteringInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {}\n },\n \"name\": \"ai-gateway-metering-inbound\",\n \"policyType\": \"ai-gateway-metering\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayModelOverrideInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"models\": {\n \"completions\": {\n \"default\": \"openai/gpt-4o-mini\"\n }\n }\n }\n },\n \"name\": \"ai-gateway-model-override-inbound\",\n \"policyType\": \"ai-gateway-model-override\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayModelFilteringInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"models\": {\n \"completions\": {\n \"allowList\": [\"openai/gpt-4o-mini\"]\n }\n }\n }\n },\n \"name\": \"ai-gateway-model-filtering-inbound\",\n \"policyType\": \"ai-gateway-model-filtering\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayLoadBalancingInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"strategy\": \"round-robin\",\n \"models\": {\n \"completions\": [\"openai/gpt-4o-mini\"]\n }\n }\n },\n \"name\": \"ai-gateway-load-balancing-inbound\",\n \"policyType\": \"ai-gateway-load-balancing\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayFallbackModelInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"models\": {\n \"completions\": {\n \"fallback\": \"openai/gpt-4o-mini\"\n }\n },\n \"fallbackTimeoutSeconds\": 60\n }\n },\n \"name\": \"ai-gateway-fallback-model-inbound\",\n \"policyType\": \"ai-gateway-fallback-model\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewaySemanticCacheInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"semanticTolerance\": 0.4,\n \"expirationSecondsTtl\": 3600\n }\n },\n \"name\": \"ai-gateway-semantic-cache-inbound\",\n \"policyType\": \"ai-gateway-semantic-cache\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayAkamaiFirewallInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"configurationId\": \"\",\n \"api-key\": \"\"\n }\n },\n \"name\": \"ai-gateway-akamai-firewall-inbound\",\n \"policyType\": \"ai-gateway-akamai-firewall\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayPromptInjectionPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"execution\": \"parallel\"\n }\n },\n \"name\": \"ai-gateway-prompt-injection\",\n \"policyType\": \"ai-gateway-prompt-injection\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayOpikTracingInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"apiKey\": \"\",\n \"projectName\": \"\",\n \"workspace\": \"\"\n }\n },\n \"name\": \"ai-gateway-opik-tracing-inbound\",\n \"policyType\": \"ai-gateway-opik-tracing\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayGalileoTracingInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"apiKey\": \"\",\n \"projectId\": \"\",\n \"logStreamId\": \"\"\n }\n },\n \"name\": \"ai-gateway-galileo-tracing-inbound\",\n \"policyType\": \"ai-gateway-galileo-tracing\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewayDlpInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"rules\": {\n \"inbound\": {\n \"pii\": {\n \"action\": \"mask\"\n },\n \"contact-email\": {\n \"action\": \"off\"\n },\n \"finance-credit-card\": {\n \"action\": \"mask\"\n },\n \"finance-iban\": {\n \"action\": \"mask\"\n }\n },\n \"outbound\": {\n \"pii\": {\n \"action\": \"mask\"\n },\n \"contact-email\": {\n \"action\": \"off\"\n },\n \"finance-credit-card\": {\n \"action\": \"mask\"\n },\n \"finance-iban\": {\n \"action\": \"mask\"\n }\n }\n }\n }\n },\n \"name\": \"ai-gateway-dlp-inbound\",\n \"policyType\": \"ai-gateway-dlp\"\n },\n {\n \"handler\": {\n \"export\": \"AIGatewaySmartRouterInboundPolicy\",\n \"module\": \"$import(@zuplo/runtime)\",\n \"options\": {\n \"classifier\": {\n \"app\": {\n \"appId\": \"\",\n \"model\": \"openai/gpt-4o-mini\"\n }\n },\n \"modelsByComplexity\": {\n \"low\": \"openai/gpt-4o-mini\",\n \"medium\": \"openai/gpt-4o\",\n \"high\": \"openai/gpt-5\"\n }\n }\n },\n \"name\": \"ai-gateway-smart-router-inbound\",\n \"policyType\": \"ai-gateway-smart-router\"\n }\n ]\n}\n",
87
+ "modules/zuplo.runtime.ts": "import type { RuntimeExtensions } from \"@zuplo/runtime\";\n\n/**\n * `runtimeInit` runs once when your gateway boots. Use it to register plugins\n * and lifecycle hooks. Docs:\n * https://zuplo.com/docs/programmable-api/runtime-extensions\n */\nexport function runtimeInit(runtime: RuntimeExtensions) {\n // `runtime` is unused until you enable a plugin below. This reference keeps\n // linters from flagging it; delete it once you call `runtime.addPlugin`.\n void runtime;\n\n // --- OpenTelemetry tracing (optional) ------------------------------------\n // Send traces to Zuplo's built-in tracing. This can also be configured to\n // send traces to a third-party service such as Honeycomb, Grafana, and\n // others. Docs: https://zuplo.com/docs/articles/opentelemetry\n //\n // To enable, run `npm install @zuplo/otel`, then import `OpenTelemetryPlugin`\n // from \"@zuplo/otel\" and `environment` from \"@zuplo/runtime\":\n // const isWorkingCopy =\n // environment.ZUPLO_ENVIRONMENT_STAGE === \"working-copy\";\n // runtime.addPlugin(\n // new OpenTelemetryPlugin({\n // sampling: {\n // headSampler: { ratio: isWorkingCopy ? 1 : 0.1 },\n // },\n // }),\n // );\n // --- Logging (optional) --------------------------------------------------\n // Ship request logs to Datadog. Other log integrations (New Relic, Splunk,\n // Loki, Dynatrace, and others) follow the same pattern — see the logging\n // overview at https://zuplo.com/docs/articles/logging.\n // Docs: https://zuplo.com/docs/articles/log-plugin-datadog\n //\n // To enable, import the plugin and `environment` from \"@zuplo/runtime\":\n // runtime.addPlugin(\n // new DataDogLoggingPlugin({\n // apiKey: environment.DATADOG_API_KEY,\n // source: \"my-ai-gateway\",\n // }),\n // );\n}\n",
88
+ "tsconfig.json": "{\n \"include\": [\"modules/**/*\", \".zuplo/**/*\"],\n \"exclude\": [\"./node_modules\", \"./dist\", \"./docs\", \"./examples\"],\n \"compilerOptions\": {\n \"module\": \"ESNext\",\n \"target\": \"ES2022\",\n \"lib\": [\"ESNext\", \"WebWorker\", \"Webworker.Iterable\"],\n \"preserveConstEnums\": true,\n \"moduleResolution\": \"Bundler\",\n \"useUnknownInCatchVariables\": false,\n \"forceConsistentCasingInFileNames\": true,\n \"importHelpers\": true,\n \"removeComments\": true,\n \"esModuleInterop\": true,\n \"noEmit\": true,\n \"skipLibCheck\": true,\n \"strictNullChecks\": true,\n \"experimentalDecorators\": true\n }\n}\n",
89
+ "zuplo.jsonc": "{\n \"version\": 1,\n \"compatibilityDate\": \"2026-03-01\"\n}\n"
90
+ }
79
91
  }
80
92
  ],
81
93
  "aliases": {
@@ -394,6 +406,158 @@
394
406
  "target": "256d7e12d04fba5317a0a6277edf7eb7f1e798385af6f75aed34426aa5b771f4",
395
407
  "runtime": "release",
396
408
  "instructions": "Review the definition and application policy chains before adding it."
409
+ },
410
+ {
411
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:route:/u/:app_id/(.*)",
412
+ "kind": "route",
413
+ "key": "/u/:app_id/(.*)",
414
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
415
+ "runtime": "release",
416
+ "instructions": "Insert before /:app_id/(.*), with the displayed inbound policy order."
417
+ },
418
+ {
419
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:route:/a/:app_id/(.*)",
420
+ "kind": "route",
421
+ "key": "/a/:app_id/(.*)",
422
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
423
+ "runtime": "release",
424
+ "instructions": "Insert before /:app_id/(.*), with the displayed inbound policy order."
425
+ },
426
+ {
427
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:route:/:app_id/(.*)",
428
+ "kind": "route",
429
+ "key": "/:app_id/(.*)",
430
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
431
+ "runtime": "release",
432
+ "instructions": "Review the definition and application policy chains before adding it."
433
+ },
434
+ {
435
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-auth-inbound",
436
+ "kind": "policy",
437
+ "key": "ai-gateway-auth-inbound",
438
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
439
+ "runtime": "release",
440
+ "instructions": "Review the definition and application policy chains before adding it."
441
+ },
442
+ {
443
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-internal-only-inbound",
444
+ "kind": "policy",
445
+ "key": "internal-only-inbound",
446
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
447
+ "runtime": "release",
448
+ "instructions": "Add only if neither internal-only-inbound nor its historical AI Gateway alias is declared. Preserve existing declarations and application chain references."
449
+ },
450
+ {
451
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-configuration-loader-inbound",
452
+ "kind": "policy",
453
+ "key": "ai-gateway-configuration-loader-inbound",
454
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
455
+ "runtime": "release",
456
+ "instructions": "Review the definition and application policy chains before adding it."
457
+ },
458
+ {
459
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-configuration-executor-inbound",
460
+ "kind": "policy",
461
+ "key": "ai-gateway-configuration-executor-inbound",
462
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
463
+ "runtime": "release",
464
+ "instructions": "Review the definition and application policy chains before adding it."
465
+ },
466
+ {
467
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-metering-inbound",
468
+ "kind": "policy",
469
+ "key": "ai-gateway-metering-inbound",
470
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
471
+ "runtime": "release",
472
+ "instructions": "Review the definition and application policy chains before adding it."
473
+ },
474
+ {
475
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-model-override-inbound",
476
+ "kind": "policy",
477
+ "key": "ai-gateway-model-override-inbound",
478
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
479
+ "runtime": "release",
480
+ "instructions": "Review the definition and application policy chains before adding it."
481
+ },
482
+ {
483
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-model-filtering-inbound",
484
+ "kind": "policy",
485
+ "key": "ai-gateway-model-filtering-inbound",
486
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
487
+ "runtime": "release",
488
+ "instructions": "Review the definition and application policy chains before adding it."
489
+ },
490
+ {
491
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-load-balancing-inbound",
492
+ "kind": "policy",
493
+ "key": "ai-gateway-load-balancing-inbound",
494
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
495
+ "runtime": "release",
496
+ "instructions": "Add only if it is not declared. No route or app uses it until an app's inboundPolicyChain names it; review the default models list before an app does."
497
+ },
498
+ {
499
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-fallback-model-inbound",
500
+ "kind": "policy",
501
+ "key": "ai-gateway-fallback-model-inbound",
502
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
503
+ "runtime": "release",
504
+ "instructions": "Review the definition and application policy chains before adding it."
505
+ },
506
+ {
507
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-semantic-cache-inbound",
508
+ "kind": "policy",
509
+ "key": "ai-gateway-semantic-cache-inbound",
510
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
511
+ "runtime": "release",
512
+ "instructions": "Review the definition and application policy chains before adding it."
513
+ },
514
+ {
515
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-akamai-firewall-inbound",
516
+ "kind": "policy",
517
+ "key": "ai-gateway-akamai-firewall-inbound",
518
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
519
+ "runtime": "release",
520
+ "instructions": "Review the definition and application policy chains before adding it."
521
+ },
522
+ {
523
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-prompt-injection",
524
+ "kind": "policy",
525
+ "key": "ai-gateway-prompt-injection",
526
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
527
+ "runtime": "release",
528
+ "instructions": "Review the definition and application policy chains before adding it."
529
+ },
530
+ {
531
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-opik-tracing-inbound",
532
+ "kind": "policy",
533
+ "key": "ai-gateway-opik-tracing-inbound",
534
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
535
+ "runtime": "release",
536
+ "instructions": "Review the definition and application policy chains before adding it."
537
+ },
538
+ {
539
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-galileo-tracing-inbound",
540
+ "kind": "policy",
541
+ "key": "ai-gateway-galileo-tracing-inbound",
542
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
543
+ "runtime": "release",
544
+ "instructions": "Review the definition and application policy chains before adding it."
545
+ },
546
+ {
547
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-dlp-inbound",
548
+ "kind": "policy",
549
+ "key": "ai-gateway-dlp-inbound",
550
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
551
+ "runtime": "release",
552
+ "instructions": "Review the definition and application policy chains before adding it."
553
+ },
554
+ {
555
+ "id": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d:policy:ai-gateway-smart-router-inbound",
556
+ "kind": "policy",
557
+ "key": "ai-gateway-smart-router-inbound",
558
+ "target": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
559
+ "runtime": "release",
560
+ "instructions": "Review the definition and application policy chains before adding it."
397
561
  }
398
562
  ],
399
563
  "assessments": [
@@ -406,6 +570,11 @@
406
570
  "revision": "256d7e12d04fba5317a0a6277edf7eb7f1e798385af6f75aed34426aa5b771f4",
407
571
  "classification": "additions",
408
572
  "instructions": "The Internal Only policy now uses internal-only-inbound, InternalOnlyInboundPolicy, and policyType internal-only. Existing AI Gateway names remain supported runtime aliases and are preserved; do not add a duplicate declaration or automatically rename existing configuration. Missing declarations use the new name. Other existing policy options and defaults still require separate review."
573
+ },
574
+ {
575
+ "revision": "70b476fc2524c3c2ce0cc10ad5f91837a7aefceefb088542390c02754238693d",
576
+ "classification": "additions",
577
+ "instructions": "Adds the Model Load Balancing policy declaration, ai-gateway-load-balancing-inbound (AIGatewayLoadBalancingInboundPolicy, policyType ai-gateway-load-balancing). Declaring it changes no routing: an app uses it only when its inboundPolicyChain names it. Existing routes, declarations, and historical aliases are preserved. Other existing policy options and defaults still require separate review."
409
578
  }
410
579
  ]
411
580
  }
@@ -3,7 +3,21 @@ import { describe, it } from "node:test";
3
3
  import { AI_OAS, catalog, POLICIES, PROVENANCE, planUpgrade, readJson, } from "./planner.js";
4
4
  const oldest = catalog.snapshots[1];
5
5
  const original = catalog.snapshots[2];
6
+ const internalOnlyRename = catalog.snapshots[5];
7
+ const loadBalancing = catalog.snapshots[6];
6
8
  const latest = catalog.snapshots.find((s) => s.revision === catalog.target);
9
+ const LOAD_BALANCING = "ai-gateway-load-balancing-inbound";
10
+ function releaseCatalog(revision) {
11
+ const end = catalog.snapshots.findIndex((s) => s.revision === revision) + 1;
12
+ const shipped = new Set(catalog.snapshots.slice(0, end).map((s) => s.revision));
13
+ return structuredClone({
14
+ ...catalog,
15
+ target: revision,
16
+ snapshots: catalog.snapshots.slice(0, end),
17
+ steps: catalog.steps.filter((s) => shipped.has(s.target)),
18
+ assessments: catalog.assessments.filter((a) => shipped.has(a.revision)),
19
+ });
20
+ }
7
21
  const files = () => ({ ...original.files });
8
22
  function change(file, mutate) {
9
23
  const input = files();
@@ -58,7 +72,7 @@ describe("conservative versioned gateway planner", () => {
58
72
  });
59
73
  it("preserves the previous template after the Internal Only rename", () => {
60
74
  const previous = catalog.snapshots[4];
61
- const result = planUpgrade(previous.files, "1.0.0");
75
+ const result = planUpgrade(previous.files, "1.0.0", releaseCatalog(internalOnlyRename.revision));
62
76
  assert.deepEqual(result.blockers, []);
63
77
  assert.deepEqual(result.additions, []);
64
78
  assert.deepEqual(result.after, {});
@@ -66,6 +80,40 @@ describe("conservative versioned gateway planner", () => {
66
80
  assert.deepEqual(historical.blockers, []);
67
81
  assert.ok(!historical.additions.some((a) => a.key === "internal-only-inbound"));
68
82
  });
83
+ it("adds only the Model Load Balancing declaration after the Internal Only rename", () => {
84
+ const release = releaseCatalog(loadBalancing.revision);
85
+ const declaration = JSON.parse(loadBalancing.files[POLICIES]).policies.find((p) => p.name === LOAD_BALANCING);
86
+ for (const previous of [catalog.snapshots[4], internalOnlyRename]) {
87
+ const result = planUpgrade(previous.files, "1.0.0", release);
88
+ assert.deepEqual(result.blockers, []);
89
+ assert.deepEqual(result.additions.map((a) => a.key), [LOAD_BALANCING]);
90
+ assert.equal(result.after[AI_OAS], undefined);
91
+ const before = JSON.parse(previous.files[POLICIES]).policies;
92
+ const after = JSON.parse(result.after[POLICIES]).policies;
93
+ assert.deepEqual(after.slice(0, -1), before);
94
+ assert.deepEqual(after.at(-1), declaration);
95
+ const again = planUpgrade({ ...previous.files, ...result.after }, "1.0.0", release);
96
+ assert.deepEqual(again.blockers, []);
97
+ assert.deepEqual(again.additions, []);
98
+ assert.deepEqual(again.after, {});
99
+ }
100
+ });
101
+ it("upgrades a project that the Internal Only release already upgraded", () => {
102
+ const first = planUpgrade(original.files, "1.0.0", releaseCatalog(internalOnlyRename.revision));
103
+ assert.deepEqual(first.blockers, []);
104
+ const next = planUpgrade({ ...original.files, ...first.after }, "2.0.0", releaseCatalog(loadBalancing.revision));
105
+ assert.deepEqual(next.blockers, []);
106
+ assert.deepEqual(next.additions.map((a) => a.key), [LOAD_BALANCING]);
107
+ assert.ok(JSON.parse(next.after[PROVENANCE]).appliedSteps.includes(`${loadBalancing.revision}:policy:${LOAD_BALANCING}`));
108
+ });
109
+ it("does not restore a deliberately removed Model Load Balancing declaration", () => {
110
+ const first = planUpgrade(internalOnlyRename.files, "1.0.0", releaseCatalog(loadBalancing.revision));
111
+ const input = { ...internalOnlyRename.files, ...first.after };
112
+ const policies = JSON.parse(input[POLICIES]);
113
+ policies.policies = policies.policies.filter((p) => p.name !== LOAD_BALANCING);
114
+ input[POLICIES] = JSON.stringify(policies);
115
+ blocked(input, /ai-gateway-load-balancing-inbound.*previously present/);
116
+ });
69
117
  it("does not restore a deliberately removed Internal Only policy under its new name", () => {
70
118
  const previous = catalog.snapshots[4];
71
119
  const input = {
@@ -1 +1 @@
1
- {"version":3,"file":"planner.test.js","sourceRoot":"","sources":["../../../src/source/ai-gateway/planner.test.ts"],"names":[],"mappings":"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","sourcesContent":["// biome-ignore-all lint/suspicious/noExplicitAny: Tests deliberately mutate fixture configuration shapes.\nimport assert from \"node:assert/strict\";\nimport { describe, it } from \"node:test\";\nimport {\n AI_OAS,\n catalog,\n POLICIES,\n PROVENANCE,\n planUpgrade,\n readJson,\n} from \"./planner.js\";\n\nconst oldest = catalog.snapshots[1];\nconst original = catalog.snapshots[2];\nconst latest = catalog.snapshots.find((s) => s.revision === catalog.target)!;\nconst files = () => ({ ...original.files });\nfunction change(file: string, mutate: (value: any) => void) {\n const input = files();\n const value = JSON.parse(input[file]);\n mutate(value);\n input[file] = JSON.stringify(value, null, 2);\n return input;\n}\nfunction blocked(input: Record<string, string>, expected: RegExp) {\n const before = structuredClone(input);\n const result = planUpgrade(input, \"1.0.0\");\n assert.match(result.blockers.join(\"\\n\"), expected);\n assert.deepEqual(result.after, {});\n assert.deepEqual(input, before);\n}\n\ndescribe(\"conservative versioned gateway planner\", () => {\n it(\"upgrades several historical snapshots without changing existing definitions\", () => {\n for (const snapshot of catalog.snapshots.slice(0, 4)) {\n const result = planUpgrade(snapshot.files, \"1.0.0\");\n assert.deepEqual(result.blockers, []);\n assert.ok(result.additions.length);\n const before = JSON.parse(snapshot.files[AI_OAS]);\n const after = JSON.parse(result.after[AI_OAS] ?? snapshot.files[AI_OAS]);\n for (const [key, value] of Object.entries(before.paths))\n assert.deepEqual(after.paths[key], value);\n assert.deepEqual(Object.keys(after.paths), [\n \"/u/:app_id/(.*)\",\n \"/a/:app_id/(.*)\",\n \"/:app_id/(.*)\",\n ]);\n const oldPolicies = JSON.parse(snapshot.files[POLICIES]).policies;\n const newPolicies = JSON.parse(\n result.after[POLICIES] ?? snapshot.files[POLICIES]\n ).policies;\n assert.deepEqual(newPolicies.slice(0, oldPolicies.length), oldPolicies);\n const again = planUpgrade(\n { ...snapshot.files, ...result.after },\n \"1.0.0\"\n );\n assert.deepEqual(again.blockers, []);\n assert.deepEqual(again.additions, []);\n assert.deepEqual(again.after, {});\n }\n });\n it(\"preserves the original loader defaults and rejects customized historical loaders\", () => {\n const historical = catalog.snapshots[0];\n const input = { ...historical.files };\n const result = planUpgrade(input, \"1.0.0\");\n assert.deepEqual(result.blockers, []);\n const loader = (raw: string) =>\n JSON.parse(raw).policies.find(\n (p: any) => p.name === \"ai-gateway-configuration-loader-v2-inbound\"\n );\n assert.deepEqual(loader(result.after[POLICIES]).handler.options, {});\n assert.deepEqual(loader(result.after[POLICIES]), loader(input[POLICIES]));\n const policies = JSON.parse(input[POLICIES]);\n policies.policies.find(\n (p: any) => p.name === \"ai-gateway-configuration-loader-v2-inbound\"\n ).handler.options.cacheTtlSeconds = 45;\n input[POLICIES] = JSON.stringify(policies);\n blocked(input, /required policy.*customized/);\n });\n it(\"preserves the previous template after the Internal Only rename\", () => {\n const previous = catalog.snapshots[4];\n const result = planUpgrade(previous.files, \"1.0.0\");\n assert.deepEqual(result.blockers, []);\n assert.deepEqual(result.additions, []);\n assert.deepEqual(result.after, {});\n const historical = planUpgrade(files(), \"1.0.0\");\n assert.deepEqual(historical.blockers, []);\n assert.ok(\n !historical.additions.some((a) => a.key === \"internal-only-inbound\")\n );\n });\n it(\"does not restore a deliberately removed Internal Only policy under its new name\", () => {\n const previous = catalog.snapshots[4];\n const input: Record<string, string> = {\n ...previous.files,\n [PROVENANCE]: JSON.stringify({\n formatVersion: 1,\n template: \"ai-gateway\",\n origin: { revision: previous.revision, release: \"1.0.0\" },\n appliedSteps: [],\n }),\n };\n const policies = JSON.parse(input[POLICIES]);\n policies.policies = policies.policies.filter(\n (p: any) => p.name !== \"ai-gateway-internal-only-inbound\"\n );\n input[POLICIES] = JSON.stringify(policies);\n blocked(input, /internal-only-inbound.*previously present/);\n });\n it(\"reuses historical dependency names rather than adding aliases\", () => {\n const result = planUpgrade(files(), \"1.0.0\");\n const after = JSON.parse(result.after[AI_OAS]);\n const inbound =\n after.paths[\"/u/:app_id/(.*)\"][\"get,post,put,patch,delete,head\"][\n \"x-zuplo-route\"\n ].policies.inbound;\n assert.deepEqual(inbound, [\n \"ai-gateway-configuration-loader-v2-inbound\",\n \"ai-gateway-configuration-executor-v2-inbound\",\n ]);\n assert.equal(\n result.additions.filter((a) => a.key.includes(\"configuration-loader\"))\n .length,\n 0\n );\n });\n it(\"does not modify an up-to-date project's bytes or create provenance\", () => {\n assert.deepEqual(planUpgrade(latest.files, \"1.0.0\").after, {});\n });\n it(\"preserves unrelated policy text and whitespace\", () => {\n const input = files();\n assert.ok(input[POLICIES].startsWith(\"{\"));\n input[POLICIES] = `{\\n\\n${input[POLICIES].slice(1)}`;\n const result = planUpgrade(input, \"1.0.0\");\n assert.deepEqual(result.blockers, []);\n assert.ok(result.after[POLICIES].startsWith(\"{\\n\\n\"));\n });\n it(\"blocks customized catch-all authentication\", () => {\n blocked(\n change(AI_OAS, (v) =>\n v.paths[\"/:app_id/(.*)\"][\"get,post,put,patch,delete,head\"][\n \"x-zuplo-route\"\n ].policies.inbound.unshift(\"my-auth\")\n ),\n /custom authentication/\n );\n });\n it(\"blocks reordered prerequisite chains\", () => {\n blocked(\n change(AI_OAS, (v) =>\n v.paths[\"/:app_id/(.*)\"][\"get,post,put,patch,delete,head\"][\n \"x-zuplo-route\"\n ].policies.inbound.reverse()\n ),\n /supported template/\n );\n });\n it(\"blocks customized required policies\", () => {\n blocked(\n change(POLICIES, (v) => {\n v.policies.find((p: any) =>\n p.name.includes(\"configuration-loader\")\n ).handler.options.cacheTtlSeconds = 45;\n }),\n /required policy.*customized/\n );\n });\n it(\"blocks ambiguous canonical policy aliases\", () => {\n blocked(\n change(POLICIES, (v) =>\n v.policies.push({\n ...v.policies.find((p: any) =>\n p.name.includes(\"configuration-loader\")\n ),\n name: \"ai-gateway-configuration-loader-inbound\",\n })\n ),\n /duplicate policy identity/\n );\n });\n it(\"blocks cross-file overlapping and unsupported routes but allows disjoint literal prefixes\", () => {\n for (const key of [\"/u/custom\", \"/:prefix/foo\", \"/(.*)\"]) {\n blocked(\n {\n ...files(),\n \"config/custom.oas.json\": JSON.stringify({\n openapi: \"3.1.0\",\n paths: { [key]: { get: { operationId: \"custom\" } } },\n }),\n },\n /cannot prove route/\n );\n }\n const result = planUpgrade(\n {\n ...files(),\n \"config/custom.oas.json\": JSON.stringify({\n openapi: \"3.1.0\",\n paths: { \"/health\": { get: { operationId: \"health\" } } },\n }),\n },\n \"1.0.0\"\n );\n assert.deepEqual(result.blockers, []);\n });\n for (const method of [\"get\", \"GET,POST\", \"Get,pOsT\", \" GET, POST \"]) {\n it(`blocks duplicate operation IDs across files for ${method}`, () => {\n blocked(\n {\n ...files(),\n \"config/custom.oas.json\": JSON.stringify({\n openapi: \"3.1.0\",\n paths: {\n \"/health\": {\n [method]: { operationId: \"aiGatewayUserAppCatchAll\" },\n },\n },\n }),\n },\n /operationId.*already in use/\n );\n });\n }\n it(\"blocks custom runtime extensions and document-level security\", () => {\n blocked(\n {\n ...files(),\n \"modules/zuplo.runtime.ts\":\n \"export function runtimeInit(runtime) { runtime.addPlugin(custom); }\",\n },\n /custom runtime/\n );\n blocked(\n change(AI_OAS, (v) => {\n v.security = [{ apiKey: [] }];\n }),\n /document-level/\n );\n });\n it(\"blocks malformed JSON and duplicate keys, retaining manual snippets\", () => {\n for (const text of ['{\"paths\":', '{\"policies\": [], \"policies\": []}']) {\n const result = planUpgrade({ ...files(), [POLICIES]: text }, \"1.0.0\");\n assert.ok(result.blockers.length);\n assert.ok(result.additions.length);\n assert.deepEqual(result.after, {});\n }\n assert.throws(() => readJson('{\"a\":{\"b\":1,\"b\":2}}', \"x\"), /duplicate/);\n });\n it(\"refuses to silently restore deliberate deletion using origin or applied steps\", () => {\n const first = planUpgrade(files(), \"1.0.0\");\n const input = { ...files(), ...first.after };\n const oas = JSON.parse(input[AI_OAS]);\n delete oas.paths[\"/u/:app_id/(.*)\"];\n input[AI_OAS] = JSON.stringify(oas);\n blocked(input, /previously present/);\n const known = {\n ...latest.files,\n [AI_OAS]: JSON.stringify(oas),\n [PROVENANCE]: JSON.stringify({\n formatVersion: 1,\n template: \"ai-gateway\",\n origin: { revision: latest.revision, release: \"1.0.0\" },\n appliedSteps: [],\n }),\n };\n blocked(known, /previously present/);\n });\n it(\"never invents a whole-project origin for legacy projects\", () => {\n const result = planUpgrade(files(), \"1.0.0\");\n assert.equal(JSON.parse(result.after[PROVENANCE]).origin, null);\n });\n it(\"fails closed for unsupported metadata formats and missing snapshots\", () => {\n blocked(\n { ...files(), [PROVENANCE]: '{\"formatVersion\":999}' },\n /formatVersion/\n );\n blocked(\n {\n ...files(),\n [PROVENANCE]: JSON.stringify({\n formatVersion: 1,\n template: \"ai-gateway\",\n origin: { revision: \"unknown\", release: \"1.0.0\" },\n appliedSteps: [],\n }),\n },\n /originating snapshot/\n );\n });\n it(\"supports a future assessed release, including upgrading across skipped releases\", () => {\n const future = structuredClone(catalog);\n const snapshot = structuredClone(latest);\n snapshot.revision = \"future-test-revision\";\n const policyFile = JSON.parse(snapshot.files[POLICIES]);\n const policy = {\n name: \"future-policy\",\n handler: {\n module: \"$import(@zuplo/runtime)\",\n export: \"FuturePolicy\",\n options: {},\n },\n };\n policyFile.policies.push(policy);\n snapshot.files[POLICIES] = JSON.stringify(policyFile);\n future.snapshots.push(snapshot);\n future.target = snapshot.revision;\n future.steps.push(\n ...catalog.steps\n .filter((s) => s.target === catalog.target)\n .map((s) => ({\n ...s,\n id: `next:${s.id}`,\n target: snapshot.revision,\n })),\n {\n id: \"next:future\",\n target: snapshot.revision,\n kind: \"policy\",\n key: policy.name,\n runtime: \"release\",\n instructions: \"Add the future declaration.\",\n }\n );\n future.assessments.push({\n revision: snapshot.revision,\n classification: \"additions\",\n instructions: \"New declaration only.\",\n });\n const direct = planUpgrade(oldest.files, \"2.0.0\", future);\n assert.deepEqual(direct.blockers, []);\n assert.ok(direct.additions.some((a) => a.key === policy.name));\n const first = planUpgrade(original.files, \"1.0.0\");\n const next = planUpgrade(\n { ...original.files, ...first.after },\n \"2.0.0\",\n future\n );\n assert.deepEqual(next.blockers, []);\n assert.deepEqual(\n next.additions.map((a) => a.key),\n [policy.name]\n );\n });\n it(\"cannot bypass a required manual migration by skipping a release\", () => {\n const future = structuredClone(catalog);\n future.assessments.unshift({\n revision: catalog.snapshots[3].revision,\n classification: \"manual\",\n instructions: \"Required manual authentication migration.\",\n });\n const result = planUpgrade(original.files, \"2.0.0\", future);\n assert.match(result.blockers.join(\"\\n\"), /manual authentication migration/);\n assert.deepEqual(result.after, {});\n });\n\n it(\"checks executor prerequisites for policy-only additions\", () => {\n const input = { ...latest.files };\n const policies = JSON.parse(input[POLICIES]);\n policies.policies = policies.policies.filter(\n (p: any) => p.name !== \"ai-gateway-smart-router-inbound\"\n );\n policies.policies.find((p: any) =>\n p.name.includes(\"configuration-executor\")\n ).handler.export = \"CustomExecutor\";\n input[POLICIES] = JSON.stringify(policies);\n blocked(input, /required policy.*customized/);\n });\n\n it(\"blocks a future release classified as requiring manual changes\", () => {\n const future = structuredClone(catalog);\n future.assessments[0].classification = \"manual\";\n const result = planUpgrade(files(), \"2.0.0\", future);\n assert.ok(result.blockers.length);\n assert.deepEqual(result.after, {});\n });\n});\n"]}
1
+ {"version":3,"file":"planner.test.js","sourceRoot":"","sources":["../../../src/source/ai-gateway/planner.test.ts"],"names":[],"mappings":"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","sourcesContent":["// biome-ignore-all lint/suspicious/noExplicitAny: Tests deliberately mutate fixture configuration shapes.\nimport assert from \"node:assert/strict\";\nimport { describe, it } from \"node:test\";\nimport {\n AI_OAS,\n type Catalog,\n catalog,\n POLICIES,\n PROVENANCE,\n planUpgrade,\n readJson,\n} from \"./planner.js\";\n\nconst oldest = catalog.snapshots[1];\nconst original = catalog.snapshots[2];\nconst internalOnlyRename = catalog.snapshots[5];\nconst loadBalancing = catalog.snapshots[6];\nconst latest = catalog.snapshots.find((s) => s.revision === catalog.target)!;\nconst LOAD_BALANCING = \"ai-gateway-load-balancing-inbound\";\n/** The catalog as the release that made `revision` its target shipped it. */\nfunction releaseCatalog(revision: string): Catalog {\n const end = catalog.snapshots.findIndex((s) => s.revision === revision) + 1;\n const shipped = new Set(\n catalog.snapshots.slice(0, end).map((s) => s.revision)\n );\n return structuredClone({\n ...catalog,\n target: revision,\n snapshots: catalog.snapshots.slice(0, end),\n steps: catalog.steps.filter((s) => shipped.has(s.target)),\n assessments: catalog.assessments.filter((a) => shipped.has(a.revision)),\n });\n}\nconst files = () => ({ ...original.files });\nfunction change(file: string, mutate: (value: any) => void) {\n const input = files();\n const value = JSON.parse(input[file]);\n mutate(value);\n input[file] = JSON.stringify(value, null, 2);\n return input;\n}\nfunction blocked(input: Record<string, string>, expected: RegExp) {\n const before = structuredClone(input);\n const result = planUpgrade(input, \"1.0.0\");\n assert.match(result.blockers.join(\"\\n\"), expected);\n assert.deepEqual(result.after, {});\n assert.deepEqual(input, before);\n}\n\ndescribe(\"conservative versioned gateway planner\", () => {\n it(\"upgrades several historical snapshots without changing existing definitions\", () => {\n for (const snapshot of catalog.snapshots.slice(0, 4)) {\n const result = planUpgrade(snapshot.files, \"1.0.0\");\n assert.deepEqual(result.blockers, []);\n assert.ok(result.additions.length);\n const before = JSON.parse(snapshot.files[AI_OAS]);\n const after = JSON.parse(result.after[AI_OAS] ?? snapshot.files[AI_OAS]);\n for (const [key, value] of Object.entries(before.paths))\n assert.deepEqual(after.paths[key], value);\n assert.deepEqual(Object.keys(after.paths), [\n \"/u/:app_id/(.*)\",\n \"/a/:app_id/(.*)\",\n \"/:app_id/(.*)\",\n ]);\n const oldPolicies = JSON.parse(snapshot.files[POLICIES]).policies;\n const newPolicies = JSON.parse(\n result.after[POLICIES] ?? snapshot.files[POLICIES]\n ).policies;\n assert.deepEqual(newPolicies.slice(0, oldPolicies.length), oldPolicies);\n const again = planUpgrade(\n { ...snapshot.files, ...result.after },\n \"1.0.0\"\n );\n assert.deepEqual(again.blockers, []);\n assert.deepEqual(again.additions, []);\n assert.deepEqual(again.after, {});\n }\n });\n it(\"preserves the original loader defaults and rejects customized historical loaders\", () => {\n const historical = catalog.snapshots[0];\n const input = { ...historical.files };\n const result = planUpgrade(input, \"1.0.0\");\n assert.deepEqual(result.blockers, []);\n const loader = (raw: string) =>\n JSON.parse(raw).policies.find(\n (p: any) => p.name === \"ai-gateway-configuration-loader-v2-inbound\"\n );\n assert.deepEqual(loader(result.after[POLICIES]).handler.options, {});\n assert.deepEqual(loader(result.after[POLICIES]), loader(input[POLICIES]));\n const policies = JSON.parse(input[POLICIES]);\n policies.policies.find(\n (p: any) => p.name === \"ai-gateway-configuration-loader-v2-inbound\"\n ).handler.options.cacheTtlSeconds = 45;\n input[POLICIES] = JSON.stringify(policies);\n blocked(input, /required policy.*customized/);\n });\n it(\"preserves the previous template after the Internal Only rename\", () => {\n const previous = catalog.snapshots[4];\n const result = planUpgrade(\n previous.files,\n \"1.0.0\",\n releaseCatalog(internalOnlyRename.revision)\n );\n assert.deepEqual(result.blockers, []);\n assert.deepEqual(result.additions, []);\n assert.deepEqual(result.after, {});\n const historical = planUpgrade(files(), \"1.0.0\");\n assert.deepEqual(historical.blockers, []);\n assert.ok(\n !historical.additions.some((a) => a.key === \"internal-only-inbound\")\n );\n });\n it(\"adds only the Model Load Balancing declaration after the Internal Only rename\", () => {\n const release = releaseCatalog(loadBalancing.revision);\n const declaration = JSON.parse(loadBalancing.files[POLICIES]).policies.find(\n (p: any) => p.name === LOAD_BALANCING\n );\n for (const previous of [catalog.snapshots[4], internalOnlyRename]) {\n const result = planUpgrade(previous.files, \"1.0.0\", release);\n assert.deepEqual(result.blockers, []);\n assert.deepEqual(\n result.additions.map((a) => a.key),\n [LOAD_BALANCING]\n );\n assert.equal(result.after[AI_OAS], undefined);\n const before = JSON.parse(previous.files[POLICIES]).policies;\n const after = JSON.parse(result.after[POLICIES]).policies;\n assert.deepEqual(after.slice(0, -1), before);\n assert.deepEqual(after.at(-1), declaration);\n const again = planUpgrade(\n { ...previous.files, ...result.after },\n \"1.0.0\",\n release\n );\n assert.deepEqual(again.blockers, []);\n assert.deepEqual(again.additions, []);\n assert.deepEqual(again.after, {});\n }\n });\n it(\"upgrades a project that the Internal Only release already upgraded\", () => {\n const first = planUpgrade(\n original.files,\n \"1.0.0\",\n releaseCatalog(internalOnlyRename.revision)\n );\n assert.deepEqual(first.blockers, []);\n const next = planUpgrade(\n { ...original.files, ...first.after },\n \"2.0.0\",\n releaseCatalog(loadBalancing.revision)\n );\n assert.deepEqual(next.blockers, []);\n assert.deepEqual(\n next.additions.map((a) => a.key),\n [LOAD_BALANCING]\n );\n assert.ok(\n JSON.parse(next.after[PROVENANCE]).appliedSteps.includes(\n `${loadBalancing.revision}:policy:${LOAD_BALANCING}`\n )\n );\n });\n it(\"does not restore a deliberately removed Model Load Balancing declaration\", () => {\n const first = planUpgrade(\n internalOnlyRename.files,\n \"1.0.0\",\n releaseCatalog(loadBalancing.revision)\n );\n const input = { ...internalOnlyRename.files, ...first.after };\n const policies = JSON.parse(input[POLICIES]);\n policies.policies = policies.policies.filter(\n (p: any) => p.name !== LOAD_BALANCING\n );\n input[POLICIES] = JSON.stringify(policies);\n blocked(input, /ai-gateway-load-balancing-inbound.*previously present/);\n });\n it(\"does not restore a deliberately removed Internal Only policy under its new name\", () => {\n const previous = catalog.snapshots[4];\n const input: Record<string, string> = {\n ...previous.files,\n [PROVENANCE]: JSON.stringify({\n formatVersion: 1,\n template: \"ai-gateway\",\n origin: { revision: previous.revision, release: \"1.0.0\" },\n appliedSteps: [],\n }),\n };\n const policies = JSON.parse(input[POLICIES]);\n policies.policies = policies.policies.filter(\n (p: any) => p.name !== \"ai-gateway-internal-only-inbound\"\n );\n input[POLICIES] = JSON.stringify(policies);\n blocked(input, /internal-only-inbound.*previously present/);\n });\n it(\"reuses historical dependency names rather than adding aliases\", () => {\n const result = planUpgrade(files(), \"1.0.0\");\n const after = JSON.parse(result.after[AI_OAS]);\n const inbound =\n after.paths[\"/u/:app_id/(.*)\"][\"get,post,put,patch,delete,head\"][\n \"x-zuplo-route\"\n ].policies.inbound;\n assert.deepEqual(inbound, [\n \"ai-gateway-configuration-loader-v2-inbound\",\n \"ai-gateway-configuration-executor-v2-inbound\",\n ]);\n assert.equal(\n result.additions.filter((a) => a.key.includes(\"configuration-loader\"))\n .length,\n 0\n );\n });\n it(\"does not modify an up-to-date project's bytes or create provenance\", () => {\n assert.deepEqual(planUpgrade(latest.files, \"1.0.0\").after, {});\n });\n it(\"preserves unrelated policy text and whitespace\", () => {\n const input = files();\n assert.ok(input[POLICIES].startsWith(\"{\"));\n input[POLICIES] = `{\\n\\n${input[POLICIES].slice(1)}`;\n const result = planUpgrade(input, \"1.0.0\");\n assert.deepEqual(result.blockers, []);\n assert.ok(result.after[POLICIES].startsWith(\"{\\n\\n\"));\n });\n it(\"blocks customized catch-all authentication\", () => {\n blocked(\n change(AI_OAS, (v) =>\n v.paths[\"/:app_id/(.*)\"][\"get,post,put,patch,delete,head\"][\n \"x-zuplo-route\"\n ].policies.inbound.unshift(\"my-auth\")\n ),\n /custom authentication/\n );\n });\n it(\"blocks reordered prerequisite chains\", () => {\n blocked(\n change(AI_OAS, (v) =>\n v.paths[\"/:app_id/(.*)\"][\"get,post,put,patch,delete,head\"][\n \"x-zuplo-route\"\n ].policies.inbound.reverse()\n ),\n /supported template/\n );\n });\n it(\"blocks customized required policies\", () => {\n blocked(\n change(POLICIES, (v) => {\n v.policies.find((p: any) =>\n p.name.includes(\"configuration-loader\")\n ).handler.options.cacheTtlSeconds = 45;\n }),\n /required policy.*customized/\n );\n });\n it(\"blocks ambiguous canonical policy aliases\", () => {\n blocked(\n change(POLICIES, (v) =>\n v.policies.push({\n ...v.policies.find((p: any) =>\n p.name.includes(\"configuration-loader\")\n ),\n name: \"ai-gateway-configuration-loader-inbound\",\n })\n ),\n /duplicate policy identity/\n );\n });\n it(\"blocks cross-file overlapping and unsupported routes but allows disjoint literal prefixes\", () => {\n for (const key of [\"/u/custom\", \"/:prefix/foo\", \"/(.*)\"]) {\n blocked(\n {\n ...files(),\n \"config/custom.oas.json\": JSON.stringify({\n openapi: \"3.1.0\",\n paths: { [key]: { get: { operationId: \"custom\" } } },\n }),\n },\n /cannot prove route/\n );\n }\n const result = planUpgrade(\n {\n ...files(),\n \"config/custom.oas.json\": JSON.stringify({\n openapi: \"3.1.0\",\n paths: { \"/health\": { get: { operationId: \"health\" } } },\n }),\n },\n \"1.0.0\"\n );\n assert.deepEqual(result.blockers, []);\n });\n for (const method of [\"get\", \"GET,POST\", \"Get,pOsT\", \" GET, POST \"]) {\n it(`blocks duplicate operation IDs across files for ${method}`, () => {\n blocked(\n {\n ...files(),\n \"config/custom.oas.json\": JSON.stringify({\n openapi: \"3.1.0\",\n paths: {\n \"/health\": {\n [method]: { operationId: \"aiGatewayUserAppCatchAll\" },\n },\n },\n }),\n },\n /operationId.*already in use/\n );\n });\n }\n it(\"blocks custom runtime extensions and document-level security\", () => {\n blocked(\n {\n ...files(),\n \"modules/zuplo.runtime.ts\":\n \"export function runtimeInit(runtime) { runtime.addPlugin(custom); }\",\n },\n /custom runtime/\n );\n blocked(\n change(AI_OAS, (v) => {\n v.security = [{ apiKey: [] }];\n }),\n /document-level/\n );\n });\n it(\"blocks malformed JSON and duplicate keys, retaining manual snippets\", () => {\n for (const text of ['{\"paths\":', '{\"policies\": [], \"policies\": []}']) {\n const result = planUpgrade({ ...files(), [POLICIES]: text }, \"1.0.0\");\n assert.ok(result.blockers.length);\n assert.ok(result.additions.length);\n assert.deepEqual(result.after, {});\n }\n assert.throws(() => readJson('{\"a\":{\"b\":1,\"b\":2}}', \"x\"), /duplicate/);\n });\n it(\"refuses to silently restore deliberate deletion using origin or applied steps\", () => {\n const first = planUpgrade(files(), \"1.0.0\");\n const input = { ...files(), ...first.after };\n const oas = JSON.parse(input[AI_OAS]);\n delete oas.paths[\"/u/:app_id/(.*)\"];\n input[AI_OAS] = JSON.stringify(oas);\n blocked(input, /previously present/);\n const known = {\n ...latest.files,\n [AI_OAS]: JSON.stringify(oas),\n [PROVENANCE]: JSON.stringify({\n formatVersion: 1,\n template: \"ai-gateway\",\n origin: { revision: latest.revision, release: \"1.0.0\" },\n appliedSteps: [],\n }),\n };\n blocked(known, /previously present/);\n });\n it(\"never invents a whole-project origin for legacy projects\", () => {\n const result = planUpgrade(files(), \"1.0.0\");\n assert.equal(JSON.parse(result.after[PROVENANCE]).origin, null);\n });\n it(\"fails closed for unsupported metadata formats and missing snapshots\", () => {\n blocked(\n { ...files(), [PROVENANCE]: '{\"formatVersion\":999}' },\n /formatVersion/\n );\n blocked(\n {\n ...files(),\n [PROVENANCE]: JSON.stringify({\n formatVersion: 1,\n template: \"ai-gateway\",\n origin: { revision: \"unknown\", release: \"1.0.0\" },\n appliedSteps: [],\n }),\n },\n /originating snapshot/\n );\n });\n it(\"supports a future assessed release, including upgrading across skipped releases\", () => {\n const future = structuredClone(catalog);\n const snapshot = structuredClone(latest);\n snapshot.revision = \"future-test-revision\";\n const policyFile = JSON.parse(snapshot.files[POLICIES]);\n const policy = {\n name: \"future-policy\",\n handler: {\n module: \"$import(@zuplo/runtime)\",\n export: \"FuturePolicy\",\n options: {},\n },\n };\n policyFile.policies.push(policy);\n snapshot.files[POLICIES] = JSON.stringify(policyFile);\n future.snapshots.push(snapshot);\n future.target = snapshot.revision;\n future.steps.push(\n ...catalog.steps\n .filter((s) => s.target === catalog.target)\n .map((s) => ({\n ...s,\n id: `next:${s.id}`,\n target: snapshot.revision,\n })),\n {\n id: \"next:future\",\n target: snapshot.revision,\n kind: \"policy\",\n key: policy.name,\n runtime: \"release\",\n instructions: \"Add the future declaration.\",\n }\n );\n future.assessments.push({\n revision: snapshot.revision,\n classification: \"additions\",\n instructions: \"New declaration only.\",\n });\n const direct = planUpgrade(oldest.files, \"2.0.0\", future);\n assert.deepEqual(direct.blockers, []);\n assert.ok(direct.additions.some((a) => a.key === policy.name));\n const first = planUpgrade(original.files, \"1.0.0\");\n const next = planUpgrade(\n { ...original.files, ...first.after },\n \"2.0.0\",\n future\n );\n assert.deepEqual(next.blockers, []);\n assert.deepEqual(\n next.additions.map((a) => a.key),\n [policy.name]\n );\n });\n it(\"cannot bypass a required manual migration by skipping a release\", () => {\n const future = structuredClone(catalog);\n future.assessments.unshift({\n revision: catalog.snapshots[3].revision,\n classification: \"manual\",\n instructions: \"Required manual authentication migration.\",\n });\n const result = planUpgrade(original.files, \"2.0.0\", future);\n assert.match(result.blockers.join(\"\\n\"), /manual authentication migration/);\n assert.deepEqual(result.after, {});\n });\n\n it(\"checks executor prerequisites for policy-only additions\", () => {\n const input = { ...latest.files };\n const policies = JSON.parse(input[POLICIES]);\n policies.policies = policies.policies.filter(\n (p: any) => p.name !== \"ai-gateway-smart-router-inbound\"\n );\n policies.policies.find((p: any) =>\n p.name.includes(\"configuration-executor\")\n ).handler.export = \"CustomExecutor\";\n input[POLICIES] = JSON.stringify(policies);\n blocked(input, /required policy.*customized/);\n });\n\n it(\"blocks a future release classified as requiring manual changes\", () => {\n const future = structuredClone(catalog);\n future.assessments[0].classification = \"manual\";\n const result = planUpgrade(files(), \"2.0.0\", future);\n assert.ok(result.blockers.length);\n assert.deepEqual(result.after, {});\n });\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-ids.d.ts","sourceRoot":"","sources":["../../src/error-tracking/chunk-ids.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAK1C,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAM5C,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS,CA2C5F"}
1
+ {"version":3,"file":"chunk-ids.d.ts","sourceRoot":"","sources":["../../src/error-tracking/chunk-ids.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAK1C,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAO5C,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS,CAwD5F"}
@@ -28,38 +28,52 @@ var __webpack_require__ = {};
28
28
  var __webpack_exports__ = {};
29
29
  __webpack_require__.r(__webpack_exports__);
30
30
  let parsedStackResults;
31
- let lastKeysCount;
31
+ let lastPostHogChunkIds;
32
+ let lastNativeDebugIds;
32
33
  let cachedFilenameChunkIds;
33
34
  function getFilenameToChunkIdMap(stackParser) {
34
- const chunkIdMap = globalThis._posthogChunkIds;
35
- if (!chunkIdMap) return;
36
- const chunkIdKeys = Object.keys(chunkIdMap);
37
- if (cachedFilenameChunkIds && chunkIdKeys.length === lastKeysCount) return cachedFilenameChunkIds;
38
- lastKeysCount = chunkIdKeys.length;
39
- cachedFilenameChunkIds = chunkIdKeys.reduce((acc, stackKey)=>{
40
- if (!parsedStackResults) parsedStackResults = {};
41
- const result = parsedStackResults[stackKey];
42
- if (result) acc[result[0]] = result[1];
43
- else {
35
+ const posthogChunkIds = globalThis._posthogChunkIds;
36
+ const nativeDebugIds = globalThis._debugIds;
37
+ if (!posthogChunkIds && !nativeDebugIds) return;
38
+ const posthogKeys = posthogChunkIds ? Object.keys(posthogChunkIds) : [];
39
+ const nativeKeys = nativeDebugIds ? Object.keys(nativeDebugIds) : [];
40
+ if (cachedFilenameChunkIds && matchesSnapshot(posthogChunkIds, posthogKeys, lastPostHogChunkIds) && matchesSnapshot(nativeDebugIds, nativeKeys, lastNativeDebugIds)) return cachedFilenameChunkIds;
41
+ lastPostHogChunkIds = posthogChunkIds ? {
42
+ ...posthogChunkIds
43
+ } : void 0;
44
+ lastNativeDebugIds = nativeDebugIds ? {
45
+ ...nativeDebugIds
46
+ } : void 0;
47
+ cachedFilenameChunkIds = {};
48
+ parsedStackResults ??= {};
49
+ const addChunkIds = (keys, chunkIds)=>{
50
+ for (const stackKey of keys){
51
+ const chunkId = chunkIds[stackKey];
52
+ if (!chunkId) continue;
53
+ const cachedFilename = parsedStackResults?.[stackKey];
54
+ if (cachedFilename) {
55
+ cachedFilenameChunkIds[cachedFilename] = chunkId;
56
+ continue;
57
+ }
44
58
  const parsedStack = stackParser(stackKey);
45
59
  for(let i = parsedStack.length - 1; i >= 0; i--){
46
- const stackFrame = parsedStack[i];
47
- const filename = stackFrame?.filename;
48
- const chunkId = chunkIdMap[stackKey];
49
- if (filename && chunkId) {
50
- acc[filename] = chunkId;
51
- parsedStackResults[stackKey] = [
52
- filename,
53
- chunkId
54
- ];
60
+ const filename = parsedStack[i]?.filename;
61
+ if (filename) {
62
+ cachedFilenameChunkIds[filename] = chunkId;
63
+ parsedStackResults[stackKey] = filename;
55
64
  break;
56
65
  }
57
66
  }
58
67
  }
59
- return acc;
60
- }, {});
68
+ };
69
+ if (nativeDebugIds) addChunkIds(nativeKeys, nativeDebugIds);
70
+ if (posthogChunkIds) addChunkIds(posthogKeys, posthogChunkIds);
61
71
  return cachedFilenameChunkIds;
62
72
  }
73
+ function matchesSnapshot(chunkIds, keys, snapshot) {
74
+ if (!chunkIds || !snapshot) return chunkIds === snapshot;
75
+ return keys.length === Object.keys(snapshot).length && keys.every((key)=>chunkIds[key] === snapshot[key]);
76
+ }
63
77
  __webpack_require__.d(__webpack_exports__, {
64
78
  getFilenameToChunkIdMap: ()=>getFilenameToChunkIdMap
65
79
  });