@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
package/dist/runtime/state.js
CHANGED
|
@@ -1,21 +1,96 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { providerStateRedisUrlFromEnv } from "../config/loader.js";
|
|
2
3
|
import { ProviderError } from "../errors.js";
|
|
3
4
|
import { createProviderRedisClient, ensureRedisReady, withRedisTimeout, } from "./redis.js";
|
|
4
5
|
const DEFAULT_REDIS_TIMEOUT_MS = 250;
|
|
5
|
-
const REDIS_STATE_PREFIX = "apifuse:provider-state:
|
|
6
|
+
const REDIS_STATE_PREFIX = "apifuse:provider-state:v2";
|
|
7
|
+
const PROVIDER_SCOPE_DISCRIMINATOR = "scope:provider";
|
|
8
|
+
const MISSING_CONNECTION_SCOPE_DISCRIMINATOR = "scope:connection:missing";
|
|
9
|
+
const SET_WITH_QUOTA_SCRIPT = `
|
|
10
|
+
local now = tonumber(ARGV[1])
|
|
11
|
+
local max_entries = tonumber(ARGV[2])
|
|
12
|
+
local expires_at = tonumber(ARGV[3])
|
|
13
|
+
local index_ttl = tonumber(ARGV[4])
|
|
14
|
+
local envelope = ARGV[5]
|
|
15
|
+
|
|
16
|
+
redis.call("ZREMRANGEBYSCORE", KEYS[2], "-inf", now)
|
|
17
|
+
local indexed = redis.call("ZSCORE", KEYS[2], KEYS[1])
|
|
18
|
+
if not indexed and redis.call("ZCARD", KEYS[2]) >= max_entries then
|
|
19
|
+
return {0, false}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
redis.call("SET", KEYS[1], envelope, "PXAT", expires_at)
|
|
23
|
+
redis.call("ZADD", KEYS[2], expires_at, KEYS[1])
|
|
24
|
+
redis.call("PEXPIRE", KEYS[2], index_ttl)
|
|
25
|
+
return {1, envelope}
|
|
26
|
+
`;
|
|
27
|
+
const COMPARE_AND_SET_WITH_QUOTA_SCRIPT = `
|
|
28
|
+
local current = redis.call("GET", KEYS[1])
|
|
29
|
+
if current then
|
|
30
|
+
local ok, decoded = pcall(cjson.decode, current)
|
|
31
|
+
if ok and type(decoded) == "table" and decoded.deleted == true then
|
|
32
|
+
current = false
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
local now = tonumber(ARGV[2])
|
|
37
|
+
local max_entries = tonumber(ARGV[3])
|
|
38
|
+
local expires_at = tonumber(ARGV[4])
|
|
39
|
+
local index_ttl = tonumber(ARGV[5])
|
|
40
|
+
local envelope = ARGV[6]
|
|
41
|
+
|
|
42
|
+
local current_version = 0
|
|
43
|
+
local current_decoded = false
|
|
44
|
+
if current then
|
|
45
|
+
local ok, decoded = pcall(cjson.decode, current)
|
|
46
|
+
if not ok or type(decoded) ~= "table" or type(decoded.version) ~= "number" then
|
|
47
|
+
return {-2, current}
|
|
48
|
+
end
|
|
49
|
+
current_version = decoded.version
|
|
50
|
+
current_decoded = decoded
|
|
51
|
+
end
|
|
52
|
+
if current_version ~= tonumber(ARGV[1]) then
|
|
53
|
+
return {-1, current or false}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
redis.call("ZREMRANGEBYSCORE", KEYS[2], "-inf", now)
|
|
57
|
+
local indexed = redis.call("ZSCORE", KEYS[2], KEYS[1])
|
|
58
|
+
if not indexed and redis.call("ZCARD", KEYS[2]) >= max_entries then
|
|
59
|
+
return {0, false}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
if current_decoded and type(current_decoded.createdAt) == "string" then
|
|
63
|
+
-- Preserve the original createdAt by patching the encoded envelope in place.
|
|
64
|
+
-- The envelope puts its metadata before "value", so the first match is the
|
|
65
|
+
-- envelope's own key, never a provider value; re-encoding with cjson would
|
|
66
|
+
-- silently turn [] inside the value into {}.
|
|
67
|
+
local preserved = string.gsub(current_decoded.createdAt, "%%", "%%%%")
|
|
68
|
+
envelope = (string.gsub(envelope, '"createdAt":"[^"]*"', '"createdAt":"' .. preserved .. '"', 1))
|
|
69
|
+
end
|
|
70
|
+
redis.call("SET", KEYS[1], envelope, "PXAT", expires_at)
|
|
71
|
+
redis.call("ZADD", KEYS[2], expires_at, KEYS[1])
|
|
72
|
+
redis.call("PEXPIRE", KEYS[2], index_ttl)
|
|
73
|
+
return {1, envelope}
|
|
74
|
+
`;
|
|
75
|
+
const DELETE_WITH_INDEX_SCRIPT = `
|
|
76
|
+
redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2])
|
|
77
|
+
redis.call("ZREM", KEYS[2], KEYS[1])
|
|
78
|
+
return 1
|
|
79
|
+
`;
|
|
6
80
|
const redisBackends = new Map();
|
|
7
|
-
function getRedisBackend(redisUrl) {
|
|
81
|
+
function getRedisBackend(redisUrl, injectedRedis) {
|
|
8
82
|
const existing = redisBackends.get(redisUrl);
|
|
9
83
|
if (existing)
|
|
10
84
|
return existing;
|
|
11
|
-
const redis =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
85
|
+
const redis = injectedRedis ??
|
|
86
|
+
createProviderRedisClient({
|
|
87
|
+
redisUrl,
|
|
88
|
+
timeoutMs: DEFAULT_REDIS_TIMEOUT_MS,
|
|
89
|
+
onError: () => {
|
|
90
|
+
// Runtime state operations fail closed at their call sites. Avoid noisy
|
|
91
|
+
// unhandled Redis errors from background reconnect attempts.
|
|
92
|
+
},
|
|
93
|
+
});
|
|
19
94
|
const backend = { redis };
|
|
20
95
|
redisBackends.set(redisUrl, backend);
|
|
21
96
|
return backend;
|
|
@@ -36,15 +111,26 @@ async function requireRedisReady(redis) {
|
|
|
36
111
|
return;
|
|
37
112
|
throw new UnsupportedProviderStateError("Provider runtime state Redis is unavailable");
|
|
38
113
|
}
|
|
39
|
-
function
|
|
40
|
-
|
|
114
|
+
function connectionScopeDiscriminator(connectionId) {
|
|
115
|
+
if (connectionId === undefined)
|
|
116
|
+
return MISSING_CONNECTION_SCOPE_DISCRIMINATOR;
|
|
117
|
+
const digest = createHash("sha256").update(connectionId, "utf8").digest("hex");
|
|
118
|
+
return `scope:connection:sha256:${digest}`;
|
|
119
|
+
}
|
|
120
|
+
function providerStatePrefix(providerId, namespace, scopeDiscriminator) {
|
|
121
|
+
return `${REDIS_STATE_PREFIX}:${providerId ?? "default"}:${namespace}:${scopeDiscriminator}`;
|
|
41
122
|
}
|
|
42
|
-
function providerStateKey(providerId, namespace, key) {
|
|
43
|
-
return `${providerStatePrefix(providerId, namespace)}:${key}`;
|
|
123
|
+
function providerStateKey(providerId, namespace, scopeDiscriminator, key) {
|
|
124
|
+
return `${providerStatePrefix(providerId, namespace, scopeDiscriminator)}:${key}`;
|
|
44
125
|
}
|
|
45
|
-
function publicStateKey(
|
|
46
|
-
const prefix
|
|
47
|
-
|
|
126
|
+
function publicStateKey(redisKey, prefixes) {
|
|
127
|
+
for (const prefix of prefixes) {
|
|
128
|
+
const prefixWithSeparator = `${prefix}:`;
|
|
129
|
+
if (redisKey.startsWith(prefixWithSeparator)) {
|
|
130
|
+
return redisKey.slice(prefixWithSeparator.length);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return redisKey;
|
|
48
134
|
}
|
|
49
135
|
function parseStateDurationMs(ttl) {
|
|
50
136
|
const match = /^(\d+)(ms|s|m|h|d)$/.exec(ttl ?? "1h");
|
|
@@ -63,13 +149,21 @@ function parseStateDurationMs(ttl) {
|
|
|
63
149
|
: 86_400_000;
|
|
64
150
|
return Math.max(1, amount * multiplier);
|
|
65
151
|
}
|
|
66
|
-
function resolveExpiresAt(ttl) {
|
|
67
|
-
return new Date(Date.now() + parseStateDurationMs(ttl)).toISOString();
|
|
68
|
-
}
|
|
69
152
|
function envelopeFromJson(key, raw) {
|
|
70
153
|
if (!raw)
|
|
71
154
|
return null;
|
|
72
|
-
|
|
155
|
+
// A corrupt/undecodable persisted envelope must be treated as absent rather
|
|
156
|
+
// than throwing a raw JSON.parse SyntaxError: an uncaught SyntaxError escapes
|
|
157
|
+
// the provider error taxonomy, is masked as internal_error 500, and is then
|
|
158
|
+
// retried by the hub (2026-07-22 catchtable reserve RCA, candidate A). Returning
|
|
159
|
+
// null also keeps list() from aborting the whole scan on a single bad entry.
|
|
160
|
+
let parsed;
|
|
161
|
+
try {
|
|
162
|
+
parsed = JSON.parse(raw);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
73
167
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
74
168
|
return null;
|
|
75
169
|
}
|
|
@@ -90,83 +184,134 @@ function envelopeFromJson(key, raw) {
|
|
|
90
184
|
updatedAt: record.updatedAt,
|
|
91
185
|
};
|
|
92
186
|
}
|
|
187
|
+
function isStateTombstone(raw) {
|
|
188
|
+
if (!raw)
|
|
189
|
+
return false;
|
|
190
|
+
try {
|
|
191
|
+
const parsed = JSON.parse(raw);
|
|
192
|
+
return (parsed !== null &&
|
|
193
|
+
typeof parsed === "object" &&
|
|
194
|
+
!Array.isArray(parsed) &&
|
|
195
|
+
parsed.deleted === true);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
93
201
|
function redisEnvelope(value, version, createdAt, expiresAt) {
|
|
94
202
|
const updatedAt = new Date().toISOString();
|
|
95
|
-
|
|
203
|
+
// Metadata precedes `value` on purpose: the compare-and-set Lua script patches
|
|
204
|
+
// `createdAt` in the encoded string (first occurrence) instead of decoding and
|
|
205
|
+
// re-encoding with cjson, which would turn empty arrays inside `value` into
|
|
206
|
+
// objects. Keep the key order stable.
|
|
207
|
+
return { version, expiresAt, createdAt, updatedAt, value };
|
|
96
208
|
}
|
|
97
209
|
class RedisProviderStateNamespace {
|
|
98
210
|
backend;
|
|
99
211
|
providerId;
|
|
100
212
|
namespaceName;
|
|
101
213
|
options;
|
|
102
|
-
|
|
214
|
+
scopeDiscriminator;
|
|
215
|
+
constructor(backend, providerId, namespaceName, options, scopeDiscriminator) {
|
|
103
216
|
this.backend = backend;
|
|
104
217
|
this.providerId = providerId;
|
|
105
218
|
this.namespaceName = namespaceName;
|
|
106
219
|
this.options = options;
|
|
220
|
+
this.scopeDiscriminator = scopeDiscriminator;
|
|
107
221
|
}
|
|
108
222
|
redisKey(key) {
|
|
109
|
-
return providerStateKey(this.providerId, this.namespaceName, key);
|
|
223
|
+
return providerStateKey(this.providerId, this.namespaceName, this.scopeDiscriminator, key);
|
|
224
|
+
}
|
|
225
|
+
statePrefix() {
|
|
226
|
+
return providerStatePrefix(this.providerId, this.namespaceName, this.scopeDiscriminator);
|
|
110
227
|
}
|
|
111
|
-
|
|
112
|
-
|
|
228
|
+
indexKey() {
|
|
229
|
+
// Keep bookkeeping outside the caller-owned keyspace. A provider may use
|
|
230
|
+
// any state key (including "__index"), so a suffix inside the namespace
|
|
231
|
+
// could turn the ZSET into a string and break every subsequent write.
|
|
232
|
+
const namespaceIdentity = Buffer.from(providerStatePrefix(this.providerId, this.namespaceName, this.scopeDiscriminator), "utf8").toString("base64url");
|
|
233
|
+
return `${REDIS_STATE_PREFIX}:index:${namespaceIdentity}`;
|
|
113
234
|
}
|
|
114
|
-
async
|
|
235
|
+
async indexedKeys(limit) {
|
|
115
236
|
await requireRedisReady(this.backend.redis);
|
|
116
|
-
|
|
237
|
+
const now = Date.now();
|
|
238
|
+
return await withRequiredRedis(async () => {
|
|
239
|
+
await this.backend.redis.zremrangebyscore(this.indexKey(), "-inf", now);
|
|
240
|
+
return await this.backend.redis.zrangebyscore(this.indexKey(), now + 1, "+inf", "LIMIT", 0, limit);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
async readRaw(key) {
|
|
244
|
+
await requireRedisReady(this.backend.redis);
|
|
245
|
+
return await withRequiredRedis(() => this.backend.redis.get(this.redisKey(key)));
|
|
117
246
|
}
|
|
118
247
|
enforceValueSize(value) {
|
|
119
248
|
const bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
120
249
|
if (bytes > this.options.maxValueBytes) {
|
|
121
|
-
throw
|
|
250
|
+
throw stateViolationError(`Provider runtime state value exceeds maxValueBytes (${bytes} > ${this.options.maxValueBytes})`, "size");
|
|
122
251
|
}
|
|
123
252
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
253
|
+
quotaExceeded() {
|
|
254
|
+
return stateViolationError(`Provider runtime state namespace quota exceeded (${this.options.maxEntries + 1} > ${this.options.maxEntries})`, "quota");
|
|
255
|
+
}
|
|
256
|
+
writeTiming(ttl) {
|
|
257
|
+
const ttlMs = parseStateDurationMs(ttl ?? this.options.defaultTtl);
|
|
258
|
+
const maxTtlMs = parseStateDurationMs(this.options.maxTtl);
|
|
259
|
+
if (ttlMs > maxTtlMs) {
|
|
260
|
+
throw stateViolationError(`Provider runtime state ttl exceeds maxTtl (${ttlMs} > ${maxTtlMs})`, "ttl");
|
|
130
261
|
}
|
|
262
|
+
const expiresAtMs = Date.now() + ttlMs;
|
|
263
|
+
return {
|
|
264
|
+
expiresAt: new Date(expiresAtMs).toISOString(),
|
|
265
|
+
expiresAtMs,
|
|
266
|
+
indexTtlMs: maxTtlMs,
|
|
267
|
+
};
|
|
131
268
|
}
|
|
132
269
|
async list(options) {
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
return
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (limited.length === 0)
|
|
270
|
+
const requestedLimit = Math.max(0, options?.limit ?? this.options.maxEntries);
|
|
271
|
+
if (requestedLimit === 0)
|
|
272
|
+
return recordStateRedisNoop([]);
|
|
273
|
+
const keys = await this.indexedKeys(this.options.maxEntries);
|
|
274
|
+
if (keys.length === 0)
|
|
139
275
|
return [];
|
|
140
|
-
const values = await withRequiredRedis(() => this.backend.redis.mget(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
276
|
+
const values = await withRequiredRedis(() => this.backend.redis.mget(keys));
|
|
277
|
+
const rows = [];
|
|
278
|
+
for (const [index, raw] of values.entries()) {
|
|
279
|
+
const redisKey = keys[index];
|
|
280
|
+
if (!redisKey)
|
|
281
|
+
continue;
|
|
282
|
+
const publicKey = publicStateKey(redisKey, [this.statePrefix()]);
|
|
283
|
+
if (options?.prefix && !publicKey.startsWith(options.prefix))
|
|
284
|
+
continue;
|
|
285
|
+
const value = envelopeFromJson(publicKey, raw);
|
|
286
|
+
if (!value)
|
|
287
|
+
continue;
|
|
288
|
+
rows.push(value);
|
|
289
|
+
}
|
|
290
|
+
return rows.slice(0, requestedLimit);
|
|
148
291
|
}
|
|
149
292
|
async get(key) {
|
|
150
|
-
await
|
|
151
|
-
|
|
293
|
+
const raw = await this.readRaw(key);
|
|
294
|
+
if (isStateTombstone(raw))
|
|
295
|
+
return null;
|
|
152
296
|
return envelopeFromJson(key, raw);
|
|
153
297
|
}
|
|
154
298
|
async set(key, value, options) {
|
|
155
299
|
this.enforceValueSize(value);
|
|
156
|
-
await this.enforceMaxEntries(key);
|
|
157
300
|
const current = await this.get(key);
|
|
158
301
|
const createdAt = current?.createdAt ?? new Date().toISOString();
|
|
159
302
|
const version = (current?.version ?? 0) + 1;
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
303
|
+
const timing = this.writeTiming(options?.ttl);
|
|
304
|
+
const envelope = redisEnvelope(value, version, createdAt, timing.expiresAt);
|
|
305
|
+
await requireRedisReady(this.backend.redis);
|
|
306
|
+
const result = await withRequiredRedis(() => this.backend.redis.eval(SET_WITH_QUOTA_SCRIPT, 2, this.redisKey(key), this.indexKey(), String(Date.now()), String(this.options.maxEntries), String(timing.expiresAtMs), String(timing.indexTtlMs), JSON.stringify(envelope)));
|
|
307
|
+
if (!Array.isArray(result) || Number(result[0]) !== 1) {
|
|
308
|
+
throw this.quotaExceeded();
|
|
309
|
+
}
|
|
165
310
|
return {
|
|
166
311
|
key,
|
|
167
312
|
value,
|
|
168
313
|
version,
|
|
169
|
-
expiresAt,
|
|
314
|
+
expiresAt: timing.expiresAt,
|
|
170
315
|
createdAt,
|
|
171
316
|
updatedAt: envelope.updatedAt,
|
|
172
317
|
};
|
|
@@ -179,15 +324,29 @@ class RedisProviderStateNamespace {
|
|
|
179
324
|
}
|
|
180
325
|
async compareAndSet(key, expectedVersion, value, options) {
|
|
181
326
|
this.enforceValueSize(value);
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
327
|
+
const createdAt = new Date().toISOString();
|
|
328
|
+
const timing = this.writeTiming(options?.ttl);
|
|
329
|
+
const envelope = redisEnvelope(value, expectedVersion + 1, createdAt, timing.expiresAt);
|
|
330
|
+
await requireRedisReady(this.backend.redis);
|
|
331
|
+
const result = await withRequiredRedis(() => this.backend.redis.eval(COMPARE_AND_SET_WITH_QUOTA_SCRIPT, 2, this.redisKey(key), this.indexKey(), String(expectedVersion), String(Date.now()), String(this.options.maxEntries), String(timing.expiresAtMs), String(timing.indexTtlMs), JSON.stringify(envelope)));
|
|
332
|
+
if (Array.isArray(result) && Number(result[0]) === 0) {
|
|
333
|
+
throw this.quotaExceeded();
|
|
334
|
+
}
|
|
335
|
+
if (!Array.isArray(result) || Number(result[0]) !== 1) {
|
|
336
|
+
const rawCurrent = Array.isArray(result) && typeof result[1] === "string" ? result[1] : null;
|
|
337
|
+
return { ok: false, current: envelopeFromJson(key, rawCurrent) };
|
|
185
338
|
}
|
|
186
|
-
|
|
339
|
+
const rawWritten = typeof result[1] === "string" ? result[1] : null;
|
|
340
|
+
const written = envelopeFromJson(key, rawWritten);
|
|
341
|
+
if (!written) {
|
|
342
|
+
throw new UnsupportedProviderStateError("Provider runtime state CAS returned an invalid value");
|
|
343
|
+
}
|
|
344
|
+
return { ok: true, value: written };
|
|
187
345
|
}
|
|
188
346
|
async delete(key) {
|
|
189
347
|
await requireRedisReady(this.backend.redis);
|
|
190
|
-
|
|
348
|
+
const tombstone = JSON.stringify({ deleted: true });
|
|
349
|
+
await withRequiredRedis(() => this.backend.redis.eval(DELETE_WITH_INDEX_SCRIPT, 2, this.redisKey(key), this.indexKey(), tombstone, String(parseStateDurationMs(this.options.maxTtl))));
|
|
191
350
|
}
|
|
192
351
|
async increment(key, field, delta = 1, options) {
|
|
193
352
|
const current = (await this.get(key))?.value ?? {};
|
|
@@ -198,20 +357,46 @@ class RedisProviderStateNamespace {
|
|
|
198
357
|
class RedisProviderRuntimeState {
|
|
199
358
|
backend;
|
|
200
359
|
providerId;
|
|
201
|
-
|
|
202
|
-
|
|
360
|
+
redisUrl;
|
|
361
|
+
scopeDiscriminator;
|
|
362
|
+
constructor(options, scopeDiscriminator = MISSING_CONNECTION_SCOPE_DISCRIMINATOR) {
|
|
363
|
+
this.backend = getRedisBackend(options.redisUrl, options.__redisClient);
|
|
203
364
|
this.providerId = options.providerId;
|
|
365
|
+
this.redisUrl = options.redisUrl;
|
|
366
|
+
this.scopeDiscriminator = scopeDiscriminator;
|
|
367
|
+
}
|
|
368
|
+
forConnection(connectionId) {
|
|
369
|
+
return new RedisProviderRuntimeState({ redisUrl: this.redisUrl, providerId: this.providerId }, connectionScopeDiscriminator(connectionId));
|
|
204
370
|
}
|
|
205
371
|
namespace(name, options) {
|
|
206
|
-
return new RedisProviderStateNamespace(this.backend, this.providerId, name, options);
|
|
372
|
+
return new RedisProviderStateNamespace(this.backend, this.providerId, name, options, options.scope === "provider" ? PROVIDER_SCOPE_DISCRIMINATOR : this.scopeDiscriminator);
|
|
207
373
|
}
|
|
208
374
|
}
|
|
209
375
|
export class UnsupportedProviderStateError extends ProviderError {
|
|
210
376
|
constructor(message = "Provider runtime state is not available in this runtime") {
|
|
211
377
|
super(message, { code: "PROVIDER_STATE_UNSUPPORTED" });
|
|
212
378
|
this.name = "UnsupportedProviderStateError";
|
|
379
|
+
stateViolationKinds.set(this, "unsupported");
|
|
213
380
|
}
|
|
214
381
|
}
|
|
382
|
+
// Keep the exact no-command result out of band, without inspecting caller options twice.
|
|
383
|
+
const stateRedisNoopResults = new WeakSet();
|
|
384
|
+
function recordStateRedisNoop(result) {
|
|
385
|
+
stateRedisNoopResults.add(result);
|
|
386
|
+
return result;
|
|
387
|
+
}
|
|
388
|
+
export function isStateRedisNoop(result) {
|
|
389
|
+
return result !== null && typeof result === "object" && stateRedisNoopResults.has(result);
|
|
390
|
+
}
|
|
391
|
+
const stateViolationKinds = new WeakMap();
|
|
392
|
+
function stateViolationError(message, kind) {
|
|
393
|
+
const error = new UnsupportedProviderStateError(message);
|
|
394
|
+
stateViolationKinds.set(error, kind);
|
|
395
|
+
return error;
|
|
396
|
+
}
|
|
397
|
+
export function getStateViolation(error) {
|
|
398
|
+
return error !== null && typeof error === "object" ? stateViolationKinds.get(error) : undefined;
|
|
399
|
+
}
|
|
215
400
|
class UnsupportedProviderStateNamespace {
|
|
216
401
|
async list(_options) {
|
|
217
402
|
throw new UnsupportedProviderStateError();
|
|
@@ -236,18 +421,40 @@ class UnsupportedProviderStateNamespace {
|
|
|
236
421
|
}
|
|
237
422
|
}
|
|
238
423
|
class UnsupportedProviderRuntimeState {
|
|
424
|
+
forConnection(_connectionId) {
|
|
425
|
+
return new UnsupportedProviderRuntimeState();
|
|
426
|
+
}
|
|
239
427
|
namespace(_name, _options) {
|
|
240
428
|
return new UnsupportedProviderStateNamespace();
|
|
241
429
|
}
|
|
242
430
|
}
|
|
243
431
|
class MemoryProviderStateNamespace {
|
|
244
432
|
options;
|
|
433
|
+
now;
|
|
245
434
|
// biome-ignore lint/suspicious/noExplicitAny: in-memory state stores heterogeneous generic values by key.
|
|
246
435
|
values = new Map();
|
|
247
|
-
constructor(options) {
|
|
436
|
+
constructor(options, now = Date.now) {
|
|
248
437
|
this.options = options;
|
|
438
|
+
this.now = now;
|
|
249
439
|
}
|
|
250
|
-
|
|
440
|
+
enforceValueSize(value) {
|
|
441
|
+
const bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
442
|
+
if (bytes > this.options.maxValueBytes) {
|
|
443
|
+
throw stateViolationError(`Provider runtime state value exceeds maxValueBytes (${bytes} > ${this.options.maxValueBytes})`, "size");
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
enforceWritePolicy(key, value, ttl) {
|
|
447
|
+
this.enforceValueSize(value);
|
|
448
|
+
const ttlMs = parseStateDurationMs(ttl ?? this.options.defaultTtl);
|
|
449
|
+
const maxTtlMs = parseStateDurationMs(this.options.maxTtl);
|
|
450
|
+
if (ttlMs > maxTtlMs) {
|
|
451
|
+
throw stateViolationError(`Provider runtime state ttl exceeds maxTtl (${ttlMs} > ${maxTtlMs})`, "ttl");
|
|
452
|
+
}
|
|
453
|
+
if (!this.values.has(key) && this.values.size >= this.options.maxEntries) {
|
|
454
|
+
throw stateViolationError(`Provider runtime state namespace quota exceeded (${this.options.maxEntries + 1} > ${this.options.maxEntries})`, "quota");
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
pruneExpired(nowMs = this.now()) {
|
|
251
458
|
for (const [key, row] of this.values.entries()) {
|
|
252
459
|
if (row.expiresAt && Date.parse(row.expiresAt) <= nowMs) {
|
|
253
460
|
this.values.delete(key);
|
|
@@ -265,9 +472,10 @@ class MemoryProviderStateNamespace {
|
|
|
265
472
|
}
|
|
266
473
|
async set(key, value, options) {
|
|
267
474
|
this.pruneExpired();
|
|
268
|
-
|
|
475
|
+
this.enforceWritePolicy(key, value, options?.ttl);
|
|
476
|
+
const now = new Date(this.now()).toISOString();
|
|
269
477
|
const current = this.values.get(key);
|
|
270
|
-
const expiresAt = resolveMemoryStateExpiresAt(options?.ttl ?? this.options.defaultTtl);
|
|
478
|
+
const expiresAt = resolveMemoryStateExpiresAt(options?.ttl ?? this.options.defaultTtl, this.now());
|
|
271
479
|
const row = {
|
|
272
480
|
key,
|
|
273
481
|
value,
|
|
@@ -282,8 +490,24 @@ class MemoryProviderStateNamespace {
|
|
|
282
490
|
async patch(_key, _partial, _options) {
|
|
283
491
|
throw new UnsupportedProviderStateError("In-memory provider runtime state does not support patch");
|
|
284
492
|
}
|
|
285
|
-
async compareAndSet(
|
|
286
|
-
|
|
493
|
+
async compareAndSet(key, expectedVersion, value, options) {
|
|
494
|
+
this.pruneExpired();
|
|
495
|
+
const current = this.values.get(key);
|
|
496
|
+
if ((current?.version ?? 0) !== expectedVersion) {
|
|
497
|
+
return { ok: false, current: current ?? null };
|
|
498
|
+
}
|
|
499
|
+
this.enforceWritePolicy(key, value, options?.ttl);
|
|
500
|
+
const now = new Date(this.now()).toISOString();
|
|
501
|
+
const stored = {
|
|
502
|
+
key,
|
|
503
|
+
value,
|
|
504
|
+
version: expectedVersion + 1,
|
|
505
|
+
expiresAt: resolveMemoryStateExpiresAt(options?.ttl ?? this.options.defaultTtl, this.now()),
|
|
506
|
+
createdAt: current?.createdAt ?? now,
|
|
507
|
+
updatedAt: now,
|
|
508
|
+
};
|
|
509
|
+
this.values.set(key, stored);
|
|
510
|
+
return { ok: true, value: stored };
|
|
287
511
|
}
|
|
288
512
|
async delete(key) {
|
|
289
513
|
this.values.delete(key);
|
|
@@ -293,20 +517,33 @@ class MemoryProviderStateNamespace {
|
|
|
293
517
|
}
|
|
294
518
|
}
|
|
295
519
|
class MemoryProviderRuntimeState {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
520
|
+
backend;
|
|
521
|
+
scopeDiscriminator;
|
|
522
|
+
constructor(backend = {
|
|
523
|
+
namespaces: new Map(),
|
|
524
|
+
now: Date.now,
|
|
525
|
+
}, scopeDiscriminator = MISSING_CONNECTION_SCOPE_DISCRIMINATOR) {
|
|
526
|
+
this.backend = backend;
|
|
527
|
+
this.scopeDiscriminator = scopeDiscriminator;
|
|
528
|
+
}
|
|
529
|
+
forConnection(connectionId) {
|
|
530
|
+
return new MemoryProviderRuntimeState(this.backend, connectionScopeDiscriminator(connectionId));
|
|
531
|
+
}
|
|
532
|
+
namespace(name, options) {
|
|
533
|
+
const scopeDiscriminator = options.scope === "provider" ? PROVIDER_SCOPE_DISCRIMINATOR : this.scopeDiscriminator;
|
|
534
|
+
const namespaceIdentity = `${scopeDiscriminator}\0${name}`;
|
|
535
|
+
const existing = this.backend.namespaces.get(namespaceIdentity);
|
|
299
536
|
if (existing)
|
|
300
537
|
return existing;
|
|
301
|
-
const created = new MemoryProviderStateNamespace(
|
|
302
|
-
this.namespaces.set(
|
|
538
|
+
const created = new MemoryProviderStateNamespace(options, this.backend.now);
|
|
539
|
+
this.backend.namespaces.set(namespaceIdentity, created);
|
|
303
540
|
return created;
|
|
304
541
|
}
|
|
305
542
|
}
|
|
306
|
-
function resolveMemoryStateExpiresAt(ttl) {
|
|
543
|
+
function resolveMemoryStateExpiresAt(ttl, nowMs = Date.now()) {
|
|
307
544
|
const match = /^(\d+)(ms|s|m|h|d)$/.exec(ttl ?? "1h");
|
|
308
545
|
if (!match)
|
|
309
|
-
return new Date(
|
|
546
|
+
return new Date(nowMs + 3_600_000).toISOString();
|
|
310
547
|
const amount = Number(match[1]);
|
|
311
548
|
const unit = match[2];
|
|
312
549
|
const multiplier = unit === "ms"
|
|
@@ -318,7 +555,7 @@ function resolveMemoryStateExpiresAt(ttl) {
|
|
|
318
555
|
: unit === "h"
|
|
319
556
|
? 3_600_000
|
|
320
557
|
: 86_400_000;
|
|
321
|
-
return new Date(
|
|
558
|
+
return new Date(nowMs + amount * multiplier).toISOString();
|
|
322
559
|
}
|
|
323
560
|
export function createRedisProviderRuntimeState(options) {
|
|
324
561
|
return new RedisProviderRuntimeState(options);
|
|
@@ -336,8 +573,11 @@ export function createProviderRuntimeStateFromEnv(options = {}) {
|
|
|
336
573
|
}
|
|
337
574
|
return createUnsupportedProviderRuntimeState();
|
|
338
575
|
}
|
|
339
|
-
export function createMemoryProviderRuntimeState() {
|
|
340
|
-
return new MemoryProviderRuntimeState(
|
|
576
|
+
export function createMemoryProviderRuntimeState(options) {
|
|
577
|
+
return new MemoryProviderRuntimeState({
|
|
578
|
+
namespaces: new Map(),
|
|
579
|
+
now: options?.now ?? Date.now,
|
|
580
|
+
});
|
|
341
581
|
}
|
|
342
582
|
export function createUnsupportedProviderRuntimeState() {
|
|
343
583
|
return new UnsupportedProviderRuntimeState();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ChallengeSolution, ProviderChallenge, StealthResponse } from "../types.js";
|
|
2
|
+
import type { ResolverVendorTransport } from "./resolver-vendors/types.js";
|
|
3
|
+
import type { StealthCookieJar } from "./stealth-cookies.js";
|
|
4
|
+
export type AkamaiSbsdChallenge = Extract<ProviderChallenge, {
|
|
5
|
+
readonly kind: "akamai_sbsd";
|
|
6
|
+
}>;
|
|
7
|
+
/**
|
|
8
|
+
* SDK-owned response detection and solve wiring derived from the provider declaration.
|
|
9
|
+
* The server attaches it to the stealth client options it builds; it is deliberately not
|
|
10
|
+
* part of the public `StealthClientOptions` type.
|
|
11
|
+
*/
|
|
12
|
+
export type StealthChallengeRuntime = {
|
|
13
|
+
readonly akamaiSbsd?: {
|
|
14
|
+
readonly allowedHosts: readonly string[];
|
|
15
|
+
/** Resolver-declared client profile; must name the initiating session's browser family. */
|
|
16
|
+
readonly clientProfile?: string;
|
|
17
|
+
readonly solve?: (challenge: AkamaiSbsdChallenge, transport: ResolverVendorTransport, signal: AbortSignal) => Promise<ChallengeSolution>;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export type AkamaiSbsdSessionState = {
|
|
21
|
+
/**
|
|
22
|
+
* Latest v-only script for this session; Phase 2 deliberately has no wall-clock TTL.
|
|
23
|
+
* Challenge-state expiry belongs to the Phase 3 ceremony/solve lease handle
|
|
24
|
+
* (ADR-0010 v1.1), not to the stealth session.
|
|
25
|
+
*/
|
|
26
|
+
rememberedScript?: URL;
|
|
27
|
+
/** In-flight solves keyed by akamaiSbsdChallengeKey; an entry leaves only when it settles. */
|
|
28
|
+
readonly transactions: Map<string, {
|
|
29
|
+
readonly result: Promise<{
|
|
30
|
+
readonly solved: true;
|
|
31
|
+
} | {
|
|
32
|
+
readonly solved: false;
|
|
33
|
+
error: unknown;
|
|
34
|
+
}>;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Key of the last automatic (safe-method) solve whose refetch was not challenged again.
|
|
38
|
+
* An explicit replay for the same key reuses the solved cookies once instead of solving
|
|
39
|
+
* again; any newer solve or lease expiry clears it.
|
|
40
|
+
*/
|
|
41
|
+
completedSuccessKey?: string;
|
|
42
|
+
};
|
|
43
|
+
export declare function findAkamaiSbsdScript(body: string, page: URL): URL | undefined;
|
|
44
|
+
export declare function detectAkamaiSbsdChallenge(response: Pick<StealthResponse, "url" | "body">, pageUrl: string, jar: Pick<StealthCookieJar, "has">, allowedHosts: readonly string[], state: AkamaiSbsdSessionState): AkamaiSbsdChallenge | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* One in-flight solve per session, challenge, and bound identity: requests on one session
|
|
47
|
+
* may still pick a different wreq browser/OS or proxy, and a solve is only valid for the
|
|
48
|
+
* identity that ran it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function akamaiSbsdChallengeKey(challenge: AkamaiSbsdChallenge, identity: {
|
|
51
|
+
readonly wreqBrowser: string;
|
|
52
|
+
readonly wreqOs: string;
|
|
53
|
+
readonly proxyUrl: string | undefined;
|
|
54
|
+
}): string;
|