@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,65 @@
|
|
|
1
|
+
import type { ProxyVendorName } from "../config/loader.js";
|
|
2
|
+
/**
|
|
3
|
+
* AES-256-GCM key for the engine ceremony egress lease handle (ADR-0010 v1.1).
|
|
4
|
+
* Engine host only: the `APIFUSE__ENGINE__` family is rejected from provider
|
|
5
|
+
* secrets and filtered from provider env projections. Supply at least 32 random
|
|
6
|
+
* bytes (for example `openssl rand -base64 32`). Rotating the key fails
|
|
7
|
+
* verification of every outstanding handle (`EGRESS_LEASE_INVALID`, 409): the
|
|
8
|
+
* affected ceremonies restart; they do not expire.
|
|
9
|
+
*/
|
|
10
|
+
export declare const APIFUSE__ENGINE__CEREMONY_LEASE_KEY = "APIFUSE__ENGINE__CEREMONY_LEASE_KEY";
|
|
11
|
+
/** Internal option key shared only by the server assembler and stealth runtime. */
|
|
12
|
+
export declare const ENGINE_CEREMONY_EGRESS_LEASE: unique symbol;
|
|
13
|
+
export type CeremonyEgressBinding = {
|
|
14
|
+
readonly vendor: ProxyVendorName;
|
|
15
|
+
readonly proxyUrl: string;
|
|
16
|
+
readonly poolIndex: number;
|
|
17
|
+
readonly affinityKey: string;
|
|
18
|
+
readonly refreshEpoch: number;
|
|
19
|
+
/**
|
|
20
|
+
* Vendor session lifetime the endpoint was allocated for (`proxy.session.lifetimeMinutes`,
|
|
21
|
+
* Smartproxy `life`, NodeMaven SID window). It is the upper bound of the lease, counted
|
|
22
|
+
* from the mint (the first successful attempt), so a Smartproxy lease can outlive the
|
|
23
|
+
* vendor session by the extraction-cache reuse window; expiry then drops and rebinds.
|
|
24
|
+
* The bound is not a vendor guarantee (ADR-0010: a raw Smartproxy endpoint is not a hard
|
|
25
|
+
* lease): the stealth runtime releases the binding as soon as the endpoint fails at the
|
|
26
|
+
* transport, so a dead endpoint is left before the lifetime elapses.
|
|
27
|
+
*/
|
|
28
|
+
readonly lifetimeMinutes: number;
|
|
29
|
+
};
|
|
30
|
+
export type CeremonyEgressLeaseRuntime = {
|
|
31
|
+
/** Exact egress bound to this ceremony; undefined until the first successful attempt binds one. */
|
|
32
|
+
readonly binding: CeremonyEgressBinding | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Drops the binding once its vendor session lifetime has elapsed so the next attempt
|
|
35
|
+
* selects and binds a fresh endpoint. Returns true when a binding was dropped: the
|
|
36
|
+
* caller must also discard challenge state that was only valid for that endpoint.
|
|
37
|
+
*/
|
|
38
|
+
dropExpiredBinding(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Drops the binding unconditionally; the stealth runtime calls this when the bound
|
|
41
|
+
* endpoint fails at the transport. Same return and caller obligation as
|
|
42
|
+
* `dropExpiredBinding`.
|
|
43
|
+
*/
|
|
44
|
+
dropBinding(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Number of bindings dropped so far (expiry or release). Every stealth session created
|
|
47
|
+
* from one client shares the lease; a session compares this to what it last observed to
|
|
48
|
+
* retire endpoint-bound challenge state and replay records after another session moved
|
|
49
|
+
* the ceremony.
|
|
50
|
+
*/
|
|
51
|
+
readonly generation: number;
|
|
52
|
+
bind(binding: CeremonyEgressBinding): void;
|
|
53
|
+
handle(): string | undefined;
|
|
54
|
+
};
|
|
55
|
+
export declare function createCeremonyEgressLeaseRuntime(options: {
|
|
56
|
+
readonly tenantId?: string;
|
|
57
|
+
readonly providerId: string;
|
|
58
|
+
readonly flowId: string;
|
|
59
|
+
readonly affinityKey: string;
|
|
60
|
+
readonly handle?: string;
|
|
61
|
+
/** Server-only registration before a handle can reach request diagnostics. */
|
|
62
|
+
readonly onHandle?: (handle: string) => void;
|
|
63
|
+
readonly environment?: Readonly<Record<string, string | undefined>>;
|
|
64
|
+
readonly now?: () => number;
|
|
65
|
+
}): CeremonyEgressLeaseRuntime;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { readDiagnosticEnv } from "./diagnostic-env.js";
|
|
2
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
|
|
3
|
+
import { SDKError } from "../errors.js";
|
|
4
|
+
/**
|
|
5
|
+
* AES-256-GCM key for the engine ceremony egress lease handle (ADR-0010 v1.1).
|
|
6
|
+
* Engine host only: the `APIFUSE__ENGINE__` family is rejected from provider
|
|
7
|
+
* secrets and filtered from provider env projections. Supply at least 32 random
|
|
8
|
+
* bytes (for example `openssl rand -base64 32`). Rotating the key fails
|
|
9
|
+
* verification of every outstanding handle (`EGRESS_LEASE_INVALID`, 409): the
|
|
10
|
+
* affected ceremonies restart; they do not expire.
|
|
11
|
+
*/
|
|
12
|
+
export const APIFUSE__ENGINE__CEREMONY_LEASE_KEY = "APIFUSE__ENGINE__CEREMONY_LEASE_KEY";
|
|
13
|
+
/** Internal option key shared only by the server assembler and stealth runtime. */
|
|
14
|
+
export const ENGINE_CEREMONY_EGRESS_LEASE = Symbol.for("@apifuse/provider-sdk/engine-ceremony-egress-lease");
|
|
15
|
+
const HANDLE_VERSION = 1;
|
|
16
|
+
const HANDLE_MAX_BYTES = 16_384;
|
|
17
|
+
const AEAD_NONCE_BYTES = 12;
|
|
18
|
+
const AEAD_TAG_BYTES = 16;
|
|
19
|
+
const AEAD_ALGORITHM = "aes-256-gcm";
|
|
20
|
+
const AEAD_KEY_DOMAIN = "apifuse:ceremony-egress-lease:v1\0";
|
|
21
|
+
const LEASE_KEY_MIN_BYTES = 32;
|
|
22
|
+
/** The caller presented a handle this engine cannot verify for this ceremony (409). */
|
|
23
|
+
function invalidLease() {
|
|
24
|
+
throw new SDKError("The engine ceremony egress lease is invalid", {
|
|
25
|
+
code: "EGRESS_LEASE_INVALID",
|
|
26
|
+
fix: "Restart the authentication ceremony to obtain a new engine-owned egress lease.",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/** The engine violated its own binding invariant (500); never attributable to the caller. */
|
|
30
|
+
function bindingFault(message) {
|
|
31
|
+
throw new SDKError(message, {
|
|
32
|
+
code: "EGRESS_LEASE_BINDING_INVALID",
|
|
33
|
+
fix: "This is an SDK or provider-runtime fault; the ceremony lease cannot bind the selected egress.",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function encryptionKey(key) {
|
|
37
|
+
return createHash("sha256").update(AEAD_KEY_DOMAIN, "utf8").update(key, "utf8").digest();
|
|
38
|
+
}
|
|
39
|
+
/** Fixed field order and JSON.stringify's compact form are the canonical AAD encoding. */
|
|
40
|
+
function scopeAad(scope) {
|
|
41
|
+
return Buffer.from(JSON.stringify({
|
|
42
|
+
version: HANDLE_VERSION,
|
|
43
|
+
tenantId: scope.tenantId,
|
|
44
|
+
providerId: scope.providerId,
|
|
45
|
+
flowId: scope.flowId,
|
|
46
|
+
affinityKey: scope.affinityKey,
|
|
47
|
+
}), "utf8");
|
|
48
|
+
}
|
|
49
|
+
function encodeHandle(payload, key, scope) {
|
|
50
|
+
const nonce = randomBytes(AEAD_NONCE_BYTES);
|
|
51
|
+
const cipher = createCipheriv(AEAD_ALGORITHM, encryptionKey(key), nonce, {
|
|
52
|
+
authTagLength: AEAD_TAG_BYTES,
|
|
53
|
+
});
|
|
54
|
+
cipher.setAAD(scopeAad(scope));
|
|
55
|
+
const ciphertext = Buffer.concat([
|
|
56
|
+
cipher.update(JSON.stringify(payload), "utf8"),
|
|
57
|
+
cipher.final(),
|
|
58
|
+
]);
|
|
59
|
+
return [
|
|
60
|
+
`v${HANDLE_VERSION}`,
|
|
61
|
+
nonce.toString("base64url"),
|
|
62
|
+
ciphertext.toString("base64url"),
|
|
63
|
+
cipher.getAuthTag().toString("base64url"),
|
|
64
|
+
].join(".");
|
|
65
|
+
}
|
|
66
|
+
function isFiniteInteger(value) {
|
|
67
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
68
|
+
}
|
|
69
|
+
function isPositiveFinite(value) {
|
|
70
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
71
|
+
}
|
|
72
|
+
function decodeHandle(handle, key, scope) {
|
|
73
|
+
if (Buffer.byteLength(handle, "utf8") > HANDLE_MAX_BYTES)
|
|
74
|
+
return invalidLease();
|
|
75
|
+
const parts = handle.split(".");
|
|
76
|
+
if (parts.length !== 4 ||
|
|
77
|
+
parts[0] !== `v${HANDLE_VERSION}` ||
|
|
78
|
+
parts.slice(1).some((part) => !/^[A-Za-z0-9_-]+$/u.test(part))) {
|
|
79
|
+
return invalidLease();
|
|
80
|
+
}
|
|
81
|
+
let parsed;
|
|
82
|
+
try {
|
|
83
|
+
const nonce = Buffer.from(parts[1], "base64url");
|
|
84
|
+
const ciphertext = Buffer.from(parts[2], "base64url");
|
|
85
|
+
const tag = Buffer.from(parts[3], "base64url");
|
|
86
|
+
if (nonce.byteLength !== AEAD_NONCE_BYTES ||
|
|
87
|
+
tag.byteLength !== AEAD_TAG_BYTES ||
|
|
88
|
+
ciphertext.byteLength === 0) {
|
|
89
|
+
return invalidLease();
|
|
90
|
+
}
|
|
91
|
+
const decipher = createDecipheriv(AEAD_ALGORITHM, encryptionKey(key), nonce, {
|
|
92
|
+
authTagLength: AEAD_TAG_BYTES,
|
|
93
|
+
});
|
|
94
|
+
decipher.setAAD(scopeAad(scope));
|
|
95
|
+
decipher.setAuthTag(tag);
|
|
96
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
97
|
+
parsed = JSON.parse(plaintext.toString("utf8"));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return invalidLease();
|
|
101
|
+
}
|
|
102
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
103
|
+
return invalidLease();
|
|
104
|
+
const candidate = parsed;
|
|
105
|
+
if (candidate.version !== HANDLE_VERSION ||
|
|
106
|
+
(candidate.vendor !== "smartproxy" && candidate.vendor !== "nodemaven") ||
|
|
107
|
+
typeof candidate.proxyUrl !== "string" ||
|
|
108
|
+
!candidate.proxyUrl ||
|
|
109
|
+
!isFiniteInteger(candidate.poolIndex) ||
|
|
110
|
+
typeof candidate.affinityKey !== "string" ||
|
|
111
|
+
!candidate.affinityKey ||
|
|
112
|
+
!isFiniteInteger(candidate.refreshEpoch) ||
|
|
113
|
+
typeof candidate.tenantId !== "string" ||
|
|
114
|
+
!candidate.tenantId ||
|
|
115
|
+
typeof candidate.providerId !== "string" ||
|
|
116
|
+
!candidate.providerId ||
|
|
117
|
+
typeof candidate.flowId !== "string" ||
|
|
118
|
+
!candidate.flowId ||
|
|
119
|
+
!isFiniteInteger(candidate.mintedAtMs) ||
|
|
120
|
+
!isFiniteInteger(candidate.expiresAtMs) ||
|
|
121
|
+
candidate.expiresAtMs <= candidate.mintedAtMs ||
|
|
122
|
+
!isPositiveFinite(candidate.lifetimeMinutes)) {
|
|
123
|
+
return invalidLease();
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const proxy = new URL(candidate.proxyUrl);
|
|
127
|
+
if (proxy.protocol !== "http:" && proxy.protocol !== "socks5:")
|
|
128
|
+
return invalidLease();
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return invalidLease();
|
|
132
|
+
}
|
|
133
|
+
return candidate;
|
|
134
|
+
}
|
|
135
|
+
function bindingOf(payload) {
|
|
136
|
+
return {
|
|
137
|
+
vendor: payload.vendor,
|
|
138
|
+
proxyUrl: payload.proxyUrl,
|
|
139
|
+
poolIndex: payload.poolIndex,
|
|
140
|
+
affinityKey: payload.affinityKey,
|
|
141
|
+
refreshEpoch: payload.refreshEpoch,
|
|
142
|
+
lifetimeMinutes: payload.lifetimeMinutes,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
export function createCeremonyEgressLeaseRuntime(options) {
|
|
146
|
+
const environment = options.environment ?? process.env;
|
|
147
|
+
const now = options.now ?? Date.now;
|
|
148
|
+
if (typeof options.tenantId !== "string" || !options.tenantId.trim()) {
|
|
149
|
+
throw new SDKError("A tenant is required for an engine ceremony egress lease", {
|
|
150
|
+
code: "EGRESS_LEASE_INVALID",
|
|
151
|
+
fix: "Restart the authentication ceremony with its tenant identity.",
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
const scope = {
|
|
155
|
+
tenantId: options.tenantId,
|
|
156
|
+
providerId: options.providerId,
|
|
157
|
+
flowId: options.flowId,
|
|
158
|
+
affinityKey: options.affinityKey,
|
|
159
|
+
};
|
|
160
|
+
const key = readDiagnosticEnv(APIFUSE__ENGINE__CEREMONY_LEASE_KEY, environment)?.trim();
|
|
161
|
+
if (!key) {
|
|
162
|
+
throw new SDKError("The engine ceremony egress lease key is not configured", {
|
|
163
|
+
code: "EGRESS_LEASE_KEY_MISSING",
|
|
164
|
+
fix: `Configure ${APIFUSE__ENGINE__CEREMONY_LEASE_KEY} in the engine host.`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
// The key is the whole authenticity boundary of a handle (a verified handle's proxyUrl
|
|
168
|
+
// becomes the ceremony egress), so the documented minimum is enforced, not advisory.
|
|
169
|
+
if (Buffer.byteLength(key, "utf8") < LEASE_KEY_MIN_BYTES) {
|
|
170
|
+
throw new SDKError(`The engine ceremony egress lease key is shorter than ${LEASE_KEY_MIN_BYTES} bytes`, {
|
|
171
|
+
code: "EGRESS_LEASE_KEY_WEAK",
|
|
172
|
+
fix: `Set ${APIFUSE__ENGINE__CEREMONY_LEASE_KEY} to at least ${LEASE_KEY_MIN_BYTES} random bytes (for example \`openssl rand -base64 32\`).`,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
let payload;
|
|
176
|
+
let currentHandle;
|
|
177
|
+
let generation = 0;
|
|
178
|
+
const dropBinding = () => {
|
|
179
|
+
if (!payload)
|
|
180
|
+
return false;
|
|
181
|
+
payload = undefined;
|
|
182
|
+
currentHandle = undefined;
|
|
183
|
+
generation += 1;
|
|
184
|
+
return true;
|
|
185
|
+
};
|
|
186
|
+
const dropExpiredBinding = () => payload !== undefined && now() >= payload.expiresAtMs ? dropBinding() : false;
|
|
187
|
+
if (options.handle !== undefined) {
|
|
188
|
+
options.onHandle?.(options.handle);
|
|
189
|
+
// Scope mismatch fails GCM authentication because scope is AAD; there is
|
|
190
|
+
// deliberately no post-decrypt scope comparison fallback.
|
|
191
|
+
payload = decodeHandle(options.handle, key, scope);
|
|
192
|
+
currentHandle = options.handle;
|
|
193
|
+
// An expired but authentic handle is not a fault: the ceremony continues on a
|
|
194
|
+
// freshly selected endpoint and the next turn receives the new handle.
|
|
195
|
+
dropExpiredBinding();
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
get binding() {
|
|
199
|
+
return payload ? bindingOf(payload) : undefined;
|
|
200
|
+
},
|
|
201
|
+
dropExpiredBinding,
|
|
202
|
+
dropBinding,
|
|
203
|
+
get generation() {
|
|
204
|
+
return generation;
|
|
205
|
+
},
|
|
206
|
+
bind(binding) {
|
|
207
|
+
if (payload) {
|
|
208
|
+
// The stealth runtime reuses the bound endpoint while a binding exists and
|
|
209
|
+
// never rebinds it; a second bind is an engine invariant break.
|
|
210
|
+
return bindingFault("The ceremony egress lease is already bound");
|
|
211
|
+
}
|
|
212
|
+
if (binding.affinityKey !== options.affinityKey ||
|
|
213
|
+
!binding.proxyUrl ||
|
|
214
|
+
(binding.vendor !== "smartproxy" && binding.vendor !== "nodemaven") ||
|
|
215
|
+
!isFiniteInteger(binding.poolIndex) ||
|
|
216
|
+
!isPositiveFinite(binding.lifetimeMinutes)) {
|
|
217
|
+
return bindingFault("The selected egress cannot be bound to the ceremony lease");
|
|
218
|
+
}
|
|
219
|
+
const mintedAtMs = now();
|
|
220
|
+
payload = {
|
|
221
|
+
version: HANDLE_VERSION,
|
|
222
|
+
tenantId: scope.tenantId,
|
|
223
|
+
providerId: options.providerId,
|
|
224
|
+
flowId: options.flowId,
|
|
225
|
+
...binding,
|
|
226
|
+
mintedAtMs,
|
|
227
|
+
expiresAtMs: mintedAtMs + binding.lifetimeMinutes * 60_000,
|
|
228
|
+
};
|
|
229
|
+
currentHandle = encodeHandle(payload, key, scope);
|
|
230
|
+
options.onHandle?.(currentHandle);
|
|
231
|
+
},
|
|
232
|
+
handle() {
|
|
233
|
+
return currentHandle;
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type ProviderEngine, type ProviderEngineMode } from "../engine.js";
|
|
2
|
+
/** Mode used when neither `engineMode` nor `APIFUSE__ENGINE__MODE` names one. */
|
|
3
|
+
export declare const DEFAULT_PROVIDER_ENGINE_MODE = "in-process";
|
|
4
|
+
/** Remote engine client endpoint; engine-owned, so never declarable as a provider secret. */
|
|
5
|
+
export declare const REMOTE_ENGINE_CLIENT_URL_ENV = "APIFUSE__ENGINE__URL";
|
|
6
|
+
/** Remote engine client credential; registered with the diagnostic redactor by sensitive-values. */
|
|
7
|
+
export declare const REMOTE_ENGINE_CLIENT_API_KEY_ENV = "APIFUSE__ENGINE__API_KEY";
|
|
8
|
+
/** Where the resolved mode came from, in precedence order. */
|
|
9
|
+
export type ProviderEngineModeSource = "engine" | "env" | "option" | "default";
|
|
10
|
+
/**
|
|
11
|
+
* Non-fatal conditions worth reporting at boot:
|
|
12
|
+
* - `invalid_env_value`: `APIFUSE__ENGINE__MODE` held an unrecognized value.
|
|
13
|
+
* - `env_overrode_option`: the manifest and the host code disagreed; the manifest won.
|
|
14
|
+
* - `engine_object_overrode_requested_mode`: an explicit engine object cannot honour the requested mode.
|
|
15
|
+
* - `engine_env_ignored`: remote-only engine env is projected but the mode is not
|
|
16
|
+
* remote (not reported for a host-supplied engine, which may consume it itself).
|
|
17
|
+
*/
|
|
18
|
+
export type ProviderEngineModeWarning = "invalid_env_value" | "env_overrode_option" | "engine_object_overrode_requested_mode" | "engine_env_ignored";
|
|
19
|
+
export interface ProviderEngineModeResolution {
|
|
20
|
+
readonly mode: ProviderEngineMode | "custom";
|
|
21
|
+
readonly source: ProviderEngineModeSource;
|
|
22
|
+
/** True for every non-remote attachment, whose removal is scheduled by ADR-0011. */
|
|
23
|
+
readonly deprecated: boolean;
|
|
24
|
+
readonly warnings: readonly ProviderEngineModeWarning[];
|
|
25
|
+
}
|
|
26
|
+
export interface ProviderEngineModeResolutionInput {
|
|
27
|
+
/** Explicit `engineMode` option passed by the host process. */
|
|
28
|
+
readonly option?: ProviderEngineMode;
|
|
29
|
+
/** Explicit engine object passed by the host; used as given when present. */
|
|
30
|
+
readonly engine?: Pick<ProviderEngine, "kind">;
|
|
31
|
+
/** Environment to read the engine env from; defaults to `process.env`. */
|
|
32
|
+
readonly environment?: Readonly<Record<string, string | undefined>>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the engine attachment mode once at boot.
|
|
36
|
+
*
|
|
37
|
+
* Precedence: an explicit `engine` object, then `APIFUSE__ENGINE__MODE`, then the
|
|
38
|
+
* `engineMode` option, then the default. The manifest outranks the option so
|
|
39
|
+
* provider code cannot silently downgrade what the deployment asked for; every
|
|
40
|
+
* disagreement is reported in `warnings` with a truthful `source` rather than
|
|
41
|
+
* being dropped.
|
|
42
|
+
*
|
|
43
|
+
* Deployment-authored input is never fatal (an unrecognized env value is warned
|
|
44
|
+
* about and ignored). A malformed `engineMode` option is a fault in the calling
|
|
45
|
+
* code, which no manifest can reach, so it throws.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resolveProviderEngineMode(input?: ProviderEngineModeResolutionInput): ProviderEngineModeResolution;
|
|
48
|
+
/** True when the engine cannot attach, so readiness must report the process as unready. */
|
|
49
|
+
export declare function isUnavailableProviderEngine(engine: ProviderEngine): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Engine that refuses every attachment with `reason`.
|
|
52
|
+
*
|
|
53
|
+
* Requesting a mode this release cannot serve must not crash the process: the SDK
|
|
54
|
+
* answers a structured error per request and reports the process unready, the same
|
|
55
|
+
* trade-off `provider_secrets_missing` already makes at boot. Non-retryable, so a
|
|
56
|
+
* caller-side retry loop cannot turn a configuration fault into load.
|
|
57
|
+
*/
|
|
58
|
+
export declare function createUnavailableProviderEngine(kind: ProviderEngineMode, reason: {
|
|
59
|
+
message: string;
|
|
60
|
+
code: string;
|
|
61
|
+
fix: string;
|
|
62
|
+
}): ProviderEngine;
|
|
63
|
+
/**
|
|
64
|
+
* Engine for a resolved mode. `remote` has no client in this release, so it
|
|
65
|
+
* resolves to an engine that fails every request instead of falling back to
|
|
66
|
+
* in-process (ADR-0011 Pitfall 2: in-process is never a fallback).
|
|
67
|
+
*/
|
|
68
|
+
export declare function createEngineForMode(mode: ProviderEngineMode | "custom"): ProviderEngine;
|
|
69
|
+
/**
|
|
70
|
+
* Fail fast for the CLIs (`apifuse dev`, `apifuse record`). They are developer
|
|
71
|
+
* tools, not pods: an unusable mode should stop the command rather than start a
|
|
72
|
+
* server that refuses every request.
|
|
73
|
+
*/
|
|
74
|
+
export declare function assertProcessEngineModeSupported(environment?: Readonly<Record<string, string | undefined>>): ProviderEngineModeResolution;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { createInProcessProviderEngine, PROVIDER_ENGINE_MODE_ENV, } from "../engine.js";
|
|
2
|
+
import { ProviderError, ValidationError } from "../errors.js";
|
|
3
|
+
/**
|
|
4
|
+
* Engine attachment selection. Internal on purpose: ADR-0011 removes the
|
|
5
|
+
* in-process lane, so every symbol exported here would become a breaking removal
|
|
6
|
+
* later. The server surfaces the outcome on the `provider_engine_mode` boot
|
|
7
|
+
* event instead.
|
|
8
|
+
*/
|
|
9
|
+
const KNOWN_PROVIDER_ENGINE_MODES = ["in-process", "remote"];
|
|
10
|
+
/** Mode used when neither `engineMode` nor `APIFUSE__ENGINE__MODE` names one. */
|
|
11
|
+
export const DEFAULT_PROVIDER_ENGINE_MODE = "in-process";
|
|
12
|
+
/** Remote engine client endpoint; engine-owned, so never declarable as a provider secret. */
|
|
13
|
+
export const REMOTE_ENGINE_CLIENT_URL_ENV = "APIFUSE__ENGINE__URL";
|
|
14
|
+
/** Remote engine client credential; registered with the diagnostic redactor by sensitive-values. */
|
|
15
|
+
export const REMOTE_ENGINE_CLIENT_API_KEY_ENV = "APIFUSE__ENGINE__API_KEY";
|
|
16
|
+
/** Engine env names that only mean something in remote mode. */
|
|
17
|
+
const REMOTE_ONLY_ENGINE_ENV_NAMES = [
|
|
18
|
+
REMOTE_ENGINE_CLIENT_URL_ENV,
|
|
19
|
+
REMOTE_ENGINE_CLIENT_API_KEY_ENV,
|
|
20
|
+
];
|
|
21
|
+
function isKnownMode(value) {
|
|
22
|
+
return KNOWN_PROVIDER_ENGINE_MODES.includes(value);
|
|
23
|
+
}
|
|
24
|
+
function normalize(value) {
|
|
25
|
+
if (typeof value !== "string")
|
|
26
|
+
return undefined;
|
|
27
|
+
const trimmed = value.trim().toLowerCase();
|
|
28
|
+
return trimmed === "" ? undefined : trimmed;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolve the engine attachment mode once at boot.
|
|
32
|
+
*
|
|
33
|
+
* Precedence: an explicit `engine` object, then `APIFUSE__ENGINE__MODE`, then the
|
|
34
|
+
* `engineMode` option, then the default. The manifest outranks the option so
|
|
35
|
+
* provider code cannot silently downgrade what the deployment asked for; every
|
|
36
|
+
* disagreement is reported in `warnings` with a truthful `source` rather than
|
|
37
|
+
* being dropped.
|
|
38
|
+
*
|
|
39
|
+
* Deployment-authored input is never fatal (an unrecognized env value is warned
|
|
40
|
+
* about and ignored). A malformed `engineMode` option is a fault in the calling
|
|
41
|
+
* code, which no manifest can reach, so it throws.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveProviderEngineMode(input = {}) {
|
|
44
|
+
const environment = input.environment ?? process.env;
|
|
45
|
+
const warnings = [];
|
|
46
|
+
// Read directly: this is a mode name, not a credential, so it must not be
|
|
47
|
+
// registered with the diagnostic redactor the way readDiagnosticEnv is.
|
|
48
|
+
const rawEnv = normalize(environment[PROVIDER_ENGINE_MODE_ENV]);
|
|
49
|
+
let envMode;
|
|
50
|
+
if (rawEnv !== undefined) {
|
|
51
|
+
if (isKnownMode(rawEnv))
|
|
52
|
+
envMode = rawEnv;
|
|
53
|
+
else
|
|
54
|
+
warnings.push("invalid_env_value");
|
|
55
|
+
}
|
|
56
|
+
const optionMode = normalize(input.option);
|
|
57
|
+
if (input.option !== undefined && (optionMode === undefined || !isKnownMode(optionMode))) {
|
|
58
|
+
throw new ValidationError(`Invalid engineMode option "${String(input.option)}"; expected ${KNOWN_PROVIDER_ENGINE_MODES.join(" or ")}`, { fix: 'Pass engineMode: "in-process" | "remote", or omit it to use the default.' });
|
|
59
|
+
}
|
|
60
|
+
if (optionMode !== undefined && envMode !== undefined && optionMode !== envMode) {
|
|
61
|
+
warnings.push("env_overrode_option");
|
|
62
|
+
}
|
|
63
|
+
const requested = envMode ?? optionMode;
|
|
64
|
+
const requestedSource = envMode !== undefined ? "env" : optionMode !== undefined ? "option" : undefined;
|
|
65
|
+
// Normalized like every other mode input: an un-normalized host `kind` would
|
|
66
|
+
// reach the boot event and /readyz verbatim and mis-score the fleet audit.
|
|
67
|
+
const engineKind = input.engine === undefined ? undefined : (normalize(input.engine.kind) ?? "custom");
|
|
68
|
+
if (engineKind !== undefined) {
|
|
69
|
+
if (requested !== undefined && engineKind !== requested) {
|
|
70
|
+
warnings.push("engine_object_overrode_requested_mode");
|
|
71
|
+
}
|
|
72
|
+
return finalize(engineKind, "engine", environment, warnings);
|
|
73
|
+
}
|
|
74
|
+
return finalize(requested ?? DEFAULT_PROVIDER_ENGINE_MODE, requestedSource ?? "default", environment, warnings);
|
|
75
|
+
}
|
|
76
|
+
function finalize(mode, source, environment, warnings) {
|
|
77
|
+
// Presence of the remote engine env never selects a mode (the manifest
|
|
78
|
+
// generator projects it fleet-wide before the SDK can use it), but staying
|
|
79
|
+
// silent about it would hide a half-applied cutover. Skipped for a host-supplied
|
|
80
|
+
// engine: that object may be a remote client that consumes the env itself, so
|
|
81
|
+
// claiming the env is unused would be a false report.
|
|
82
|
+
if (mode !== "remote" &&
|
|
83
|
+
source !== "engine" &&
|
|
84
|
+
REMOTE_ONLY_ENGINE_ENV_NAMES.some((name) => normalize(environment[name]) !== undefined)) {
|
|
85
|
+
warnings.push("engine_env_ignored");
|
|
86
|
+
}
|
|
87
|
+
// Fail-closed for the ADR-0011 audit: everything that is not the remote lane
|
|
88
|
+
// still attaches capabilities inside the pod process and is scheduled for
|
|
89
|
+
// removal, including an opaque host engine that declares no kind.
|
|
90
|
+
return { mode, source, deprecated: mode !== "remote", warnings };
|
|
91
|
+
}
|
|
92
|
+
const UNAVAILABLE_ENGINES = new WeakSet();
|
|
93
|
+
/** True when the engine cannot attach, so readiness must report the process as unready. */
|
|
94
|
+
export function isUnavailableProviderEngine(engine) {
|
|
95
|
+
return UNAVAILABLE_ENGINES.has(engine);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Engine that refuses every attachment with `reason`.
|
|
99
|
+
*
|
|
100
|
+
* Requesting a mode this release cannot serve must not crash the process: the SDK
|
|
101
|
+
* answers a structured error per request and reports the process unready, the same
|
|
102
|
+
* trade-off `provider_secrets_missing` already makes at boot. Non-retryable, so a
|
|
103
|
+
* caller-side retry loop cannot turn a configuration fault into load.
|
|
104
|
+
*/
|
|
105
|
+
export function createUnavailableProviderEngine(kind, reason) {
|
|
106
|
+
const engine = {
|
|
107
|
+
kind,
|
|
108
|
+
attach: () => {
|
|
109
|
+
throw new ProviderError(reason.message, {
|
|
110
|
+
code: reason.code,
|
|
111
|
+
retryable: false,
|
|
112
|
+
fix: reason.fix,
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
UNAVAILABLE_ENGINES.add(engine);
|
|
117
|
+
return engine;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Engine for a resolved mode. `remote` has no client in this release, so it
|
|
121
|
+
* resolves to an engine that fails every request instead of falling back to
|
|
122
|
+
* in-process (ADR-0011 Pitfall 2: in-process is never a fallback).
|
|
123
|
+
*/
|
|
124
|
+
export function createEngineForMode(mode) {
|
|
125
|
+
// Default-deny on the mode name, not just on "remote": the mode union is open
|
|
126
|
+
// so a later lane can be named, and falling through to in-process for an
|
|
127
|
+
// unserved lane is the silent downgrade ADR-0011 Pitfall 2 forbids.
|
|
128
|
+
if (mode === DEFAULT_PROVIDER_ENGINE_MODE)
|
|
129
|
+
return createInProcessProviderEngine();
|
|
130
|
+
return createUnavailableProviderEngine(mode, {
|
|
131
|
+
message: `Provider engine mode "${mode}" was requested but this @apifuse/provider-sdk release only serves the ${DEFAULT_PROVIDER_ENGINE_MODE} engine`,
|
|
132
|
+
code: "PROVIDER_ENGINE_MODE_UNSUPPORTED",
|
|
133
|
+
fix: `Unset ${PROVIDER_ENGINE_MODE_ENV} (or the engineMode option), or upgrade to a release that serves this mode.`,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Fail fast for the CLIs (`apifuse dev`, `apifuse record`). They are developer
|
|
138
|
+
* tools, not pods: an unusable mode should stop the command rather than start a
|
|
139
|
+
* server that refuses every request.
|
|
140
|
+
*/
|
|
141
|
+
export function assertProcessEngineModeSupported(environment = process.env) {
|
|
142
|
+
const resolution = resolveProviderEngineMode({ environment });
|
|
143
|
+
if (resolution.mode !== DEFAULT_PROVIDER_ENGINE_MODE) {
|
|
144
|
+
throw new ProviderError(`Provider engine mode "${resolution.mode}" was requested but this @apifuse/provider-sdk release only serves the ${DEFAULT_PROVIDER_ENGINE_MODE} engine`, {
|
|
145
|
+
code: "PROVIDER_ENGINE_MODE_UNSUPPORTED",
|
|
146
|
+
retryable: false,
|
|
147
|
+
details: { mode: resolution.mode, source: resolution.source },
|
|
148
|
+
fix: `Unset ${PROVIDER_ENGINE_MODE_ENV}, or upgrade to a release that serves this mode.`,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return resolution;
|
|
152
|
+
}
|
package/dist/runtime/env.js
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
|
+
import { readDiagnosticEnv } from "./diagnostic-env.js";
|
|
2
|
+
import { isEngineOwnedEnvName } from "../engine.js";
|
|
3
|
+
/**
|
|
4
|
+
* A mixed-case spelling of an engine-owned name is an alias: on Windows it resolves to the
|
|
5
|
+
* engine's variable while evading an exact-name allowlist. Only the exact (upper-case) name,
|
|
6
|
+
* which engine-internal contexts allowlist explicitly, may read those values.
|
|
7
|
+
*/
|
|
8
|
+
function isEngineOwnedEnvAlias(key) {
|
|
9
|
+
return isEngineOwnedEnvName(key) && key !== key.toUpperCase();
|
|
10
|
+
}
|
|
1
11
|
export function createEnvContext(allowedKeys) {
|
|
2
12
|
return {
|
|
3
13
|
get(key) {
|
|
4
14
|
if (allowedKeys && !allowedKeys.includes(key)) {
|
|
5
15
|
return undefined;
|
|
6
16
|
}
|
|
7
|
-
|
|
17
|
+
if (isEngineOwnedEnvAlias(key)) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
return readDiagnosticEnv(key);
|
|
8
21
|
},
|
|
9
22
|
};
|
|
10
23
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EnvContext, ProviderDefinition } from "../types.js";
|
|
2
2
|
export declare function isStreamingOperation(provider: ProviderDefinition, operationId: string): boolean;
|
|
3
3
|
/**
|
|
4
4
|
* Execute a provider operation by calling its handler.
|
|
@@ -11,6 +11,7 @@ export declare function isStreamingOperation(provider: ProviderDefinition, opera
|
|
|
11
11
|
*
|
|
12
12
|
* @see openspec/provider-sdk/03-sdk-core.md §3.6
|
|
13
13
|
*/
|
|
14
|
-
export declare function executeOperation(provider:
|
|
14
|
+
export declare function executeOperation<const TProvider extends ProviderDefinition, const TOperationId extends keyof TProvider["operations"] & string>(provider: TProvider, operationId: TOperationId, ctx: NoInfer<Parameters<TProvider["operations"][TOperationId]["handler"]>[0]>, input: unknown, _options?: {
|
|
15
15
|
skipAuth?: boolean;
|
|
16
|
+
env?: EnvContext;
|
|
16
17
|
}): Promise<unknown>;
|
package/dist/runtime/executor.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isValidationError, ProviderError, ValidationError, } from "../errors.js";
|
|
2
|
+
import { z } from "zod";
|
|
2
3
|
import { parseSchema } from "../schema.js";
|
|
3
4
|
import { assertRequiredSecretsPresent } from "./secrets.js";
|
|
4
5
|
export function isStreamingOperation(provider, operationId) {
|
|
@@ -21,6 +22,9 @@ export async function executeOperation(provider, operationId, ctx, input, _optio
|
|
|
21
22
|
if (!operation) {
|
|
22
23
|
throw new ProviderError(`Unknown operation: ${provider.id}/${operationId}`, {
|
|
23
24
|
code: "NOT_FOUND",
|
|
25
|
+
// No upstream was asked: keep the registered NOT_FOUND code from
|
|
26
|
+
// attributing this to one.
|
|
27
|
+
category: "provider_error",
|
|
24
28
|
fix: `Valid operations: ${Object.keys(provider.operations).join(", ")}`,
|
|
25
29
|
});
|
|
26
30
|
}
|
|
@@ -29,35 +33,28 @@ export async function executeOperation(provider, operationId, ctx, input, _optio
|
|
|
29
33
|
// handler, so every invocation path (serve /v1, self-test probes, perf,
|
|
30
34
|
// record) fails with the same structured MISSING_SECRET error instead of a
|
|
31
35
|
// handler-specific crash. Providers must not re-check presence locally.
|
|
32
|
-
|
|
36
|
+
if (provider.secrets?.some((secret) => secret.required === true)) {
|
|
37
|
+
assertRequiredSecretsPresent(provider, _options?.env ?? ("env" in ctx ? ctx.env : { get: () => undefined }));
|
|
38
|
+
}
|
|
33
39
|
const validatedInput = await parseSchema(operation.input, input, `operations.${operationId}.input`);
|
|
34
40
|
const execute = () => ctx.trace.span(`handler:${operationId}`, () => Promise.resolve(operation.handler(ctx, validatedInput)));
|
|
35
|
-
|
|
41
|
+
const result = await execute();
|
|
42
|
+
if (isStreamingOperation(provider, operationId)) {
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
36
45
|
try {
|
|
37
|
-
|
|
46
|
+
return await parseSchema(operation.output, result, `operations.${operationId}.output`);
|
|
38
47
|
}
|
|
39
|
-
catch (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// so an in-process retry would just repeat the call with the same stale
|
|
43
|
-
// credential (and risk repeating partial side-effects). Instead we surface
|
|
44
|
-
// the expiry so Credential Service refreshes and re-drives the operation
|
|
45
|
-
// with a fresh credential. `retryOnAuthRefresh` declares that this
|
|
46
|
-
// operation is safe to re-drive after refresh, which we signal by marking
|
|
47
|
-
// the surfaced error retryable; non-idempotent operations (the default)
|
|
48
|
-
// stay non-retryable so they are not auto-re-driven. See design.md §4.3 D3.
|
|
49
|
-
// Use the branded guard, not `instanceof`: a handler loaded through a
|
|
50
|
-
// duplicate/published SDK module can throw a correctly branded
|
|
51
|
-
// SessionExpiredError whose constructor identity differs from this
|
|
52
|
-
// executor's, which `instanceof` would miss — dropping the retryable
|
|
53
|
-
// upgrade and stranding an operation that opted into auth refresh.
|
|
54
|
-
if (isSessionExpiredError(error) && operation.retryOnAuthRefresh) {
|
|
55
|
-
throw new SessionExpiredError(error.message, { retryable: true });
|
|
48
|
+
catch (cause) {
|
|
49
|
+
if (!(cause instanceof z.ZodError) && !isValidationError(cause)) {
|
|
50
|
+
throw cause;
|
|
56
51
|
}
|
|
57
|
-
throw
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
throw new ValidationError(`Operation handler output failed schema validation.`, {
|
|
53
|
+
code: "OUTPUT_VALIDATION_FAILED",
|
|
54
|
+
category: "output_validation",
|
|
55
|
+
retryable: false,
|
|
56
|
+
zodError: isValidationError(cause) ? cause.zodError : cause,
|
|
57
|
+
...(cause instanceof Error ? { cause } : {}),
|
|
58
|
+
});
|
|
61
59
|
}
|
|
62
|
-
return parseSchema(operation.output, result, `operations.${operationId}.output`);
|
|
63
60
|
}
|