@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,668 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
allSensitiveValueVariants,
|
|
5
|
+
normalizePercentEscapes,
|
|
6
|
+
REDACTED_QUERY_VALUE,
|
|
7
|
+
type SensitiveValueVariant,
|
|
8
|
+
} from "./request-options.js";
|
|
9
|
+
import { RESOLVER_CLOSED_ENUM_SPAN_ATTRIBUTES } from "./resolver-span-attributes.js";
|
|
10
|
+
|
|
11
|
+
export type DiagnosticRedactor = (text: string) => string;
|
|
12
|
+
export const REDACTION_FAILED = "[REDACTION_FAILED]";
|
|
13
|
+
// No supported credential source needs 1–3 character secrets. Registering these
|
|
14
|
+
// common tokens damages unrelated IDs and diagnostics even with token boundaries.
|
|
15
|
+
// Apply the same floor to static, fallback and request inventories (as self-test does).
|
|
16
|
+
const MINIMUM_REGISTERED_VALUE_LENGTH = 4;
|
|
17
|
+
|
|
18
|
+
/** Observer failures must never return the original diagnostic, including via ?? fallbacks. */
|
|
19
|
+
export function redactDiagnosticText(text: string, redact?: DiagnosticRedactor): string {
|
|
20
|
+
try {
|
|
21
|
+
const result = redact ? redact(text) : text;
|
|
22
|
+
return typeof result === "string" ? result : REDACTION_FAILED;
|
|
23
|
+
} catch {
|
|
24
|
+
return REDACTION_FAILED;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DiagnosticSensitiveRegistry {
|
|
29
|
+
redact: DiagnosticRedactor;
|
|
30
|
+
readonly redactCritical: DiagnosticRedactor;
|
|
31
|
+
readonly redactStructured: DiagnosticRedactor;
|
|
32
|
+
readonly suppressed: boolean;
|
|
33
|
+
has(value: string): boolean;
|
|
34
|
+
add(values: readonly string[]): void;
|
|
35
|
+
suppress(): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type Node = { next: Map<string, Node>; boundary?: boolean };
|
|
39
|
+
type Matcher = {
|
|
40
|
+
exact: Node;
|
|
41
|
+
folded: Node;
|
|
42
|
+
hasFolded: boolean;
|
|
43
|
+
canMatchPercentEncoded: boolean;
|
|
44
|
+
size: number;
|
|
45
|
+
minimumLength: number;
|
|
46
|
+
cache: Map<string, string>;
|
|
47
|
+
needles?: SensitiveValueVariant[];
|
|
48
|
+
encoded?: Matcher;
|
|
49
|
+
};
|
|
50
|
+
function node(): Node {
|
|
51
|
+
return { next: new Map() };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function compile(variants: readonly SensitiveValueVariant[]): Matcher {
|
|
55
|
+
const matcher: Matcher = {
|
|
56
|
+
exact: node(),
|
|
57
|
+
folded: node(),
|
|
58
|
+
hasFolded: false,
|
|
59
|
+
// Only long values participate in encoded matching. Unreserved ASCII is
|
|
60
|
+
// percent-encodable too (%6f for o); encodeURIComponent(value) === value
|
|
61
|
+
// does NOT prove that residual escapes cannot conceal that credential.
|
|
62
|
+
canMatchPercentEncoded: variants.some((variant) => variant.caseInsensitive),
|
|
63
|
+
size: variants.length,
|
|
64
|
+
minimumLength: variants.reduce(
|
|
65
|
+
(minimum, variant) => Math.min(minimum, variant.value.length),
|
|
66
|
+
Infinity,
|
|
67
|
+
),
|
|
68
|
+
cache: new Map(),
|
|
69
|
+
};
|
|
70
|
+
if (variants.some((variant) => !variant.caseInsensitive))
|
|
71
|
+
matcher.encoded = compile(variants.filter((variant) => variant.caseInsensitive));
|
|
72
|
+
// A few request credentials are cheaper as native literal searches; large
|
|
73
|
+
// process-static inventories use tries to avoid per-needle scans.
|
|
74
|
+
if (variants.length <= 16) {
|
|
75
|
+
matcher.needles = variants.map((variant) => ({
|
|
76
|
+
...variant,
|
|
77
|
+
value: variant.caseInsensitive ? variant.value.toLowerCase() : variant.value,
|
|
78
|
+
}));
|
|
79
|
+
matcher.hasFolded = variants.some((variant) => variant.caseInsensitive);
|
|
80
|
+
return matcher;
|
|
81
|
+
}
|
|
82
|
+
for (const variant of variants) {
|
|
83
|
+
let current = variant.caseInsensitive ? matcher.folded : matcher.exact;
|
|
84
|
+
if (variant.caseInsensitive) matcher.hasFolded = true;
|
|
85
|
+
const value = variant.caseInsensitive ? variant.value.toLowerCase() : variant.value;
|
|
86
|
+
for (let index = 0; index < value.length; index++) {
|
|
87
|
+
const character = value[index];
|
|
88
|
+
let child = current.next.get(character);
|
|
89
|
+
if (!child) {
|
|
90
|
+
child = node();
|
|
91
|
+
current.next.set(character, child);
|
|
92
|
+
}
|
|
93
|
+
current = child;
|
|
94
|
+
}
|
|
95
|
+
current.boundary =
|
|
96
|
+
current.boundary === undefined
|
|
97
|
+
? variant.requiresTokenBoundary
|
|
98
|
+
: current.boundary && variant.requiresTokenBoundary;
|
|
99
|
+
}
|
|
100
|
+
return matcher;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Opaque handle: callers cannot modify either of the two independently compiled matchers. */
|
|
104
|
+
export interface CompiledDiagnosticSensitiveValues {
|
|
105
|
+
readonly kind: "compiled-diagnostic-sensitive-values";
|
|
106
|
+
}
|
|
107
|
+
const compiledValues = new WeakMap<
|
|
108
|
+
CompiledDiagnosticSensitiveValues,
|
|
109
|
+
{
|
|
110
|
+
primary: Matcher;
|
|
111
|
+
check: Matcher;
|
|
112
|
+
criticalPrimary: Matcher;
|
|
113
|
+
criticalCheck: Matcher;
|
|
114
|
+
values: ReadonlySet<string>;
|
|
115
|
+
}
|
|
116
|
+
>();
|
|
117
|
+
|
|
118
|
+
export function compileDiagnosticSensitiveValues(
|
|
119
|
+
values: readonly string[],
|
|
120
|
+
): CompiledDiagnosticSensitiveValues {
|
|
121
|
+
const handle: CompiledDiagnosticSensitiveValues = Object.freeze({
|
|
122
|
+
kind: "compiled-diagnostic-sensitive-values",
|
|
123
|
+
});
|
|
124
|
+
const registered = values.filter((value) => value.length >= MINIMUM_REGISTERED_VALUE_LENGTH);
|
|
125
|
+
const variants = allSensitiveValueVariants(registered);
|
|
126
|
+
const primary = compile(variants),
|
|
127
|
+
check = compile(variants);
|
|
128
|
+
compiledValues.set(handle, {
|
|
129
|
+
primary,
|
|
130
|
+
check,
|
|
131
|
+
criticalPrimary: primary.encoded ?? primary,
|
|
132
|
+
criticalCheck: check.encoded ?? check,
|
|
133
|
+
values: new Set(registered),
|
|
134
|
+
});
|
|
135
|
+
return handle;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Outside-context reads are process lifetime data, bounded like request harvesting.
|
|
139
|
+
const PROCESS_VALUE_LIMIT = 1024;
|
|
140
|
+
const PROCESS_BYTE_LIMIT = 64 * 1024;
|
|
141
|
+
function createDiagnosticFallback() {
|
|
142
|
+
return {
|
|
143
|
+
values: new Set<string>(),
|
|
144
|
+
bytes: 0,
|
|
145
|
+
suppressed: false,
|
|
146
|
+
version: 0,
|
|
147
|
+
compiledVersion: -1,
|
|
148
|
+
compiled: undefined as CompiledDiagnosticSensitiveValues | undefined,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
type DiagnosticFallback = ReturnType<typeof createDiagnosticFallback>;
|
|
152
|
+
const processFallback = createDiagnosticFallback();
|
|
153
|
+
const fallbackScopes = new AsyncLocalStorage<DiagnosticFallback>();
|
|
154
|
+
const processCompositions = new WeakMap<CompiledDiagnosticSensitiveValues, DiagnosticFallback>();
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Give an independent runtime/configuration evaluation its own fallback lifetime.
|
|
158
|
+
* Apps created inside the scope retain that inventory, including across awaits and
|
|
159
|
+
* after the callback returns. Ordinary startup/CLI reads keep the process lifetime.
|
|
160
|
+
* This also lets test harnesses isolate synthetic env reads without clearing live
|
|
161
|
+
* apps' credentials or weakening registration, matching, or exhaustion policies.
|
|
162
|
+
*/
|
|
163
|
+
export function withDiagnosticFallbackScope<T>(fn: () => T): T {
|
|
164
|
+
return fallbackScopes.run(createDiagnosticFallback(), fn);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Re-enter the app's existing lifetime, including for detached request callbacks. */
|
|
168
|
+
export function withAppDiagnosticFallback<T>(
|
|
169
|
+
staticValues: CompiledDiagnosticSensitiveValues,
|
|
170
|
+
fn: () => T,
|
|
171
|
+
): T {
|
|
172
|
+
return fallbackScopes.run(
|
|
173
|
+
processCompositions.get(staticValues) ?? currentDiagnosticFallback(),
|
|
174
|
+
fn,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function currentDiagnosticFallback(): DiagnosticFallback {
|
|
179
|
+
return fallbackScopes.getStore() ?? processFallback;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function suppressProcessDiagnosticValues(): void {
|
|
183
|
+
const fallback = currentDiagnosticFallback();
|
|
184
|
+
if (fallback.suppressed) return;
|
|
185
|
+
fallback.suppressed = true;
|
|
186
|
+
console.warn(
|
|
187
|
+
"[apifuse] diagnostic redaction failed closed; reason=static_registry_limit; limit=1024_entries/65536_bytes",
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function registerProcessDiagnosticValues(values: readonly string[]): void {
|
|
192
|
+
const fallback = currentDiagnosticFallback();
|
|
193
|
+
if (fallback.suppressed) return;
|
|
194
|
+
for (const value of values) {
|
|
195
|
+
if (value.length < MINIMUM_REGISTERED_VALUE_LENGTH || fallback.values.has(value)) continue;
|
|
196
|
+
const bytes = Buffer.byteLength(value);
|
|
197
|
+
if (
|
|
198
|
+
fallback.values.size >= PROCESS_VALUE_LIMIT ||
|
|
199
|
+
fallback.bytes + bytes > PROCESS_BYTE_LIMIT
|
|
200
|
+
) {
|
|
201
|
+
suppressProcessDiagnosticValues();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
fallback.values.add(value);
|
|
205
|
+
fallback.bytes += bytes;
|
|
206
|
+
fallback.version++;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** The app's static inventory and the shared outside-context fallback form one static part. */
|
|
211
|
+
export function compileProcessDiagnosticSensitiveValues(
|
|
212
|
+
values: readonly string[],
|
|
213
|
+
): CompiledDiagnosticSensitiveValues {
|
|
214
|
+
const handle = compileDiagnosticSensitiveValues(values);
|
|
215
|
+
processCompositions.set(handle, currentDiagnosticFallback());
|
|
216
|
+
return handle;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function isTokenCharacter(value: string | undefined): boolean {
|
|
220
|
+
return value !== undefined && /[\p{L}\p{N}]/u.test(value);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function matchLength(root: Node, comparable: string, original: string, start: number): number {
|
|
224
|
+
let current = root;
|
|
225
|
+
let length = 0;
|
|
226
|
+
for (let index = start; index < comparable.length; index++) {
|
|
227
|
+
const child = current.next.get(comparable[index]);
|
|
228
|
+
if (!child) break;
|
|
229
|
+
current = child;
|
|
230
|
+
if (
|
|
231
|
+
current.boundary !== undefined &&
|
|
232
|
+
(!current.boundary ||
|
|
233
|
+
(!isTokenCharacter(original[start - 1]) && !isTokenCharacter(original[index + 1])))
|
|
234
|
+
) {
|
|
235
|
+
length = index - start + 1;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return length;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function replaceNeedles(text: string, matcher: Matcher, checkOnly: boolean): string {
|
|
242
|
+
const comparable = text.includes("%") ? normalizePercentEscapes(text) : text;
|
|
243
|
+
const folded = matcher.hasFolded ? comparable.toLowerCase() : comparable;
|
|
244
|
+
let cursor = 0;
|
|
245
|
+
let output = "";
|
|
246
|
+
while (cursor < text.length) {
|
|
247
|
+
let first = text.length;
|
|
248
|
+
let length = 0;
|
|
249
|
+
for (const variant of matcher.needles ?? []) {
|
|
250
|
+
const source = variant.caseInsensitive ? folded : text;
|
|
251
|
+
let index = source.indexOf(variant.value, cursor);
|
|
252
|
+
while (index !== -1) {
|
|
253
|
+
const end = index + variant.value.length;
|
|
254
|
+
const markerStart = source.lastIndexOf("[", index);
|
|
255
|
+
const withinMarker =
|
|
256
|
+
markerStart !== -1 && index < markerStart + markerLength(comparable, markerStart);
|
|
257
|
+
if (
|
|
258
|
+
!withinMarker &&
|
|
259
|
+
(!variant.requiresTokenBoundary ||
|
|
260
|
+
(!isTokenCharacter(source[index - 1]) && !isTokenCharacter(source[end])))
|
|
261
|
+
)
|
|
262
|
+
break;
|
|
263
|
+
index = source.indexOf(variant.value, index + 1);
|
|
264
|
+
}
|
|
265
|
+
if (index === -1) continue;
|
|
266
|
+
if (checkOnly || (variant.caseInsensitive && folded.length !== text.length))
|
|
267
|
+
return REDACTED_QUERY_VALUE;
|
|
268
|
+
if (index < first || (index === first && variant.value.length > length)) {
|
|
269
|
+
first = index;
|
|
270
|
+
length = variant.value.length;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (!length) break;
|
|
274
|
+
output += text.slice(cursor, first) + REDACTED_QUERY_VALUE;
|
|
275
|
+
cursor = first + length;
|
|
276
|
+
}
|
|
277
|
+
return cursor ? output + text.slice(cursor) : text;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Literal tries have no secret-built regular expressions and no per-needle no-match scans. */
|
|
281
|
+
function replaceMatches(text: string, matchers: readonly Matcher[], checkOnly = false): string {
|
|
282
|
+
if (matchers.length === 1 && matchers[0].needles)
|
|
283
|
+
return replaceNeedles(text, matchers[0], checkOnly);
|
|
284
|
+
const comparable = text.includes("%") ? normalizePercentEscapes(text) : text;
|
|
285
|
+
const folded = matchers.some((matcher) => matcher.hasFolded)
|
|
286
|
+
? comparable.toLowerCase()
|
|
287
|
+
: comparable;
|
|
288
|
+
// Unicode case folding can change offsets. Suppress the field on a folded match.
|
|
289
|
+
if (folded.length !== comparable.length) {
|
|
290
|
+
for (const matcher of matchers) {
|
|
291
|
+
for (let index = 0; index < folded.length; index++) {
|
|
292
|
+
if (matchLength(matcher.folded, folded, folded, index)) return REDACTED_QUERY_VALUE;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
let cursor = 0;
|
|
297
|
+
let output = "";
|
|
298
|
+
for (let index = 0; index < text.length; index++) {
|
|
299
|
+
// Sentinels are emitted constants, not evidence of a residual credential.
|
|
300
|
+
const marker = markerLength(text, index);
|
|
301
|
+
if (marker) {
|
|
302
|
+
index += marker - 1;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
let length = 0;
|
|
306
|
+
for (const matcher of matchers) {
|
|
307
|
+
length = Math.max(length, matchLength(matcher.exact, text, text, index));
|
|
308
|
+
if (matcher.hasFolded && folded.length === comparable.length) {
|
|
309
|
+
length = Math.max(length, matchLength(matcher.folded, folded, text, index));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (!length) continue;
|
|
313
|
+
if (checkOnly) return REDACTED_QUERY_VALUE;
|
|
314
|
+
output += text.slice(cursor, index) + REDACTED_QUERY_VALUE;
|
|
315
|
+
cursor = index + length;
|
|
316
|
+
index = cursor - 1;
|
|
317
|
+
}
|
|
318
|
+
return cursor === 0 ? text : output + text.slice(cursor);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function decodePercent(text: string): string {
|
|
322
|
+
try {
|
|
323
|
+
return decodeURIComponent(text);
|
|
324
|
+
} catch {
|
|
325
|
+
// An unrelated malformed escape must not hide valid encoded credential text.
|
|
326
|
+
return text.replace(/(?:%[\da-f]{2})+/gi, (part) => {
|
|
327
|
+
try {
|
|
328
|
+
return decodeURIComponent(part);
|
|
329
|
+
} catch {
|
|
330
|
+
return part.replace(/%([\da-f]{2})/gi, (encodedByte, hex: string) => {
|
|
331
|
+
const byte = Number.parseInt(hex, 16);
|
|
332
|
+
return byte < 128 ? String.fromCharCode(byte) : encodedByte;
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function applyMatcher(text: string, matcher: Matcher): string {
|
|
340
|
+
if (!matcher.size) return text;
|
|
341
|
+
// ASCII cannot grow under NFC/case folding; without escapes, a shorter field
|
|
342
|
+
// cannot contain any needle. Keep decoding and Unicode checks on the full path.
|
|
343
|
+
if (text.length < matcher.minimumLength && !text.includes("%") && !/[\u0080-\uffff]/.test(text))
|
|
344
|
+
return text;
|
|
345
|
+
const cached = matcher.cache.get(text);
|
|
346
|
+
if (cached !== undefined) return cached;
|
|
347
|
+
const matchers = [matcher];
|
|
348
|
+
const direct = replaceMatches(text, matchers);
|
|
349
|
+
let result = direct;
|
|
350
|
+
let normalized = direct;
|
|
351
|
+
for (let round = 0; round < 3 && normalized.includes("%"); round++) {
|
|
352
|
+
const decoded = decodePercent(normalized);
|
|
353
|
+
if (decoded === normalized) break;
|
|
354
|
+
normalized = decoded;
|
|
355
|
+
if (replaceMatches(normalized, [matcher.encoded ?? matcher], true) !== normalized) {
|
|
356
|
+
result = REDACTED_QUERY_VALUE;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
// A residual escape is unresolved after the fixed work budget: emit less,
|
|
360
|
+
// if this nonempty registry can match percent-encoded credentials, even when
|
|
361
|
+
// another representation matched. Both independent roles use this.
|
|
362
|
+
if (matcher.canMatchPercentEncoded && /%[\da-f]{2}/i.test(normalized)) result = REDACTION_FAILED;
|
|
363
|
+
const nfc = normalized.normalize("NFC");
|
|
364
|
+
if (
|
|
365
|
+
result !== REDACTION_FAILED &&
|
|
366
|
+
nfc !== normalized &&
|
|
367
|
+
replaceMatches(nfc, [matcher.encoded ?? matcher], true) !== nfc
|
|
368
|
+
)
|
|
369
|
+
result = REDACTED_QUERY_VALUE;
|
|
370
|
+
// Matchers are immutable; additions compile a new request matcher. Independent
|
|
371
|
+
// request-local caches reuse results across repeated span/resource exports. Bound
|
|
372
|
+
// entry count and input size; process-static lookup data never caches request text.
|
|
373
|
+
if (text.length <= 4096) {
|
|
374
|
+
if (matcher.cache.size >= 256) matcher.cache.clear();
|
|
375
|
+
matcher.cache.set(text, result);
|
|
376
|
+
}
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function applyMatchers(text: string, matchers: readonly Matcher[]): string {
|
|
381
|
+
let result = text;
|
|
382
|
+
for (const matcher of matchers) {
|
|
383
|
+
const candidate = applyMatcher(text, matcher);
|
|
384
|
+
if (candidate === text) continue;
|
|
385
|
+
// Static and request secrets may overlap. Scan original text independently
|
|
386
|
+
// so an earlier replacement cannot hide the rest of a longer credential.
|
|
387
|
+
if (result !== text && result !== candidate) return REDACTED_QUERY_VALUE;
|
|
388
|
+
result = candidate;
|
|
389
|
+
}
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** One small mutable registry per request, composed with precompiled process-static values. */
|
|
394
|
+
export function createDiagnosticRedactor(
|
|
395
|
+
values: readonly string[] = [],
|
|
396
|
+
staticValues?: CompiledDiagnosticSensitiveValues,
|
|
397
|
+
): DiagnosticSensitiveRegistry {
|
|
398
|
+
const compiled = staticValues ? compiledValues.get(staticValues) : undefined;
|
|
399
|
+
// Share immutable lookup data only. Raw diagnostic cache keys die with this scope.
|
|
400
|
+
const requestMatcher = (matcher: Matcher): Matcher => ({ ...matcher, cache: new Map() });
|
|
401
|
+
const staticPrimary = compiled?.primary.size ? requestMatcher(compiled.primary) : undefined;
|
|
402
|
+
const staticCheck = compiled?.check.size ? requestMatcher(compiled.check) : undefined;
|
|
403
|
+
const staticCriticalPrimary = compiled?.criticalPrimary.size
|
|
404
|
+
? compiled.criticalPrimary === compiled.primary
|
|
405
|
+
? staticPrimary
|
|
406
|
+
: requestMatcher(compiled.criticalPrimary)
|
|
407
|
+
: undefined;
|
|
408
|
+
const staticCriticalCheck = compiled?.criticalCheck.size
|
|
409
|
+
? compiled.criticalCheck === compiled.check
|
|
410
|
+
? staticCheck
|
|
411
|
+
: requestMatcher(compiled.criticalCheck)
|
|
412
|
+
: undefined;
|
|
413
|
+
const known = new Set<string>();
|
|
414
|
+
const fallback = staticValues === undefined ? undefined : processCompositions.get(staticValues);
|
|
415
|
+
const has = (value: string) =>
|
|
416
|
+
known.has(value) ||
|
|
417
|
+
compiled?.values.has(value) === true ||
|
|
418
|
+
fallback?.values.has(value) === true;
|
|
419
|
+
let seenProcessVersion = -1;
|
|
420
|
+
let fallbackPrimary: Matcher[] = [];
|
|
421
|
+
let fallbackCheck: Matcher[] = [];
|
|
422
|
+
let fallbackCriticalPrimary: Matcher[] = [];
|
|
423
|
+
let fallbackCriticalCheck: Matcher[] = [];
|
|
424
|
+
let requestPrimary: Matcher | undefined;
|
|
425
|
+
let requestCheck: Matcher | undefined;
|
|
426
|
+
let suppressed = false;
|
|
427
|
+
let primary: Matcher[] = staticPrimary ? [staticPrimary] : [];
|
|
428
|
+
let check: Matcher[] = staticCheck ? [staticCheck] : [];
|
|
429
|
+
let criticalPrimary: Matcher[] = staticCriticalPrimary ? [staticCriticalPrimary] : [];
|
|
430
|
+
let criticalCheck: Matcher[] = staticCriticalCheck ? [staticCriticalCheck] : [];
|
|
431
|
+
const variants: SensitiveValueVariant[] = [];
|
|
432
|
+
let uncompiled: string[] = [];
|
|
433
|
+
const add = (incoming: readonly string[]) => {
|
|
434
|
+
for (const value of incoming) {
|
|
435
|
+
if (value.length < MINIMUM_REGISTERED_VALUE_LENGTH || has(value)) continue;
|
|
436
|
+
known.add(value);
|
|
437
|
+
uncompiled.push(value);
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
const ensureCompiled = () => {
|
|
441
|
+
const processChanged = fallback !== undefined && seenProcessVersion !== fallback.version;
|
|
442
|
+
if (!uncompiled.length && !processChanged) return;
|
|
443
|
+
if (processChanged) {
|
|
444
|
+
if (fallback.compiledVersion !== fallback.version) {
|
|
445
|
+
fallback.compiled = compileDiagnosticSensitiveValues([...fallback.values]);
|
|
446
|
+
fallback.compiledVersion = fallback.version;
|
|
447
|
+
}
|
|
448
|
+
const compiledFallback = fallback.compiled && compiledValues.get(fallback.compiled);
|
|
449
|
+
fallbackPrimary = compiledFallback?.primary.size
|
|
450
|
+
? [requestMatcher(compiledFallback.primary)]
|
|
451
|
+
: [];
|
|
452
|
+
fallbackCheck = compiledFallback?.check.size ? [requestMatcher(compiledFallback.check)] : [];
|
|
453
|
+
fallbackCriticalPrimary = compiledFallback?.criticalPrimary.size
|
|
454
|
+
? [requestMatcher(compiledFallback.criticalPrimary)]
|
|
455
|
+
: [];
|
|
456
|
+
fallbackCriticalCheck = compiledFallback?.criticalCheck.size
|
|
457
|
+
? [requestMatcher(compiledFallback.criticalCheck)]
|
|
458
|
+
: [];
|
|
459
|
+
seenProcessVersion = fallback.version;
|
|
460
|
+
}
|
|
461
|
+
if (uncompiled.length) {
|
|
462
|
+
// Generate each value's variants once, lazily on the first actual match.
|
|
463
|
+
variants.push(...allSensitiveValueVariants(uncompiled));
|
|
464
|
+
uncompiled = [];
|
|
465
|
+
requestPrimary = compile(variants);
|
|
466
|
+
requestCheck = compile(variants);
|
|
467
|
+
}
|
|
468
|
+
primary = [
|
|
469
|
+
...(staticPrimary ? [staticPrimary] : []),
|
|
470
|
+
...fallbackPrimary,
|
|
471
|
+
...(requestPrimary ? [requestPrimary] : []),
|
|
472
|
+
];
|
|
473
|
+
check = [
|
|
474
|
+
...(staticCheck ? [staticCheck] : []),
|
|
475
|
+
...fallbackCheck,
|
|
476
|
+
...(requestCheck ? [requestCheck] : []),
|
|
477
|
+
];
|
|
478
|
+
criticalPrimary = [
|
|
479
|
+
...(staticCriticalPrimary ? [staticCriticalPrimary] : []),
|
|
480
|
+
...fallbackCriticalPrimary,
|
|
481
|
+
...(requestPrimary ? [requestPrimary.encoded ?? requestPrimary] : []),
|
|
482
|
+
];
|
|
483
|
+
criticalCheck = [
|
|
484
|
+
...(staticCriticalCheck ? [staticCriticalCheck] : []),
|
|
485
|
+
...fallbackCriticalCheck,
|
|
486
|
+
...(requestCheck ? [requestCheck.encoded ?? requestCheck] : []),
|
|
487
|
+
];
|
|
488
|
+
};
|
|
489
|
+
add(values);
|
|
490
|
+
const protect =
|
|
491
|
+
(callback: DiagnosticRedactor, critical = false, freeText = !critical): DiagnosticRedactor =>
|
|
492
|
+
(text) => {
|
|
493
|
+
if (text === REDACTED_QUERY_VALUE || text === REDACTION_FAILED) return text;
|
|
494
|
+
if ((suppressed || fallback?.suppressed) && freeText) return REDACTION_FAILED;
|
|
495
|
+
ensureCompiled();
|
|
496
|
+
const result = redactDiagnosticText(text, callback);
|
|
497
|
+
ensureCompiled(); // A replaced callback may register a value during the call.
|
|
498
|
+
if ((suppressed || fallback?.suppressed) && freeText) return REDACTION_FAILED;
|
|
499
|
+
// This private matcher is a separate object from the callback's matcher,
|
|
500
|
+
// survives callback replacement, and sees additions made after trace creation.
|
|
501
|
+
return applyMatchers(result, critical ? criticalCheck : check) === result
|
|
502
|
+
? result
|
|
503
|
+
: REDACTION_FAILED;
|
|
504
|
+
};
|
|
505
|
+
let safeRedact = protect((text) => applyMatchers(text, primary));
|
|
506
|
+
let safeCritical = protect((text) => applyMatchers(text, criticalPrimary), true);
|
|
507
|
+
let safeStructured = protect((text) => applyMatchers(text, primary), false, false);
|
|
508
|
+
const critical = (text: string) => safeCritical(text);
|
|
509
|
+
const structured = (text: string) => safeStructured(text);
|
|
510
|
+
const isSuppressed = () => suppressed || fallback?.suppressed === true;
|
|
511
|
+
bindCriticalDiagnosticRedactor(safeRedact, critical, structured, isSuppressed);
|
|
512
|
+
return {
|
|
513
|
+
add,
|
|
514
|
+
has,
|
|
515
|
+
redactCritical: critical,
|
|
516
|
+
redactStructured: structured,
|
|
517
|
+
get suppressed() {
|
|
518
|
+
return isSuppressed();
|
|
519
|
+
},
|
|
520
|
+
suppress() {
|
|
521
|
+
suppressed = true;
|
|
522
|
+
},
|
|
523
|
+
get redact() {
|
|
524
|
+
return safeRedact;
|
|
525
|
+
},
|
|
526
|
+
set redact(callback: DiagnosticRedactor) {
|
|
527
|
+
safeRedact = protect(callback);
|
|
528
|
+
safeCritical = protect(callback, true);
|
|
529
|
+
safeStructured = protect(callback, false, false);
|
|
530
|
+
bindCriticalDiagnosticRedactor(safeRedact, critical, structured, isSuppressed);
|
|
531
|
+
},
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const requestRegistries = new WeakMap<object, DiagnosticSensitiveRegistry>();
|
|
536
|
+
|
|
537
|
+
/** Internal binding; neither the registry nor its values enter provider-facing context types. */
|
|
538
|
+
export function bindDiagnosticSensitiveRegistry(
|
|
539
|
+
trace: object,
|
|
540
|
+
registry: DiagnosticSensitiveRegistry,
|
|
541
|
+
): void {
|
|
542
|
+
requestRegistries.set(trace, registry);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export function registerDiagnosticSensitiveValues(trace: object, values: readonly string[]): void {
|
|
546
|
+
requestRegistries.get(trace)?.add(values);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Policy metadata is internal: public callbacks remain backward-compatible unary functions.
|
|
550
|
+
const criticalRedactors = new WeakMap<DiagnosticRedactor, DiagnosticRedactor>();
|
|
551
|
+
const structuredRedactors = new WeakMap<DiagnosticRedactor, DiagnosticRedactor>();
|
|
552
|
+
const redactionSuppression = new WeakMap<DiagnosticRedactor, () => boolean>();
|
|
553
|
+
export function bindCriticalDiagnosticRedactor(
|
|
554
|
+
redact: DiagnosticRedactor,
|
|
555
|
+
critical: DiagnosticRedactor,
|
|
556
|
+
structured?: DiagnosticRedactor,
|
|
557
|
+
isSuppressed?: () => boolean,
|
|
558
|
+
): void {
|
|
559
|
+
criticalRedactors.set(redact, critical);
|
|
560
|
+
if (structured) structuredRedactors.set(redact, structured);
|
|
561
|
+
if (isSuppressed) redactionSuppression.set(redact, isSuppressed);
|
|
562
|
+
}
|
|
563
|
+
export function isDiagnosticRedactionSuppressed(redact?: DiagnosticRedactor): boolean {
|
|
564
|
+
return redact ? redactionSuppression.get(redact)?.() === true : false;
|
|
565
|
+
}
|
|
566
|
+
/** Typed values and keys still match known credentials, but never blanket suppression. */
|
|
567
|
+
export function diagnosticStructuredRedactor(
|
|
568
|
+
redact?: DiagnosticRedactor,
|
|
569
|
+
): DiagnosticRedactor | undefined {
|
|
570
|
+
return redact ? (structuredRedactors.get(redact) ?? redact) : undefined;
|
|
571
|
+
}
|
|
572
|
+
export function redactDiagnosticAttributeKey(key: string, redact?: DiagnosticRedactor): string {
|
|
573
|
+
return redactDiagnosticText(
|
|
574
|
+
key,
|
|
575
|
+
diagnosticStructuredRedactor(diagnosticAttributeRedactor(key, redact)),
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
export function redactCriticalDiagnosticText(text: string, redact?: DiagnosticRedactor): string {
|
|
579
|
+
return redactDiagnosticText(text, redact ? (criticalRedactors.get(redact) ?? redact) : undefined);
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* The redactor for one attribute value. A value its producer declares as a closed-enum literal for
|
|
583
|
+
* this key is a contract, like a typed number: exhaustion suppression skips it, while registered
|
|
584
|
+
* credentials still match. Any other value under the same key stays free text.
|
|
585
|
+
*/
|
|
586
|
+
export function diagnosticAttributeRedactor(
|
|
587
|
+
key: string,
|
|
588
|
+
redact?: DiagnosticRedactor,
|
|
589
|
+
value?: unknown,
|
|
590
|
+
): DiagnosticRedactor | undefined {
|
|
591
|
+
if (typeof value === "string" && CLOSED_ENUM_ATTRIBUTE_VALUES.get(key)?.has(value)) {
|
|
592
|
+
return diagnosticStructuredRedactor(redact);
|
|
593
|
+
}
|
|
594
|
+
return CRITICAL_ATTRIBUTE_KEYS.has(key) && redact
|
|
595
|
+
? (criticalRedactors.get(redact) ?? redact)
|
|
596
|
+
: redact;
|
|
597
|
+
}
|
|
598
|
+
// Structured debugging contracts use only >=8-character values. Other attributes are free text.
|
|
599
|
+
const CRITICAL_ATTRIBUTE_KEYS = new Set([
|
|
600
|
+
"request_id",
|
|
601
|
+
"requestId",
|
|
602
|
+
"trace_id",
|
|
603
|
+
"traceId",
|
|
604
|
+
"span_id",
|
|
605
|
+
"spanId",
|
|
606
|
+
"route",
|
|
607
|
+
"operation_id",
|
|
608
|
+
"operation",
|
|
609
|
+
"http.status_code",
|
|
610
|
+
]);
|
|
611
|
+
|
|
612
|
+
// SDK-owned closed enums are contracts, like typed numbers and booleans. The table is the
|
|
613
|
+
// producers' own declaration of which span attributes are closed enums and of every literal each
|
|
614
|
+
// one can carry; it exempts only exhaustion suppression, and registered credentials still match.
|
|
615
|
+
const CLOSED_ENUM_ATTRIBUTE_VALUES: ReadonlyMap<string, ReadonlySet<string>> = new Map(
|
|
616
|
+
Object.entries(RESOLVER_CLOSED_ENUM_SPAN_ATTRIBUTES).map(([key, values]) => [
|
|
617
|
+
key,
|
|
618
|
+
new Set<string>(values),
|
|
619
|
+
]),
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
// Bigints normalize to strings for OTLP. Carry their type policy through internal
|
|
623
|
+
// detached copies without treating arbitrary numeric-looking free text as typed.
|
|
624
|
+
const bigintAttributes = new WeakMap<object, ReadonlySet<string>>();
|
|
625
|
+
export function isDiagnosticBigintAttribute(attributes: object, key: string): boolean {
|
|
626
|
+
return bigintAttributes.get(attributes)?.has(key) === true;
|
|
627
|
+
}
|
|
628
|
+
export function copyDiagnosticAttributeTypes<T extends Record<string, unknown>>(
|
|
629
|
+
source: Record<string, unknown>,
|
|
630
|
+
target: T,
|
|
631
|
+
): T {
|
|
632
|
+
const keys = Object.keys(source).filter(
|
|
633
|
+
(key) =>
|
|
634
|
+
(typeof source[key] === "bigint" || isDiagnosticBigintAttribute(source, key)) &&
|
|
635
|
+
String(source[key]) === target[key],
|
|
636
|
+
);
|
|
637
|
+
if (keys.length) bigintAttributes.set(target, new Set(keys));
|
|
638
|
+
return target;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/** Reserve existing keys so independently redacted names never drop an attribute. */
|
|
642
|
+
export function redactedKeyAllocator(keys: Iterable<string>): () => string {
|
|
643
|
+
// Snapshot the inventory now, but allocate its lookup only if a key changes.
|
|
644
|
+
const inventory = [...keys];
|
|
645
|
+
let reserved: Set<string> | undefined;
|
|
646
|
+
let sequence = 0;
|
|
647
|
+
return () => {
|
|
648
|
+
reserved ??= new Set(inventory);
|
|
649
|
+
let key: string;
|
|
650
|
+
do {
|
|
651
|
+
key = `[REDACTED#${++sequence}]`;
|
|
652
|
+
} while (reserved.has(key));
|
|
653
|
+
reserved.add(key);
|
|
654
|
+
return key;
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
export function isRedactedKey(key: string): boolean {
|
|
658
|
+
return /^\[REDACTED#\d+\]$/.test(key);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function markerLength(text: string, index: number): number {
|
|
662
|
+
if (text[index] !== "[") return 0;
|
|
663
|
+
if (text.startsWith(REDACTED_QUERY_VALUE, index)) return REDACTED_QUERY_VALUE.length;
|
|
664
|
+
if (text.startsWith(REDACTION_FAILED, index)) return REDACTION_FAILED.length;
|
|
665
|
+
if (text.startsWith("[REDACTED#", index))
|
|
666
|
+
return /^\[REDACTED#\d+\]/.exec(text.slice(index))?.[0].length ?? 0;
|
|
667
|
+
return 0;
|
|
668
|
+
}
|