@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,142 @@
|
|
|
1
|
+
import { normalizedResolverHostname } from "./resolver-vendors/hosts.js";
|
|
2
|
+
/**
|
|
3
|
+
* Live SBSD `<script src>` paths are per-site obfuscated (zozo.jp captures, 2026-08-28:
|
|
4
|
+
* `/EdTyEb8Lyxqf/9iGcpl/GmKux0/DXObrkm53w/B1p4AQ/ZCF5f/VoJN08X?v=<uuid>` on the
|
|
5
|
+
* `Access Denied` shape, `/SHO9K/...?v=<uuid>&t=<token>` on the behavioral-tile hard shape).
|
|
6
|
+
* `/.well-known/sbsd` is only the POST endpoint string inside the deobfuscated
|
|
7
|
+
* script, never the page's script path. The stable shape is a same-origin script whose `v`
|
|
8
|
+
* is a UUID; cache-busted bundles (`?v=abc123`, hashes, semver) never match it.
|
|
9
|
+
*/
|
|
10
|
+
const SBSD_SCRIPT_VERSION = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/iu;
|
|
11
|
+
const SBSD_INTERSTITIAL_MAX_BYTES = 4_000;
|
|
12
|
+
const SBSD_SCRIPT_DISCOVERY_MAX_BYTES = 4 * 1_024 * 1_024;
|
|
13
|
+
const SBSD_CHALLENGE_TOKEN_MAX_BYTES = 1_024;
|
|
14
|
+
const SBSD_INTERSTITIAL_MARKER = /sec-bc-tile-container|Access Denied|Reference #\d|Pardon Our Interruption|cpr_chlge/iu;
|
|
15
|
+
function htmlAttribute(value) {
|
|
16
|
+
return value.replace(/&/giu, "&");
|
|
17
|
+
}
|
|
18
|
+
function isDeclaredHost(url, allowedHosts) {
|
|
19
|
+
const hostname = normalizedResolverHostname(url.hostname);
|
|
20
|
+
return allowedHosts.some((host) => normalizedResolverHostname(host) === hostname);
|
|
21
|
+
}
|
|
22
|
+
export function findAkamaiSbsdScript(body, page) {
|
|
23
|
+
// Only markup carries the script tag; JSON and binary bodies skip the scan entirely.
|
|
24
|
+
if (!/^\s*</u.test(body) || Buffer.byteLength(body) > SBSD_SCRIPT_DISCOVERY_MAX_BYTES) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
let passiveScript;
|
|
28
|
+
for (const match of body.matchAll(/<script\b[^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/giu)) {
|
|
29
|
+
let script;
|
|
30
|
+
try {
|
|
31
|
+
// Relative sources resolve against the page URL, as the browser would.
|
|
32
|
+
script = new URL(htmlAttribute(match[1]), page);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const version = script.searchParams.get("v")?.trim();
|
|
38
|
+
if (script.origin !== page.origin || !version || !SBSD_SCRIPT_VERSION.test(version)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (script.searchParams.get("t")?.trim())
|
|
42
|
+
return script;
|
|
43
|
+
passiveScript ??= script;
|
|
44
|
+
}
|
|
45
|
+
return passiveScript;
|
|
46
|
+
}
|
|
47
|
+
function isAkamaiSbsdInterstitial(body) {
|
|
48
|
+
return (Buffer.byteLength(body) < SBSD_INTERSTITIAL_MAX_BYTES && SBSD_INTERSTITIAL_MARKER.test(body));
|
|
49
|
+
}
|
|
50
|
+
function parseAkamaiSbsdChallengeToken(body) {
|
|
51
|
+
if (Buffer.byteLength(body) >= SBSD_INTERSTITIAL_MAX_BYTES)
|
|
52
|
+
return undefined;
|
|
53
|
+
let parsed;
|
|
54
|
+
try {
|
|
55
|
+
parsed = JSON.parse(body);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
if (!parsed ||
|
|
61
|
+
typeof parsed !== "object" ||
|
|
62
|
+
Array.isArray(parsed) ||
|
|
63
|
+
!("cpr_chlge" in parsed) ||
|
|
64
|
+
parsed.cpr_chlge !== "true" ||
|
|
65
|
+
!("t" in parsed) ||
|
|
66
|
+
typeof parsed.t !== "string") {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
const token = parsed.t.trim();
|
|
70
|
+
return token && Buffer.byteLength(token) <= SBSD_CHALLENGE_TOKEN_MAX_BYTES ? token : undefined;
|
|
71
|
+
}
|
|
72
|
+
export function detectAkamaiSbsdChallenge(response, pageUrl, jar, allowedHosts, state) {
|
|
73
|
+
let page;
|
|
74
|
+
try {
|
|
75
|
+
page = new URL(response.url ?? pageUrl);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
if (!isDeclaredHost(page, allowedHosts))
|
|
81
|
+
return undefined;
|
|
82
|
+
const stateCookieName = jar.has("sbsd_o", page.toString())
|
|
83
|
+
? "sbsd_o"
|
|
84
|
+
: jar.has("bm_so", page.toString())
|
|
85
|
+
? "bm_so"
|
|
86
|
+
: undefined;
|
|
87
|
+
const interstitial = isAkamaiSbsdInterstitial(response.body);
|
|
88
|
+
// Without the state cookie (the jar already holds the Set-Cookie of this very response;
|
|
89
|
+
// the live capture sets bm_so on the page that carries the script) or an interstitial,
|
|
90
|
+
// nothing here is Akamai's: do not scan, and do not let an unrelated UUID-versioned
|
|
91
|
+
// bundle become the remembered script.
|
|
92
|
+
if (!stateCookieName && !interstitial)
|
|
93
|
+
return undefined;
|
|
94
|
+
const currentScript = findAkamaiSbsdScript(response.body, page);
|
|
95
|
+
if (currentScript) {
|
|
96
|
+
const version = currentScript.searchParams.get("v")?.trim();
|
|
97
|
+
if (version) {
|
|
98
|
+
// Clone rather than re-parse the pathname: a `//x` path would re-parse as a host.
|
|
99
|
+
const rememberedScript = new URL(currentScript);
|
|
100
|
+
rememberedScript.search = "";
|
|
101
|
+
rememberedScript.hash = "";
|
|
102
|
+
rememberedScript.searchParams.set("v", version);
|
|
103
|
+
state.rememberedScript = rememberedScript;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!stateCookieName)
|
|
107
|
+
return undefined;
|
|
108
|
+
const laterToken = parseAkamaiSbsdChallengeToken(response.body);
|
|
109
|
+
const rememberedScript = state.rememberedScript;
|
|
110
|
+
if (laterToken && rememberedScript && rememberedScript.origin === page.origin) {
|
|
111
|
+
return {
|
|
112
|
+
kind: "akamai_sbsd",
|
|
113
|
+
pageUrl: page.toString(),
|
|
114
|
+
scriptUrl: rememberedScript.toString(),
|
|
115
|
+
stateCookieName,
|
|
116
|
+
challengeToken: laterToken,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (!currentScript || !interstitial)
|
|
120
|
+
return undefined;
|
|
121
|
+
return {
|
|
122
|
+
kind: "akamai_sbsd",
|
|
123
|
+
pageUrl: page.toString(),
|
|
124
|
+
scriptUrl: currentScript.toString(),
|
|
125
|
+
stateCookieName,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* One in-flight solve per session, challenge, and bound identity: requests on one session
|
|
130
|
+
* may still pick a different wreq browser/OS or proxy, and a solve is only valid for the
|
|
131
|
+
* identity that ran it.
|
|
132
|
+
*/
|
|
133
|
+
export function akamaiSbsdChallengeKey(challenge, identity) {
|
|
134
|
+
return JSON.stringify([
|
|
135
|
+
new URL(challenge.scriptUrl).toString(),
|
|
136
|
+
challenge.challengeToken ?? "",
|
|
137
|
+
challenge.stateCookieName,
|
|
138
|
+
identity.wreqBrowser,
|
|
139
|
+
identity.wreqOs,
|
|
140
|
+
identity.proxyUrl ?? "",
|
|
141
|
+
]);
|
|
142
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CookieJar, StealthCookieStore, StealthCookieStoreV1, StealthSessionCookies } from "../types.js";
|
|
2
|
+
/** Extract cookie values from Set-Cookie/header strings for request-scoped registration. */
|
|
3
|
+
export declare function cookieValuesFromHeaders(headers: string | readonly string[]): string[];
|
|
4
|
+
export declare class StealthCookieJar implements CookieJar, StealthSessionCookies {
|
|
5
|
+
private cookies;
|
|
6
|
+
private readonly defaultUrl;
|
|
7
|
+
private readonly register;
|
|
8
|
+
constructor(cookieStrings: readonly string[], defaultUrl?: string, register?: (value: string) => void);
|
|
9
|
+
setFromCookieStrings(cookieStrings: readonly string[], url?: string): void;
|
|
10
|
+
get(name: string, url?: string): string | undefined;
|
|
11
|
+
getAll(url?: string): Record<string, string>;
|
|
12
|
+
has(name: string, url?: string): boolean;
|
|
13
|
+
toString(url?: string): string;
|
|
14
|
+
toHeader(url?: string): string;
|
|
15
|
+
snapshot(): Record<string, string>;
|
|
16
|
+
restore(cookies: Record<string, string>): void;
|
|
17
|
+
serialize(): StealthCookieStoreV1;
|
|
18
|
+
deserialize(state: StealthCookieStore): void;
|
|
19
|
+
clear(): void;
|
|
20
|
+
find(predicate: (cookie: string) => boolean, url?: string): string | undefined;
|
|
21
|
+
names(url?: string): string[];
|
|
22
|
+
private normalizeUrl;
|
|
23
|
+
private getUniqueCookies;
|
|
24
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Cookie, CookieJar as ToughCookieJar } from "tough-cookie";
|
|
2
|
+
import { SDKError, StealthCookieStoreVersionError } from "../errors.js";
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return typeof value === "object" && value !== null;
|
|
5
|
+
}
|
|
6
|
+
const LEGACY_COOKIE_ORIGIN = "https://legacy-cookie.invalid/";
|
|
7
|
+
/** Extract cookie values from Set-Cookie/header strings for request-scoped registration. */
|
|
8
|
+
export function cookieValuesFromHeaders(headers) {
|
|
9
|
+
const values = [];
|
|
10
|
+
for (const header of typeof headers === "string" ? [headers] : headers) {
|
|
11
|
+
const cookie = Cookie.parse(header);
|
|
12
|
+
if (cookie?.value)
|
|
13
|
+
values.push(cookie.value);
|
|
14
|
+
}
|
|
15
|
+
return values;
|
|
16
|
+
}
|
|
17
|
+
export class StealthCookieJar {
|
|
18
|
+
cookies;
|
|
19
|
+
defaultUrl;
|
|
20
|
+
register;
|
|
21
|
+
constructor(cookieStrings, defaultUrl = LEGACY_COOKIE_ORIGIN, register) {
|
|
22
|
+
this.cookies = new ToughCookieJar(undefined, {
|
|
23
|
+
allowSecureOnLocal: false,
|
|
24
|
+
rejectPublicSuffixes: true,
|
|
25
|
+
});
|
|
26
|
+
this.defaultUrl = this.normalizeUrl(defaultUrl) ?? LEGACY_COOKIE_ORIGIN;
|
|
27
|
+
this.register = register;
|
|
28
|
+
this.setFromCookieStrings(cookieStrings);
|
|
29
|
+
}
|
|
30
|
+
setFromCookieStrings(cookieStrings, url = this.defaultUrl) {
|
|
31
|
+
const cookieUrl = this.normalizeUrl(url);
|
|
32
|
+
if (!cookieUrl)
|
|
33
|
+
return;
|
|
34
|
+
for (const cookieString of cookieStrings) {
|
|
35
|
+
for (const value of cookieValuesFromHeaders(cookieString))
|
|
36
|
+
this.register?.(value);
|
|
37
|
+
this.cookies.setCookieSync(cookieString, cookieUrl, { ignoreError: true });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
get(name, url) {
|
|
41
|
+
return this.getAll(url)[name];
|
|
42
|
+
}
|
|
43
|
+
getAll(url) {
|
|
44
|
+
return Object.fromEntries(this.getUniqueCookies(url ?? this.defaultUrl).map((cookie) => [cookie.key, cookie.value]));
|
|
45
|
+
}
|
|
46
|
+
has(name, url) {
|
|
47
|
+
return Object.hasOwn(this.getAll(url), name);
|
|
48
|
+
}
|
|
49
|
+
toString(url) {
|
|
50
|
+
return this.getUniqueCookies(url ?? this.defaultUrl)
|
|
51
|
+
.map((cookie) => cookie.cookieString())
|
|
52
|
+
.join("; ");
|
|
53
|
+
}
|
|
54
|
+
toHeader(url) {
|
|
55
|
+
return this.toString(url);
|
|
56
|
+
}
|
|
57
|
+
snapshot() {
|
|
58
|
+
const entries = [];
|
|
59
|
+
for (const cookie of this.serialize().jar.cookies) {
|
|
60
|
+
if (typeof cookie.key === "string" && typeof cookie.value === "string" && cookie.key) {
|
|
61
|
+
entries.push([cookie.key, cookie.value]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return Object.fromEntries(entries);
|
|
65
|
+
}
|
|
66
|
+
restore(cookies) {
|
|
67
|
+
this.clear();
|
|
68
|
+
for (const [name, value] of Object.entries(cookies)) {
|
|
69
|
+
if (!name)
|
|
70
|
+
continue;
|
|
71
|
+
this.register?.(value);
|
|
72
|
+
this.cookies.setCookieSync(new Cookie({ key: name, path: "/", value }), this.defaultUrl, {
|
|
73
|
+
ignoreError: true,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
serialize() {
|
|
78
|
+
const jar = this.cookies.serializeSync();
|
|
79
|
+
if (!jar) {
|
|
80
|
+
throw new SDKError("Stealth cookie store could not be serialized", {
|
|
81
|
+
code: "stealth_cookie_store_serialize_failed",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return { version: 1, jar };
|
|
85
|
+
}
|
|
86
|
+
deserialize(state) {
|
|
87
|
+
const version = isRecord(state) ? state.version : undefined;
|
|
88
|
+
if (version !== 1) {
|
|
89
|
+
throw new StealthCookieStoreVersionError(version);
|
|
90
|
+
}
|
|
91
|
+
const restored = ToughCookieJar.deserializeSync(state.jar);
|
|
92
|
+
Reflect.set(restored, "allowSecureOnLocal", false);
|
|
93
|
+
this.cookies = restored;
|
|
94
|
+
}
|
|
95
|
+
clear() {
|
|
96
|
+
this.cookies.removeAllCookiesSync();
|
|
97
|
+
}
|
|
98
|
+
find(predicate, url) {
|
|
99
|
+
for (const cookie of this.getUniqueCookies(url ?? this.defaultUrl)) {
|
|
100
|
+
const cookieString = cookie.cookieString();
|
|
101
|
+
if (predicate(cookieString))
|
|
102
|
+
return cookieString;
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
names(url) {
|
|
107
|
+
return this.getUniqueCookies(url ?? this.defaultUrl).map((cookie) => cookie.key);
|
|
108
|
+
}
|
|
109
|
+
normalizeUrl(url) {
|
|
110
|
+
try {
|
|
111
|
+
return new URL(url).toString();
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
getUniqueCookies(url) {
|
|
118
|
+
const cookieUrl = this.normalizeUrl(url);
|
|
119
|
+
if (!cookieUrl)
|
|
120
|
+
return [];
|
|
121
|
+
const names = new Set();
|
|
122
|
+
return this.cookies.getCookiesSync(cookieUrl).filter((cookie) => {
|
|
123
|
+
if (cookie.value)
|
|
124
|
+
this.register?.(cookie.value);
|
|
125
|
+
if (names.has(cookie.key))
|
|
126
|
+
return false;
|
|
127
|
+
names.add(cookie.key);
|
|
128
|
+
return true;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header names the stealth transport owns.
|
|
3
|
+
*
|
|
4
|
+
* `ctx.stealth` composes these from the Chrome emulation and the request class,
|
|
5
|
+
* so a caller value is rejected at request time with `SDKError` code
|
|
6
|
+
* `STEALTH_HEADER_OVERRIDE_UNSUPPORTED` ({@link assertCallerHeadersSupported}).
|
|
7
|
+
* The offline test harness (`runStandardTests`) calls the same function on
|
|
8
|
+
* every `ctx.stealth` request it serves, so a provider test fails exactly where
|
|
9
|
+
* production would, whichever file or variable the header came from. That is
|
|
10
|
+
* the cross-file enforcement.
|
|
11
|
+
*
|
|
12
|
+
* The `browser-version-literal` lint reports a subset of these names — the
|
|
13
|
+
* `sec-fetch-*` and `sec-ch-ua*` families — in files that use `ctx.stealth`
|
|
14
|
+
* (`isReportedOwnedHeaderName` in lint.ts). Both read this module, so the lint
|
|
15
|
+
* cannot claim a rejection the runtime does not perform; it reports
|
|
16
|
+
* deliberately less than the runtime rejects (host, connection,
|
|
17
|
+
* accept-encoding and a non-literal user-agent stay silent — see that
|
|
18
|
+
* predicate for why), so a green `apifuse check` is not proof that no owned
|
|
19
|
+
* header is set.
|
|
20
|
+
*
|
|
21
|
+
* Kept off the stealth runtime: the only import is `errors.js`, whose own
|
|
22
|
+
* imports are type-only, so the lazily loaded CLI lint path does not pull the
|
|
23
|
+
* stealth runtime in.
|
|
24
|
+
*/
|
|
25
|
+
export declare const SDK_OWNED_EXACT_CHROME_HEADERS: ReadonlySet<string>;
|
|
26
|
+
export declare const SDK_OWNED_CHROME_HEADER_PREFIX = "sec-fetch-";
|
|
27
|
+
/**
|
|
28
|
+
* True when `ctx.stealth` rejects a caller value for `name` (case-insensitive):
|
|
29
|
+
* an exact member of {@link SDK_OWNED_EXACT_CHROME_HEADERS} or any
|
|
30
|
+
* `sec-fetch-*` name.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isStealthOwnedHeaderName(name: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Throws `SDKError` `STEALTH_HEADER_OVERRIDE_UNSUPPORTED` for the first caller
|
|
35
|
+
* header `ctx.stealth` owns. An `undefined` value is skipped, as the transport
|
|
36
|
+
* drops it before sending. The stealth runtime and the offline test harness
|
|
37
|
+
* both call this, so the two cannot disagree about what is rejected.
|
|
38
|
+
*/
|
|
39
|
+
export declare function assertCallerHeadersSupported(headers: Readonly<Record<string, string | readonly string[] | undefined>>): void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { SDKError } from "../errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Header names the stealth transport owns.
|
|
4
|
+
*
|
|
5
|
+
* `ctx.stealth` composes these from the Chrome emulation and the request class,
|
|
6
|
+
* so a caller value is rejected at request time with `SDKError` code
|
|
7
|
+
* `STEALTH_HEADER_OVERRIDE_UNSUPPORTED` ({@link assertCallerHeadersSupported}).
|
|
8
|
+
* The offline test harness (`runStandardTests`) calls the same function on
|
|
9
|
+
* every `ctx.stealth` request it serves, so a provider test fails exactly where
|
|
10
|
+
* production would, whichever file or variable the header came from. That is
|
|
11
|
+
* the cross-file enforcement.
|
|
12
|
+
*
|
|
13
|
+
* The `browser-version-literal` lint reports a subset of these names — the
|
|
14
|
+
* `sec-fetch-*` and `sec-ch-ua*` families — in files that use `ctx.stealth`
|
|
15
|
+
* (`isReportedOwnedHeaderName` in lint.ts). Both read this module, so the lint
|
|
16
|
+
* cannot claim a rejection the runtime does not perform; it reports
|
|
17
|
+
* deliberately less than the runtime rejects (host, connection,
|
|
18
|
+
* accept-encoding and a non-literal user-agent stay silent — see that
|
|
19
|
+
* predicate for why), so a green `apifuse check` is not proof that no owned
|
|
20
|
+
* header is set.
|
|
21
|
+
*
|
|
22
|
+
* Kept off the stealth runtime: the only import is `errors.js`, whose own
|
|
23
|
+
* imports are type-only, so the lazily loaded CLI lint path does not pull the
|
|
24
|
+
* stealth runtime in.
|
|
25
|
+
*/
|
|
26
|
+
export const SDK_OWNED_EXACT_CHROME_HEADERS = new Set([
|
|
27
|
+
"host",
|
|
28
|
+
"connection",
|
|
29
|
+
"user-agent",
|
|
30
|
+
"sec-ch-ua",
|
|
31
|
+
"sec-ch-ua-mobile",
|
|
32
|
+
"sec-ch-ua-platform",
|
|
33
|
+
"accept-encoding",
|
|
34
|
+
]);
|
|
35
|
+
export const SDK_OWNED_CHROME_HEADER_PREFIX = "sec-fetch-";
|
|
36
|
+
/**
|
|
37
|
+
* True when `ctx.stealth` rejects a caller value for `name` (case-insensitive):
|
|
38
|
+
* an exact member of {@link SDK_OWNED_EXACT_CHROME_HEADERS} or any
|
|
39
|
+
* `sec-fetch-*` name.
|
|
40
|
+
*/
|
|
41
|
+
export function isStealthOwnedHeaderName(name) {
|
|
42
|
+
const lower = name.toLowerCase();
|
|
43
|
+
return (SDK_OWNED_EXACT_CHROME_HEADERS.has(lower) || lower.startsWith(SDK_OWNED_CHROME_HEADER_PREFIX));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Throws `SDKError` `STEALTH_HEADER_OVERRIDE_UNSUPPORTED` for the first caller
|
|
47
|
+
* header `ctx.stealth` owns. An `undefined` value is skipped, as the transport
|
|
48
|
+
* drops it before sending. The stealth runtime and the offline test harness
|
|
49
|
+
* both call this, so the two cannot disagree about what is rejected.
|
|
50
|
+
*/
|
|
51
|
+
export function assertCallerHeadersSupported(headers) {
|
|
52
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
53
|
+
if (value === undefined || !isStealthOwnedHeaderName(name))
|
|
54
|
+
continue;
|
|
55
|
+
throw new SDKError(`Stealth transport owns the "${name.toLowerCase()}" header; remove it from headers.`, { code: "STEALTH_HEADER_OVERRIDE_UNSUPPORTED" });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { StealthBrowser, StealthChallengeClassification, StealthOS, StealthProfileDescriptor } from "../types.js";
|
|
2
|
+
import { type ClosedEnum, type TelemetryContributor } from "./request-telemetry.js";
|
|
3
|
+
export type StealthTelemetryErrorCode = "transport_network_error" | "transport_timeout" | "transport_cancelled" | "upstream_http_error" | "response_too_large" | "proxy_connect_failed" | "PROXY_POOL_STALE" | "PROXY_EDGE_AUTH_REJECTED" | "PROXY_AUTH_IP_DENIED" | "PROXY_EDGE_TLS_REJECTED" | "PROXY_REQUIRED" | "other";
|
|
4
|
+
export type StealthTelemetryRequestClass = "navigation" | "script_navigation" | "form_post" | "xhr" | "post";
|
|
5
|
+
export type StealthTelemetryAttemptKind = "request" | "resolver" | "proxy_diagnostic";
|
|
6
|
+
export type StealthTelemetrySbsdOutcome = StealthChallengeClassification["outcome"] | "detected" | "refetch_clear";
|
|
7
|
+
export type StealthTelemetryDiagnostics = {
|
|
8
|
+
readonly name?: string;
|
|
9
|
+
readonly message?: string;
|
|
10
|
+
readonly code?: string;
|
|
11
|
+
};
|
|
12
|
+
export type StealthTelemetryAttemptEvent = {
|
|
13
|
+
readonly ms: number;
|
|
14
|
+
readonly status?: number;
|
|
15
|
+
readonly errorCode?: StealthTelemetryErrorCode;
|
|
16
|
+
readonly diagnostics?: StealthTelemetryDiagnostics;
|
|
17
|
+
readonly profileId: StealthProfileDescriptor;
|
|
18
|
+
readonly proxyUsed: boolean;
|
|
19
|
+
readonly requestClass: StealthTelemetryRequestClass;
|
|
20
|
+
readonly kind?: StealthTelemetryAttemptKind;
|
|
21
|
+
};
|
|
22
|
+
export type StealthTelemetrySbsdEvent = {
|
|
23
|
+
readonly detected: boolean;
|
|
24
|
+
readonly outcome?: StealthTelemetrySbsdOutcome;
|
|
25
|
+
};
|
|
26
|
+
export interface StealthTelemetrySink {
|
|
27
|
+
recordAttempt(event: StealthTelemetryAttemptEvent): void;
|
|
28
|
+
recordPoolRefresh(): void;
|
|
29
|
+
recordRedirectHop(): void;
|
|
30
|
+
recordSbsd(event?: StealthTelemetrySbsdEvent | StealthTelemetrySbsdOutcome): void;
|
|
31
|
+
recordSafeRefetch(): void;
|
|
32
|
+
}
|
|
33
|
+
export type StealthTelemetryAttemptSample = {
|
|
34
|
+
n: number;
|
|
35
|
+
ms: number;
|
|
36
|
+
status?: number;
|
|
37
|
+
e?: StealthTelemetryErrorCode;
|
|
38
|
+
kind?: StealthTelemetryAttemptKind;
|
|
39
|
+
diagnostics?: StealthTelemetryDiagnostics;
|
|
40
|
+
};
|
|
41
|
+
export type StealthTelemetryLogPayload = {
|
|
42
|
+
attempts: number;
|
|
43
|
+
poolRefreshes: number;
|
|
44
|
+
redirectHops: number;
|
|
45
|
+
profileId?: StealthProfileDescriptor;
|
|
46
|
+
proxyUsed: boolean;
|
|
47
|
+
requestClass?: StealthTelemetryRequestClass;
|
|
48
|
+
sbsdDetected?: boolean;
|
|
49
|
+
sbsdOutcome?: StealthTelemetrySbsdOutcome;
|
|
50
|
+
safeRefetch: number;
|
|
51
|
+
lastStatus?: number;
|
|
52
|
+
ms: number;
|
|
53
|
+
attemptSamples?: StealthTelemetryAttemptSample[];
|
|
54
|
+
attemptSamplesDropped?: number;
|
|
55
|
+
};
|
|
56
|
+
export type StealthTelemetryHeaderPayload = {
|
|
57
|
+
attempts: number;
|
|
58
|
+
poolRefreshes: number;
|
|
59
|
+
redirectHops: number;
|
|
60
|
+
profileId?: {
|
|
61
|
+
browser: ClosedEnum<StealthBrowser>;
|
|
62
|
+
os: ClosedEnum<StealthOS>;
|
|
63
|
+
};
|
|
64
|
+
proxyUsed: boolean;
|
|
65
|
+
requestClass?: ClosedEnum<StealthTelemetryRequestClass>;
|
|
66
|
+
sbsdDetected?: boolean;
|
|
67
|
+
sbsdOutcome?: ClosedEnum<StealthTelemetrySbsdOutcome>;
|
|
68
|
+
safeRefetch: number;
|
|
69
|
+
lastStatus?: number;
|
|
70
|
+
ms: number;
|
|
71
|
+
attemptSamples?: {
|
|
72
|
+
n: number;
|
|
73
|
+
ms: number;
|
|
74
|
+
status?: number;
|
|
75
|
+
e?: ClosedEnum<StealthTelemetryErrorCode>;
|
|
76
|
+
kind?: ClosedEnum<StealthTelemetryAttemptKind>;
|
|
77
|
+
}[];
|
|
78
|
+
};
|
|
79
|
+
export declare class StealthTelemetryCollector implements StealthTelemetrySink, TelemetryContributor<StealthTelemetryLogPayload, StealthTelemetryHeaderPayload> {
|
|
80
|
+
#private;
|
|
81
|
+
readonly key: "stealth";
|
|
82
|
+
constructor(options?: {
|
|
83
|
+
redact?: (text: string) => string;
|
|
84
|
+
});
|
|
85
|
+
recordAttempt(event: StealthTelemetryAttemptEvent): void;
|
|
86
|
+
recordPoolRefresh(): void;
|
|
87
|
+
recordRedirectHop(): void;
|
|
88
|
+
recordSbsd(event?: StealthTelemetrySbsdEvent | StealthTelemetrySbsdOutcome): void;
|
|
89
|
+
recordSafeRefetch(): void;
|
|
90
|
+
toLogPayload(): StealthTelemetryLogPayload | undefined;
|
|
91
|
+
toHeaderPayload(log: StealthTelemetryLogPayload): StealthTelemetryHeaderPayload;
|
|
92
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { closedEnum } from "./request-telemetry.js";
|
|
3
|
+
const MAX_ATTEMPT_SAMPLES = 24;
|
|
4
|
+
const MAX_DIAGNOSTIC_TEXT = 300;
|
|
5
|
+
function boundedInteger(value) {
|
|
6
|
+
if (!Number.isFinite(value))
|
|
7
|
+
return Number.MAX_SAFE_INTEGER;
|
|
8
|
+
return Math.min(Number.MAX_SAFE_INTEGER, Math.max(0, Math.floor(value)));
|
|
9
|
+
}
|
|
10
|
+
function saturatingAdd(a, b) {
|
|
11
|
+
if (!Number.isFinite(a) || !Number.isFinite(b))
|
|
12
|
+
return Number.MAX_SAFE_INTEGER;
|
|
13
|
+
return Math.min(Number.MAX_SAFE_INTEGER, boundedInteger(a) + boundedInteger(b));
|
|
14
|
+
}
|
|
15
|
+
function boundedText(value, redact) {
|
|
16
|
+
let result = value;
|
|
17
|
+
try {
|
|
18
|
+
result = redact?.(value) ?? value;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
result = "[REDACTION_FAILED]";
|
|
22
|
+
}
|
|
23
|
+
const length = Math.min(result.length, MAX_DIAGNOSTIC_TEXT);
|
|
24
|
+
const units = Buffer.alloc(length * 2);
|
|
25
|
+
for (let index = 0; index < length; index += 1) {
|
|
26
|
+
units.writeUInt16LE(result.charCodeAt(index), index * 2);
|
|
27
|
+
}
|
|
28
|
+
return units.toString("utf16le");
|
|
29
|
+
}
|
|
30
|
+
function cloneDiagnostics(diagnostics, redact) {
|
|
31
|
+
if (!diagnostics)
|
|
32
|
+
return undefined;
|
|
33
|
+
const result = {
|
|
34
|
+
...(diagnostics.name === undefined ? {} : { name: boundedText(diagnostics.name, redact) }),
|
|
35
|
+
...(diagnostics.message === undefined
|
|
36
|
+
? {}
|
|
37
|
+
: { message: boundedText(diagnostics.message, redact) }),
|
|
38
|
+
...(diagnostics.code === undefined ? {} : { code: boundedText(diagnostics.code, redact) }),
|
|
39
|
+
};
|
|
40
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
41
|
+
}
|
|
42
|
+
function cloneProfile(profile) {
|
|
43
|
+
return { browser: profile.browser, os: profile.os };
|
|
44
|
+
}
|
|
45
|
+
export class StealthTelemetryCollector {
|
|
46
|
+
key = "stealth";
|
|
47
|
+
#redact;
|
|
48
|
+
#attempts = 0;
|
|
49
|
+
#poolRefreshes = 0;
|
|
50
|
+
#redirectHops = 0;
|
|
51
|
+
#profileId;
|
|
52
|
+
#proxyUsed = false;
|
|
53
|
+
#requestClass;
|
|
54
|
+
#sbsdDetected;
|
|
55
|
+
#sbsdOutcome;
|
|
56
|
+
#safeRefetch = 0;
|
|
57
|
+
#lastStatus;
|
|
58
|
+
#ms = 0;
|
|
59
|
+
#attemptSamples = [];
|
|
60
|
+
#attemptSamplesDropped = 0;
|
|
61
|
+
constructor(options = {}) {
|
|
62
|
+
this.#redact = options.redact;
|
|
63
|
+
}
|
|
64
|
+
recordAttempt(event) {
|
|
65
|
+
const ms = boundedInteger(event.ms);
|
|
66
|
+
this.#attempts = saturatingAdd(this.#attempts, 1);
|
|
67
|
+
this.#ms = saturatingAdd(this.#ms, ms);
|
|
68
|
+
this.#profileId = cloneProfile(event.profileId);
|
|
69
|
+
this.#proxyUsed ||= event.proxyUsed;
|
|
70
|
+
this.#requestClass = event.requestClass;
|
|
71
|
+
if (event.status !== undefined)
|
|
72
|
+
this.#lastStatus = boundedInteger(event.status);
|
|
73
|
+
if (this.#attemptSamples.length >= MAX_ATTEMPT_SAMPLES) {
|
|
74
|
+
this.#attemptSamplesDropped = saturatingAdd(this.#attemptSamplesDropped, 1);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const sample = {
|
|
78
|
+
n: this.#attempts,
|
|
79
|
+
ms,
|
|
80
|
+
...(event.status === undefined ? {} : { status: boundedInteger(event.status) }),
|
|
81
|
+
...(event.errorCode === undefined ? {} : { e: event.errorCode }),
|
|
82
|
+
...(event.kind === undefined ? {} : { kind: event.kind }),
|
|
83
|
+
...(event.diagnostics
|
|
84
|
+
? { diagnostics: cloneDiagnostics(event.diagnostics, this.#redact) }
|
|
85
|
+
: {}),
|
|
86
|
+
};
|
|
87
|
+
if (sample.diagnostics === undefined)
|
|
88
|
+
delete sample.diagnostics;
|
|
89
|
+
this.#attemptSamples.push(sample);
|
|
90
|
+
}
|
|
91
|
+
recordPoolRefresh() {
|
|
92
|
+
this.#poolRefreshes = saturatingAdd(this.#poolRefreshes, 1);
|
|
93
|
+
}
|
|
94
|
+
recordRedirectHop() {
|
|
95
|
+
this.#redirectHops = saturatingAdd(this.#redirectHops, 1);
|
|
96
|
+
}
|
|
97
|
+
recordSbsd(event) {
|
|
98
|
+
if (typeof event === "string") {
|
|
99
|
+
this.#sbsdDetected = true;
|
|
100
|
+
this.#sbsdOutcome = event;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!event)
|
|
104
|
+
return;
|
|
105
|
+
this.#sbsdDetected = (this.#sbsdDetected ?? false) || event.detected;
|
|
106
|
+
if (event.outcome !== undefined)
|
|
107
|
+
this.#sbsdOutcome = event.outcome;
|
|
108
|
+
}
|
|
109
|
+
recordSafeRefetch() {
|
|
110
|
+
this.#safeRefetch = saturatingAdd(this.#safeRefetch, 1);
|
|
111
|
+
}
|
|
112
|
+
toLogPayload() {
|
|
113
|
+
if (this.#attempts === 0 &&
|
|
114
|
+
this.#poolRefreshes === 0 &&
|
|
115
|
+
this.#redirectHops === 0 &&
|
|
116
|
+
this.#safeRefetch === 0 &&
|
|
117
|
+
this.#sbsdDetected === undefined) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
attempts: this.#attempts,
|
|
122
|
+
poolRefreshes: this.#poolRefreshes,
|
|
123
|
+
redirectHops: this.#redirectHops,
|
|
124
|
+
...(this.#profileId ? { profileId: cloneProfile(this.#profileId) } : {}),
|
|
125
|
+
proxyUsed: this.#proxyUsed,
|
|
126
|
+
...(this.#requestClass ? { requestClass: this.#requestClass } : {}),
|
|
127
|
+
...(this.#sbsdDetected === undefined ? {} : { sbsdDetected: this.#sbsdDetected }),
|
|
128
|
+
...(this.#sbsdOutcome ? { sbsdOutcome: this.#sbsdOutcome } : {}),
|
|
129
|
+
safeRefetch: this.#safeRefetch,
|
|
130
|
+
...(this.#lastStatus === undefined ? {} : { lastStatus: this.#lastStatus }),
|
|
131
|
+
ms: this.#ms,
|
|
132
|
+
...(this.#attemptSamples.length > 0
|
|
133
|
+
? {
|
|
134
|
+
attemptSamples: this.#attemptSamples.map((sample) => ({
|
|
135
|
+
...sample,
|
|
136
|
+
...(sample.diagnostics
|
|
137
|
+
? {
|
|
138
|
+
diagnostics: { ...sample.diagnostics },
|
|
139
|
+
}
|
|
140
|
+
: {}),
|
|
141
|
+
})),
|
|
142
|
+
}
|
|
143
|
+
: {}),
|
|
144
|
+
...(this.#attemptSamplesDropped > 0
|
|
145
|
+
? { attemptSamplesDropped: this.#attemptSamplesDropped }
|
|
146
|
+
: {}),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
toHeaderPayload(log) {
|
|
150
|
+
const attemptSamples = log.attemptSamples?.map((sample) => ({
|
|
151
|
+
n: sample.n,
|
|
152
|
+
ms: sample.ms,
|
|
153
|
+
...(sample.status === undefined ? {} : { status: sample.status }),
|
|
154
|
+
...(sample.e ? { e: closedEnum(sample.e) } : {}),
|
|
155
|
+
...(sample.kind ? { kind: closedEnum(sample.kind) } : {}),
|
|
156
|
+
}));
|
|
157
|
+
return {
|
|
158
|
+
attempts: log.attempts,
|
|
159
|
+
poolRefreshes: log.poolRefreshes,
|
|
160
|
+
redirectHops: log.redirectHops,
|
|
161
|
+
...(log.profileId
|
|
162
|
+
? {
|
|
163
|
+
profileId: {
|
|
164
|
+
browser: closedEnum(log.profileId.browser),
|
|
165
|
+
os: closedEnum(log.profileId.os),
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
: {}),
|
|
169
|
+
proxyUsed: log.proxyUsed,
|
|
170
|
+
...(log.requestClass ? { requestClass: closedEnum(log.requestClass) } : {}),
|
|
171
|
+
...(log.sbsdDetected === undefined ? {} : { sbsdDetected: log.sbsdDetected }),
|
|
172
|
+
...(log.sbsdOutcome ? { sbsdOutcome: closedEnum(log.sbsdOutcome) } : {}),
|
|
173
|
+
safeRefetch: log.safeRefetch,
|
|
174
|
+
...(log.lastStatus === undefined ? {} : { lastStatus: log.lastStatus }),
|
|
175
|
+
ms: log.ms,
|
|
176
|
+
...(attemptSamples && attemptSamples.length > 0 ? { attemptSamples } : {}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|