@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,960 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { JsonPrimitive, JsonValue } from "./contract-json.js";
|
|
3
|
+
export type NonEmpty<T> = readonly [T, ...T[]];
|
|
4
|
+
|
|
5
|
+
const finiteInt = (min: number, max?: number) => {
|
|
6
|
+
const schema = z.number().finite().int().min(min);
|
|
7
|
+
return max === undefined ? schema : schema.max(max);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const nonEmptyArray = <T>(schema: z.ZodType<T>) =>
|
|
11
|
+
z
|
|
12
|
+
.array(schema)
|
|
13
|
+
.min(1)
|
|
14
|
+
.transform((value) => value as [T, ...T[]]);
|
|
15
|
+
|
|
16
|
+
const jsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>
|
|
17
|
+
z.union([
|
|
18
|
+
z.null(),
|
|
19
|
+
z.boolean(),
|
|
20
|
+
z.number().finite(),
|
|
21
|
+
z.string(),
|
|
22
|
+
z.array(jsonValueSchema),
|
|
23
|
+
z.record(z.string(), jsonValueSchema),
|
|
24
|
+
]),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const pathPartSchema = z.union([z.string().min(1), finiteInt(0, 10_000)]);
|
|
28
|
+
|
|
29
|
+
const propertyNameSchema = z
|
|
30
|
+
.string()
|
|
31
|
+
.min(1)
|
|
32
|
+
.superRefine((value, ctx) => {
|
|
33
|
+
if (new TextEncoder().encode(value).byteLength > 128)
|
|
34
|
+
ctx.addIssue({ code: "custom", message: "property name exceeds 128 UTF-8 bytes" });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const pathSegmentSchema = z.union([
|
|
38
|
+
z.object({ kind: z.literal("property"), name: propertyNameSchema }).strict(),
|
|
39
|
+
z.object({ kind: z.literal("index"), index: finiteInt(0, 10_000) }).strict(),
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
export const BoundedJsonPathSchema = z
|
|
43
|
+
.object({ root: z.literal("$"), segments: z.array(pathSegmentSchema).max(12) })
|
|
44
|
+
.strict();
|
|
45
|
+
export type BoundedJsonPath = z.infer<typeof BoundedJsonPathSchema>;
|
|
46
|
+
|
|
47
|
+
const valueTypeSchema = z.enum([
|
|
48
|
+
"null",
|
|
49
|
+
"boolean",
|
|
50
|
+
"number",
|
|
51
|
+
"string",
|
|
52
|
+
"object",
|
|
53
|
+
"array",
|
|
54
|
+
"json",
|
|
55
|
+
"established_connection",
|
|
56
|
+
]);
|
|
57
|
+
export const ValueTypeSchema = valueTypeSchema;
|
|
58
|
+
export type ValueType = z.infer<typeof valueTypeSchema>;
|
|
59
|
+
|
|
60
|
+
const stepReferenceSchema = z
|
|
61
|
+
.object({
|
|
62
|
+
namespace: z.literal("steps"),
|
|
63
|
+
binding: z.string().min(1),
|
|
64
|
+
path: z.array(pathPartSchema).min(1).max(12),
|
|
65
|
+
})
|
|
66
|
+
.strict();
|
|
67
|
+
export const StepReferenceSchema = stepReferenceSchema;
|
|
68
|
+
export type StepReference = z.infer<typeof stepReferenceSchema>;
|
|
69
|
+
|
|
70
|
+
const credentialReferenceSchema = z
|
|
71
|
+
.object({
|
|
72
|
+
namespace: z.literal("credentials"),
|
|
73
|
+
alias: z.string().min(1),
|
|
74
|
+
field: z.literal("connection"),
|
|
75
|
+
})
|
|
76
|
+
.strict();
|
|
77
|
+
const attemptReferenceSchema = z
|
|
78
|
+
.object({
|
|
79
|
+
namespace: z.literal("attempt"),
|
|
80
|
+
field: z.enum([
|
|
81
|
+
"id",
|
|
82
|
+
"external_ref",
|
|
83
|
+
"provider_id",
|
|
84
|
+
"scenario_id",
|
|
85
|
+
"started_at",
|
|
86
|
+
"deadline_at",
|
|
87
|
+
]),
|
|
88
|
+
})
|
|
89
|
+
.strict();
|
|
90
|
+
const candidateReferenceSchema = z
|
|
91
|
+
.object({
|
|
92
|
+
namespace: z.literal("candidate"),
|
|
93
|
+
binding: z.string().min(1),
|
|
94
|
+
path: z.union([
|
|
95
|
+
z.tuple([z.literal("item")]).rest(pathPartSchema),
|
|
96
|
+
z.tuple([z.literal("result")]).rest(pathPartSchema),
|
|
97
|
+
]),
|
|
98
|
+
})
|
|
99
|
+
.strict();
|
|
100
|
+
|
|
101
|
+
export type CredentialReference = z.infer<typeof credentialReferenceSchema>;
|
|
102
|
+
export type EstablishedConnectionReference = CredentialReference & { field: "connection" };
|
|
103
|
+
export type AttemptReference = z.infer<typeof attemptReferenceSchema>;
|
|
104
|
+
export type CandidateReference = z.infer<typeof candidateReferenceSchema>;
|
|
105
|
+
export type Reference = StepReference | CredentialReference | AttemptReference | CandidateReference;
|
|
106
|
+
const referenceSchema = z.discriminatedUnion("namespace", [
|
|
107
|
+
stepReferenceSchema,
|
|
108
|
+
credentialReferenceSchema,
|
|
109
|
+
attemptReferenceSchema,
|
|
110
|
+
candidateReferenceSchema,
|
|
111
|
+
]);
|
|
112
|
+
export const ReferenceSchema = referenceSchema;
|
|
113
|
+
|
|
114
|
+
const relativeDateNodeSchema = z
|
|
115
|
+
.object({
|
|
116
|
+
relativeDate: z
|
|
117
|
+
.object({
|
|
118
|
+
anchor: z.literal("operation_started_at"),
|
|
119
|
+
offsetDays: finiteInt(1, 365),
|
|
120
|
+
timeZone: z.literal("Asia/Seoul"),
|
|
121
|
+
format: z.enum(["YYYY-MM-DD", "YYYYMMDD"]),
|
|
122
|
+
})
|
|
123
|
+
.strict(),
|
|
124
|
+
})
|
|
125
|
+
.strict();
|
|
126
|
+
export type RelativeDateNode = z.infer<typeof relativeDateNodeSchema>;
|
|
127
|
+
export const RelativeDateNodeSchema = relativeDateNodeSchema;
|
|
128
|
+
const referenceNodeSchema = z.object({ ref: referenceSchema }).strict();
|
|
129
|
+
|
|
130
|
+
export type ReferenceNode = { ref: Reference };
|
|
131
|
+
export type JsonTemplate =
|
|
132
|
+
| JsonPrimitive
|
|
133
|
+
| ReferenceNode
|
|
134
|
+
| RelativeDateNode
|
|
135
|
+
| JsonTemplate[]
|
|
136
|
+
| { [key: string]: JsonTemplate };
|
|
137
|
+
// Reserved template keys may only occur in their exact, single-key node forms.
|
|
138
|
+
const templateRecordSchema = z
|
|
139
|
+
.record(
|
|
140
|
+
z.string(),
|
|
141
|
+
z.lazy(() => jsonTemplateSchema),
|
|
142
|
+
)
|
|
143
|
+
.superRefine((value, ctx) => {
|
|
144
|
+
if (Object.hasOwn(value, "ref") || Object.hasOwn(value, "relativeDate"))
|
|
145
|
+
ctx.addIssue({ code: "custom", message: "reserved template keys require a sole-key node" });
|
|
146
|
+
});
|
|
147
|
+
const jsonTemplateSchema: z.ZodType<JsonTemplate> = z.lazy(() =>
|
|
148
|
+
z.union([
|
|
149
|
+
z.null(),
|
|
150
|
+
z.boolean(),
|
|
151
|
+
z.number().finite(),
|
|
152
|
+
z.string(),
|
|
153
|
+
referenceNodeSchema,
|
|
154
|
+
relativeDateNodeSchema,
|
|
155
|
+
z.array(jsonTemplateSchema),
|
|
156
|
+
templateRecordSchema,
|
|
157
|
+
]),
|
|
158
|
+
);
|
|
159
|
+
export const JsonTemplateSchema = jsonTemplateSchema;
|
|
160
|
+
|
|
161
|
+
export type SafeRegex = {
|
|
162
|
+
readonly engine: "re2";
|
|
163
|
+
readonly pattern: string;
|
|
164
|
+
readonly flags: "" | "i";
|
|
165
|
+
};
|
|
166
|
+
function containsNestedRegexQuantifier(pattern: string): boolean {
|
|
167
|
+
const groups: boolean[] = [];
|
|
168
|
+
let escaped = false;
|
|
169
|
+
let inClass = false;
|
|
170
|
+
let closedQuantified = false;
|
|
171
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
172
|
+
const c = pattern[i];
|
|
173
|
+
if (escaped) {
|
|
174
|
+
escaped = false;
|
|
175
|
+
closedQuantified = false;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (c === "\\") {
|
|
179
|
+
escaped = true;
|
|
180
|
+
closedQuantified = false;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (c === "[" && !inClass) {
|
|
184
|
+
inClass = true;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (c === "]" && inClass) {
|
|
188
|
+
inClass = false;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (inClass) continue;
|
|
192
|
+
if (c === "(") {
|
|
193
|
+
groups.push(false);
|
|
194
|
+
closedQuantified = false;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (c === ")") {
|
|
198
|
+
closedQuantified = groups.pop() ?? false;
|
|
199
|
+
if (closedQuantified && groups.length > 0) groups[groups.length - 1] = true;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const brace = c === "{" && /^\{\d+(?:,\d*)?\}/.test(pattern.slice(i));
|
|
203
|
+
if (c === "*" || c === "+" || brace || (c === "?" && pattern[i - 1] !== "(")) {
|
|
204
|
+
if (closedQuantified) return true;
|
|
205
|
+
if (groups.length > 0) groups[groups.length - 1] = true;
|
|
206
|
+
}
|
|
207
|
+
closedQuantified = false;
|
|
208
|
+
}
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
export const SafeRegexSchema = z
|
|
212
|
+
.object({ engine: z.literal("re2"), pattern: z.string().max(256), flags: z.enum(["", "i"]) })
|
|
213
|
+
.strict()
|
|
214
|
+
.superRefine((value, ctx) => {
|
|
215
|
+
if (/\\[1-9kg]/.test(value.pattern))
|
|
216
|
+
ctx.addIssue({
|
|
217
|
+
code: "custom",
|
|
218
|
+
path: ["pattern"],
|
|
219
|
+
message: "backreferences are not supported by RE2",
|
|
220
|
+
});
|
|
221
|
+
if (/\(\?/.test(value.pattern))
|
|
222
|
+
ctx.addIssue({
|
|
223
|
+
code: "custom",
|
|
224
|
+
path: ["pattern"],
|
|
225
|
+
message: "extended groups are not supported by RE2",
|
|
226
|
+
});
|
|
227
|
+
if (containsNestedRegexQuantifier(value.pattern))
|
|
228
|
+
ctx.addIssue({
|
|
229
|
+
code: "custom",
|
|
230
|
+
path: ["pattern"],
|
|
231
|
+
message: "nested regex quantifiers are not permitted",
|
|
232
|
+
});
|
|
233
|
+
try {
|
|
234
|
+
new RegExp(value.pattern, value.flags);
|
|
235
|
+
} catch {
|
|
236
|
+
ctx.addIssue({
|
|
237
|
+
code: "custom",
|
|
238
|
+
path: ["pattern"],
|
|
239
|
+
message: "pattern must be valid regular-expression syntax",
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const operandLiteralSchema = jsonValueSchema.superRefine((value, ctx) => {
|
|
245
|
+
if (
|
|
246
|
+
value !== null &&
|
|
247
|
+
typeof value === "object" &&
|
|
248
|
+
!Array.isArray(value) &&
|
|
249
|
+
Object.hasOwn(value, "ref")
|
|
250
|
+
)
|
|
251
|
+
ctx.addIssue({
|
|
252
|
+
code: "custom",
|
|
253
|
+
message: 'the object key "ref" is reserved for a validated reference',
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
const operandSchema = z.union([operandLiteralSchema, referenceNodeSchema]);
|
|
257
|
+
export const OperandSchema = operandSchema;
|
|
258
|
+
export type Operand = JsonValue | ReferenceNode;
|
|
259
|
+
|
|
260
|
+
const predicateSchema = z.union([
|
|
261
|
+
z
|
|
262
|
+
.object({
|
|
263
|
+
kind: z.literal("predicate"),
|
|
264
|
+
operator: z.enum(["exists", "not_exists", "non_empty", "is_true"]),
|
|
265
|
+
actual: operandSchema,
|
|
266
|
+
})
|
|
267
|
+
.strict(),
|
|
268
|
+
z
|
|
269
|
+
.object({
|
|
270
|
+
kind: z.literal("predicate"),
|
|
271
|
+
operator: z.enum(["equals", "not_equals", "contains"]),
|
|
272
|
+
actual: operandSchema,
|
|
273
|
+
expected: operandSchema,
|
|
274
|
+
})
|
|
275
|
+
.strict(),
|
|
276
|
+
z
|
|
277
|
+
.object({
|
|
278
|
+
kind: z.literal("predicate"),
|
|
279
|
+
operator: z.literal("matches"),
|
|
280
|
+
actual: operandSchema,
|
|
281
|
+
pattern: SafeRegexSchema,
|
|
282
|
+
})
|
|
283
|
+
.strict(),
|
|
284
|
+
z
|
|
285
|
+
.object({
|
|
286
|
+
kind: z.literal("predicate"),
|
|
287
|
+
operator: z.enum(["number_gt", "number_gte", "number_lt", "number_lte"]),
|
|
288
|
+
actual: operandSchema,
|
|
289
|
+
expected: operandSchema,
|
|
290
|
+
})
|
|
291
|
+
.strict(),
|
|
292
|
+
z
|
|
293
|
+
.object({
|
|
294
|
+
kind: z.literal("predicate"),
|
|
295
|
+
operator: z.enum(["array_length_eq", "array_length_gte", "array_length_lte"]),
|
|
296
|
+
actual: operandSchema,
|
|
297
|
+
expected: z.number().finite().int().nonnegative(),
|
|
298
|
+
})
|
|
299
|
+
.strict(),
|
|
300
|
+
z
|
|
301
|
+
.object({
|
|
302
|
+
kind: z.literal("predicate"),
|
|
303
|
+
operator: z.literal("status_2xx"),
|
|
304
|
+
actual: operandSchema,
|
|
305
|
+
})
|
|
306
|
+
.strict(),
|
|
307
|
+
z
|
|
308
|
+
.object({
|
|
309
|
+
kind: z.literal("predicate"),
|
|
310
|
+
operator: z.literal("type_is"),
|
|
311
|
+
actual: operandSchema,
|
|
312
|
+
expected: z.enum(["null", "boolean", "number", "string", "object", "array"]),
|
|
313
|
+
})
|
|
314
|
+
.strict(),
|
|
315
|
+
]);
|
|
316
|
+
export const AssertionPredicateSchema = predicateSchema;
|
|
317
|
+
export type AssertionPredicate = z.infer<typeof predicateSchema>;
|
|
318
|
+
|
|
319
|
+
const scopedItemReferenceSchema = z
|
|
320
|
+
.object({
|
|
321
|
+
namespace: z.literal("item"),
|
|
322
|
+
binding: z.string().min(1),
|
|
323
|
+
path: z.array(pathPartSchema).max(12),
|
|
324
|
+
})
|
|
325
|
+
.strict();
|
|
326
|
+
export const ScopedItemReferenceSchema = scopedItemReferenceSchema;
|
|
327
|
+
export type ScopedItemReference = z.infer<typeof scopedItemReferenceSchema>;
|
|
328
|
+
|
|
329
|
+
const scopedOperandSchema = z.union([
|
|
330
|
+
operandLiteralSchema,
|
|
331
|
+
z.object({ ref: scopedItemReferenceSchema }).strict(),
|
|
332
|
+
referenceNodeSchema,
|
|
333
|
+
]);
|
|
334
|
+
export const ScopedOperandSchema = scopedOperandSchema;
|
|
335
|
+
export type ScopedOperand = z.infer<typeof scopedOperandSchema>;
|
|
336
|
+
|
|
337
|
+
const scopedPredicateSchema = z.union([
|
|
338
|
+
z
|
|
339
|
+
.object({
|
|
340
|
+
kind: z.literal("predicate"),
|
|
341
|
+
operator: z.enum(["exists", "not_exists", "non_empty", "is_true"]),
|
|
342
|
+
actual: scopedOperandSchema,
|
|
343
|
+
})
|
|
344
|
+
.strict(),
|
|
345
|
+
z
|
|
346
|
+
.object({
|
|
347
|
+
kind: z.literal("predicate"),
|
|
348
|
+
operator: z.enum(["equals", "not_equals", "contains"]),
|
|
349
|
+
actual: scopedOperandSchema,
|
|
350
|
+
expected: scopedOperandSchema,
|
|
351
|
+
})
|
|
352
|
+
.strict(),
|
|
353
|
+
z
|
|
354
|
+
.object({
|
|
355
|
+
kind: z.literal("predicate"),
|
|
356
|
+
operator: z.literal("matches"),
|
|
357
|
+
actual: scopedOperandSchema,
|
|
358
|
+
pattern: SafeRegexSchema,
|
|
359
|
+
})
|
|
360
|
+
.strict(),
|
|
361
|
+
z
|
|
362
|
+
.object({
|
|
363
|
+
kind: z.literal("predicate"),
|
|
364
|
+
operator: z.enum(["number_gt", "number_gte", "number_lt", "number_lte"]),
|
|
365
|
+
actual: scopedOperandSchema,
|
|
366
|
+
expected: scopedOperandSchema,
|
|
367
|
+
})
|
|
368
|
+
.strict(),
|
|
369
|
+
z
|
|
370
|
+
.object({
|
|
371
|
+
kind: z.literal("predicate"),
|
|
372
|
+
operator: z.enum(["array_length_eq", "array_length_gte", "array_length_lte"]),
|
|
373
|
+
actual: scopedOperandSchema,
|
|
374
|
+
expected: z.number().finite().int().nonnegative(),
|
|
375
|
+
})
|
|
376
|
+
.strict(),
|
|
377
|
+
z
|
|
378
|
+
.object({
|
|
379
|
+
kind: z.literal("predicate"),
|
|
380
|
+
operator: z.literal("status_2xx"),
|
|
381
|
+
actual: scopedOperandSchema,
|
|
382
|
+
})
|
|
383
|
+
.strict(),
|
|
384
|
+
z
|
|
385
|
+
.object({
|
|
386
|
+
kind: z.literal("predicate"),
|
|
387
|
+
operator: z.literal("type_is"),
|
|
388
|
+
actual: scopedOperandSchema,
|
|
389
|
+
expected: z.enum(["null", "boolean", "number", "string", "object", "array"]),
|
|
390
|
+
})
|
|
391
|
+
.strict(),
|
|
392
|
+
]);
|
|
393
|
+
export const ScopedAssertionPredicateSchema = scopedPredicateSchema;
|
|
394
|
+
export type ScopedAssertionPredicate = z.infer<typeof scopedPredicateSchema>;
|
|
395
|
+
|
|
396
|
+
export type ScopedAssertionExpression =
|
|
397
|
+
| { kind: "all" | "any"; clauses: NonEmpty<ScopedAssertionExpression> }
|
|
398
|
+
| { kind: "not"; clause: ScopedAssertionExpression }
|
|
399
|
+
| ScopedAssertionPredicate;
|
|
400
|
+
const scopedExpressionSchema: z.ZodType<ScopedAssertionExpression> = z.lazy(() =>
|
|
401
|
+
z.union([
|
|
402
|
+
z.object({ kind: z.literal("all"), clauses: nonEmptyArray(scopedExpressionSchema) }).strict(),
|
|
403
|
+
z.object({ kind: z.literal("any"), clauses: nonEmptyArray(scopedExpressionSchema) }).strict(),
|
|
404
|
+
z.object({ kind: z.literal("not"), clause: scopedExpressionSchema }).strict(),
|
|
405
|
+
scopedPredicateSchema,
|
|
406
|
+
]),
|
|
407
|
+
);
|
|
408
|
+
export const ScopedAssertionExpressionSchema = scopedExpressionSchema;
|
|
409
|
+
|
|
410
|
+
export type Quantifier = {
|
|
411
|
+
kind: "quantifier";
|
|
412
|
+
quantifier: "every" | "any";
|
|
413
|
+
items: { ref: StepReference | CandidateReference };
|
|
414
|
+
itemBinding: string;
|
|
415
|
+
maxItems: number;
|
|
416
|
+
clause: ScopedAssertionExpression;
|
|
417
|
+
};
|
|
418
|
+
const quantifierSchema: z.ZodType<Quantifier> = z
|
|
419
|
+
.object({
|
|
420
|
+
kind: z.literal("quantifier"),
|
|
421
|
+
quantifier: z.enum(["every", "any"]),
|
|
422
|
+
items: z.object({ ref: z.union([stepReferenceSchema, candidateReferenceSchema]) }).strict(),
|
|
423
|
+
itemBinding: z.string().min(1),
|
|
424
|
+
maxItems: finiteInt(1, 100),
|
|
425
|
+
clause: scopedExpressionSchema,
|
|
426
|
+
})
|
|
427
|
+
.strict();
|
|
428
|
+
export const QuantifierSchema = quantifierSchema;
|
|
429
|
+
|
|
430
|
+
export type AssertionExpression =
|
|
431
|
+
| { kind: "all" | "any"; clauses: NonEmpty<AssertionExpression> }
|
|
432
|
+
| { kind: "not"; clause: AssertionExpression }
|
|
433
|
+
| AssertionPredicate
|
|
434
|
+
| Quantifier;
|
|
435
|
+
const expressionSchema: z.ZodType<AssertionExpression> = z.lazy(() =>
|
|
436
|
+
z.union([
|
|
437
|
+
z.object({ kind: z.literal("all"), clauses: nonEmptyArray(expressionSchema) }).strict(),
|
|
438
|
+
z.object({ kind: z.literal("any"), clauses: nonEmptyArray(expressionSchema) }).strict(),
|
|
439
|
+
z.object({ kind: z.literal("not"), clause: expressionSchema }).strict(),
|
|
440
|
+
predicateSchema,
|
|
441
|
+
quantifierSchema,
|
|
442
|
+
]),
|
|
443
|
+
);
|
|
444
|
+
export const AssertionExpressionSchema = expressionSchema;
|
|
445
|
+
|
|
446
|
+
export type RetryPolicy = {
|
|
447
|
+
maxAttempts: number;
|
|
448
|
+
retryOn: NonEmpty<"transport_error" | "timeout" | "http_429" | "http_5xx">;
|
|
449
|
+
backoff:
|
|
450
|
+
| { kind: "fixed"; delayMs: number }
|
|
451
|
+
| { kind: "exponential"; initialDelayMs: number; maxDelayMs: number };
|
|
452
|
+
attemptTimeoutMs?: number;
|
|
453
|
+
};
|
|
454
|
+
const retryPolicySchema: z.ZodType<RetryPolicy> = z
|
|
455
|
+
.object({
|
|
456
|
+
maxAttempts: finiteInt(1, 3),
|
|
457
|
+
retryOn: nonEmptyArray(z.enum(["transport_error", "timeout", "http_429", "http_5xx"])),
|
|
458
|
+
backoff: z.union([
|
|
459
|
+
z
|
|
460
|
+
.object({ kind: z.literal("fixed"), delayMs: z.number().finite().int().nonnegative() })
|
|
461
|
+
.strict(),
|
|
462
|
+
z
|
|
463
|
+
.object({
|
|
464
|
+
kind: z.literal("exponential"),
|
|
465
|
+
initialDelayMs: z.number().finite().int().nonnegative(),
|
|
466
|
+
maxDelayMs: z.number().finite().int().nonnegative(),
|
|
467
|
+
})
|
|
468
|
+
.strict(),
|
|
469
|
+
]),
|
|
470
|
+
attemptTimeoutMs: finiteInt(1, 600_000).optional(),
|
|
471
|
+
})
|
|
472
|
+
.strict();
|
|
473
|
+
export const RetryPolicySchema = retryPolicySchema;
|
|
474
|
+
|
|
475
|
+
export type CandidatePolicy = {
|
|
476
|
+
items: StepReference;
|
|
477
|
+
itemBinding: string;
|
|
478
|
+
itemType: "string" | "number" | "object";
|
|
479
|
+
maxAttempts: number;
|
|
480
|
+
accept: AssertionExpression;
|
|
481
|
+
};
|
|
482
|
+
const candidatePolicySchema: z.ZodType<CandidatePolicy> = z
|
|
483
|
+
.object({
|
|
484
|
+
items: stepReferenceSchema,
|
|
485
|
+
itemBinding: z.string().min(1),
|
|
486
|
+
itemType: z.enum(["string", "number", "object"]),
|
|
487
|
+
maxAttempts: finiteInt(1, 10),
|
|
488
|
+
accept: expressionSchema,
|
|
489
|
+
})
|
|
490
|
+
.strict();
|
|
491
|
+
export const CandidatePolicySchema = candidatePolicySchema;
|
|
492
|
+
|
|
493
|
+
export type CandidateBlock = {
|
|
494
|
+
scope: "step_block";
|
|
495
|
+
items: StepReference;
|
|
496
|
+
itemBinding: string;
|
|
497
|
+
itemType: "string" | "number" | "object";
|
|
498
|
+
members: readonly [string, string, ...string[]];
|
|
499
|
+
maxAttempts: number;
|
|
500
|
+
accept: AssertionExpression;
|
|
501
|
+
};
|
|
502
|
+
const candidateBlockSchema: z.ZodType<CandidateBlock> = z
|
|
503
|
+
.object({
|
|
504
|
+
scope: z.literal("step_block"),
|
|
505
|
+
items: stepReferenceSchema,
|
|
506
|
+
itemBinding: z.string().min(1),
|
|
507
|
+
itemType: z.enum(["string", "number", "object"]),
|
|
508
|
+
members: z
|
|
509
|
+
.array(z.string().min(1))
|
|
510
|
+
.min(2)
|
|
511
|
+
.max(16)
|
|
512
|
+
.transform((value) => value as [string, string, ...string[]]),
|
|
513
|
+
maxAttempts: finiteInt(1, 10),
|
|
514
|
+
accept: expressionSchema,
|
|
515
|
+
})
|
|
516
|
+
.strict();
|
|
517
|
+
export const CandidateBlockSchema = candidateBlockSchema;
|
|
518
|
+
|
|
519
|
+
export type JournalPolicy = {
|
|
520
|
+
kind: "side_effect_barrier";
|
|
521
|
+
version: 1;
|
|
522
|
+
key: Reference;
|
|
523
|
+
before: "required";
|
|
524
|
+
after: "required";
|
|
525
|
+
replay: "deny_after_started";
|
|
526
|
+
};
|
|
527
|
+
const journalPolicySchema: z.ZodType<JournalPolicy> = z
|
|
528
|
+
.object({
|
|
529
|
+
kind: z.literal("side_effect_barrier"),
|
|
530
|
+
version: z.literal(1),
|
|
531
|
+
key: referenceSchema,
|
|
532
|
+
before: z.literal("required"),
|
|
533
|
+
after: z.literal("required"),
|
|
534
|
+
replay: z.literal("deny_after_started"),
|
|
535
|
+
})
|
|
536
|
+
.strict();
|
|
537
|
+
export const JournalPolicySchema = journalPolicySchema;
|
|
538
|
+
|
|
539
|
+
export type StepBase = { id: string; result: string; timeoutMs?: number };
|
|
540
|
+
const stepBaseSchema = z
|
|
541
|
+
.object({
|
|
542
|
+
id: z.string().min(1),
|
|
543
|
+
result: z.string().min(1),
|
|
544
|
+
timeoutMs: finiteInt(1, 600_000).optional(),
|
|
545
|
+
})
|
|
546
|
+
.strict();
|
|
547
|
+
/**
|
|
548
|
+
* Why a guard stopped the scenario. The platform decides what each reason
|
|
549
|
+
* publishes; the attribution's `status` is only the scenario rollup.
|
|
550
|
+
*
|
|
551
|
+
* - `expected_absence`: the upstream answered exactly as this provider says it
|
|
552
|
+
* does when there is nothing to return — no weather alert today, an empty
|
|
553
|
+
* shelf, a transit feed outside its operating hours. Published as the
|
|
554
|
+
* non-incident status `expected_absence`: neither ok nor degraded, excluded
|
|
555
|
+
* from uptime, never an incident. Use it only for an absence the provider
|
|
556
|
+
* can declare in advance. Data that must be there (a product that must have
|
|
557
|
+
* options) is a failing assertion, not an expected absence.
|
|
558
|
+
* - `served_stale_cache`: the operation step was answered from the
|
|
559
|
+
* stale-if-error window (`served_stale_cache` on the step result) instead
|
|
560
|
+
* of a live read. A measured upstream verdict: published `degraded`, opens
|
|
561
|
+
* incidents.
|
|
562
|
+
*/
|
|
563
|
+
export type GuardReasonCode = "expected_absence" | "served_stale_cache";
|
|
564
|
+
/**
|
|
565
|
+
* Every member of the union, pinned in both directions: a code missing here
|
|
566
|
+
* fails `satisfies Record<GuardReasonCode, true>`, a code added here without
|
|
567
|
+
* the union fails the excess-property check. The union itself stays a
|
|
568
|
+
* literal so the type-only entry points (contract, server, testing) do not
|
|
569
|
+
* have to re-export the runtime tuple.
|
|
570
|
+
*/
|
|
571
|
+
const GUARD_REASON_CODE_SET = {
|
|
572
|
+
expected_absence: true,
|
|
573
|
+
served_stale_cache: true,
|
|
574
|
+
} as const satisfies Record<GuardReasonCode, true>;
|
|
575
|
+
/** The closed vocabulary as a non-empty tuple, for schema and lint consumers. */
|
|
576
|
+
export const GUARD_REASON_CODES = Object.keys(GUARD_REASON_CODE_SET) as [
|
|
577
|
+
GuardReasonCode,
|
|
578
|
+
...GuardReasonCode[],
|
|
579
|
+
];
|
|
580
|
+
export type GuardAttribution = {
|
|
581
|
+
operationId: string;
|
|
582
|
+
status: "degraded";
|
|
583
|
+
reasonCode: GuardReasonCode;
|
|
584
|
+
reasonKey: string;
|
|
585
|
+
};
|
|
586
|
+
export type OperationResult = {
|
|
587
|
+
kind: "operation_result";
|
|
588
|
+
status_code: number;
|
|
589
|
+
data: JsonValue;
|
|
590
|
+
request_id: string;
|
|
591
|
+
duration_ms: number;
|
|
592
|
+
/**
|
|
593
|
+
* Whether the monitor's operation step was answered from a stale-if-error
|
|
594
|
+
* cache serve — the runtime's projection of the response envelope's
|
|
595
|
+
* `meta.stale`.
|
|
596
|
+
*
|
|
597
|
+
* An operation that reads its upstream through `ctx.cache.getOrSet(...,
|
|
598
|
+
* { staleIfErrorMs })` answers an upstream outage with HTTP 200 and a
|
|
599
|
+
* well-formed, fully schema-valid body for the whole stale window. Every
|
|
600
|
+
* assertion over `status_code` and `data` therefore passes, and the probe
|
|
601
|
+
* rolls up `ok` while the upstream is down. This boolean is the only thing
|
|
602
|
+
* in the step result that separates that from a live read.
|
|
603
|
+
*
|
|
604
|
+
* Always present, so a reference to it resolves to `false` rather than
|
|
605
|
+
* raising `reference_unresolvable`; a response whose `meta` is malformed
|
|
606
|
+
* fails the step rather than projecting as fresh.
|
|
607
|
+
*
|
|
608
|
+
* Providers that serve stale-if-error should guard on it, so an outage the
|
|
609
|
+
* cache is absorbing stops reading as green. Attribute it
|
|
610
|
+
* `served_stale_cache`, not `expected_absence`: a stale serve is an upstream
|
|
611
|
+
* fault the platform publishes as `degraded`, while an expected absence is
|
|
612
|
+
* published as a non-incident status.
|
|
613
|
+
*
|
|
614
|
+
* ```ts
|
|
615
|
+
* {
|
|
616
|
+
* id: "guard-freshness",
|
|
617
|
+
* result: "freshness-guarded",
|
|
618
|
+
* kind: "guard",
|
|
619
|
+
* condition: {
|
|
620
|
+
* kind: "predicate",
|
|
621
|
+
* operator: "not_equals",
|
|
622
|
+
* actual: { ref: { namespace: "steps", binding: "case", path: ["served_stale_cache"] } },
|
|
623
|
+
* expected: true,
|
|
624
|
+
* },
|
|
625
|
+
* onFail: {
|
|
626
|
+
* attribute: [{ operationId, status: "degraded", reasonCode: "served_stale_cache", reasonKey }],
|
|
627
|
+
* stop: "scenario",
|
|
628
|
+
* },
|
|
629
|
+
* }
|
|
630
|
+
* ```
|
|
631
|
+
*/
|
|
632
|
+
served_stale_cache: boolean;
|
|
633
|
+
candidate?: { attempts: number; selected_index: number | null };
|
|
634
|
+
};
|
|
635
|
+
export type ExtractResult<T extends JsonValue = JsonValue> = {
|
|
636
|
+
kind: "extract_result";
|
|
637
|
+
found: boolean;
|
|
638
|
+
value: T | null;
|
|
639
|
+
};
|
|
640
|
+
export type AssertResult = {
|
|
641
|
+
kind: "assert_result";
|
|
642
|
+
passed: boolean;
|
|
643
|
+
failed_clause_paths: string[];
|
|
644
|
+
};
|
|
645
|
+
export type GuardResult = { kind: "guard_result"; passed: true };
|
|
646
|
+
|
|
647
|
+
const attributionSchema = z
|
|
648
|
+
.object({
|
|
649
|
+
operationId: z.string().min(1),
|
|
650
|
+
status: z.literal("degraded"),
|
|
651
|
+
reasonCode: z.enum(GUARD_REASON_CODES),
|
|
652
|
+
reasonKey: z.string().min(1),
|
|
653
|
+
})
|
|
654
|
+
.strict();
|
|
655
|
+
|
|
656
|
+
export type FindFirst = {
|
|
657
|
+
kind: "find_first";
|
|
658
|
+
itemBinding: string;
|
|
659
|
+
predicate: ScopedAssertionExpression;
|
|
660
|
+
maxScan: number;
|
|
661
|
+
};
|
|
662
|
+
const findFirstSchema: z.ZodType<FindFirst> = z
|
|
663
|
+
.object({
|
|
664
|
+
kind: z.literal("find_first"),
|
|
665
|
+
itemBinding: z.string().min(1),
|
|
666
|
+
predicate: scopedExpressionSchema,
|
|
667
|
+
maxScan: finiteInt(1, 100),
|
|
668
|
+
})
|
|
669
|
+
.strict();
|
|
670
|
+
export const FindFirstSchema = findFirstSchema;
|
|
671
|
+
|
|
672
|
+
export type OperationStep = StepBase & {
|
|
673
|
+
kind: "operation";
|
|
674
|
+
operationId: string;
|
|
675
|
+
inputTemplate: JsonTemplate;
|
|
676
|
+
connection?: CredentialReference;
|
|
677
|
+
retry?: RetryPolicy;
|
|
678
|
+
candidate?: CandidatePolicy | CandidateBlock;
|
|
679
|
+
journal?: JournalPolicy;
|
|
680
|
+
};
|
|
681
|
+
export type ExtractStep = StepBase & {
|
|
682
|
+
kind: "extract";
|
|
683
|
+
from: StepReference;
|
|
684
|
+
selector: BoundedJsonPath | FindFirst;
|
|
685
|
+
valueType: ValueType;
|
|
686
|
+
required: boolean;
|
|
687
|
+
};
|
|
688
|
+
export type AssertStep = StepBase & {
|
|
689
|
+
kind: "assert";
|
|
690
|
+
coversOperations: NonEmpty<string>;
|
|
691
|
+
expression: AssertionExpression;
|
|
692
|
+
};
|
|
693
|
+
export type GuardStep = StepBase & {
|
|
694
|
+
kind: "guard";
|
|
695
|
+
condition: AssertionExpression;
|
|
696
|
+
onFail: { attribute: NonEmpty<GuardAttribution>; stop: "scenario" };
|
|
697
|
+
};
|
|
698
|
+
export type HealthStep = OperationStep | ExtractStep | AssertStep | GuardStep;
|
|
699
|
+
|
|
700
|
+
const operationStepSchema = stepBaseSchema
|
|
701
|
+
.extend({
|
|
702
|
+
kind: z.literal("operation"),
|
|
703
|
+
operationId: z.string().min(1),
|
|
704
|
+
inputTemplate: jsonTemplateSchema,
|
|
705
|
+
connection: credentialReferenceSchema.optional(),
|
|
706
|
+
retry: retryPolicySchema.optional(),
|
|
707
|
+
candidate: z.union([candidatePolicySchema, candidateBlockSchema]).optional(),
|
|
708
|
+
journal: journalPolicySchema.optional(),
|
|
709
|
+
})
|
|
710
|
+
.strict();
|
|
711
|
+
const extractStepSchema = stepBaseSchema
|
|
712
|
+
.extend({
|
|
713
|
+
kind: z.literal("extract"),
|
|
714
|
+
from: stepReferenceSchema,
|
|
715
|
+
selector: z.union([BoundedJsonPathSchema, findFirstSchema]),
|
|
716
|
+
valueType: valueTypeSchema,
|
|
717
|
+
required: z.boolean(),
|
|
718
|
+
})
|
|
719
|
+
.strict();
|
|
720
|
+
const assertStepSchema = stepBaseSchema
|
|
721
|
+
.extend({
|
|
722
|
+
kind: z.literal("assert"),
|
|
723
|
+
coversOperations: nonEmptyArray(z.string().min(1)),
|
|
724
|
+
expression: expressionSchema,
|
|
725
|
+
})
|
|
726
|
+
.strict();
|
|
727
|
+
const guardStepSchema = stepBaseSchema
|
|
728
|
+
.extend({
|
|
729
|
+
kind: z.literal("guard"),
|
|
730
|
+
condition: expressionSchema,
|
|
731
|
+
onFail: z
|
|
732
|
+
.object({ attribute: nonEmptyArray(attributionSchema), stop: z.literal("scenario") })
|
|
733
|
+
.strict(),
|
|
734
|
+
})
|
|
735
|
+
.strict();
|
|
736
|
+
export const HealthStepSchema = z.discriminatedUnion("kind", [
|
|
737
|
+
operationStepSchema,
|
|
738
|
+
extractStepSchema,
|
|
739
|
+
assertStepSchema,
|
|
740
|
+
guardStepSchema,
|
|
741
|
+
]);
|
|
742
|
+
export const OperationStepSchema = operationStepSchema;
|
|
743
|
+
export const ExtractStepSchema = extractStepSchema;
|
|
744
|
+
export const AssertStepSchema = assertStepSchema;
|
|
745
|
+
export const GuardStepSchema = guardStepSchema;
|
|
746
|
+
|
|
747
|
+
export type ManualTriggerPolicy =
|
|
748
|
+
| { enabled: false; reasonKey: string }
|
|
749
|
+
| {
|
|
750
|
+
enabled: true;
|
|
751
|
+
requiresAcknowledgement: boolean;
|
|
752
|
+
risk: "read_only" | "writes_external_state";
|
|
753
|
+
minManualIntervalMs: number;
|
|
754
|
+
publicRationaleKey: string;
|
|
755
|
+
};
|
|
756
|
+
const manualTriggerSchema: z.ZodType<ManualTriggerPolicy> = z.union([
|
|
757
|
+
z.object({ enabled: z.literal(false), reasonKey: z.string().min(1) }).strict(),
|
|
758
|
+
z
|
|
759
|
+
.object({
|
|
760
|
+
enabled: z.literal(true),
|
|
761
|
+
requiresAcknowledgement: z.boolean(),
|
|
762
|
+
risk: z.enum(["read_only", "writes_external_state"]),
|
|
763
|
+
minManualIntervalMs: finiteInt(1000, 86_400_000),
|
|
764
|
+
publicRationaleKey: z.string().min(1),
|
|
765
|
+
})
|
|
766
|
+
.strict(),
|
|
767
|
+
]);
|
|
768
|
+
export const ManualTriggerPolicySchema = manualTriggerSchema;
|
|
769
|
+
|
|
770
|
+
export type CredentialRefDeclaration = { alias: string; kind: "connection" };
|
|
771
|
+
const credentialRefDeclarationSchema = z
|
|
772
|
+
.object({ alias: z.string().min(1), kind: z.literal("connection") })
|
|
773
|
+
.strict();
|
|
774
|
+
export const CredentialRefDeclarationSchema = credentialRefDeclarationSchema;
|
|
775
|
+
|
|
776
|
+
export type HealthScenario = {
|
|
777
|
+
scenarioVersion: 2;
|
|
778
|
+
id: string;
|
|
779
|
+
display: { titleKey: string; descriptionKey?: string };
|
|
780
|
+
schedule: { kind: "interval"; intervalMs: number; jitterMs: number };
|
|
781
|
+
timeoutMs: number;
|
|
782
|
+
cooldownMs?: number;
|
|
783
|
+
manualTrigger?: ManualTriggerPolicy;
|
|
784
|
+
coversOperations: NonEmpty<string>;
|
|
785
|
+
credentialRefs: CredentialRefDeclaration[];
|
|
786
|
+
steps: NonEmpty<HealthStep>;
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
export const HealthScenarioSchema: z.ZodType<HealthScenario> = z
|
|
790
|
+
.object({
|
|
791
|
+
scenarioVersion: z.literal(2),
|
|
792
|
+
id: z.string().min(1),
|
|
793
|
+
display: z
|
|
794
|
+
.object({ titleKey: z.string().min(1), descriptionKey: z.string().min(1).optional() })
|
|
795
|
+
.strict(),
|
|
796
|
+
schedule: z
|
|
797
|
+
.object({
|
|
798
|
+
kind: z.literal("interval"),
|
|
799
|
+
intervalMs: finiteInt(30_000, 604_800_000),
|
|
800
|
+
jitterMs: finiteInt(0),
|
|
801
|
+
})
|
|
802
|
+
.strict(),
|
|
803
|
+
timeoutMs: finiteInt(1000, 600_000),
|
|
804
|
+
cooldownMs: finiteInt(0, 86_400_000).optional(),
|
|
805
|
+
manualTrigger: manualTriggerSchema.optional(),
|
|
806
|
+
coversOperations: nonEmptyArray(z.string().min(1)),
|
|
807
|
+
credentialRefs: z.array(credentialRefDeclarationSchema),
|
|
808
|
+
steps: z
|
|
809
|
+
.array(HealthStepSchema)
|
|
810
|
+
.min(1)
|
|
811
|
+
.max(64)
|
|
812
|
+
.transform((value) => value as [HealthStep, ...HealthStep[]]),
|
|
813
|
+
})
|
|
814
|
+
.strict()
|
|
815
|
+
.superRefine((value, ctx) => {
|
|
816
|
+
if (value.schedule.jitterMs > value.schedule.intervalMs)
|
|
817
|
+
ctx.addIssue({
|
|
818
|
+
code: "custom",
|
|
819
|
+
path: ["schedule", "jitterMs"],
|
|
820
|
+
message: "jitterMs must not exceed intervalMs",
|
|
821
|
+
});
|
|
822
|
+
if (value.timeoutMs > value.schedule.intervalMs)
|
|
823
|
+
ctx.addIssue({
|
|
824
|
+
code: "custom",
|
|
825
|
+
path: ["timeoutMs"],
|
|
826
|
+
message: "timeoutMs must not exceed intervalMs",
|
|
827
|
+
});
|
|
828
|
+
const ids = new Set<string>();
|
|
829
|
+
const bindings = new Set<string>();
|
|
830
|
+
for (const [index, step] of value.steps.entries()) {
|
|
831
|
+
if (ids.has(step.id))
|
|
832
|
+
ctx.addIssue({
|
|
833
|
+
code: "custom",
|
|
834
|
+
path: ["steps", index, "id"],
|
|
835
|
+
message: `duplicate step ${step.id}`,
|
|
836
|
+
});
|
|
837
|
+
ids.add(step.id);
|
|
838
|
+
if (bindings.has(step.result))
|
|
839
|
+
ctx.addIssue({
|
|
840
|
+
code: "custom",
|
|
841
|
+
path: ["steps", index, "result"],
|
|
842
|
+
message: `duplicate result binding ${step.result}`,
|
|
843
|
+
});
|
|
844
|
+
bindings.add(step.result);
|
|
845
|
+
if (step.timeoutMs !== undefined && step.timeoutMs > value.timeoutMs)
|
|
846
|
+
ctx.addIssue({
|
|
847
|
+
code: "custom",
|
|
848
|
+
path: ["steps", index, "timeoutMs"],
|
|
849
|
+
message: "step timeout must not exceed the scenario timeout",
|
|
850
|
+
});
|
|
851
|
+
if (
|
|
852
|
+
step.kind === "operation" &&
|
|
853
|
+
step.retry?.retryOn.includes("timeout") &&
|
|
854
|
+
step.retry.attemptTimeoutMs === undefined
|
|
855
|
+
)
|
|
856
|
+
ctx.addIssue({
|
|
857
|
+
code: "custom",
|
|
858
|
+
path: ["steps", index, "retry", "attemptTimeoutMs"],
|
|
859
|
+
message: "timeout retry requires attemptTimeoutMs",
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
const expression =
|
|
863
|
+
step.kind === "assert"
|
|
864
|
+
? step.expression
|
|
865
|
+
: step.kind === "guard"
|
|
866
|
+
? step.condition
|
|
867
|
+
: step.kind === "operation" && step.candidate
|
|
868
|
+
? step.candidate.accept
|
|
869
|
+
: undefined;
|
|
870
|
+
if (expression) walkExpression(expression, ctx, ["steps", index], 0, { count: 0 }, true);
|
|
871
|
+
if (step.kind === "extract" && "kind" in step.selector && step.selector.kind === "find_first")
|
|
872
|
+
walkExpression(
|
|
873
|
+
step.selector.predicate,
|
|
874
|
+
ctx,
|
|
875
|
+
["steps", index, "selector", "predicate"],
|
|
876
|
+
0,
|
|
877
|
+
{ count: 0 },
|
|
878
|
+
false,
|
|
879
|
+
step.selector.itemBinding,
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
try {
|
|
883
|
+
const serialized = JSON.stringify(value);
|
|
884
|
+
if (new TextEncoder().encode(serialized).byteLength > 128 * 1024)
|
|
885
|
+
ctx.addIssue({ code: "custom", path: [], message: "serialized scenario exceeds 128 KiB" });
|
|
886
|
+
} catch {
|
|
887
|
+
ctx.addIssue({ code: "custom", path: [], message: "scenario must be JSON-serializable" });
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
function walkExpression(
|
|
892
|
+
expression: AssertionExpression | ScopedAssertionExpression,
|
|
893
|
+
ctx: z.RefinementCtx,
|
|
894
|
+
path: PropertyKey[],
|
|
895
|
+
depth: number,
|
|
896
|
+
leaves: { count: number },
|
|
897
|
+
allowQuantifier: boolean,
|
|
898
|
+
scopedBinding?: string,
|
|
899
|
+
): void {
|
|
900
|
+
if (depth > 8) {
|
|
901
|
+
ctx.addIssue({ code: "custom", path, message: "expression depth exceeds 8" });
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
if (expression.kind === "quantifier") {
|
|
905
|
+
if (!allowQuantifier) {
|
|
906
|
+
ctx.addIssue({ code: "custom", path, message: "quantifiers may not be nested" });
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
if (expression.itemBinding === scopedBinding) {
|
|
910
|
+
ctx.addIssue({
|
|
911
|
+
code: "custom",
|
|
912
|
+
path: [...path, "itemBinding"],
|
|
913
|
+
message: "item binding is invalid or shadows an enclosing binding",
|
|
914
|
+
});
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
walkExpression(
|
|
918
|
+
expression.clause,
|
|
919
|
+
ctx,
|
|
920
|
+
[...path, "clause"],
|
|
921
|
+
depth + 1,
|
|
922
|
+
leaves,
|
|
923
|
+
false,
|
|
924
|
+
expression.itemBinding,
|
|
925
|
+
);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
if (expression.kind === "all" || expression.kind === "any") {
|
|
929
|
+
for (const [index, clause] of expression.clauses.entries())
|
|
930
|
+
walkExpression(
|
|
931
|
+
clause,
|
|
932
|
+
ctx,
|
|
933
|
+
[...path, "clauses", index],
|
|
934
|
+
depth + 1,
|
|
935
|
+
leaves,
|
|
936
|
+
allowQuantifier,
|
|
937
|
+
scopedBinding,
|
|
938
|
+
);
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
if (expression.kind === "not") {
|
|
942
|
+
walkExpression(
|
|
943
|
+
expression.clause,
|
|
944
|
+
ctx,
|
|
945
|
+
[...path, "clause"],
|
|
946
|
+
depth + 1,
|
|
947
|
+
leaves,
|
|
948
|
+
allowQuantifier,
|
|
949
|
+
scopedBinding,
|
|
950
|
+
);
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
leaves.count += 1;
|
|
954
|
+
if (leaves.count > 64)
|
|
955
|
+
ctx.addIssue({ code: "custom", path, message: "expression contains more than 64 leaves" });
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
export function defineHealthScenario(input: unknown): HealthScenario {
|
|
959
|
+
return HealthScenarioSchema.parse(input);
|
|
960
|
+
}
|