@apifuse/provider-sdk 2.2.0-beta.7 → 2.2.0-beta.70
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.
- package/AUTHORING.md +943 -51
- package/CHANGELOG.md +273 -1
- package/LICENSE +21 -0
- package/README.md +117 -29
- package/SUBMISSION.md +3 -2
- package/bin/apifuse-check.ts +293 -13
- package/bin/apifuse-dev.ts +42 -19
- package/bin/apifuse-migrate-deployment.ts +200 -0
- package/bin/apifuse-migrate-operation-declaration.ts +55 -0
- package/bin/apifuse-migrate-shape.ts +202 -0
- package/bin/apifuse-pack-check.ts +19 -6
- package/bin/apifuse-pack-smoke.ts +57 -2
- package/bin/apifuse-pack-types.ts +736 -38
- package/bin/apifuse-perf.ts +59 -23
- package/bin/apifuse-record.ts +923 -94
- package/bin/apifuse-submit-check.ts +2313 -281
- package/bin/apifuse-test.ts +3 -1
- package/bin/submit-check-delimited-text.ts +50 -0
- package/dist/auth-turn/index.d.ts +3 -3
- package/dist/auth-turn/index.js +1 -1
- package/dist/auth.d.ts +14 -0
- package/dist/auth.js +67 -0
- package/dist/ceremonies/index.d.ts +16 -0
- package/dist/ceremonies/index.js +141 -36
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +30 -0
- package/dist/cli/create.d.ts +1 -1
- package/dist/cli/create.js +8 -2
- package/dist/cli/deployment-intent.d.ts +178 -0
- package/dist/cli/deployment-intent.js +277 -0
- package/dist/cli/local-connection.d.ts +111 -0
- package/dist/cli/local-connection.js +151 -0
- package/dist/cli/migrate-deployment-intent.d.ts +132 -0
- package/dist/cli/migrate-deployment-intent.js +1552 -0
- package/dist/cli/migrate-operation-declaration.d.ts +70 -0
- package/dist/cli/migrate-operation-declaration.js +2206 -0
- package/dist/cli/migrate-operation-shape.d.ts +44 -0
- package/dist/cli/migrate-operation-shape.js +113 -0
- package/dist/cli/migrate-provider-shape.d.ts +56 -0
- package/dist/cli/migrate-provider-shape.js +580 -0
- package/dist/cli/resolver-runtime.d.ts +21 -0
- package/dist/cli/resolver-runtime.js +31 -0
- package/dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +141 -23
- package/dist/cli/templates/provider/Dockerfile.tpl +1 -1
- package/dist/cli/templates/provider/README.md.tpl +41 -19
- package/dist/cli/templates/provider/index.ts.tpl +6 -3
- package/dist/cli/templates/provider/operations/ping.ts.tpl +4 -1
- package/dist/cli/templates/provider/provider.json.tpl +6 -0
- package/dist/config/loader.d.ts +202 -16
- package/dist/config/loader.js +491 -141
- package/dist/contract-serialization.js +4 -8
- package/dist/contract-types.d.ts +12 -5
- package/dist/contract.js +24 -10
- package/dist/declaration-validation.d.ts +34 -0
- package/dist/declaration-validation.js +305 -0
- package/dist/define.d.ts +71 -29
- package/dist/define.js +807 -69
- package/dist/dev.d.ts +5 -0
- package/dist/dev.js +8 -1
- package/dist/engine.d.ts +129 -0
- package/dist/engine.js +209 -0
- package/dist/error-observability.d.ts +7 -0
- package/dist/error-observability.js +61 -0
- package/dist/error-resolution.d.ts +18 -0
- package/dist/error-resolution.js +243 -0
- package/dist/errors.d.ts +74 -0
- package/dist/errors.js +99 -0
- package/dist/fixture-sanitization.d.ts +28 -0
- package/dist/fixture-sanitization.js +371 -0
- package/dist/handle-meta.d.ts +90 -0
- package/dist/handle-meta.js +73 -0
- package/dist/handle.d.ts +226 -0
- package/dist/handle.js +345 -0
- package/dist/health-scenario.d.ts +1901 -0
- package/dist/health-scenario.js +653 -0
- package/dist/i18n/catalog.js +21 -1
- package/dist/i18n/error-messages.d.ts +86 -0
- package/dist/i18n/error-messages.js +192 -0
- package/dist/i18n/index.d.ts +1 -0
- package/dist/i18n/index.js +1 -0
- package/dist/i18n/keys.d.ts +12 -0
- package/dist/i18n/keys.js +76 -5
- package/dist/i18n/operation-locale-namespace.d.ts +2 -0
- package/dist/i18n/operation-locale-namespace.js +7 -0
- package/dist/index.d.ts +40 -15
- package/dist/index.js +29 -11
- package/dist/lint.d.ts +43 -11
- package/dist/lint.js +2512 -177
- package/dist/native-address.d.ts +43 -0
- package/dist/native-address.js +281 -0
- package/dist/native-egress-policy.d.ts +31 -0
- package/dist/native-egress-policy.js +288 -0
- package/dist/observability.d.ts +5 -2
- package/dist/observability.js +48 -1
- package/dist/provider.d.ts +18 -8
- package/dist/provider.js +11 -6
- package/dist/runtime/auth-flow.d.ts +6 -1
- package/dist/runtime/auth-flow.js +8 -0
- package/dist/runtime/bounded-expiring-map.d.ts +21 -0
- package/dist/runtime/bounded-expiring-map.js +64 -0
- package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
- package/dist/runtime/browser-telemetry-binding.js +83 -0
- package/dist/runtime/browser-telemetry.d.ts +76 -0
- package/dist/runtime/browser-telemetry.js +165 -0
- package/dist/runtime/browser.d.ts +4 -0
- package/dist/runtime/browser.js +688 -83
- package/dist/runtime/cache-telemetry.d.ts +71 -0
- package/dist/runtime/cache-telemetry.js +177 -0
- package/dist/runtime/cache.d.ts +3 -0
- package/dist/runtime/cache.js +245 -23
- package/dist/runtime/capability-response.d.ts +3 -0
- package/dist/runtime/capability-response.js +56 -0
- package/dist/runtime/capability-telemetry.d.ts +46 -0
- package/dist/runtime/capability-telemetry.js +68 -0
- package/dist/runtime/chrome149-header-order.d.ts +58 -0
- package/dist/runtime/chrome149-header-order.js +289 -0
- package/dist/runtime/diagnostic-env-values.d.ts +5 -0
- package/dist/runtime/diagnostic-env-values.js +57 -0
- package/dist/runtime/diagnostic-env.d.ts +14 -0
- package/dist/runtime/diagnostic-env.js +44 -0
- package/dist/runtime/diagnostic-redactor.d.ts +54 -0
- package/dist/runtime/diagnostic-redactor.js +571 -0
- package/dist/runtime/egress-lease.d.ts +65 -0
- package/dist/runtime/egress-lease.js +236 -0
- package/dist/runtime/engine-mode.d.ts +74 -0
- package/dist/runtime/engine-mode.js +152 -0
- package/dist/runtime/env.js +14 -1
- package/dist/runtime/executor.d.ts +3 -2
- package/dist/runtime/executor.js +23 -26
- package/dist/runtime/handle-wordlist.d.ts +43 -0
- package/dist/runtime/handle-wordlist.js +256 -0
- package/dist/runtime/handle.d.ts +71 -0
- package/dist/runtime/handle.js +908 -0
- package/dist/runtime/http-telemetry-binding.d.ts +10 -0
- package/dist/runtime/http-telemetry-binding.js +152 -0
- package/dist/runtime/http-telemetry-guard.d.ts +16 -0
- package/dist/runtime/http-telemetry-guard.js +202 -0
- package/dist/runtime/http-telemetry.d.ts +104 -0
- package/dist/runtime/http-telemetry.js +190 -0
- package/dist/runtime/http.d.ts +34 -0
- package/dist/runtime/http.js +680 -121
- package/dist/runtime/insights.js +2 -2
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/instrumentation.js +462 -19
- package/dist/runtime/native-network-errors.d.ts +33 -0
- package/dist/runtime/native-network-errors.js +69 -0
- package/dist/runtime/native-network.d.ts +105 -0
- package/dist/runtime/native-network.js +1433 -0
- package/dist/runtime/native-telemetry.d.ts +142 -0
- package/dist/runtime/native-telemetry.js +231 -0
- package/dist/runtime/ocr-telemetry.d.ts +82 -0
- package/dist/runtime/ocr-telemetry.js +192 -0
- package/dist/runtime/ocr.d.ts +29 -0
- package/dist/runtime/ocr.js +468 -0
- package/dist/runtime/otlp.d.ts +71 -2
- package/dist/runtime/otlp.js +398 -16
- package/dist/runtime/provider.d.ts +7 -0
- package/dist/runtime/provider.js +14 -0
- package/dist/runtime/proxy-errors.js +6 -2
- package/dist/runtime/proxy-nodemaven.d.ts +56 -0
- package/dist/runtime/proxy-nodemaven.js +147 -0
- package/dist/runtime/proxy-retry-policy.d.ts +4 -1
- package/dist/runtime/proxy-retry-policy.js +11 -2
- package/dist/runtime/proxy-telemetry.d.ts +133 -3
- package/dist/runtime/proxy-telemetry.js +218 -70
- package/dist/runtime/redirect-hop-headers.d.ts +19 -0
- package/dist/runtime/redirect-hop-headers.js +58 -0
- package/dist/runtime/redirects.d.ts +61 -0
- package/dist/runtime/redirects.js +149 -0
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +4 -2
- package/dist/runtime/request-options.d.ts +75 -1
- package/dist/runtime/request-options.js +588 -0
- package/dist/runtime/request-telemetry.d.ts +70 -0
- package/dist/runtime/request-telemetry.js +229 -0
- package/dist/runtime/resolver-config.d.ts +13 -0
- package/dist/runtime/resolver-config.js +14 -0
- package/dist/runtime/resolver-public.d.ts +3 -0
- package/dist/runtime/resolver-public.js +1 -0
- package/dist/runtime/resolver-runtime-options.d.ts +14 -0
- package/dist/runtime/resolver-runtime-options.js +33 -0
- package/dist/runtime/resolver-shared.d.ts +24 -0
- package/dist/runtime/resolver-shared.js +69 -0
- package/dist/runtime/resolver-span-attributes.d.ts +34 -0
- package/dist/runtime/resolver-span-attributes.js +93 -0
- package/dist/runtime/resolver-telemetry.d.ts +135 -0
- package/dist/runtime/resolver-telemetry.js +204 -0
- package/dist/runtime/resolver-usage.d.ts +32 -0
- package/dist/runtime/resolver-usage.js +54 -0
- package/dist/runtime/resolver-vendors/bindings.d.ts +53 -0
- package/dist/runtime/resolver-vendors/bindings.js +45 -0
- package/dist/runtime/resolver-vendors/browser.d.ts +24 -0
- package/dist/runtime/resolver-vendors/browser.js +423 -0
- package/dist/runtime/resolver-vendors/capsolver.d.ts +22 -0
- package/dist/runtime/resolver-vendors/capsolver.js +544 -0
- package/dist/runtime/resolver-vendors/hosts.d.ts +23 -0
- package/dist/runtime/resolver-vendors/hosts.js +68 -0
- package/dist/runtime/resolver-vendors/hypersolutions.d.ts +27 -0
- package/dist/runtime/resolver-vendors/hypersolutions.js +369 -0
- package/dist/runtime/resolver-vendors/twocaptcha.d.ts +24 -0
- package/dist/runtime/resolver-vendors/twocaptcha.js +425 -0
- package/dist/runtime/resolver-vendors/types.d.ts +125 -0
- package/dist/runtime/resolver-vendors/types.js +106 -0
- package/dist/runtime/resolver.d.ts +79 -0
- package/dist/runtime/resolver.js +1094 -0
- package/dist/runtime/state-telemetry.d.ts +79 -0
- package/dist/runtime/state-telemetry.js +194 -0
- package/dist/runtime/state.d.ts +10 -1
- package/dist/runtime/state.js +322 -82
- package/dist/runtime/stealth-akamai-sbsd.d.ts +54 -0
- package/dist/runtime/stealth-akamai-sbsd.js +142 -0
- package/dist/runtime/stealth-cookies.d.ts +24 -0
- package/dist/runtime/stealth-cookies.js +131 -0
- package/dist/runtime/stealth-owned-headers.d.ts +39 -0
- package/dist/runtime/stealth-owned-headers.js +57 -0
- package/dist/runtime/stealth-telemetry.d.ts +92 -0
- package/dist/runtime/stealth-telemetry.js +179 -0
- package/dist/runtime/stealth.d.ts +45 -6
- package/dist/runtime/stealth.js +1775 -320
- package/dist/runtime/stt-telemetry.d.ts +77 -0
- package/dist/runtime/stt-telemetry.js +176 -0
- package/dist/runtime/stt.js +18 -19
- package/dist/runtime/timeout.d.ts +5 -0
- package/dist/runtime/timeout.js +12 -0
- package/dist/runtime/trace-config.d.ts +12 -0
- package/dist/runtime/trace-config.js +62 -0
- package/dist/runtime/trace.d.ts +17 -0
- package/dist/runtime/trace.js +176 -25
- package/dist/runtime-boundary-lint.d.ts +137 -0
- package/dist/runtime-boundary-lint.js +590 -0
- package/dist/schema.d.ts +20 -0
- package/dist/schema.js +72 -2
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/error-observability.d.ts +1 -0
- package/dist/server/error-observability.js +1 -0
- package/dist/server/error-stack-frames.d.ts +35 -0
- package/dist/server/error-stack-frames.js +219 -0
- package/dist/server/index.d.ts +21 -2
- package/dist/server/index.js +11 -2
- package/dist/server/runtime-diagnostics.d.ts +165 -0
- package/dist/server/runtime-diagnostics.js +360 -0
- package/dist/server/sdk-version.d.ts +3 -0
- package/dist/server/sdk-version.js +19 -0
- package/dist/server/self-test-input-tokens.d.ts +2 -1
- package/dist/server/self-test-input-tokens.js +18 -14
- package/dist/server/self-test-redaction.js +8 -33
- package/dist/server/self-test.d.ts +18 -3
- package/dist/server/self-test.js +139 -76
- package/dist/server/sensitive-values.d.ts +26 -0
- package/dist/server/sensitive-values.js +137 -0
- package/dist/server/serve-implementation.d.ts +285 -0
- package/dist/server/serve-implementation.js +3172 -0
- package/dist/server/serve.d.ts +1 -75
- package/dist/server/serve.js +1 -1170
- package/dist/server/state-backend-report.d.ts +70 -0
- package/dist/server/state-backend-report.js +113 -0
- package/dist/server/trace-output.d.ts +7 -0
- package/dist/server/trace-output.js +111 -0
- package/dist/server/types.d.ts +37 -5
- package/dist/server/types.js +34 -1
- package/dist/stateful/errors.d.ts +19 -0
- package/dist/stateful/errors.js +24 -0
- package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
- package/dist/stateful/http-provider-event-emitter.js +237 -0
- package/dist/stateful/http-session-owner-registry.d.ts +44 -0
- package/dist/stateful/http-session-owner-registry.js +210 -0
- package/dist/stateful/index.d.ts +18 -0
- package/dist/stateful/index.js +18 -0
- package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
- package/dist/stateful/provider-event-delivery-failures.js +43 -0
- package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
- package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
- package/dist/stateful/provider-event-pipeline.d.ts +50 -0
- package/dist/stateful/provider-event-pipeline.js +1 -0
- package/dist/stateful/provider-events.d.ts +101 -0
- package/dist/stateful/provider-events.js +289 -0
- package/dist/stateful/session-key.d.ts +15 -0
- package/dist/stateful/session-key.js +86 -0
- package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
- package/dist/stateful/stateful-provider-adapter-context.js +42 -0
- package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
- package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
- package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
- package/dist/stateful/stateful-provider-adapter.js +287 -0
- package/dist/stateful/stateful-provider-observability.d.ts +62 -0
- package/dist/stateful/stateful-provider-observability.js +161 -0
- package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +237 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +35 -0
- package/dist/stateful/stateful-provider-runtime-context.js +61 -0
- package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
- package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
- package/dist/stateful/stateful-provider-session-routing.d.ts +67 -0
- package/dist/stateful/stateful-provider-session-routing.js +345 -0
- package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
- package/dist/stateful/stateful-provider-session-runtime.js +245 -0
- package/dist/stateful-signing.d.ts +18 -0
- package/dist/stateful-signing.js +27 -0
- package/dist/stealth/profiles.d.ts +15 -4
- package/dist/stealth/profiles.js +169 -211
- package/dist/stream-evidence.d.ts +74 -0
- package/dist/stream-evidence.js +785 -0
- package/dist/stream.js +7 -1
- package/dist/testing/index.d.ts +2 -1
- package/dist/testing/index.js +2 -1
- package/dist/testing/run.d.ts +32 -2
- package/dist/testing/run.js +509 -25
- package/dist/trace-sanitization.d.ts +6 -0
- package/dist/trace-sanitization.js +68 -0
- package/dist/types.d.ts +899 -210
- package/dist/types.js +1 -0
- package/dist/typescript-module.d.ts +39 -0
- package/dist/typescript-module.js +72 -0
- package/package.json +52 -6
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +118 -0
- package/src/ceremonies/index.ts +189 -46
- package/src/cli/commands.ts +37 -0
- package/src/cli/create.ts +8 -2
- package/src/cli/deployment-intent.ts +364 -0
- package/src/cli/local-connection.ts +221 -0
- package/src/cli/migrate-deployment-intent.ts +1908 -0
- package/src/cli/migrate-operation-declaration.ts +3026 -0
- package/src/cli/migrate-operation-shape.ts +184 -0
- package/src/cli/migrate-provider-shape.ts +778 -0
- package/src/cli/resolver-runtime.ts +72 -0
- package/src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +141 -23
- package/src/cli/templates/provider/Dockerfile.tpl +1 -1
- package/src/cli/templates/provider/README.md.tpl +41 -19
- package/src/cli/templates/provider/index.ts.tpl +6 -3
- package/src/cli/templates/provider/operations/ping.ts.tpl +4 -1
- package/src/cli/templates/provider/provider.json.tpl +6 -0
- package/src/config/loader.ts +774 -181
- package/src/contract-serialization.ts +5 -7
- package/src/contract-types.ts +12 -5
- package/src/contract.ts +24 -10
- package/src/declaration-validation.ts +376 -0
- package/src/define.ts +1105 -131
- package/src/dev.ts +13 -1
- package/src/engine.ts +390 -0
- package/src/error-observability.ts +64 -0
- package/src/error-resolution.ts +269 -0
- package/src/errors.ts +157 -0
- package/src/fixture-sanitization.ts +422 -0
- package/src/handle-meta.ts +143 -0
- package/src/handle.ts +694 -0
- package/src/health-scenario.ts +960 -0
- package/src/i18n/catalog.ts +23 -1
- package/src/i18n/error-messages.ts +255 -0
- package/src/i18n/index.ts +1 -0
- package/src/i18n/keys.ts +80 -6
- package/src/i18n/operation-locale-namespace.ts +10 -0
- package/src/index.ts +390 -23
- package/src/lint.ts +2975 -234
- package/src/native-address.ts +340 -0
- package/src/native-egress-policy.ts +358 -0
- package/src/observability.ts +51 -1
- package/src/provider.ts +207 -27
- package/src/runtime/auth-flow.ts +15 -0
- package/src/runtime/bounded-expiring-map.ts +67 -0
- package/src/runtime/browser-telemetry-binding.ts +92 -0
- package/src/runtime/browser-telemetry.ts +289 -0
- package/src/runtime/browser.ts +882 -99
- package/src/runtime/cache-telemetry.ts +247 -0
- package/src/runtime/cache.ts +258 -22
- package/src/runtime/capability-response.ts +53 -0
- package/src/runtime/capability-telemetry.ts +101 -0
- package/src/runtime/chrome149-header-order.ts +330 -0
- package/src/runtime/diagnostic-env-values.ts +56 -0
- package/src/runtime/diagnostic-env.ts +65 -0
- package/src/runtime/diagnostic-redactor.ts +668 -0
- package/src/runtime/egress-lease.ts +341 -0
- package/src/runtime/engine-mode.ts +233 -0
- package/src/runtime/env.ts +15 -1
- package/src/runtime/executor.ts +39 -32
- package/src/runtime/handle-wordlist.ts +266 -0
- package/src/runtime/handle.ts +1180 -0
- package/src/runtime/http-telemetry-binding.ts +162 -0
- package/src/runtime/http-telemetry-guard.ts +208 -0
- package/src/runtime/http-telemetry.ts +292 -0
- package/src/runtime/http.ts +896 -150
- package/src/runtime/insights.ts +2 -2
- package/src/runtime/instrumentation.ts +628 -28
- package/src/runtime/native-network-errors.ts +99 -0
- package/src/runtime/native-network.ts +1853 -0
- package/src/runtime/native-telemetry.ts +366 -0
- package/src/runtime/ocr-telemetry.ts +308 -0
- package/src/runtime/ocr.ts +564 -0
- package/src/runtime/otlp.ts +468 -21
- package/src/runtime/provider.ts +15 -0
- package/src/runtime/proxy-errors.ts +12 -4
- package/src/runtime/proxy-nodemaven.ts +222 -0
- package/src/runtime/proxy-retry-policy.ts +12 -1
- package/src/runtime/proxy-telemetry.ts +358 -103
- package/src/runtime/redirect-hop-headers.ts +71 -0
- package/src/runtime/redirects.ts +215 -0
- package/src/runtime/redis.ts +7 -2
- package/src/runtime/request-options.ts +728 -1
- package/src/runtime/request-telemetry.ts +375 -0
- package/src/runtime/resolver-config.ts +18 -0
- package/src/runtime/resolver-public.ts +44 -0
- package/src/runtime/resolver-runtime-options.ts +50 -0
- package/src/runtime/resolver-shared.ts +113 -0
- package/src/runtime/resolver-span-attributes.ts +139 -0
- package/src/runtime/resolver-telemetry.ts +362 -0
- package/src/runtime/resolver-usage.ts +99 -0
- package/src/runtime/resolver-vendors/bindings.ts +61 -0
- package/src/runtime/resolver-vendors/browser.ts +591 -0
- package/src/runtime/resolver-vendors/capsolver.ts +727 -0
- package/src/runtime/resolver-vendors/hosts.ts +81 -0
- package/src/runtime/resolver-vendors/hypersolutions.ts +480 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +555 -0
- package/src/runtime/resolver-vendors/types.ts +261 -0
- package/src/runtime/resolver.ts +1543 -0
- package/src/runtime/state-telemetry.ts +298 -0
- package/src/runtime/state.ts +435 -89
- package/src/runtime/stealth-akamai-sbsd.ts +207 -0
- package/src/runtime/stealth-cookies.ts +155 -0
- package/src/runtime/stealth-owned-headers.ts +67 -0
- package/src/runtime/stealth-telemetry.ts +309 -0
- package/src/runtime/stealth.ts +2275 -372
- package/src/runtime/stt-telemetry.ts +282 -0
- package/src/runtime/stt.ts +96 -91
- package/src/runtime/timeout.ts +18 -0
- package/src/runtime/trace-config.ts +78 -0
- package/src/runtime/trace.ts +257 -35
- package/src/runtime-boundary-lint.ts +707 -0
- package/src/schema.ts +80 -2
- package/src/serve.ts +6 -1
- package/src/server/error-observability.ts +1 -0
- package/src/server/error-stack-frames.ts +214 -0
- package/src/server/index.ts +184 -2
- package/src/server/runtime-diagnostics.ts +522 -0
- package/src/server/sdk-version.ts +22 -0
- package/src/server/self-test-input-tokens.ts +29 -14
- package/src/server/self-test-redaction.ts +10 -40
- package/src/server/self-test.ts +205 -89
- package/src/server/sensitive-values.ts +211 -0
- package/src/server/serve-implementation.ts +4596 -0
- package/src/server/serve.ts +1 -1685
- package/src/server/state-backend-report.ts +185 -0
- package/src/server/trace-output.ts +189 -0
- package/src/server/types.ts +34 -1
- package/src/stateful/README.md +146 -0
- package/src/stateful/errors.ts +35 -0
- package/src/stateful/http-provider-event-emitter.ts +314 -0
- package/src/stateful/http-session-owner-registry.ts +306 -0
- package/src/stateful/index.ts +18 -0
- package/src/stateful/provider-event-delivery-failures.ts +80 -0
- package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
- package/src/stateful/provider-event-pipeline.ts +61 -0
- package/src/stateful/provider-events.ts +462 -0
- package/src/stateful/session-key.ts +111 -0
- package/src/stateful/stateful-provider-adapter-context.ts +59 -0
- package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
- package/src/stateful/stateful-provider-adapter.ts +562 -0
- package/src/stateful/stateful-provider-observability.ts +261 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +310 -0
- package/src/stateful/stateful-provider-runtime-context.ts +96 -0
- package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
- package/src/stateful/stateful-provider-session-routing.ts +546 -0
- package/src/stateful/stateful-provider-session-runtime.ts +403 -0
- package/src/stateful-signing.ts +46 -0
- package/src/stealth/profiles.ts +212 -231
- package/src/stream-evidence.ts +988 -0
- package/src/stream.ts +8 -1
- package/src/testing/index.ts +10 -1
- package/src/testing/run.ts +685 -19
- package/src/trace-sanitization.ts +110 -0
- package/src/types.ts +1014 -273
- package/src/typescript-module.ts +100 -0
- package/dist/choice-token.d.ts +0 -24
- package/dist/choice-token.js +0 -74
- package/dist/runtime/choice.d.ts +0 -15
- package/dist/runtime/choice.js +0 -413
- package/src/choice-token.ts +0 -165
- package/src/runtime/choice.ts +0 -686
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ProviderCacheLookupMeta } from "../types.js";
|
|
2
|
+
import { type ClosedEnum, type SpanIndex, type TelemetryContributor } from "./request-telemetry.js";
|
|
3
|
+
export type CacheTelemetrySource = "memory" | "redis" | "loader";
|
|
4
|
+
export type CacheTelemetrySample = {
|
|
5
|
+
key: string;
|
|
6
|
+
hit: boolean;
|
|
7
|
+
stale: boolean;
|
|
8
|
+
ageMs?: number;
|
|
9
|
+
source: CacheTelemetrySource;
|
|
10
|
+
};
|
|
11
|
+
export type CacheTelemetryLogPayload = {
|
|
12
|
+
telemetryFailed?: true;
|
|
13
|
+
lookups: number;
|
|
14
|
+
hits: number;
|
|
15
|
+
stale: number;
|
|
16
|
+
misses: number;
|
|
17
|
+
writes: number;
|
|
18
|
+
loaderErrors: number;
|
|
19
|
+
writeErrors: number;
|
|
20
|
+
source: {
|
|
21
|
+
memory: number;
|
|
22
|
+
redis: number;
|
|
23
|
+
loader: number;
|
|
24
|
+
};
|
|
25
|
+
redisFallbacks: number;
|
|
26
|
+
redisMode: ClosedEnum<"not_configured" | "configured" | "degraded">;
|
|
27
|
+
/** Successful Redis commands, including reads, writes, and deletes; memory-only work counts zero. */
|
|
28
|
+
redisRoundTrips: number;
|
|
29
|
+
ageMs?: {
|
|
30
|
+
p50: number;
|
|
31
|
+
max: number;
|
|
32
|
+
};
|
|
33
|
+
samples: CacheTelemetrySample[];
|
|
34
|
+
dropped: number;
|
|
35
|
+
};
|
|
36
|
+
export type CacheTelemetryHeaderPayload = Omit<CacheTelemetryLogPayload, "samples" | "ageMs" | "telemetryFailed"> & {
|
|
37
|
+
ageP50?: number;
|
|
38
|
+
ageMax?: number;
|
|
39
|
+
};
|
|
40
|
+
export interface CacheTelemetrySink {
|
|
41
|
+
markTelemetryFailed?(): void;
|
|
42
|
+
setRedisConfigured?(configured: boolean): void;
|
|
43
|
+
recordLookup(meta: ProviderCacheLookupMeta): void;
|
|
44
|
+
recordWrite(): void;
|
|
45
|
+
recordLoaderError?(): void;
|
|
46
|
+
recordWriteError?(): void;
|
|
47
|
+
recordRedisFallback(): void;
|
|
48
|
+
recordRedisRoundTrip?(): void;
|
|
49
|
+
}
|
|
50
|
+
/** Internal construction binding; keeps sink plumbing off ProviderCacheOptions. */
|
|
51
|
+
export declare function bindCacheTelemetry<T extends object>(options: T, sink: CacheTelemetrySink | undefined): T;
|
|
52
|
+
/** Internal lookup used only by the cache factory. */
|
|
53
|
+
export declare function cacheTelemetryFor(options: object): CacheTelemetrySink | undefined;
|
|
54
|
+
export declare function guardCacheTelemetry(sink: CacheTelemetrySink | undefined): CacheTelemetrySink;
|
|
55
|
+
export declare class CacheTelemetryCollector implements TelemetryContributor<CacheTelemetryLogPayload, CacheTelemetryHeaderPayload>, CacheTelemetrySink {
|
|
56
|
+
#private;
|
|
57
|
+
readonly key: "cache";
|
|
58
|
+
constructor(options?: {
|
|
59
|
+
redact?: (text: string) => string;
|
|
60
|
+
});
|
|
61
|
+
setRedisConfigured(configured: boolean): void;
|
|
62
|
+
markTelemetryFailed(): void;
|
|
63
|
+
recordLookup(meta: ProviderCacheLookupMeta): void;
|
|
64
|
+
recordWrite(): void;
|
|
65
|
+
recordLoaderError(): void;
|
|
66
|
+
recordWriteError(): void;
|
|
67
|
+
recordRedisFallback(): void;
|
|
68
|
+
recordRedisRoundTrip(): void;
|
|
69
|
+
toLogPayload(_spans: SpanIndex): CacheTelemetryLogPayload | undefined;
|
|
70
|
+
toHeaderPayload(log: CacheTelemetryLogPayload): CacheTelemetryHeaderPayload;
|
|
71
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { closedEnum, } from "./request-telemetry.js";
|
|
3
|
+
import { createTelemetryObserver } from "./http-telemetry-guard.js";
|
|
4
|
+
const cacheTelemetryBindings = new WeakMap();
|
|
5
|
+
/** Internal construction binding; keeps sink plumbing off ProviderCacheOptions. */
|
|
6
|
+
export function bindCacheTelemetry(options, sink) {
|
|
7
|
+
if (sink)
|
|
8
|
+
cacheTelemetryBindings.set(options, sink);
|
|
9
|
+
return options;
|
|
10
|
+
}
|
|
11
|
+
/** Internal lookup used only by the cache factory. */
|
|
12
|
+
export function cacheTelemetryFor(options) {
|
|
13
|
+
return cacheTelemetryBindings.get(options);
|
|
14
|
+
}
|
|
15
|
+
export function guardCacheTelemetry(sink) {
|
|
16
|
+
const observe = createTelemetryObserver(sink);
|
|
17
|
+
return {
|
|
18
|
+
setRedisConfigured: (configured) => observe(() => sink?.setRedisConfigured?.(configured)),
|
|
19
|
+
recordLookup: (meta) => observe(() => sink?.recordLookup(meta)),
|
|
20
|
+
recordWrite: () => observe(() => sink?.recordWrite()),
|
|
21
|
+
recordLoaderError: () => observe(() => sink?.recordLoaderError?.()),
|
|
22
|
+
recordWriteError: () => observe(() => sink?.recordWriteError?.()),
|
|
23
|
+
recordRedisFallback: () => observe(() => sink?.recordRedisFallback()),
|
|
24
|
+
recordRedisRoundTrip: () => observe(() => sink?.recordRedisRoundTrip?.()),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const MAX_SAMPLES = 24;
|
|
28
|
+
const MAX_TEXT = 300;
|
|
29
|
+
function integer(value) {
|
|
30
|
+
return Number.isFinite(value)
|
|
31
|
+
? Math.min(Number.MAX_SAFE_INTEGER, Math.max(0, Math.floor(value)))
|
|
32
|
+
: Number.MAX_SAFE_INTEGER;
|
|
33
|
+
}
|
|
34
|
+
function add(a, b) {
|
|
35
|
+
return Math.min(Number.MAX_SAFE_INTEGER, integer(a) + integer(b));
|
|
36
|
+
}
|
|
37
|
+
function boundedText(value, redact) {
|
|
38
|
+
let result;
|
|
39
|
+
try {
|
|
40
|
+
result = redact?.(value) ?? value;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
result = "[REDACTION_FAILED]";
|
|
44
|
+
}
|
|
45
|
+
const length = Math.min(result.length, MAX_TEXT);
|
|
46
|
+
const units = Buffer.alloc(length * 2);
|
|
47
|
+
for (let index = 0; index < length; index += 1)
|
|
48
|
+
units.writeUInt16LE(result.charCodeAt(index), index * 2);
|
|
49
|
+
return units.toString("utf16le");
|
|
50
|
+
}
|
|
51
|
+
export class CacheTelemetryCollector {
|
|
52
|
+
key = "cache";
|
|
53
|
+
#redact;
|
|
54
|
+
#telemetryFailed = false;
|
|
55
|
+
#lookups = 0;
|
|
56
|
+
#hits = 0;
|
|
57
|
+
#stale = 0;
|
|
58
|
+
#misses = 0;
|
|
59
|
+
#writes = 0;
|
|
60
|
+
#loaderErrors = 0;
|
|
61
|
+
#writeErrors = 0;
|
|
62
|
+
#source = { memory: 0, redis: 0, loader: 0 };
|
|
63
|
+
#redisFallbacks = 0;
|
|
64
|
+
#configured;
|
|
65
|
+
#redisRoundTrips = 0;
|
|
66
|
+
#ages = [];
|
|
67
|
+
#maxAge = 0;
|
|
68
|
+
#samples = [];
|
|
69
|
+
#dropped = 0;
|
|
70
|
+
constructor(options = {}) {
|
|
71
|
+
this.#redact = options.redact;
|
|
72
|
+
this.#configured = "not_configured";
|
|
73
|
+
}
|
|
74
|
+
setRedisConfigured(configured) {
|
|
75
|
+
if (configured && this.#configured === "not_configured")
|
|
76
|
+
this.#configured = "configured";
|
|
77
|
+
}
|
|
78
|
+
markTelemetryFailed() {
|
|
79
|
+
this.#telemetryFailed = true;
|
|
80
|
+
}
|
|
81
|
+
recordLookup(meta) {
|
|
82
|
+
if (!["memory", "redis", "loader"].includes(meta.source)) {
|
|
83
|
+
this.#telemetryFailed = true;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.#lookups = add(this.#lookups, 1);
|
|
87
|
+
if (meta.hit)
|
|
88
|
+
this.#hits = add(this.#hits, 1);
|
|
89
|
+
else
|
|
90
|
+
this.#misses = add(this.#misses, 1);
|
|
91
|
+
if (meta.stale)
|
|
92
|
+
this.#stale = add(this.#stale, 1);
|
|
93
|
+
this.#source[meta.source] = add(this.#source[meta.source], 1);
|
|
94
|
+
if (meta.source === "redis" && this.#configured === "not_configured")
|
|
95
|
+
this.#configured = "configured";
|
|
96
|
+
if (meta.ageMs !== undefined) {
|
|
97
|
+
const age = integer(meta.ageMs);
|
|
98
|
+
if (this.#ages.length < MAX_SAMPLES)
|
|
99
|
+
this.#ages.push(age);
|
|
100
|
+
this.#maxAge = Math.max(this.#maxAge, age);
|
|
101
|
+
}
|
|
102
|
+
if (this.#samples.length < MAX_SAMPLES) {
|
|
103
|
+
this.#samples.push({
|
|
104
|
+
key: boundedText(meta.key, this.#redact),
|
|
105
|
+
hit: meta.hit,
|
|
106
|
+
stale: meta.stale,
|
|
107
|
+
...(meta.ageMs === undefined ? {} : { ageMs: integer(meta.ageMs) }),
|
|
108
|
+
source: meta.source,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else
|
|
112
|
+
this.#dropped = add(this.#dropped, 1);
|
|
113
|
+
}
|
|
114
|
+
recordWrite() {
|
|
115
|
+
this.#writes = add(this.#writes, 1);
|
|
116
|
+
}
|
|
117
|
+
recordLoaderError() {
|
|
118
|
+
this.#loaderErrors = add(this.#loaderErrors, 1);
|
|
119
|
+
}
|
|
120
|
+
recordWriteError() {
|
|
121
|
+
this.#writeErrors = add(this.#writeErrors, 1);
|
|
122
|
+
}
|
|
123
|
+
recordRedisFallback() {
|
|
124
|
+
this.#redisFallbacks = add(this.#redisFallbacks, 1);
|
|
125
|
+
this.#configured = "degraded";
|
|
126
|
+
}
|
|
127
|
+
recordRedisRoundTrip() {
|
|
128
|
+
this.#redisRoundTrips = add(this.#redisRoundTrips, 1);
|
|
129
|
+
}
|
|
130
|
+
toLogPayload(_spans) {
|
|
131
|
+
if (this.#lookups === 0 &&
|
|
132
|
+
this.#writes === 0 &&
|
|
133
|
+
this.#redisFallbacks === 0 &&
|
|
134
|
+
!this.#telemetryFailed)
|
|
135
|
+
return undefined;
|
|
136
|
+
const sorted = [...this.#ages].sort((a, b) => a - b);
|
|
137
|
+
const p50 = sorted.length ? sorted[Math.floor((sorted.length - 1) / 2)] : undefined;
|
|
138
|
+
return {
|
|
139
|
+
...(this.#telemetryFailed ? { telemetryFailed: true } : {}),
|
|
140
|
+
lookups: this.#lookups,
|
|
141
|
+
hits: this.#hits,
|
|
142
|
+
stale: this.#stale,
|
|
143
|
+
misses: this.#misses,
|
|
144
|
+
writes: this.#writes,
|
|
145
|
+
loaderErrors: this.#loaderErrors,
|
|
146
|
+
writeErrors: this.#writeErrors,
|
|
147
|
+
source: { ...this.#source },
|
|
148
|
+
redisFallbacks: this.#redisFallbacks,
|
|
149
|
+
redisMode: closedEnum(this.#configured),
|
|
150
|
+
redisRoundTrips: this.#redisRoundTrips,
|
|
151
|
+
...(p50 === undefined ? {} : { ageMs: { p50, max: this.#maxAge } }),
|
|
152
|
+
samples: this.#samples.map((sample) => ({ ...sample })),
|
|
153
|
+
dropped: this.#dropped,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
toHeaderPayload(log) {
|
|
157
|
+
return {
|
|
158
|
+
lookups: log.lookups,
|
|
159
|
+
hits: log.hits,
|
|
160
|
+
stale: log.stale,
|
|
161
|
+
misses: log.misses,
|
|
162
|
+
writes: log.writes,
|
|
163
|
+
loaderErrors: log.loaderErrors,
|
|
164
|
+
writeErrors: log.writeErrors,
|
|
165
|
+
source: {
|
|
166
|
+
memory: log.source.memory,
|
|
167
|
+
redis: log.source.redis,
|
|
168
|
+
loader: log.source.loader,
|
|
169
|
+
},
|
|
170
|
+
redisFallbacks: log.redisFallbacks,
|
|
171
|
+
redisMode: log.redisMode,
|
|
172
|
+
redisRoundTrips: log.redisRoundTrips,
|
|
173
|
+
...(log.ageMs ? { ageP50: log.ageMs.p50, ageMax: log.ageMs.max } : {}),
|
|
174
|
+
dropped: log.dropped,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
package/dist/runtime/cache.d.ts
CHANGED
|
@@ -5,6 +5,9 @@ export type ProviderCacheOptions = {
|
|
|
5
5
|
memoryMaxEntries?: number;
|
|
6
6
|
now?: () => number;
|
|
7
7
|
};
|
|
8
|
+
export declare const APIFUSE__CACHE__KEY_PEPPER_ENV = "APIFUSE__CACHE__KEY_PEPPER";
|
|
8
9
|
export declare function createProviderCache(options: ProviderCacheOptions): ProviderCache;
|
|
10
|
+
/** Internal cache capability, separate from hit/miss metadata. */
|
|
11
|
+
export declare function isProviderCacheBypassed(cache: ProviderCache): boolean;
|
|
9
12
|
export declare function createBypassProviderCache(options: Pick<ProviderCacheOptions, "providerId">): ProviderCache;
|
|
10
13
|
export declare function resetProviderCacheForTests(): void;
|
package/dist/runtime/cache.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readDiagnosticEnv } from "./diagnostic-env.js";
|
|
2
|
+
import { createHash, createHmac } from "node:crypto";
|
|
2
3
|
import { providerCacheRedisUrlFromEnv } from "../config/loader.js";
|
|
4
|
+
import { ProviderError } from "../errors.js";
|
|
5
|
+
import { cacheTelemetryFor, guardCacheTelemetry } from "./cache-telemetry.js";
|
|
3
6
|
import { createProviderRedisClient, ensureRedisReady, withRedisTimeout, } from "./redis.js";
|
|
7
|
+
export const APIFUSE__CACHE__KEY_PEPPER_ENV = "APIFUSE__CACHE__KEY_PEPPER";
|
|
4
8
|
const DEFAULT_PREFIX = "apifuse:provider-cache:v1";
|
|
5
9
|
const DEFAULT_MEMORY_MAX_ENTRIES = 1_000;
|
|
6
10
|
const DEFAULT_REDIS_TIMEOUT_MS = 150;
|
|
11
|
+
const SECRET_SCOPED_KEY_MARKER = "[secret-scoped";
|
|
7
12
|
const SECRET_FIELD_NAMES = new Set([
|
|
8
13
|
"authorization",
|
|
9
14
|
"cookie",
|
|
@@ -18,6 +23,7 @@ const SECRET_FIELD_NAMES = new Set([
|
|
|
18
23
|
"refresh_token",
|
|
19
24
|
]);
|
|
20
25
|
const sharedBackends = new Map();
|
|
26
|
+
let warnedAboutUnpepperedSecretKeys = false;
|
|
21
27
|
function backendKey(redisUrl) {
|
|
22
28
|
return redisUrl ?? "memory";
|
|
23
29
|
}
|
|
@@ -55,20 +61,157 @@ function shouldRedactField(name, extra) {
|
|
|
55
61
|
normalized.includes("password") ||
|
|
56
62
|
normalized.includes("secret"));
|
|
57
63
|
}
|
|
58
|
-
function
|
|
64
|
+
function unsupportedSecretValue(path, reason) {
|
|
65
|
+
throw new ProviderError(`Secret cache-key values must be JSON-safe; ${reason} at ${path}.`, {
|
|
66
|
+
code: "CACHE_KEY_SECRET_VALUE_UNSUPPORTED",
|
|
67
|
+
fix: "Convert the secret cache-key selector to JSON-safe primitives, arrays, or plain objects.",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function assertJsonSafeSecretValue(value, reportedPath, ancestors = new Set()) {
|
|
71
|
+
if (value === undefined)
|
|
72
|
+
return;
|
|
73
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
74
|
+
return;
|
|
75
|
+
if (typeof value === "number") {
|
|
76
|
+
if (!Number.isFinite(value))
|
|
77
|
+
unsupportedSecretValue(reportedPath, "non-finite numbers are unsupported");
|
|
78
|
+
if (Object.is(value, -0))
|
|
79
|
+
unsupportedSecretValue(reportedPath, "negative zero is unsupported");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (typeof value !== "object") {
|
|
83
|
+
unsupportedSecretValue(reportedPath, `${typeof value} values are unsupported`);
|
|
84
|
+
}
|
|
85
|
+
if (ancestors.has(value))
|
|
86
|
+
unsupportedSecretValue(reportedPath, "cyclic values are unsupported");
|
|
87
|
+
ancestors.add(value);
|
|
88
|
+
try {
|
|
89
|
+
if (Array.isArray(value)) {
|
|
90
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
91
|
+
unsupportedSecretValue(reportedPath, "symbol-keyed array properties are unsupported");
|
|
92
|
+
}
|
|
93
|
+
const expectedNames = new Set(["length"]);
|
|
94
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
95
|
+
const key = String(index);
|
|
96
|
+
expectedNames.add(key);
|
|
97
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
98
|
+
if (!descriptor)
|
|
99
|
+
unsupportedSecretValue(reportedPath, "sparse arrays are unsupported");
|
|
100
|
+
if (!descriptor.enumerable || !("value" in descriptor)) {
|
|
101
|
+
unsupportedSecretValue(reportedPath, "array accessors are unsupported");
|
|
102
|
+
}
|
|
103
|
+
assertJsonSafeSecretValue(descriptor.value, reportedPath, ancestors);
|
|
104
|
+
}
|
|
105
|
+
if (Object.getOwnPropertyNames(value).some((name) => !expectedNames.has(name))) {
|
|
106
|
+
unsupportedSecretValue(reportedPath, "custom array properties are unsupported");
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const prototype = Object.getPrototypeOf(value);
|
|
111
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
112
|
+
unsupportedSecretValue(reportedPath, "non-plain objects are unsupported");
|
|
113
|
+
}
|
|
114
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
115
|
+
unsupportedSecretValue(reportedPath, "symbol-keyed properties are unsupported");
|
|
116
|
+
}
|
|
117
|
+
for (const key of Object.getOwnPropertyNames(value)) {
|
|
118
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
119
|
+
if (!descriptor?.enumerable || !("value" in descriptor)) {
|
|
120
|
+
unsupportedSecretValue(reportedPath, "non-enumerable properties and accessors are unsupported");
|
|
121
|
+
}
|
|
122
|
+
assertJsonSafeSecretValue(descriptor.value, reportedPath, ancestors);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
ancestors.delete(value);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function containsUndefined(value) {
|
|
130
|
+
if (value === undefined)
|
|
131
|
+
return true;
|
|
132
|
+
if (Array.isArray(value))
|
|
133
|
+
return value.some(containsUndefined);
|
|
134
|
+
if (isRecord(value))
|
|
135
|
+
return Object.values(value).some(containsUndefined);
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
function tagSecretValue(value) {
|
|
139
|
+
if (value === undefined)
|
|
140
|
+
return ["undefined"];
|
|
141
|
+
if (value === null)
|
|
142
|
+
return ["null"];
|
|
143
|
+
if (typeof value === "string")
|
|
144
|
+
return ["string", value];
|
|
145
|
+
if (typeof value === "number")
|
|
146
|
+
return ["number", value];
|
|
147
|
+
if (typeof value === "boolean")
|
|
148
|
+
return ["boolean", value];
|
|
149
|
+
if (Array.isArray(value))
|
|
150
|
+
return ["array", value.map(tagSecretValue)];
|
|
151
|
+
return [
|
|
152
|
+
"object",
|
|
153
|
+
Object.entries(value).map(([key, entry]) => [
|
|
154
|
+
key,
|
|
155
|
+
tagSecretValue(entry),
|
|
156
|
+
]),
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
function serializeSecretValue(value) {
|
|
160
|
+
if (!containsUndefined(value))
|
|
161
|
+
return JSON.stringify([value]);
|
|
162
|
+
return `undefined-v1:${JSON.stringify(tagSecretValue(value))}`;
|
|
163
|
+
}
|
|
164
|
+
function warnAboutUnpepperedSecretKey() {
|
|
165
|
+
if (warnedAboutUnpepperedSecretKeys)
|
|
166
|
+
return;
|
|
167
|
+
warnedAboutUnpepperedSecretKeys = true;
|
|
168
|
+
console.warn(JSON.stringify({
|
|
169
|
+
level: "warn",
|
|
170
|
+
event: "provider_cache_secret_key_unpeppered",
|
|
171
|
+
message: `Secret-bearing cache keys are using unkeyed SHA-256 because ${APIFUSE__CACHE__KEY_PEPPER_ENV} is not configured.`,
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
function normalizeKeyPart(value, extra, pepper) {
|
|
59
175
|
if (Array.isArray(value)) {
|
|
60
|
-
|
|
176
|
+
const entries = value.map((entry) => normalizeKeyPart(entry, extra, pepper));
|
|
177
|
+
return {
|
|
178
|
+
value: entries.map((entry) => entry.value),
|
|
179
|
+
secretScoped: entries.some((entry) => entry.secretScoped),
|
|
180
|
+
};
|
|
61
181
|
}
|
|
62
182
|
if (isRecord(value)) {
|
|
63
|
-
const normalized =
|
|
183
|
+
const normalized = Object.create(null);
|
|
184
|
+
let secretScoped = false;
|
|
64
185
|
for (const key of Object.keys(value).sort()) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
186
|
+
const part = shouldRedactField(key, extra)
|
|
187
|
+
? hashSecretValue(value[key], key, extra, pepper)
|
|
188
|
+
: normalizeKeyPart(value[key], extra, pepper);
|
|
189
|
+
normalized[key] = part.value;
|
|
190
|
+
secretScoped ||= part.secretScoped;
|
|
68
191
|
}
|
|
69
|
-
return normalized;
|
|
192
|
+
return { value: normalized, secretScoped };
|
|
193
|
+
}
|
|
194
|
+
return { value, secretScoped: false };
|
|
195
|
+
}
|
|
196
|
+
function hashSecretValue(value, fieldName, extra, pepper) {
|
|
197
|
+
assertJsonSafeSecretValue(value, `${fieldName} (inside secret value)`);
|
|
198
|
+
const canonical = serializeSecretValue(normalizeKeyPart(value, extra, pepper).value);
|
|
199
|
+
if (pepper === undefined) {
|
|
200
|
+
warnAboutUnpepperedSecretKey();
|
|
201
|
+
const digest = createHash("sha256").update(canonical).digest("hex");
|
|
202
|
+
return { value: `sha256:${digest}`, secretScoped: true };
|
|
70
203
|
}
|
|
71
|
-
|
|
204
|
+
const digest = createHmac("sha256", pepper).update(canonical).digest("hex");
|
|
205
|
+
return { value: `hmac-sha256:${digest}`, secretScoped: true };
|
|
206
|
+
}
|
|
207
|
+
function metadataKeys(events, secretScopedKeys) {
|
|
208
|
+
let secretScopedIndex = 0;
|
|
209
|
+
return Array.from(new Set(events.map((event) => event.key))).map((key) => {
|
|
210
|
+
if (!secretScopedKeys.has(key))
|
|
211
|
+
return key;
|
|
212
|
+
secretScopedIndex += 1;
|
|
213
|
+
return `${SECRET_SCOPED_KEY_MARKER}#${secretScopedIndex}]`;
|
|
214
|
+
});
|
|
72
215
|
}
|
|
73
216
|
function stableHash(value) {
|
|
74
217
|
return createHash("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 32);
|
|
@@ -138,12 +281,19 @@ async function withRedisFallback(operation) {
|
|
|
138
281
|
}
|
|
139
282
|
export function createProviderCache(options) {
|
|
140
283
|
const redisUrl = options.redisUrl ?? providerCacheRedisUrlFromEnv();
|
|
284
|
+
const configuredPepper = readDiagnosticEnv(APIFUSE__CACHE__KEY_PEPPER_ENV);
|
|
285
|
+
const pepper = configuredPepper && configuredPepper.length > 0 ? configuredPepper : undefined;
|
|
141
286
|
const backend = getSharedBackend(redisUrl);
|
|
142
287
|
const memoryMaxEntries = Math.max(1, options.memoryMaxEntries ?? DEFAULT_MEMORY_MAX_ENTRIES);
|
|
143
288
|
const now = options.now ?? Date.now;
|
|
144
289
|
const events = [];
|
|
290
|
+
const telemetry = guardCacheTelemetry(cacheTelemetryFor(options));
|
|
291
|
+
telemetry.setRedisConfigured?.(!!redisUrl);
|
|
292
|
+
let redisUnavailable = false;
|
|
293
|
+
const secretScopedKeys = new Set();
|
|
145
294
|
function record(meta) {
|
|
146
295
|
events.push(meta);
|
|
296
|
+
telemetry?.recordLookup(meta);
|
|
147
297
|
}
|
|
148
298
|
function sweepMemory(currentTime) {
|
|
149
299
|
for (const [entryKey, entry] of backend.memory) {
|
|
@@ -177,13 +327,21 @@ export function createProviderCache(options) {
|
|
|
177
327
|
async function readRedis(key, currentTime) {
|
|
178
328
|
const redis = backend.redis;
|
|
179
329
|
if (!redis || !(await ensureRedisReady(redis, DEFAULT_REDIS_TIMEOUT_MS))) {
|
|
330
|
+
if (redis) {
|
|
331
|
+
redisUnavailable = true;
|
|
332
|
+
telemetry?.recordRedisFallback();
|
|
333
|
+
}
|
|
180
334
|
return null;
|
|
181
335
|
}
|
|
182
336
|
const raw = await withRedisFallback(async () => {
|
|
183
337
|
return await redis.get(key);
|
|
184
338
|
});
|
|
185
|
-
if (typeof raw !== "string" && raw !== null)
|
|
339
|
+
if (typeof raw !== "string" && raw !== null) {
|
|
340
|
+
redisUnavailable = true;
|
|
341
|
+
telemetry.recordRedisFallback();
|
|
186
342
|
return null;
|
|
343
|
+
}
|
|
344
|
+
telemetry?.recordRedisRoundTrip?.();
|
|
187
345
|
const envelope = safeParseEnvelope(raw);
|
|
188
346
|
if (!envelope)
|
|
189
347
|
return null;
|
|
@@ -225,6 +383,7 @@ export function createProviderCache(options) {
|
|
|
225
383
|
return staleMemoryResult;
|
|
226
384
|
}
|
|
227
385
|
async function write(key, value, cacheOptions) {
|
|
386
|
+
telemetry?.recordWrite();
|
|
228
387
|
const currentTime = now();
|
|
229
388
|
const freshTtlMs = jitteredTtlMs(cacheOptions.ttlMs, cacheOptions.jitterPct);
|
|
230
389
|
const staleIfErrorMs = cacheOptions.staleIfErrorMs ?? 0;
|
|
@@ -238,9 +397,21 @@ export function createProviderCache(options) {
|
|
|
238
397
|
rememberEnvelope(key, envelope, currentTime);
|
|
239
398
|
const redis = backend.redis;
|
|
240
399
|
if (!redis || !(await ensureRedisReady(redis, DEFAULT_REDIS_TIMEOUT_MS))) {
|
|
400
|
+
if (redis) {
|
|
401
|
+
redisUnavailable = true;
|
|
402
|
+
telemetry?.recordRedisFallback();
|
|
403
|
+
telemetry?.recordWriteError?.();
|
|
404
|
+
}
|
|
241
405
|
return;
|
|
242
406
|
}
|
|
243
|
-
await withRedisFallback(() => redis.set(key, JSON.stringify(envelope), "PX", staleTtlMs));
|
|
407
|
+
const written = await withRedisFallback(() => redis.set(key, JSON.stringify(envelope), "PX", staleTtlMs));
|
|
408
|
+
if (written === undefined) {
|
|
409
|
+
redisUnavailable = true;
|
|
410
|
+
telemetry?.recordRedisFallback();
|
|
411
|
+
telemetry?.recordWriteError?.();
|
|
412
|
+
}
|
|
413
|
+
else
|
|
414
|
+
telemetry?.recordRedisRoundTrip?.();
|
|
244
415
|
}
|
|
245
416
|
async function loadAndStore(key, loader, cacheOptions, staleCandidate) {
|
|
246
417
|
try {
|
|
@@ -260,29 +431,53 @@ export function createProviderCache(options) {
|
|
|
260
431
|
if (staleCandidate?.meta.stale) {
|
|
261
432
|
return staleCandidate;
|
|
262
433
|
}
|
|
434
|
+
telemetry?.recordLookup({
|
|
435
|
+
key,
|
|
436
|
+
hit: false,
|
|
437
|
+
stale: false,
|
|
438
|
+
source: "loader",
|
|
439
|
+
});
|
|
440
|
+
telemetry?.recordLoaderError?.();
|
|
263
441
|
throw error;
|
|
264
442
|
}
|
|
265
443
|
}
|
|
266
444
|
return {
|
|
267
445
|
key(namespace, parts, keyOptions) {
|
|
268
446
|
const extra = new Set((keyOptions?.redactFields ?? []).map((field) => field.toLowerCase()));
|
|
269
|
-
const normalized = normalizeKeyPart(parts, extra);
|
|
270
|
-
|
|
447
|
+
const normalized = normalizeKeyPart(parts, extra, pepper);
|
|
448
|
+
const key = `${DEFAULT_PREFIX}:${options.providerId}:${namespace}:${stableHash(normalized.value)}`;
|
|
449
|
+
if (normalized.secretScoped)
|
|
450
|
+
secretScopedKeys.add(key);
|
|
451
|
+
return key;
|
|
271
452
|
},
|
|
272
453
|
async get(key) {
|
|
273
454
|
const result = await read(key);
|
|
274
455
|
if (result)
|
|
275
456
|
record(result.meta);
|
|
457
|
+
else
|
|
458
|
+
telemetry.recordLookup({
|
|
459
|
+
key,
|
|
460
|
+
hit: false,
|
|
461
|
+
stale: false,
|
|
462
|
+
source: backend.redis && !redisUnavailable ? "redis" : "memory",
|
|
463
|
+
});
|
|
276
464
|
return result;
|
|
277
465
|
},
|
|
278
466
|
set: write,
|
|
279
467
|
async delete(key) {
|
|
468
|
+
telemetry?.recordWrite();
|
|
280
469
|
backend.memory.delete(key);
|
|
281
470
|
const redis = backend.redis;
|
|
282
471
|
if (!redis || !(await ensureRedisReady(redis, DEFAULT_REDIS_TIMEOUT_MS))) {
|
|
472
|
+
if (redis)
|
|
473
|
+
telemetry?.recordRedisFallback();
|
|
283
474
|
return;
|
|
284
475
|
}
|
|
285
|
-
await withRedisFallback(() => redis.del(key));
|
|
476
|
+
const deleted = await withRedisFallback(() => redis.del(key));
|
|
477
|
+
if (deleted === undefined)
|
|
478
|
+
telemetry?.recordRedisFallback();
|
|
479
|
+
else
|
|
480
|
+
telemetry?.recordRedisRoundTrip?.();
|
|
286
481
|
},
|
|
287
482
|
async getOrSet(key, loader, cacheOptions) {
|
|
288
483
|
const existing = await read(key);
|
|
@@ -292,10 +487,23 @@ export function createProviderCache(options) {
|
|
|
292
487
|
}
|
|
293
488
|
const existingInflight = backend.inflight.get(key);
|
|
294
489
|
if (existingInflight) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
490
|
+
try {
|
|
491
|
+
const inflightResult = await existingInflight;
|
|
492
|
+
const result = resultWithValue(inflightResult.value, inflightResult.meta);
|
|
493
|
+
record(result.meta);
|
|
494
|
+
return result;
|
|
495
|
+
}
|
|
496
|
+
catch (error) {
|
|
497
|
+
const meta = {
|
|
498
|
+
key,
|
|
499
|
+
hit: false,
|
|
500
|
+
stale: false,
|
|
501
|
+
source: "loader",
|
|
502
|
+
};
|
|
503
|
+
telemetry.recordLookup(meta);
|
|
504
|
+
telemetry?.recordLoaderError?.();
|
|
505
|
+
throw error;
|
|
506
|
+
}
|
|
299
507
|
}
|
|
300
508
|
const promise = loadAndStore(key, loader, cacheOptions, existing).finally(() => {
|
|
301
509
|
backend.inflight.delete(key);
|
|
@@ -312,19 +520,30 @@ export function createProviderCache(options) {
|
|
|
312
520
|
return {
|
|
313
521
|
hit: events.some((event) => event.hit),
|
|
314
522
|
stale: events.some((event) => event.stale),
|
|
315
|
-
keys:
|
|
523
|
+
keys: metadataKeys(events, secretScopedKeys),
|
|
316
524
|
source: sourceSummary(events),
|
|
317
525
|
};
|
|
318
526
|
},
|
|
319
527
|
};
|
|
320
528
|
}
|
|
529
|
+
const bypassCaches = new WeakSet();
|
|
530
|
+
/** Internal cache capability, separate from hit/miss metadata. */
|
|
531
|
+
export function isProviderCacheBypassed(cache) {
|
|
532
|
+
return bypassCaches.has(cache);
|
|
533
|
+
}
|
|
321
534
|
export function createBypassProviderCache(options) {
|
|
535
|
+
const configuredPepper = readDiagnosticEnv(APIFUSE__CACHE__KEY_PEPPER_ENV);
|
|
536
|
+
const pepper = configuredPepper && configuredPepper.length > 0 ? configuredPepper : undefined;
|
|
322
537
|
const events = [];
|
|
323
|
-
|
|
538
|
+
const secretScopedKeys = new Set();
|
|
539
|
+
const cache = {
|
|
324
540
|
key(namespace, parts, keyOptions) {
|
|
325
541
|
const extra = new Set((keyOptions?.redactFields ?? []).map((field) => field.toLowerCase()));
|
|
326
|
-
const normalized = normalizeKeyPart(parts, extra);
|
|
327
|
-
|
|
542
|
+
const normalized = normalizeKeyPart(parts, extra, pepper);
|
|
543
|
+
const key = `${DEFAULT_PREFIX}:${options.providerId}:${namespace}:${stableHash(normalized.value)}`;
|
|
544
|
+
if (normalized.secretScoped)
|
|
545
|
+
secretScopedKeys.add(key);
|
|
546
|
+
return key;
|
|
328
547
|
},
|
|
329
548
|
async get(_key) {
|
|
330
549
|
return null;
|
|
@@ -352,11 +571,13 @@ export function createBypassProviderCache(options) {
|
|
|
352
571
|
return {
|
|
353
572
|
hit: false,
|
|
354
573
|
stale: false,
|
|
355
|
-
keys:
|
|
574
|
+
keys: metadataKeys(events, secretScopedKeys),
|
|
356
575
|
source: sourceSummary(events),
|
|
357
576
|
};
|
|
358
577
|
},
|
|
359
578
|
};
|
|
579
|
+
bypassCaches.add(cache);
|
|
580
|
+
return cache;
|
|
360
581
|
}
|
|
361
582
|
export function resetProviderCacheForTests() {
|
|
362
583
|
for (const backend of sharedBackends.values()) {
|
|
@@ -365,4 +586,5 @@ export function resetProviderCacheForTests() {
|
|
|
365
586
|
backend.redis?.disconnect();
|
|
366
587
|
}
|
|
367
588
|
sharedBackends.clear();
|
|
589
|
+
warnedAboutUnpepperedSecretKeys = false;
|
|
368
590
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const CAPABILITY_BODY_UNAVAILABLE = "<body unavailable>";
|
|
2
|
+
/** Read optional diagnostic response text without letting a body stall the request failure. */
|
|
3
|
+
export declare function readCapabilityErrorBody(response: Response, signal: AbortSignal): Promise<string>;
|