okengine 0.1.4
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/AGENTS.md +91 -0
- package/README.md +327 -0
- package/docs/spec/complete-example.md +1188 -0
- package/docs/spec/console.md +739 -0
- package/docs/spec/four-applications.md +1188 -0
- package/docs/spec/unified-theory.md +489 -0
- package/package.json +152 -0
- package/spec/manifest.v1.schema.json +624 -0
- package/src/auth/api-keys.ts +168 -0
- package/src/auth/attenuation.ts +116 -0
- package/src/auth/auth.test.ts +273 -0
- package/src/auth/cross-plane.ts +98 -0
- package/src/auth/index.ts +112 -0
- package/src/auth/invites.ts +66 -0
- package/src/auth/invoke-as.ts +49 -0
- package/src/auth/operator.ts +178 -0
- package/src/auth/planes.ts +102 -0
- package/src/auth/plugin.ts +44 -0
- package/src/auth/roles.ts +84 -0
- package/src/auth/sessions.ts +418 -0
- package/src/auth/tables.ts +131 -0
- package/src/cli/args.ts +50 -0
- package/src/cli/branch.ts +75 -0
- package/src/cli/build.ts +86 -0
- package/src/cli/client-add.test.ts +140 -0
- package/src/cli/client-add.ts +164 -0
- package/src/cli/completion.test.ts +58 -0
- package/src/cli/completion.ts +296 -0
- package/src/cli/dev-app-runner.ts +77 -0
- package/src/cli/dev.test.ts +435 -0
- package/src/cli/dev.ts +594 -0
- package/src/cli/doc-drift.test.ts +146 -0
- package/src/cli/doc-drift.ts +397 -0
- package/src/cli/docker-cli.test.ts +221 -0
- package/src/cli/docker.ts +127 -0
- package/src/cli/doctor-diff-examples.ts +92 -0
- package/src/cli/doctor-diff.test.ts +91 -0
- package/src/cli/doctor-diff.ts +274 -0
- package/src/cli/doctor-pii.test.ts +98 -0
- package/src/cli/doctor-pii.ts +151 -0
- package/src/cli/doctor.test.ts +150 -0
- package/src/cli/doctor.ts +223 -0
- package/src/cli/eval.ts +122 -0
- package/src/cli/exit.ts +19 -0
- package/src/cli/gates-list.test.ts +47 -0
- package/src/cli/gates-list.ts +84 -0
- package/src/cli/images.ts +277 -0
- package/src/cli/index.ts +99 -0
- package/src/cli/json-out.test.ts +129 -0
- package/src/cli/load-config.ts +71 -0
- package/src/cli/mcp-from-console.ts +50 -0
- package/src/cli/privacy-erase.ts +73 -0
- package/src/cli/registry.ts +455 -0
- package/src/cli/safe-defaults.test.ts +92 -0
- package/src/cli/schema.ts +196 -0
- package/src/cli/stack.ts +82 -0
- package/src/cli/start.ts +106 -0
- package/src/cli/upgrade.ts +105 -0
- package/src/cli/vault-cmd.ts +140 -0
- package/src/client/budget-entry.ts +34 -0
- package/src/client/budget.test.ts +17 -0
- package/src/client/create.test.ts +159 -0
- package/src/client/create.ts +152 -0
- package/src/client/errors.ts +60 -0
- package/src/client/index.ts +35 -0
- package/src/client/notes-contract.test.ts +289 -0
- package/src/client/transport.test.ts +123 -0
- package/src/client/transport.ts +254 -0
- package/src/client/types.ts +282 -0
- package/src/compiler/aot.test.ts +248 -0
- package/src/compiler/aot.ts +182 -0
- package/src/compiler/differential.test.ts +241 -0
- package/src/compiler/dynamic.ts +54 -0
- package/src/compiler/effects-infer.ts +541 -0
- package/src/compiler/emit.ts +40 -0
- package/src/compiler/extract.test.ts +177 -0
- package/src/compiler/extract.ts +1368 -0
- package/src/compiler/fixtures/raw/raw-unannotated.ts +28 -0
- package/src/compiler/fixtures/skyport/oke.config.ts +24 -0
- package/src/compiler/fixtures/skyport/src/ai.ts +15 -0
- package/src/compiler/fixtures/skyport/src/app.ts +10 -0
- package/src/compiler/fixtures/skyport/src/channels.ts +6 -0
- package/src/compiler/fixtures/skyport/src/core.ts +3 -0
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +57 -0
- package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +11 -0
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +14 -0
- package/src/compiler/fixtures/skyport/src/flows/support/index.ts +20 -0
- package/src/compiler/fixtures/skyport/src/gates.ts +14 -0
- package/src/compiler/fixtures/skyport/src/journeys.ts +9 -0
- package/src/compiler/fixtures/skyport/src/schema.ts +3 -0
- package/src/compiler/fixtures/skyport/src/vault.ts +6 -0
- package/src/compiler/fixtures/skyport.expected.json +241 -0
- package/src/compiler/fixtures/triggers/five-triggers.ts +69 -0
- package/src/compiler/http-parse.ts +214 -0
- package/src/compiler/index.ts +67 -0
- package/src/compiler/response.ts +89 -0
- package/src/compiler/sucrose.ts +238 -0
- package/src/config/index.ts +167 -0
- package/src/console/budget.test.ts +73 -0
- package/src/console/index.ts +30 -0
- package/src/console/server/access.test.ts +370 -0
- package/src/console/server/access.ts +777 -0
- package/src/console/server/ai.test.ts +172 -0
- package/src/console/server/ai.ts +632 -0
- package/src/console/server/app.ts +294 -0
- package/src/console/server/auth-rate.test.ts +81 -0
- package/src/console/server/auth-rate.ts +72 -0
- package/src/console/server/bind.ts +25 -0
- package/src/console/server/channels.test.ts +179 -0
- package/src/console/server/channels.ts +469 -0
- package/src/console/server/claim.test.ts +66 -0
- package/src/console/server/claim.ts +134 -0
- package/src/console/server/clock.test.ts +239 -0
- package/src/console/server/clock.ts +390 -0
- package/src/console/server/console.test.ts +338 -0
- package/src/console/server/diff.test.ts +299 -0
- package/src/console/server/diff.ts +352 -0
- package/src/console/server/dry-run.audit.test.ts +118 -0
- package/src/console/server/flows-invoke.test.ts +148 -0
- package/src/console/server/flows.ts +3633 -0
- package/src/console/server/gates.test.ts +360 -0
- package/src/console/server/gates.ts +869 -0
- package/src/console/server/index.ts +141 -0
- package/src/console/server/live.test.ts +30 -0
- package/src/console/server/live.ts +109 -0
- package/src/console/server/plugin.ts +50 -0
- package/src/console/server/plugins.test.ts +131 -0
- package/src/console/server/plugins.ts +429 -0
- package/src/console/server/runs-pii.ts +98 -0
- package/src/console/server/security-headers.ts +97 -0
- package/src/console/server/security.gate.test.ts +286 -0
- package/src/console/server/serve.ts +319 -0
- package/src/console/server/signals.test.ts +88 -0
- package/src/console/server/signals.ts +282 -0
- package/src/console/server/state.ts +982 -0
- package/src/console/server/store.test.ts +233 -0
- package/src/console/server/store.ts +861 -0
- package/src/console/server/structural.ts +95 -0
- package/src/console/server/vault.test.ts +251 -0
- package/src/console/server/vault.ts +430 -0
- package/src/console/ui/access/AccessA11yView.tsx +237 -0
- package/src/console/ui/access/a11y.test.tsx +122 -0
- package/src/console/ui/access/acknowledgement.test.ts +13 -0
- package/src/console/ui/access/acknowledgement.ts +35 -0
- package/src/console/ui/access/blast-radius.test.ts +26 -0
- package/src/console/ui/access/blast-radius.ts +40 -0
- package/src/console/ui/access/confirmation.test.ts +38 -0
- package/src/console/ui/access/confirmation.ts +36 -0
- package/src/console/ui/access/fixture.ts +203 -0
- package/src/console/ui/access/grantable.test.ts +17 -0
- package/src/console/ui/access/grantable.ts +36 -0
- package/src/console/ui/access/hygiene.test.ts +12 -0
- package/src/console/ui/access/hygiene.ts +39 -0
- package/src/console/ui/access/index.ts +54 -0
- package/src/console/ui/access/provenance.test.ts +12 -0
- package/src/console/ui/access/provenance.ts +29 -0
- package/src/console/ui/access/search.test.ts +17 -0
- package/src/console/ui/access/search.ts +72 -0
- package/src/console/ui/access/types.ts +111 -0
- package/src/console/ui/ai/AiA11yView.tsx +265 -0
- package/src/console/ui/ai/a11y.test.tsx +99 -0
- package/src/console/ui/ai/findings.ts +41 -0
- package/src/console/ui/ai/fixture.ts +182 -0
- package/src/console/ui/ai/format.ts +90 -0
- package/src/console/ui/ai/group.test.ts +37 -0
- package/src/console/ui/ai/group.ts +88 -0
- package/src/console/ui/ai/index.ts +66 -0
- package/src/console/ui/ai/promotion.test.ts +77 -0
- package/src/console/ui/ai/promotion.ts +150 -0
- package/src/console/ui/ai/search.test.ts +40 -0
- package/src/console/ui/ai/search.ts +89 -0
- package/src/console/ui/ai/types.ts +139 -0
- package/src/console/ui/architecture/ArchitectureA11yView.tsx +163 -0
- package/src/console/ui/architecture/a11y.test.tsx +97 -0
- package/src/console/ui/architecture/boundary.ts +42 -0
- package/src/console/ui/architecture/declared.ts +165 -0
- package/src/console/ui/architecture/fixture.ts +74 -0
- package/src/console/ui/architecture/index.ts +84 -0
- package/src/console/ui/architecture/layers.ts +64 -0
- package/src/console/ui/architecture/layout.ts +68 -0
- package/src/console/ui/architecture/pathologies.ts +260 -0
- package/src/console/ui/architecture/search.ts +145 -0
- package/src/console/ui/architecture/traffic.ts +102 -0
- package/src/console/ui/architecture/types.ts +144 -0
- package/src/console/ui/architecture/view.test.ts +165 -0
- package/src/console/ui/architecture/view.ts +516 -0
- package/src/console/ui/channels/ChannelsA11yView.tsx +180 -0
- package/src/console/ui/channels/a11y.test.tsx +96 -0
- package/src/console/ui/channels/confirmation.test.ts +37 -0
- package/src/console/ui/channels/confirmation.ts +27 -0
- package/src/console/ui/channels/fallback.test.ts +15 -0
- package/src/console/ui/channels/fallback.ts +20 -0
- package/src/console/ui/channels/findings.ts +38 -0
- package/src/console/ui/channels/fixture.ts +134 -0
- package/src/console/ui/channels/group.ts +25 -0
- package/src/console/ui/channels/index.ts +57 -0
- package/src/console/ui/channels/locale.test.ts +17 -0
- package/src/console/ui/channels/locale.ts +31 -0
- package/src/console/ui/channels/mask.test.ts +14 -0
- package/src/console/ui/channels/mask.ts +12 -0
- package/src/console/ui/channels/search.ts +58 -0
- package/src/console/ui/channels/taxonomy.test.ts +17 -0
- package/src/console/ui/channels/taxonomy.ts +49 -0
- package/src/console/ui/channels/types.ts +117 -0
- package/src/console/ui/clock/ClockA11yView.tsx +182 -0
- package/src/console/ui/clock/a11y.test.tsx +99 -0
- package/src/console/ui/clock/confirmation.test.ts +32 -0
- package/src/console/ui/clock/confirmation.ts +34 -0
- package/src/console/ui/clock/findings.ts +40 -0
- package/src/console/ui/clock/fixture.ts +111 -0
- package/src/console/ui/clock/health.test.ts +25 -0
- package/src/console/ui/clock/health.ts +61 -0
- package/src/console/ui/clock/index.ts +49 -0
- package/src/console/ui/clock/search.test.ts +39 -0
- package/src/console/ui/clock/search.ts +76 -0
- package/src/console/ui/clock/timeline.test.ts +44 -0
- package/src/console/ui/clock/timeline.ts +41 -0
- package/src/console/ui/clock/types.ts +78 -0
- package/src/console/ui/clock/waiting-on.test.ts +34 -0
- package/src/console/ui/clock/waiting-on.ts +83 -0
- package/src/console/ui/confirmation.unify.test.ts +92 -0
- package/src/console/ui/diff/DiffA11yView.tsx +85 -0
- package/src/console/ui/diff/a11y.test.tsx +99 -0
- package/src/console/ui/diff/fixture.ts +85 -0
- package/src/console/ui/diff/group.test.ts +58 -0
- package/src/console/ui/diff/group.ts +97 -0
- package/src/console/ui/diff/index.ts +38 -0
- package/src/console/ui/diff/search.test.ts +32 -0
- package/src/console/ui/diff/search.ts +75 -0
- package/src/console/ui/diff/summary.ts +74 -0
- package/src/console/ui/diff/types.ts +48 -0
- package/src/console/ui/dist/assets/index-B71Yl_SS.js +10 -0
- package/src/console/ui/dist/assets/panel-access-Dd37LU2c.js +64 -0
- package/src/console/ui/dist/assets/panel-ai-CC7LR6-J.js +1 -0
- package/src/console/ui/dist/assets/panel-architecture-B5b3iKCz.js +1 -0
- package/src/console/ui/dist/assets/panel-channels-CeNjTKXp.js +1 -0
- package/src/console/ui/dist/assets/panel-clock-DgFTLoHV.js +1 -0
- package/src/console/ui/dist/assets/panel-diff-DxehccqB.js +1 -0
- package/src/console/ui/dist/assets/panel-flows-BtrVn-Eg.js +45 -0
- package/src/console/ui/dist/assets/panel-gates-Z9MKRGdH.js +1 -0
- package/src/console/ui/dist/assets/panel-overview-Bd48d9km.js +1 -0
- package/src/console/ui/dist/assets/panel-plugins-DWd0TowH.js +1 -0
- package/src/console/ui/dist/assets/panel-runs-BwsWqKeB.js +1 -0
- package/src/console/ui/dist/assets/panel-signals-9najbZY2.js +1 -0
- package/src/console/ui/dist/assets/panel-store-OHkP2pDp.js +1 -0
- package/src/console/ui/dist/assets/panel-traces-tn2JoY8U.js +1 -0
- package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +1 -0
- package/src/console/ui/dist/assets/rolldown-runtime-CNC7AqOf.js +1 -0
- package/src/console/ui/dist/assets/style-Cnl7WLya.css +3 -0
- package/src/console/ui/dist/index.html +14 -0
- package/src/console/ui/flows/FlowsA11yView.tsx +155 -0
- package/src/console/ui/flows/a11y.test.tsx +105 -0
- package/src/console/ui/flows/buffer.test.ts +91 -0
- package/src/console/ui/flows/buffer.ts +118 -0
- package/src/console/ui/flows/confirmation.ts +131 -0
- package/src/console/ui/flows/contract.test.ts +89 -0
- package/src/console/ui/flows/contract.ts +353 -0
- package/src/console/ui/flows/fixture.ts +81 -0
- package/src/console/ui/flows/graph.test.ts +101 -0
- package/src/console/ui/flows/graph.ts +549 -0
- package/src/console/ui/flows/index.ts +87 -0
- package/src/console/ui/flows/save-as-test.test.ts +47 -0
- package/src/console/ui/flows/save-as-test.ts +99 -0
- package/src/console/ui/flows/search.ts +255 -0
- package/src/console/ui/flows/tiers.ts +140 -0
- package/src/console/ui/gates/GatesA11yView.tsx +192 -0
- package/src/console/ui/gates/a11y.test.tsx +116 -0
- package/src/console/ui/gates/audit.test.ts +40 -0
- package/src/console/ui/gates/audit.ts +63 -0
- package/src/console/ui/gates/denial.test.ts +36 -0
- package/src/console/ui/gates/denial.ts +51 -0
- package/src/console/ui/gates/findings.ts +19 -0
- package/src/console/ui/gates/fixture.ts +189 -0
- package/src/console/ui/gates/group.test.ts +30 -0
- package/src/console/ui/gates/group.ts +102 -0
- package/src/console/ui/gates/index.ts +47 -0
- package/src/console/ui/gates/search.test.ts +49 -0
- package/src/console/ui/gates/search.ts +115 -0
- package/src/console/ui/gates/types.ts +121 -0
- package/src/console/ui/overview/OverviewA11yView.tsx +152 -0
- package/src/console/ui/overview/a11y.test.tsx +113 -0
- package/src/console/ui/overview/busiest.ts +26 -0
- package/src/console/ui/overview/compose.ts +133 -0
- package/src/console/ui/overview/cost.test.ts +39 -0
- package/src/console/ui/overview/cost.ts +138 -0
- package/src/console/ui/overview/fixture.ts +161 -0
- package/src/console/ui/overview/golden.ts +57 -0
- package/src/console/ui/overview/index.ts +59 -0
- package/src/console/ui/overview/rank.test.ts +136 -0
- package/src/console/ui/overview/rank.ts +210 -0
- package/src/console/ui/overview/slo.test.ts +73 -0
- package/src/console/ui/overview/slo.ts +229 -0
- package/src/console/ui/overview/types.ts +121 -0
- package/src/console/ui/overview/verdict.ts +131 -0
- package/src/console/ui/plugins/PluginsA11yView.tsx +227 -0
- package/src/console/ui/plugins/a11y.test.tsx +97 -0
- package/src/console/ui/plugins/command.test.ts +22 -0
- package/src/console/ui/plugins/command.ts +27 -0
- package/src/console/ui/plugins/findings.ts +39 -0
- package/src/console/ui/plugins/fixture.ts +233 -0
- package/src/console/ui/plugins/group.test.ts +38 -0
- package/src/console/ui/plugins/group.ts +67 -0
- package/src/console/ui/plugins/index.ts +40 -0
- package/src/console/ui/plugins/search.ts +67 -0
- package/src/console/ui/plugins/types.ts +115 -0
- package/src/console/ui/runs/RunsA11yView.tsx +267 -0
- package/src/console/ui/runs/a11y.test.tsx +99 -0
- package/src/console/ui/runs/explain.test.ts +23 -0
- package/src/console/ui/runs/explain.ts +32 -0
- package/src/console/ui/runs/fixture.ts +189 -0
- package/src/console/ui/runs/group.test.ts +24 -0
- package/src/console/ui/runs/group.ts +85 -0
- package/src/console/ui/runs/histogram.test.ts +31 -0
- package/src/console/ui/runs/histogram.ts +85 -0
- package/src/console/ui/runs/index.ts +75 -0
- package/src/console/ui/runs/project.ts +122 -0
- package/src/console/ui/runs/query.test.ts +50 -0
- package/src/console/ui/runs/query.ts +283 -0
- package/src/console/ui/runs/search.test.ts +45 -0
- package/src/console/ui/runs/search.ts +141 -0
- package/src/console/ui/runs/trace-link.test.ts +30 -0
- package/src/console/ui/runs/trace-link.ts +99 -0
- package/src/console/ui/runs/types.ts +137 -0
- package/src/console/ui/shell/App.tsx +56 -0
- package/src/console/ui/shell/client.ts +1715 -0
- package/src/console/ui/shell/components/ui.tsx +110 -0
- package/src/console/ui/shell/index.html +12 -0
- package/src/console/ui/shell/layout/Shell.tsx +95 -0
- package/src/console/ui/shell/main.tsx +281 -0
- package/src/console/ui/shell/panels/Access.tsx +5 -0
- package/src/console/ui/shell/panels/Ai.tsx +5 -0
- package/src/console/ui/shell/panels/Architecture.tsx +5 -0
- package/src/console/ui/shell/panels/Channels.tsx +5 -0
- package/src/console/ui/shell/panels/Clock.tsx +5 -0
- package/src/console/ui/shell/panels/Diff.tsx +5 -0
- package/src/console/ui/shell/panels/Flows.tsx +5 -0
- package/src/console/ui/shell/panels/Gates.tsx +5 -0
- package/src/console/ui/shell/panels/Overview.tsx +6 -0
- package/src/console/ui/shell/panels/Plugins.tsx +5 -0
- package/src/console/ui/shell/panels/Runs.tsx +5 -0
- package/src/console/ui/shell/panels/Signals.tsx +5 -0
- package/src/console/ui/shell/panels/Store.tsx +5 -0
- package/src/console/ui/shell/panels/Traces.tsx +5 -0
- package/src/console/ui/shell/panels/Vault.tsx +5 -0
- package/src/console/ui/shell/panels/access/AccessPanel.tsx +813 -0
- package/src/console/ui/shell/panels/ai/AiPanel.tsx +563 -0
- package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +377 -0
- package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +454 -0
- package/src/console/ui/shell/panels/clock/ClockPanel.tsx +479 -0
- package/src/console/ui/shell/panels/diff/DiffPanel.tsx +275 -0
- package/src/console/ui/shell/panels/flows/ContractEditor.tsx +301 -0
- package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +350 -0
- package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +502 -0
- package/src/console/ui/shell/panels/gates/GatesPanel.tsx +482 -0
- package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +428 -0
- package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +413 -0
- package/src/console/ui/shell/panels/runs/RunsPanel.tsx +649 -0
- package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +840 -0
- package/src/console/ui/shell/panels/store/StorePanel.tsx +787 -0
- package/src/console/ui/shell/panels/traces/TracesPanel.tsx +539 -0
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +449 -0
- package/src/console/ui/shell/plugin/Sandbox.tsx +29 -0
- package/src/console/ui/shell/setup/Wizard.tsx +189 -0
- package/src/console/ui/shell/styles.css +75 -0
- package/src/console/ui/signals/SignalsA11yView.tsx +276 -0
- package/src/console/ui/signals/a11y.test.tsx +99 -0
- package/src/console/ui/signals/confirmation.test.ts +39 -0
- package/src/console/ui/signals/confirmation.ts +63 -0
- package/src/console/ui/signals/dry-run.test.ts +18 -0
- package/src/console/ui/signals/dry-run.ts +36 -0
- package/src/console/ui/signals/durable.test.ts +22 -0
- package/src/console/ui/signals/durable.ts +41 -0
- package/src/console/ui/signals/findings.ts +33 -0
- package/src/console/ui/signals/fixture.ts +242 -0
- package/src/console/ui/signals/group.test.ts +32 -0
- package/src/console/ui/signals/group.ts +49 -0
- package/src/console/ui/signals/index.ts +52 -0
- package/src/console/ui/signals/monitor.test.ts +30 -0
- package/src/console/ui/signals/monitor.ts +79 -0
- package/src/console/ui/signals/schema-form.test.ts +34 -0
- package/src/console/ui/signals/schema-form.ts +112 -0
- package/src/console/ui/signals/search.ts +102 -0
- package/src/console/ui/signals/types.ts +71 -0
- package/src/console/ui/store/StoreA11yView.tsx +199 -0
- package/src/console/ui/store/a11y.test.tsx +99 -0
- package/src/console/ui/store/cache-view.test.ts +13 -0
- package/src/console/ui/store/cache-view.ts +36 -0
- package/src/console/ui/store/confirmation.test.ts +38 -0
- package/src/console/ui/store/confirmation.ts +55 -0
- package/src/console/ui/store/dry-run.test.ts +15 -0
- package/src/console/ui/store/dry-run.ts +29 -0
- package/src/console/ui/store/fixture.ts +163 -0
- package/src/console/ui/store/group.test.ts +21 -0
- package/src/console/ui/store/group.ts +41 -0
- package/src/console/ui/store/index.ts +40 -0
- package/src/console/ui/store/search.ts +78 -0
- package/src/console/ui/store/types.ts +65 -0
- package/src/console/ui/store/will-not-fire.test.ts +19 -0
- package/src/console/ui/store/will-not-fire.ts +40 -0
- package/src/console/ui/traces/TracesA11yView.tsx +200 -0
- package/src/console/ui/traces/a11y.test.tsx +97 -0
- package/src/console/ui/traces/chain.test.ts +61 -0
- package/src/console/ui/traces/chain.ts +144 -0
- package/src/console/ui/traces/critical-path.test.ts +24 -0
- package/src/console/ui/traces/critical-path.ts +59 -0
- package/src/console/ui/traces/filter.test.ts +78 -0
- package/src/console/ui/traces/filter.ts +112 -0
- package/src/console/ui/traces/fixture.ts +126 -0
- package/src/console/ui/traces/fold.test.ts +67 -0
- package/src/console/ui/traces/fold.ts +170 -0
- package/src/console/ui/traces/index.ts +64 -0
- package/src/console/ui/traces/mini.ts +65 -0
- package/src/console/ui/traces/replay.test.ts +24 -0
- package/src/console/ui/traces/replay.ts +35 -0
- package/src/console/ui/traces/sampling.test.ts +26 -0
- package/src/console/ui/traces/sampling.ts +66 -0
- package/src/console/ui/traces/search.ts +148 -0
- package/src/console/ui/traces/tier.ts +72 -0
- package/src/console/ui/traces/types.ts +125 -0
- package/src/console/ui/tsconfig.json +20 -0
- package/src/console/ui/vault/VaultA11yView.tsx +177 -0
- package/src/console/ui/vault/a11y.test.tsx +97 -0
- package/src/console/ui/vault/blast-radius.test.ts +35 -0
- package/src/console/ui/vault/blast-radius.ts +55 -0
- package/src/console/ui/vault/confirmation.test.ts +41 -0
- package/src/console/ui/vault/confirmation.ts +39 -0
- package/src/console/ui/vault/dormant.test.ts +37 -0
- package/src/console/ui/vault/dormant.ts +28 -0
- package/src/console/ui/vault/export-safe.test.ts +30 -0
- package/src/console/ui/vault/export-safe.ts +65 -0
- package/src/console/ui/vault/fixture.ts +75 -0
- package/src/console/ui/vault/group.test.ts +25 -0
- package/src/console/ui/vault/group.ts +50 -0
- package/src/console/ui/vault/index.ts +45 -0
- package/src/console/ui/vault/search.ts +62 -0
- package/src/console/ui/vault/types.ts +57 -0
- package/src/console/ui/vite.config.ts +84 -0
- package/src/console/xss.gate.test.ts +118 -0
- package/src/docker/compose.ts +226 -0
- package/src/docker/credentials.ts +25 -0
- package/src/docker/derive.ts +83 -0
- package/src/docker/docker.test.ts +185 -0
- package/src/docker/dockerfile.integration.test.ts +170 -0
- package/src/docker/dockerfile.ts +39 -0
- package/src/docker/helpers.ts +103 -0
- package/src/docker/index.ts +52 -0
- package/src/docker/pin.ts +90 -0
- package/src/docker/recipes/index.ts +33 -0
- package/src/docker/recipes/postgres.ts +18 -0
- package/src/docker/recipes/redis.ts +18 -0
- package/src/docker/stack.integration.test.ts +140 -0
- package/src/docker/stack.ts +62 -0
- package/src/docker/types.ts +134 -0
- package/src/drivers/ai-anthropic.ts +128 -0
- package/src/drivers/ai-mock.ts +82 -0
- package/src/drivers/ai-openai-compatible.ts +139 -0
- package/src/drivers/ai-providers.test.ts +163 -0
- package/src/drivers/ai-types.ts +83 -0
- package/src/drivers/bun-native-completeness.test.ts +155 -0
- package/src/drivers/channel-console.ts +118 -0
- package/src/drivers/channel-fcm.ts +96 -0
- package/src/drivers/channel-resend.ts +27 -0
- package/src/drivers/channel-smtp.ts +33 -0
- package/src/drivers/channel-types.ts +160 -0
- package/src/drivers/channel-unifonic.ts +79 -0
- package/src/drivers/channel-wa-cloud.ts +84 -0
- package/src/drivers/channel-webpush.ts +317 -0
- package/src/drivers/conformance.test.ts +89 -0
- package/src/drivers/conformance.ts +162 -0
- package/src/drivers/fs.ts +73 -0
- package/src/drivers/index.ts +194 -0
- package/src/drivers/kv-lua.ts +222 -0
- package/src/drivers/memory.ts +472 -0
- package/src/drivers/pgvector.ts +130 -0
- package/src/drivers/postgres.ts +221 -0
- package/src/drivers/redis.ts +254 -0
- package/src/drivers/s3.ts +129 -0
- package/src/drivers/signal-engine.ts +690 -0
- package/src/drivers/signal-memory.ts +27 -0
- package/src/drivers/signal-nats.ts +131 -0
- package/src/drivers/signal-postgres.ts +1089 -0
- package/src/drivers/signal-redis.ts +275 -0
- package/src/drivers/signal-types.ts +328 -0
- package/src/drivers/sqlite.ts +53 -0
- package/src/drivers/types.ts +334 -0
- package/src/drivers/vault-dotenv-parse.ts +56 -0
- package/src/drivers/vault-env.ts +101 -0
- package/src/drivers/vault-infisical.ts +61 -0
- package/src/drivers/vault-managed.ts +42 -0
- package/src/drivers/vault-memory.ts +38 -0
- package/src/drivers/vault-openbao.ts +80 -0
- package/src/drivers/vault-sops.ts +252 -0
- package/src/drivers/vault-types.ts +84 -0
- package/src/elements/ai/declare.ts +206 -0
- package/src/elements/ai/eval.ts +113 -0
- package/src/elements/ai/pii.ts +108 -0
- package/src/elements/ai/runtime.ts +540 -0
- package/src/elements/ai/schema.ts +194 -0
- package/src/elements/ai.test.ts +393 -0
- package/src/elements/ai.ts +56 -0
- package/src/elements/channel/consent.ts +81 -0
- package/src/elements/channel/costs.test.ts +62 -0
- package/src/elements/channel/costs.ts +130 -0
- package/src/elements/channel/declare.ts +141 -0
- package/src/elements/channel/email-auth.test.ts +47 -0
- package/src/elements/channel/email-auth.ts +109 -0
- package/src/elements/channel/locale.ts +97 -0
- package/src/elements/channel/mask.test.ts +19 -0
- package/src/elements/channel/mask.ts +60 -0
- package/src/elements/channel/mime.ts +23 -0
- package/src/elements/channel/outcomes.test.ts +40 -0
- package/src/elements/channel/outcomes.ts +126 -0
- package/src/elements/channel/receipts.ts +121 -0
- package/src/elements/channel/runtime.ts +500 -0
- package/src/elements/channel/suppression.ts +136 -0
- package/src/elements/channel.test.ts +232 -0
- package/src/elements/channel.ts +109 -0
- package/src/elements/clock/actions.ts +170 -0
- package/src/elements/clock/declare.ts +58 -0
- package/src/elements/clock/dst.ts +197 -0
- package/src/elements/clock/durable.ts +140 -0
- package/src/elements/clock/duration.ts +29 -0
- package/src/elements/clock/health.test.ts +103 -0
- package/src/elements/clock/health.ts +112 -0
- package/src/elements/clock/leader.ts +127 -0
- package/src/elements/clock/reconcile.ts +241 -0
- package/src/elements/clock/runtime.ts +243 -0
- package/src/elements/clock/schedule.ts +262 -0
- package/src/elements/clock/time-travel.ts +53 -0
- package/src/elements/clock.test.ts +365 -0
- package/src/elements/clock.ts +80 -0
- package/src/elements/gate/declare.ts +134 -0
- package/src/elements/gate/permissions.ts +104 -0
- package/src/elements/gate/runtime.ts +177 -0
- package/src/elements/gate/strategies.ts +388 -0
- package/src/elements/gate.test.ts +145 -0
- package/src/elements/gate.ts +41 -0
- package/src/elements/index.ts +169 -0
- package/src/elements/signal/chaos-child.ts +47 -0
- package/src/elements/signal/declare.ts +77 -0
- package/src/elements/signal/dry-run-replay.test.ts +95 -0
- package/src/elements/signal/dry-run-write-isolation.test.ts +94 -0
- package/src/elements/signal/reconcile.test.ts +23 -0
- package/src/elements/signal/reconcile.ts +122 -0
- package/src/elements/signal/runtime.ts +94 -0
- package/src/elements/signal.test.ts +457 -0
- package/src/elements/signal.ts +26 -0
- package/src/elements/store/cache.ts +197 -0
- package/src/elements/store/classify.ts +125 -0
- package/src/elements/store/declare.ts +200 -0
- package/src/elements/store/files-policy.test.ts +31 -0
- package/src/elements/store/files-policy.ts +70 -0
- package/src/elements/store/replica.ts +68 -0
- package/src/elements/store/runtime.ts +393 -0
- package/src/elements/store/sql-condition.ts +205 -0
- package/src/elements/store/sql-session.ts +579 -0
- package/src/elements/store/table.ts +282 -0
- package/src/elements/store.test.ts +370 -0
- package/src/elements/store.ts +99 -0
- package/src/elements/vault/declare.ts +143 -0
- package/src/elements/vault/fingerprint.ts +30 -0
- package/src/elements/vault/redact.ts +52 -0
- package/src/elements/vault/runtime.ts +461 -0
- package/src/elements/vault.test.ts +296 -0
- package/src/elements/vault.ts +44 -0
- package/src/index.ts +183 -0
- package/src/kernel/adopt-routes.ts +194 -0
- package/src/kernel/app.ts +1068 -0
- package/src/kernel/auth-resolve.ts +93 -0
- package/src/kernel/boot.test.ts +173 -0
- package/src/kernel/boot.ts +439 -0
- package/src/kernel/budget-entry.ts +55 -0
- package/src/kernel/budget.test.ts +19 -0
- package/src/kernel/call.test.ts +83 -0
- package/src/kernel/capability.ts +113 -0
- package/src/kernel/dry-run.test.ts +64 -0
- package/src/kernel/dry-run.ts +171 -0
- package/src/kernel/effects.test.ts +89 -0
- package/src/kernel/effects.ts +140 -0
- package/src/kernel/errors.registry.test.ts +39 -0
- package/src/kernel/errors.ts +225 -0
- package/src/kernel/flow.test.ts +154 -0
- package/src/kernel/flow.ts +290 -0
- package/src/kernel/fx.test.ts +245 -0
- package/src/kernel/fx.ts +996 -0
- package/src/kernel/hook-timing.test.ts +72 -0
- package/src/kernel/hook-timing.ts +190 -0
- package/src/kernel/hooks.test.ts +181 -0
- package/src/kernel/hooks.ts +275 -0
- package/src/kernel/index.ts +278 -0
- package/src/kernel/journal.ts +405 -0
- package/src/kernel/journey.ts +75 -0
- package/src/kernel/on.ts +74 -0
- package/src/kernel/pipeline.test.ts +349 -0
- package/src/kernel/pipeline.ts +245 -0
- package/src/kernel/plug.ts +82 -0
- package/src/kernel/plugin/capabilities.test.ts +84 -0
- package/src/kernel/plugin/conflicts.test.ts +106 -0
- package/src/kernel/plugin/decorate.test.ts +111 -0
- package/src/kernel/plugin/scoping.test.ts +138 -0
- package/src/kernel/plugin.ts +485 -0
- package/src/kernel/rate-limit.ts +25 -0
- package/src/kernel/registry.ts +398 -0
- package/src/kernel/router.test.ts +114 -0
- package/src/kernel/router.ts +556 -0
- package/src/kernel/routing-budget.test.ts +19 -0
- package/src/kernel/run-telemetry.ts +70 -0
- package/src/kernel/triggers.ts +324 -0
- package/src/kernel/unit.ts +88 -0
- package/src/manifest/diff.test.ts +866 -0
- package/src/manifest/diff.ts +1670 -0
- package/src/manifest/fixtures/base.manifest.json +112 -0
- package/src/manifest/fixtures/skyport.excerpt.json +51 -0
- package/src/manifest/fixtures/skyport.manifest.json +153 -0
- package/src/manifest/index.ts +27 -0
- package/src/manifest/types.ts +358 -0
- package/src/manifest/undeclared.test.ts +82 -0
- package/src/manifest/undeclared.ts +65 -0
- package/src/manifest/validate.test.ts +105 -0
- package/src/manifest/validate.ts +174 -0
- package/src/mcp/authorization.ts +340 -0
- package/src/mcp/confirmation.ts +174 -0
- package/src/mcp/data.ts +90 -0
- package/src/mcp/index.ts +80 -0
- package/src/mcp/injection.gate.test.ts +176 -0
- package/src/mcp/mcp.test.ts +348 -0
- package/src/mcp/protocol.ts +172 -0
- package/src/mcp/server.ts +283 -0
- package/src/mcp/session.ts +139 -0
- package/src/mcp/tools.ts +391 -0
- package/src/plugins/catalogue.test.ts +48 -0
- package/src/plugins/catalogue.ts +113 -0
- package/src/plugins/node-import-scan.test.ts +28 -0
- package/src/plugins/node-import-scan.ts +98 -0
- package/src/plugins/supply-chain.test.ts +48 -0
- package/src/plugins/supply-chain.ts +460 -0
- package/src/release/index.ts +30 -0
- package/src/release/limits.ts +20 -0
- package/src/release/measure.ts +443 -0
- package/src/release/publish.ts +56 -0
- package/src/runs/bench.test.ts +93 -0
- package/src/runs/collect.ts +144 -0
- package/src/runs/drivers/clickhouse.ts +108 -0
- package/src/runs/drivers/files.ts +201 -0
- package/src/runs/drivers/memory.ts +94 -0
- package/src/runs/drivers/postgres.ts +110 -0
- package/src/runs/duckdb.ts +99 -0
- package/src/runs/index.ts +82 -0
- package/src/runs/outlier.ts +180 -0
- package/src/runs/parquet.ts +210 -0
- package/src/runs/privacy.ts +49 -0
- package/src/runs/runs.test.ts +324 -0
- package/src/runs/runtime.ts +229 -0
- package/src/runs/shred.ts +243 -0
- package/src/runs/telemetry.ts +10 -0
- package/src/runs/types.ts +179 -0
- package/src/runtime/bun.ts +140 -0
- package/src/runtime/cold-start.bench.ts +20 -0
- package/src/runtime/index.ts +46 -0
- package/src/runtime/primitives.ts +242 -0
- package/src/runtime/security.test.ts +170 -0
- package/src/runtime/security.ts +168 -0
- package/src/runtime/serve.test.ts +223 -0
- package/src/runtime/types.ts +217 -0
- package/src/runtime/web-standard.ts +77 -0
- package/src/test/create-test-app.test.ts +161 -0
- package/src/test/create-test-app.ts +533 -0
- package/src/test/index.ts +21 -0
- package/src/test/provisions.integration.test.ts +174 -0
- package/src/upgrade/codemods.test.ts +58 -0
- package/src/upgrade/codemods.ts +83 -0
- package/src/validation/index.ts +23 -0
- package/src/validation/standard-schema.test.ts +96 -0
- package/src/validation/standard-schema.ts +316 -0
|
@@ -0,0 +1,1068 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `oke({ name })` — the application shell.
|
|
3
|
+
*
|
|
4
|
+
* Adopts bindings from {@link on}, builds the HTTP router, compiles
|
|
5
|
+
* per-route parse/validate handlers (AoT or dynamic), runs the hook
|
|
6
|
+
* pipeline, and wires `fx.call` to untriggered (and triggered) flows.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
compileRoute,
|
|
11
|
+
encodeExecuteResult,
|
|
12
|
+
encodeFailure,
|
|
13
|
+
type CompiledRoute,
|
|
14
|
+
} from "../compiler/index.ts";
|
|
15
|
+
import { validate } from "../validation/standard-schema.ts";
|
|
16
|
+
import {
|
|
17
|
+
accumulateAdoptArgs,
|
|
18
|
+
type AppRouteMap,
|
|
19
|
+
type RoutesFromAdoptArgs,
|
|
20
|
+
type RuntimeRouteMap,
|
|
21
|
+
} from "./adopt-routes.ts";
|
|
22
|
+
// `./boot.ts` pulls in every element + driver module (vault, store, signal,
|
|
23
|
+
// clock, gate, channel, ai, runs) — a type-only import here keeps that whole
|
|
24
|
+
// graph out of the cold-start path; `doBoot` below loads it lazily, only
|
|
25
|
+
// when an app actually boots (AGENTS.md budget: cold start < 75 ms).
|
|
26
|
+
import type { BootOptions, BootResult, ElementRuntimes } from "./boot.ts";
|
|
27
|
+
import type { CapabilityToken } from "./capability.ts";
|
|
28
|
+
import {
|
|
29
|
+
createAppAuthBinding,
|
|
30
|
+
verifyBearerToken,
|
|
31
|
+
type AppAuthBinding,
|
|
32
|
+
type CreateAppAuthBindingOptions,
|
|
33
|
+
} from "./auth-resolve.ts";
|
|
34
|
+
import { runDurable } from "../elements/clock/durable.ts";
|
|
35
|
+
import {
|
|
36
|
+
isFlow,
|
|
37
|
+
type AnyFlowDef,
|
|
38
|
+
} from "./flow.ts";
|
|
39
|
+
import {
|
|
40
|
+
createFxContext,
|
|
41
|
+
resolveName,
|
|
42
|
+
type CreateFxOptions,
|
|
43
|
+
type Fx,
|
|
44
|
+
type FxAuth,
|
|
45
|
+
type FxOperator,
|
|
46
|
+
type NamedRef,
|
|
47
|
+
} from "./fx.ts";
|
|
48
|
+
import {
|
|
49
|
+
mergeHooks,
|
|
50
|
+
runPipeline,
|
|
51
|
+
type HookFn,
|
|
52
|
+
type HookMap,
|
|
53
|
+
type HookStage,
|
|
54
|
+
type InvocationContext,
|
|
55
|
+
type PipelineResult,
|
|
56
|
+
} from "./hooks.ts";
|
|
57
|
+
import {
|
|
58
|
+
createJournal,
|
|
59
|
+
createMemoryJournalStore,
|
|
60
|
+
isJournalSuspend,
|
|
61
|
+
type JournalSession,
|
|
62
|
+
} from "./journal.ts";
|
|
63
|
+
import { listBindings, type Binding } from "./on.ts";
|
|
64
|
+
import {
|
|
65
|
+
applyPrincipal,
|
|
66
|
+
createElementPipelineHooks,
|
|
67
|
+
type PrincipalBag,
|
|
68
|
+
type ResolvedPrincipal,
|
|
69
|
+
} from "./pipeline.ts";
|
|
70
|
+
import {
|
|
71
|
+
appPluginScope,
|
|
72
|
+
applyPlugin,
|
|
73
|
+
flowPluginScope,
|
|
74
|
+
unitPluginScope,
|
|
75
|
+
type AccumulateDecorations,
|
|
76
|
+
} from "./plug.ts";
|
|
77
|
+
import type { PluginCapabilities, PluginDef } from "./plugin.ts";
|
|
78
|
+
import {
|
|
79
|
+
createPluginRegistry,
|
|
80
|
+
type PluginRegistry,
|
|
81
|
+
} from "./registry.ts";
|
|
82
|
+
import {
|
|
83
|
+
createRouter,
|
|
84
|
+
type Router,
|
|
85
|
+
type RouterPreset,
|
|
86
|
+
type SmartRouter,
|
|
87
|
+
} from "./router.ts";
|
|
88
|
+
import type {
|
|
89
|
+
CdcTrigger,
|
|
90
|
+
EveryTrigger,
|
|
91
|
+
HttpTrigger,
|
|
92
|
+
InternalTrigger,
|
|
93
|
+
SignalAsTrigger,
|
|
94
|
+
Trigger,
|
|
95
|
+
} from "./triggers.ts";
|
|
96
|
+
import { createRunTelemetry } from "./run-telemetry.ts";
|
|
97
|
+
import type { RunsRuntime } from "../runs/runtime.ts";
|
|
98
|
+
|
|
99
|
+
/** Options for {@link oke}. */
|
|
100
|
+
export interface OkeOptions {
|
|
101
|
+
/** Application name (Manifest `app`). */
|
|
102
|
+
readonly name: string;
|
|
103
|
+
/** Router preset — `default` (RegExp+Trie) or `edge` (Linear+Trie). */
|
|
104
|
+
readonly router?: RouterPreset;
|
|
105
|
+
/**
|
|
106
|
+
* AoT compile per-route parse/validate handlers (`new Function`).
|
|
107
|
+
* Set `false` on eval-restricted runtimes (Cloudflare / some edge).
|
|
108
|
+
* Default `true`.
|
|
109
|
+
*/
|
|
110
|
+
readonly aot?: boolean;
|
|
111
|
+
/** Extra bindings (in addition to the global {@link on} registry). */
|
|
112
|
+
readonly bindings?: readonly Binding[];
|
|
113
|
+
/** Base fx options applied to every invocation. */
|
|
114
|
+
readonly fx?: Omit<CreateFxOptions, "flow" | "effects" | "capability">;
|
|
115
|
+
/**
|
|
116
|
+
* Optional runs store, or create options for one. When set, every
|
|
117
|
+
* execution is recorded as one wide event with zero flow instrumentation.
|
|
118
|
+
* After {@link OkeApp.boot}, `bootResult.runs` takes precedence.
|
|
119
|
+
*/
|
|
120
|
+
readonly runs?: BootOptions["runs"];
|
|
121
|
+
/**
|
|
122
|
+
* Personal fields to archive (crypto-shred) from the validated input.
|
|
123
|
+
* Keys are input property names; values become ciphertext under the
|
|
124
|
+
* subject key.
|
|
125
|
+
*/
|
|
126
|
+
readonly archiveInputFields?: readonly string[];
|
|
127
|
+
/** Active environment for {@link OkeApp.boot} (defaults to `dev`). */
|
|
128
|
+
readonly env?: BootOptions["env"];
|
|
129
|
+
/** Optional `oke.config.ts` document consumed at boot. */
|
|
130
|
+
readonly config?: BootOptions["config"];
|
|
131
|
+
/** Pre-built element runtimes (skip construction at boot when present). */
|
|
132
|
+
readonly elements?: BootOptions["elements"];
|
|
133
|
+
/** Vault create options (chain / allowDevFallbacks / secrets). */
|
|
134
|
+
readonly vault?: BootOptions["vault"];
|
|
135
|
+
/** Vault secret contracts (shorthand for `vault.secrets`). */
|
|
136
|
+
readonly secrets?: BootOptions["secrets"];
|
|
137
|
+
/** Gate declarations for the runtime. */
|
|
138
|
+
readonly gates?: BootOptions["gates"];
|
|
139
|
+
/** Signal declarations. */
|
|
140
|
+
readonly signals?: BootOptions["signals"];
|
|
141
|
+
/** Named clock declarations. */
|
|
142
|
+
readonly clocks?: BootOptions["clocks"];
|
|
143
|
+
/** Store facet declarations to register. */
|
|
144
|
+
readonly stores?: BootOptions["stores"];
|
|
145
|
+
/** Channel runtime options. */
|
|
146
|
+
readonly channel?: BootOptions["channel"];
|
|
147
|
+
/** AI runtime options. */
|
|
148
|
+
readonly ai?: BootOptions["ai"];
|
|
149
|
+
/**
|
|
150
|
+
* Boot automatically on first {@link OkeApp.execute} / {@link OkeApp.fetch}
|
|
151
|
+
* call. Default `false` — call {@link OkeApp.boot} explicitly (tests
|
|
152
|
+
* usually want a fail-fast boot before serving any request).
|
|
153
|
+
*/
|
|
154
|
+
readonly autoBoot?: boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Start the background scheduler tick loop at boot.
|
|
157
|
+
* Default: on when `env !== "test"`; off in test. Opt out with `false`.
|
|
158
|
+
*/
|
|
159
|
+
readonly startScheduler?: boolean;
|
|
160
|
+
/** Scheduler tick period ms (default 1000). Forwarded to boot. */
|
|
161
|
+
readonly schedulerIntervalMs?: number;
|
|
162
|
+
/**
|
|
163
|
+
* Builtin hybrid-session auth (HMAC access tokens).
|
|
164
|
+
* Required to accept `Authorization: Bearer` in production.
|
|
165
|
+
*/
|
|
166
|
+
readonly auth?: CreateAppAuthBindingOptions;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Payload for a CDC invocation. */
|
|
170
|
+
export interface CdcPayload {
|
|
171
|
+
readonly before: Record<string, unknown> | null;
|
|
172
|
+
readonly after: Record<string, unknown> | null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Result of executing a flow. */
|
|
176
|
+
export interface ExecuteResult {
|
|
177
|
+
readonly output: unknown;
|
|
178
|
+
readonly failure: PipelineResult["failure"];
|
|
179
|
+
readonly response: Response | undefined;
|
|
180
|
+
readonly ctx: InvocationContext;
|
|
181
|
+
readonly fx: Fx;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Unit-scoped attachment point — hooks and plugins cover this unit only.
|
|
186
|
+
*
|
|
187
|
+
* @typeParam D - Accumulated decoration types from unit `.plug()`
|
|
188
|
+
*/
|
|
189
|
+
export interface UnitHooks<D extends Record<string, unknown> = {}> {
|
|
190
|
+
/**
|
|
191
|
+
* Register a unit-level hook.
|
|
192
|
+
*
|
|
193
|
+
* @param stage - Pipeline stage
|
|
194
|
+
* @param fn - Hook function
|
|
195
|
+
*/
|
|
196
|
+
hook(stage: HookStage, fn: HookFn): UnitHooks<D>;
|
|
197
|
+
/**
|
|
198
|
+
* Attach a plugin to this unit only. Scope is the attachment point.
|
|
199
|
+
*
|
|
200
|
+
* @param pluginDef - Plugin from {@link plugin}
|
|
201
|
+
*/
|
|
202
|
+
plug<P extends PluginDef>(
|
|
203
|
+
pluginDef: P,
|
|
204
|
+
): UnitHooks<AccumulateDecorations<D, P>>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Application instance — adopts flows, routes HTTP, runs the pipeline.
|
|
209
|
+
*
|
|
210
|
+
* @typeParam D - Accumulated decoration types from app `.plug()`
|
|
211
|
+
* @typeParam R - Accumulated client route map from `.adopt({ unit })`
|
|
212
|
+
*/
|
|
213
|
+
export interface OkeApp<
|
|
214
|
+
D extends Record<string, unknown> = {},
|
|
215
|
+
R extends AppRouteMap = {},
|
|
216
|
+
> {
|
|
217
|
+
/** App name. */
|
|
218
|
+
readonly name: string;
|
|
219
|
+
/** Whether AoT compilation is enabled. */
|
|
220
|
+
readonly aot: boolean;
|
|
221
|
+
/**
|
|
222
|
+
* Accumulated flow contracts for `createClient<typeof app>`.
|
|
223
|
+
* Type-level `in`/`out`/`errors` + runtime `method`/`path` for HTTP triggers.
|
|
224
|
+
*/
|
|
225
|
+
readonly $routes: R;
|
|
226
|
+
/** Active HTTP router (after build). */
|
|
227
|
+
readonly router: Router<Binding>;
|
|
228
|
+
/** Plugin registry (identity dedup, conflicts, capability capture). */
|
|
229
|
+
readonly plugins: PluginRegistry;
|
|
230
|
+
/** Whether {@link OkeApp.boot} has completed. */
|
|
231
|
+
readonly booted: boolean;
|
|
232
|
+
/** Result of {@link OkeApp.boot} (element runtimes, capabilities). */
|
|
233
|
+
readonly bootResult: BootResult | undefined;
|
|
234
|
+
/**
|
|
235
|
+
* Run the ordered boot sequence (vault → store → signal → clock → channel
|
|
236
|
+
* → AI → runs → capabilities). Fails fast — a missing secret throws
|
|
237
|
+
* before a single request can be served. Idempotent: a second call is a
|
|
238
|
+
* no-op once booted.
|
|
239
|
+
*
|
|
240
|
+
* @param overrides - Per-call overrides merged over the constructor options
|
|
241
|
+
*/
|
|
242
|
+
boot(overrides?: Partial<BootOptions>): Promise<OkeApp<D, R>>;
|
|
243
|
+
/**
|
|
244
|
+
* Stop the background scheduler and close element runtimes.
|
|
245
|
+
* Safe to call when not booted (no-op).
|
|
246
|
+
*/
|
|
247
|
+
stop(): Promise<void>;
|
|
248
|
+
/** Element runtimes after {@link OkeApp.boot} (`undefined` before boot). */
|
|
249
|
+
readonly elements: ElementRuntimes | undefined;
|
|
250
|
+
/** Per-flow capability tokens minted at boot from declared effects. */
|
|
251
|
+
readonly capabilities: ReadonlyMap<string, CapabilityToken>;
|
|
252
|
+
/**
|
|
253
|
+
* Builtin auth binding after boot (session store + secret), when configured.
|
|
254
|
+
*/
|
|
255
|
+
readonly authBinding: AppAuthBinding | undefined;
|
|
256
|
+
/**
|
|
257
|
+
* Attach a plugin app-wide. Scope is the attachment point.
|
|
258
|
+
* Types accumulate — decorations are visible on downstream handlers.
|
|
259
|
+
*
|
|
260
|
+
* @param pluginDef - Plugin from {@link plugin}
|
|
261
|
+
*/
|
|
262
|
+
plug<P extends PluginDef>(
|
|
263
|
+
pluginDef: P,
|
|
264
|
+
): OkeApp<AccumulateDecorations<D, P>, R>;
|
|
265
|
+
/**
|
|
266
|
+
* Captured plugin capabilities for the Manifest.
|
|
267
|
+
*/
|
|
268
|
+
pluginCapabilities(): Record<string, PluginCapabilities>;
|
|
269
|
+
/**
|
|
270
|
+
* Register an app-level hook (registration order).
|
|
271
|
+
*
|
|
272
|
+
* @param stage - Pipeline stage
|
|
273
|
+
* @param fn - Hook function
|
|
274
|
+
*/
|
|
275
|
+
hook(stage: HookStage, fn: HookFn): OkeApp<D, R>;
|
|
276
|
+
/**
|
|
277
|
+
* Unit-level attachment point (hooks + plugins).
|
|
278
|
+
*
|
|
279
|
+
* @param name - Unit name
|
|
280
|
+
*/
|
|
281
|
+
unit(name: string): UnitHooks;
|
|
282
|
+
/**
|
|
283
|
+
* Look up a flow by name or handle.
|
|
284
|
+
*
|
|
285
|
+
* @param ref - Flow name or `{ name }` / FlowDef
|
|
286
|
+
*/
|
|
287
|
+
flow(ref: NamedRef | AnyFlowDef): AnyFlowDef | undefined;
|
|
288
|
+
/**
|
|
289
|
+
* Adopt namespace objects and/or untriggered flows.
|
|
290
|
+
*
|
|
291
|
+
* - `.adopt({ notes })` — namespace key is the client unit; export names are
|
|
292
|
+
* methods. Accumulates contracts into {@link OkeApp.$routes}.
|
|
293
|
+
* - `.adopt(stats)` — registers an untriggered flow for `fx.call` only.
|
|
294
|
+
* - Variadic: `.adopt({ notes }, { bookings })`.
|
|
295
|
+
*
|
|
296
|
+
* Registration of HTTP bindings still happens in {@link on}; adopt makes
|
|
297
|
+
* the composition root honest and carries types for the client.
|
|
298
|
+
*
|
|
299
|
+
* @param args - Namespace bags and/or individual flows
|
|
300
|
+
*/
|
|
301
|
+
adopt<const Args extends readonly unknown[]>(
|
|
302
|
+
...args: Args
|
|
303
|
+
): OkeApp<D, R & RoutesFromAdoptArgs<Args>>;
|
|
304
|
+
/**
|
|
305
|
+
* Execute a flow through the hook pipeline (any trigger kind).
|
|
306
|
+
*
|
|
307
|
+
* @param flowDef - Flow to run
|
|
308
|
+
* @param input - Input payload
|
|
309
|
+
* @param trigger - Trigger for this invocation
|
|
310
|
+
* @param extras - Request / params / parse flags
|
|
311
|
+
*/
|
|
312
|
+
execute(
|
|
313
|
+
flowDef: AnyFlowDef,
|
|
314
|
+
input: unknown,
|
|
315
|
+
trigger: Trigger,
|
|
316
|
+
extras?: {
|
|
317
|
+
readonly request?: Request;
|
|
318
|
+
readonly params?: Record<string, string>;
|
|
319
|
+
/** Skip execute-time schema validation (already done by the HTTP compiler). */
|
|
320
|
+
readonly validated?: boolean;
|
|
321
|
+
/** Pre-resolved auth principal (test harness / internal dispatch). */
|
|
322
|
+
readonly auth?: ResolvedPrincipal | FxAuth;
|
|
323
|
+
/** Pre-resolved operator principal. */
|
|
324
|
+
readonly operator?: FxOperator;
|
|
325
|
+
/** Pre-resolved principal (either plane). */
|
|
326
|
+
readonly principal?: ResolvedPrincipal;
|
|
327
|
+
},
|
|
328
|
+
): Promise<ExecuteResult>;
|
|
329
|
+
/**
|
|
330
|
+
* Dispatch an HTTP request through the router + pipeline. Also serves
|
|
331
|
+
* `POST /_oke/:unit/:flow` (internal call bridge) and
|
|
332
|
+
* `GET /_oke/client.json` (routes descriptor for the client).
|
|
333
|
+
*
|
|
334
|
+
* @param request - Web-standard Request
|
|
335
|
+
*/
|
|
336
|
+
fetch(request: Request): Promise<Response>;
|
|
337
|
+
/**
|
|
338
|
+
* @internal Resume durable runs whose journaled wake time has elapsed
|
|
339
|
+
* (test harness / clock-driven schedulers).
|
|
340
|
+
*
|
|
341
|
+
* @param now - Epoch-ms to evaluate against (defaults to the app clock)
|
|
342
|
+
*/
|
|
343
|
+
resumeDurable(now?: number): Promise<void>;
|
|
344
|
+
/**
|
|
345
|
+
* Invoke all flows bound to a signal name.
|
|
346
|
+
*
|
|
347
|
+
* @param signal - Signal name or handle
|
|
348
|
+
* @param payload - Payload
|
|
349
|
+
*/
|
|
350
|
+
dispatchSignal(
|
|
351
|
+
signal: NamedRef,
|
|
352
|
+
payload?: unknown,
|
|
353
|
+
): Promise<ExecuteResult[]>;
|
|
354
|
+
/**
|
|
355
|
+
* Invoke all flows bound to an `every` interval.
|
|
356
|
+
*
|
|
357
|
+
* @param interval - Interval string (e.g. `"1h"`)
|
|
358
|
+
*/
|
|
359
|
+
dispatchEvery(interval: string): Promise<ExecuteResult[]>;
|
|
360
|
+
/**
|
|
361
|
+
* Invoke all flows bound to a CDC table change.
|
|
362
|
+
*
|
|
363
|
+
* @param tableName - Table name
|
|
364
|
+
* @param payload - before/after
|
|
365
|
+
* @param column - Optional column filter
|
|
366
|
+
*/
|
|
367
|
+
dispatchCdc(
|
|
368
|
+
tableName: string,
|
|
369
|
+
payload: CdcPayload,
|
|
370
|
+
column?: string,
|
|
371
|
+
): Promise<ExecuteResult[]>;
|
|
372
|
+
/**
|
|
373
|
+
* Call a flow by name/handle (same path as `fx.call`).
|
|
374
|
+
*
|
|
375
|
+
* @param ref - Flow ref
|
|
376
|
+
* @param input - Input
|
|
377
|
+
*/
|
|
378
|
+
call(ref: NamedRef | AnyFlowDef, input?: unknown): Promise<unknown>;
|
|
379
|
+
/** All adopted bindings. */
|
|
380
|
+
readonly bindings: readonly Binding[];
|
|
381
|
+
/**
|
|
382
|
+
* Look up the compiled parse/validate handler for an HTTP binding.
|
|
383
|
+
*
|
|
384
|
+
* @param binding - HTTP binding
|
|
385
|
+
*/
|
|
386
|
+
compiledFor(binding: Binding): CompiledRoute | undefined;
|
|
387
|
+
/**
|
|
388
|
+
* Construction options — used by the test harness to re-bind channel /
|
|
389
|
+
* secrets / gates without re-declaring them.
|
|
390
|
+
*/
|
|
391
|
+
readonly $options: OkeOptions;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Create an application. Adopts bindings registered via {@link on}.
|
|
396
|
+
*
|
|
397
|
+
* @param options - App name and router preset
|
|
398
|
+
*/
|
|
399
|
+
export function oke(options: OkeOptions): OkeApp {
|
|
400
|
+
const aot = options.aot !== false;
|
|
401
|
+
const adopted: Binding[] = [
|
|
402
|
+
...listBindings(),
|
|
403
|
+
...(options.bindings ?? []),
|
|
404
|
+
];
|
|
405
|
+
/** Retained for test harness / boot merges. */
|
|
406
|
+
const $options = options;
|
|
407
|
+
|
|
408
|
+
const appHooks: HookMap = {};
|
|
409
|
+
const unitHooks = new Map<string, HookMap>();
|
|
410
|
+
const flowsByName = new Map<string, AnyFlowDef>();
|
|
411
|
+
const compiled = new WeakMap<Binding, CompiledRoute>();
|
|
412
|
+
const pluginRegistry = createPluginRegistry();
|
|
413
|
+
const flushedPlugins = new WeakMap<AnyFlowDef, Set<PluginDef>>();
|
|
414
|
+
/** Runtime route table — types accumulate on the returned {@link OkeApp}. */
|
|
415
|
+
const routes: RuntimeRouteMap = {};
|
|
416
|
+
|
|
417
|
+
const smart = createRouter<Binding>(options.router ?? "default");
|
|
418
|
+
for (const b of adopted) {
|
|
419
|
+
if (b.trigger.kind === "http") {
|
|
420
|
+
smart.add(b.trigger.method, b.trigger.path, b);
|
|
421
|
+
compiled.set(b, compileHttpBinding(b, aot));
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
smart.build();
|
|
425
|
+
const router: Router<Binding> = smart;
|
|
426
|
+
|
|
427
|
+
function flushFlowPlugins(flowDef: AnyFlowDef): void {
|
|
428
|
+
let done = flushedPlugins.get(flowDef);
|
|
429
|
+
if (!done) {
|
|
430
|
+
done = new Set();
|
|
431
|
+
flushedPlugins.set(flowDef, done);
|
|
432
|
+
}
|
|
433
|
+
for (const p of flowDef.pendingPlugins) {
|
|
434
|
+
if (done.has(p)) continue;
|
|
435
|
+
done.add(p);
|
|
436
|
+
applyPlugin(pluginRegistry, p, flowPluginScope(flowDef));
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function registerFlow(flowDef: AnyFlowDef): void {
|
|
441
|
+
flowsByName.set(flowDef.name, flowDef);
|
|
442
|
+
flushFlowPlugins(flowDef);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
for (const b of adopted) {
|
|
446
|
+
registerFlow(b.flow);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// --- boot (vault → store → signal → clock → channel → AI → runs → caps) ---
|
|
450
|
+
let bootResult: BootResult | undefined;
|
|
451
|
+
let bootPromise: Promise<BootResult> | undefined;
|
|
452
|
+
let bootEnv: BootOptions["env"] = options.env ?? "dev";
|
|
453
|
+
let authBinding: AppAuthBinding | undefined =
|
|
454
|
+
options.auth !== undefined
|
|
455
|
+
? createAppAuthBinding({
|
|
456
|
+
...options.auth,
|
|
457
|
+
now: options.fx?.now,
|
|
458
|
+
})
|
|
459
|
+
: undefined;
|
|
460
|
+
const journalStore = createMemoryJournalStore();
|
|
461
|
+
const sleepingRuns = new Map<
|
|
462
|
+
string,
|
|
463
|
+
{ readonly flow: AnyFlowDef; readonly input: unknown; readonly wakeAt: number }
|
|
464
|
+
>();
|
|
465
|
+
const EMPTY_CAPABILITIES: ReadonlyMap<string, CapabilityToken> = new Map();
|
|
466
|
+
|
|
467
|
+
async function handleCronFire(name: string): Promise<void> {
|
|
468
|
+
await app.dispatchEvery(name);
|
|
469
|
+
// Named clocks (e.g. `clock("expire-stale", { every: "1h" })`) reconcile
|
|
470
|
+
// to a store row whose effective interval may differ from the cron name.
|
|
471
|
+
const row = await bootResult?.clock?.store.get(name);
|
|
472
|
+
if (row?.effectiveEvery && row.effectiveEvery !== name) {
|
|
473
|
+
await app.dispatchEvery(row.effectiveEvery);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
async function handleSignalFire(name: string, payload: unknown): Promise<void> {
|
|
478
|
+
await app.dispatchSignal(name, payload);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async function doBoot(overrides?: Partial<BootOptions>): Promise<BootResult> {
|
|
482
|
+
const { bootApplication } = await import("./boot.ts");
|
|
483
|
+
bootEnv = overrides?.env ?? options.env ?? "dev";
|
|
484
|
+
const merged: BootOptions = {
|
|
485
|
+
env: bootEnv,
|
|
486
|
+
config: overrides?.config ?? options.config,
|
|
487
|
+
elements: overrides?.elements ?? options.elements,
|
|
488
|
+
secrets: overrides?.secrets ?? options.secrets,
|
|
489
|
+
vault: overrides?.vault ?? options.vault,
|
|
490
|
+
gates: overrides?.gates ?? options.gates,
|
|
491
|
+
signals: overrides?.signals ?? options.signals,
|
|
492
|
+
clocks: overrides?.clocks ?? options.clocks,
|
|
493
|
+
stores: overrides?.stores ?? options.stores,
|
|
494
|
+
channel: overrides?.channel ?? options.channel,
|
|
495
|
+
ai: overrides?.ai ?? options.ai,
|
|
496
|
+
runs: overrides?.runs ?? options.runs,
|
|
497
|
+
now: overrides?.now ?? options.fx?.now,
|
|
498
|
+
instanceId: overrides?.instanceId,
|
|
499
|
+
startScheduler: overrides?.startScheduler ?? options.startScheduler,
|
|
500
|
+
schedulerIntervalMs:
|
|
501
|
+
overrides?.schedulerIntervalMs ?? options.schedulerIntervalMs,
|
|
502
|
+
bindings: adopted,
|
|
503
|
+
flows: [...flowsByName.values()],
|
|
504
|
+
onCronFire: overrides?.onCronFire ?? handleCronFire,
|
|
505
|
+
onSignal: overrides?.onSignal ?? handleSignalFire,
|
|
506
|
+
};
|
|
507
|
+
const result = await bootApplication(merged);
|
|
508
|
+
bootResult = result;
|
|
509
|
+
// Prefer the booted clock for access-token expiry checks.
|
|
510
|
+
if (authBinding) {
|
|
511
|
+
authBinding = createAppAuthBinding({
|
|
512
|
+
secret: authBinding.secret,
|
|
513
|
+
sessions: authBinding.sessions,
|
|
514
|
+
now: result.clock?.now.bind(result.clock) ?? authBinding.now,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
return result;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Boot lazily when `autoBoot` is enabled; otherwise legacy (pre-boot)
|
|
522
|
+
* execution continues to work without gates / element runtimes — existing
|
|
523
|
+
* callers that never call {@link OkeApp.boot} see unchanged behavior.
|
|
524
|
+
*/
|
|
525
|
+
async function ensureBoot(): Promise<BootResult | undefined> {
|
|
526
|
+
if (bootResult) return bootResult;
|
|
527
|
+
if (options.autoBoot !== true) return undefined;
|
|
528
|
+
if (!bootPromise) {
|
|
529
|
+
bootPromise = doBoot();
|
|
530
|
+
}
|
|
531
|
+
return bootPromise;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function isRunsRuntimeLike(
|
|
535
|
+
value: OkeOptions["runs"],
|
|
536
|
+
): value is RunsRuntime {
|
|
537
|
+
return (
|
|
538
|
+
typeof value === "object" &&
|
|
539
|
+
value !== null &&
|
|
540
|
+
"record" in value &&
|
|
541
|
+
typeof (value as RunsRuntime).record === "function"
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
async function execute(
|
|
546
|
+
flowDef: AnyFlowDef,
|
|
547
|
+
input: unknown,
|
|
548
|
+
trigger: Trigger,
|
|
549
|
+
extras?: {
|
|
550
|
+
readonly request?: Request;
|
|
551
|
+
readonly params?: Record<string, string>;
|
|
552
|
+
readonly validated?: boolean;
|
|
553
|
+
readonly auth?: ResolvedPrincipal | FxAuth;
|
|
554
|
+
readonly operator?: FxOperator;
|
|
555
|
+
readonly principal?: ResolvedPrincipal;
|
|
556
|
+
},
|
|
557
|
+
): Promise<ExecuteResult> {
|
|
558
|
+
registerFlow(flowDef);
|
|
559
|
+
|
|
560
|
+
const booted = await ensureBoot();
|
|
561
|
+
|
|
562
|
+
const unitBag =
|
|
563
|
+
flowDef.unit !== undefined ? unitHooks.get(flowDef.unit) : undefined;
|
|
564
|
+
// app (hooks + plugs) → unit (hooks + plugs) → flow (hooks + plugs)
|
|
565
|
+
const composedHooks = mergeHooks(
|
|
566
|
+
mergeHooks(
|
|
567
|
+
appHooks,
|
|
568
|
+
pluginRegistry.hooksAt("app", flowDef.unit, flowDef.name),
|
|
569
|
+
undefined,
|
|
570
|
+
),
|
|
571
|
+
mergeHooks(
|
|
572
|
+
unitBag,
|
|
573
|
+
pluginRegistry.hooksAt("unit", flowDef.unit, flowDef.name),
|
|
574
|
+
undefined,
|
|
575
|
+
),
|
|
576
|
+
mergeHooks(
|
|
577
|
+
flowDef.hooks as HookMap,
|
|
578
|
+
pluginRegistry.hooksAt("flow", flowDef.unit, flowDef.name),
|
|
579
|
+
undefined,
|
|
580
|
+
),
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
const decorations = pluginRegistry.decorationsFor(
|
|
584
|
+
flowDef.unit,
|
|
585
|
+
flowDef.name,
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
const ctx: InvocationContext = {
|
|
589
|
+
trigger,
|
|
590
|
+
flow: flowDef,
|
|
591
|
+
input,
|
|
592
|
+
params: extras?.params ? { ...extras.params } : {},
|
|
593
|
+
request: extras?.request,
|
|
594
|
+
state: {},
|
|
595
|
+
decorations,
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
const now = options.fx?.now ?? booted?.clock?.now ?? (() => Date.now());
|
|
599
|
+
const startedAt = now();
|
|
600
|
+
const telemetry = createRunTelemetry();
|
|
601
|
+
|
|
602
|
+
// Element pipeline wiring only kicks in once booted — pre-boot execution
|
|
603
|
+
// (existing tests) runs exactly as before, with no gates / elements.
|
|
604
|
+
let principals: PrincipalBag | undefined;
|
|
605
|
+
let hooks: HookMap = composedHooks;
|
|
606
|
+
let capability: CapabilityToken | undefined;
|
|
607
|
+
let journalSession: JournalSession | undefined;
|
|
608
|
+
|
|
609
|
+
if (booted) {
|
|
610
|
+
principals = {
|
|
611
|
+
auth: {
|
|
612
|
+
userId: options.fx?.auth?.userId ?? null,
|
|
613
|
+
scopes: new Set(options.fx?.auth?.scopes ?? []),
|
|
614
|
+
verified: options.fx?.auth?.verified,
|
|
615
|
+
},
|
|
616
|
+
operator: { id: options.fx?.operator?.id ?? null },
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// extras.principal / extras.auth are test-harness only — never from
|
|
620
|
+
// a real HTTP request in production mode.
|
|
621
|
+
const testMode = bootEnv === "test";
|
|
622
|
+
if (testMode) {
|
|
623
|
+
applyPrincipal(
|
|
624
|
+
principals,
|
|
625
|
+
extras?.auth as ResolvedPrincipal | undefined,
|
|
626
|
+
);
|
|
627
|
+
applyPrincipal(principals, extras?.principal);
|
|
628
|
+
if (extras?.operator) principals.operator.id = extras.operator.id;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const binding = authBinding;
|
|
632
|
+
const elementHooks = createElementPipelineHooks({
|
|
633
|
+
gates: booted.gate,
|
|
634
|
+
principals,
|
|
635
|
+
telemetry,
|
|
636
|
+
allowTestPrincipals: testMode,
|
|
637
|
+
verifyBearer: binding
|
|
638
|
+
? (token) => verifyBearerToken(binding, token)
|
|
639
|
+
: undefined,
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
// Element hooks run first in the app-level onAuth/beforeHandle chain —
|
|
643
|
+
// principal resolution and gate checks precede user-registered hooks.
|
|
644
|
+
hooks = mergeHooks(
|
|
645
|
+
{
|
|
646
|
+
onAuth: [elementHooks.onAuth],
|
|
647
|
+
beforeHandle: [elementHooks.beforeHandle],
|
|
648
|
+
},
|
|
649
|
+
composedHooks,
|
|
650
|
+
undefined,
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
capability = booted.capabilities.get(flowDef.name);
|
|
654
|
+
|
|
655
|
+
if (flowDef.durable) {
|
|
656
|
+
const journal = createJournal({ store: journalStore, now });
|
|
657
|
+
journalSession = await journal.start(flowDef.name, input);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const { fx, ledger } = createFxContext({
|
|
662
|
+
...options.fx,
|
|
663
|
+
flow: flowDef.name,
|
|
664
|
+
effects: flowDef.effects,
|
|
665
|
+
runTelemetry: telemetry,
|
|
666
|
+
now,
|
|
667
|
+
...(principals
|
|
668
|
+
? { auth: principals.auth as FxAuth, operator: principals.operator }
|
|
669
|
+
: {}),
|
|
670
|
+
...(capability ? { capability } : {}),
|
|
671
|
+
...(booted
|
|
672
|
+
? {
|
|
673
|
+
storeRuntime: booted.store,
|
|
674
|
+
signalRuntime: booted.signal,
|
|
675
|
+
vaultRuntime: booted.vault,
|
|
676
|
+
channelRuntime: booted.channel,
|
|
677
|
+
aiRuntime: booted.ai,
|
|
678
|
+
}
|
|
679
|
+
: {}),
|
|
680
|
+
...(journalSession ? { journal: journalSession, durable: true } : {}),
|
|
681
|
+
callHandler: async (name, callInput) => {
|
|
682
|
+
const target = flowsByName.get(name);
|
|
683
|
+
if (!target) {
|
|
684
|
+
return undefined;
|
|
685
|
+
}
|
|
686
|
+
const inner = await execute(target, callInput, {
|
|
687
|
+
kind: "internal",
|
|
688
|
+
} satisfies InternalTrigger);
|
|
689
|
+
if (inner.failure) return inner.failure;
|
|
690
|
+
return inner.output;
|
|
691
|
+
},
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
const alreadyValidated = extras?.validated === true;
|
|
695
|
+
|
|
696
|
+
const result = await runPipeline(ctx, fx, hooks, async () => {
|
|
697
|
+
try {
|
|
698
|
+
if (!alreadyValidated) {
|
|
699
|
+
const parsed = await validate(flowDef.in, ctx.input);
|
|
700
|
+
if (!parsed.ok) return parsed.failure;
|
|
701
|
+
ctx.input = parsed.value;
|
|
702
|
+
return await flowDef.do(parsed.value as never, fx);
|
|
703
|
+
}
|
|
704
|
+
return await flowDef.do(ctx.input as never, fx);
|
|
705
|
+
} catch (err) {
|
|
706
|
+
// A durable sleep that has not yet elapsed suspends the run — this
|
|
707
|
+
// is a park, not a pipeline failure, so it must not throw upward.
|
|
708
|
+
if (flowDef.durable && journalSession && isJournalSuspend(err)) {
|
|
709
|
+
ctx.state.sleeping = {
|
|
710
|
+
wakeAt: err.wakeAt,
|
|
711
|
+
label: err.label,
|
|
712
|
+
runId: journalSession.runId,
|
|
713
|
+
};
|
|
714
|
+
return undefined;
|
|
715
|
+
}
|
|
716
|
+
throw err;
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
const endedAt = now();
|
|
721
|
+
|
|
722
|
+
if (journalSession) {
|
|
723
|
+
const sleeping = ctx.state.sleeping as
|
|
724
|
+
| { readonly wakeAt: number; readonly label: string; readonly runId: string }
|
|
725
|
+
| undefined;
|
|
726
|
+
if (sleeping) {
|
|
727
|
+
sleepingRuns.set(sleeping.runId, {
|
|
728
|
+
flow: flowDef,
|
|
729
|
+
input: ctx.input,
|
|
730
|
+
wakeAt: sleeping.wakeAt,
|
|
731
|
+
});
|
|
732
|
+
} else if (result.failure) {
|
|
733
|
+
await journalSession.commit("failed", {
|
|
734
|
+
error: result.failure.error.code,
|
|
735
|
+
});
|
|
736
|
+
} else {
|
|
737
|
+
await journalSession.commit("completed", { output: result.output });
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const runsRuntime =
|
|
742
|
+
booted?.runs ??
|
|
743
|
+
(isRunsRuntimeLike(options.runs) ? options.runs : undefined);
|
|
744
|
+
if (runsRuntime) {
|
|
745
|
+
const archiveCleartext = archiveFromInput(
|
|
746
|
+
ctx.input,
|
|
747
|
+
options.archiveInputFields,
|
|
748
|
+
);
|
|
749
|
+
await runsRuntime.record(
|
|
750
|
+
{
|
|
751
|
+
flow: flowDef,
|
|
752
|
+
trigger,
|
|
753
|
+
fx,
|
|
754
|
+
ledger,
|
|
755
|
+
telemetry,
|
|
756
|
+
startedAt,
|
|
757
|
+
endedAt,
|
|
758
|
+
failure: result.failure,
|
|
759
|
+
},
|
|
760
|
+
archiveCleartext,
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return {
|
|
765
|
+
output: result.output,
|
|
766
|
+
failure: result.failure,
|
|
767
|
+
response: result.response,
|
|
768
|
+
ctx: result.ctx,
|
|
769
|
+
fx,
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const app: OkeApp = {
|
|
774
|
+
name: options.name,
|
|
775
|
+
aot,
|
|
776
|
+
$routes: routes as OkeApp["$routes"],
|
|
777
|
+
router,
|
|
778
|
+
bindings: adopted,
|
|
779
|
+
$options,
|
|
780
|
+
plugins: pluginRegistry,
|
|
781
|
+
get booted() {
|
|
782
|
+
return bootResult !== undefined;
|
|
783
|
+
},
|
|
784
|
+
get bootResult() {
|
|
785
|
+
return bootResult;
|
|
786
|
+
},
|
|
787
|
+
get elements() {
|
|
788
|
+
if (!bootResult) return undefined;
|
|
789
|
+
return {
|
|
790
|
+
vault: bootResult.vault,
|
|
791
|
+
store: bootResult.store,
|
|
792
|
+
signal: bootResult.signal,
|
|
793
|
+
clock: bootResult.clock,
|
|
794
|
+
gate: bootResult.gate,
|
|
795
|
+
channel: bootResult.channel,
|
|
796
|
+
ai: bootResult.ai,
|
|
797
|
+
runs: bootResult.runs,
|
|
798
|
+
};
|
|
799
|
+
},
|
|
800
|
+
get capabilities() {
|
|
801
|
+
return bootResult?.capabilities ?? EMPTY_CAPABILITIES;
|
|
802
|
+
},
|
|
803
|
+
async boot(overrides) {
|
|
804
|
+
if (bootResult) return app;
|
|
805
|
+
if (!bootPromise) {
|
|
806
|
+
bootPromise = doBoot(overrides);
|
|
807
|
+
}
|
|
808
|
+
await bootPromise;
|
|
809
|
+
return app;
|
|
810
|
+
},
|
|
811
|
+
async stop() {
|
|
812
|
+
if (!bootResult) return;
|
|
813
|
+
bootResult.stopScheduler();
|
|
814
|
+
await bootResult.close();
|
|
815
|
+
bootResult = undefined;
|
|
816
|
+
bootPromise = undefined;
|
|
817
|
+
},
|
|
818
|
+
get authBinding() {
|
|
819
|
+
return authBinding;
|
|
820
|
+
},
|
|
821
|
+
async resumeDurable(now) {
|
|
822
|
+
const t = now ?? bootResult?.clock?.now() ?? options.fx?.now?.() ?? Date.now();
|
|
823
|
+
for (const [runId, sleeper] of [...sleepingRuns.entries()]) {
|
|
824
|
+
if (t < sleeper.wakeAt) continue;
|
|
825
|
+
sleepingRuns.delete(runId);
|
|
826
|
+
const result = await runDurable({
|
|
827
|
+
flow: sleeper.flow,
|
|
828
|
+
input: sleeper.input,
|
|
829
|
+
journalStore,
|
|
830
|
+
runId,
|
|
831
|
+
now: () => t,
|
|
832
|
+
fx: {
|
|
833
|
+
storeRuntime: bootResult?.store,
|
|
834
|
+
signalRuntime: bootResult?.signal,
|
|
835
|
+
vaultRuntime: bootResult?.vault,
|
|
836
|
+
channelRuntime: bootResult?.channel,
|
|
837
|
+
aiRuntime: bootResult?.ai,
|
|
838
|
+
},
|
|
839
|
+
});
|
|
840
|
+
if (result.status === "sleeping") {
|
|
841
|
+
sleepingRuns.set(runId, {
|
|
842
|
+
flow: sleeper.flow,
|
|
843
|
+
input: sleeper.input,
|
|
844
|
+
wakeAt: result.wakeAt,
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
},
|
|
849
|
+
plug(pluginDef) {
|
|
850
|
+
applyPlugin(pluginRegistry, pluginDef, appPluginScope);
|
|
851
|
+
// Decoration accumulate on the interface; runtime object is unchanged.
|
|
852
|
+
return app as never;
|
|
853
|
+
},
|
|
854
|
+
pluginCapabilities() {
|
|
855
|
+
return pluginRegistry.capabilities();
|
|
856
|
+
},
|
|
857
|
+
hook(stage, fn) {
|
|
858
|
+
const list = appHooks[stage] ?? (appHooks[stage] = []);
|
|
859
|
+
list.push(fn);
|
|
860
|
+
return app;
|
|
861
|
+
},
|
|
862
|
+
unit(name) {
|
|
863
|
+
let bag = unitHooks.get(name);
|
|
864
|
+
if (!bag) {
|
|
865
|
+
bag = {};
|
|
866
|
+
unitHooks.set(name, bag);
|
|
867
|
+
}
|
|
868
|
+
const handle: UnitHooks = {
|
|
869
|
+
hook(stage, fn) {
|
|
870
|
+
const list = bag![stage] ?? (bag![stage] = []);
|
|
871
|
+
list.push(fn);
|
|
872
|
+
return handle;
|
|
873
|
+
},
|
|
874
|
+
plug(pluginDef) {
|
|
875
|
+
applyPlugin(pluginRegistry, pluginDef, unitPluginScope(name));
|
|
876
|
+
return handle as never;
|
|
877
|
+
},
|
|
878
|
+
};
|
|
879
|
+
return handle;
|
|
880
|
+
},
|
|
881
|
+
flow(ref) {
|
|
882
|
+
if (isFlow(ref)) return flowsByName.get(ref.name) ?? ref;
|
|
883
|
+
return flowsByName.get(resolveName(ref as NamedRef));
|
|
884
|
+
},
|
|
885
|
+
adopt(...args: readonly unknown[]) {
|
|
886
|
+
const flows = accumulateAdoptArgs(args, routes);
|
|
887
|
+
for (const flowDef of flows) {
|
|
888
|
+
registerFlow(flowDef);
|
|
889
|
+
}
|
|
890
|
+
// Route map types accumulate on the interface; runtime `routes` mutates in place.
|
|
891
|
+
return app as never;
|
|
892
|
+
},
|
|
893
|
+
execute,
|
|
894
|
+
async fetch(request) {
|
|
895
|
+
const url = new URL(request.url);
|
|
896
|
+
const method = request.method.toUpperCase();
|
|
897
|
+
|
|
898
|
+
if (method === "GET" && url.pathname === "/_oke/client.json") {
|
|
899
|
+
return new Response(JSON.stringify(routes), {
|
|
900
|
+
headers: { "content-type": "application/json" },
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
if (method === "POST" && url.pathname.startsWith("/_oke/")) {
|
|
905
|
+
const rest = url.pathname.slice("/_oke/".length);
|
|
906
|
+
const parts = rest.split("/").filter((p) => p.length > 0);
|
|
907
|
+
if (parts.length >= 2) {
|
|
908
|
+
const [unit, ...flowParts] = parts;
|
|
909
|
+
const flowName = flowParts.join("/");
|
|
910
|
+
const target =
|
|
911
|
+
flowsByName.get(`${unit}.${flowName}`) ?? flowsByName.get(flowName);
|
|
912
|
+
if (!target) {
|
|
913
|
+
return new Response("Not Found", { status: 404 });
|
|
914
|
+
}
|
|
915
|
+
let internalInput: unknown;
|
|
916
|
+
try {
|
|
917
|
+
internalInput = await request.json();
|
|
918
|
+
} catch {
|
|
919
|
+
internalInput = undefined;
|
|
920
|
+
}
|
|
921
|
+
const internalResult = await execute(
|
|
922
|
+
target,
|
|
923
|
+
internalInput,
|
|
924
|
+
{ kind: "internal" } satisfies InternalTrigger,
|
|
925
|
+
{ request },
|
|
926
|
+
);
|
|
927
|
+
return encodeExecuteResult(internalResult);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
const matched = router.match(method, url.pathname);
|
|
932
|
+
if (!matched) {
|
|
933
|
+
return new Response("Not Found", { status: 404 });
|
|
934
|
+
}
|
|
935
|
+
const { value: binding, params } = matched;
|
|
936
|
+
|
|
937
|
+
let route = compiled.get(binding);
|
|
938
|
+
if (!route && binding.trigger.kind === "http") {
|
|
939
|
+
route = compileHttpBinding(binding, aot);
|
|
940
|
+
compiled.set(binding, route);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
let input: unknown;
|
|
944
|
+
let validated = false;
|
|
945
|
+
|
|
946
|
+
if (route) {
|
|
947
|
+
const parsed = await route.parseValidate(request, params);
|
|
948
|
+
if (!parsed.ok) {
|
|
949
|
+
return encodeFailure(parsed.failure);
|
|
950
|
+
}
|
|
951
|
+
input = parsed.input;
|
|
952
|
+
validated = true;
|
|
953
|
+
} else {
|
|
954
|
+
// Non-compiled path (should not happen for HTTP bindings)
|
|
955
|
+
input = undefined;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
const result = await execute(
|
|
959
|
+
binding.flow,
|
|
960
|
+
input,
|
|
961
|
+
binding.trigger,
|
|
962
|
+
{ request, params, validated },
|
|
963
|
+
);
|
|
964
|
+
|
|
965
|
+
return encodeExecuteResult(result);
|
|
966
|
+
},
|
|
967
|
+
async dispatchSignal(signal, payload) {
|
|
968
|
+
const name = resolveName(signal);
|
|
969
|
+
const results: ExecuteResult[] = [];
|
|
970
|
+
for (const b of adopted) {
|
|
971
|
+
if (b.trigger.kind === "signal" && b.trigger.name === name) {
|
|
972
|
+
results.push(
|
|
973
|
+
await execute(b.flow, payload, b.trigger as SignalAsTrigger),
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return results;
|
|
978
|
+
},
|
|
979
|
+
async dispatchEvery(interval) {
|
|
980
|
+
const results: ExecuteResult[] = [];
|
|
981
|
+
for (const b of adopted) {
|
|
982
|
+
if (b.trigger.kind === "every" && b.trigger.interval === interval) {
|
|
983
|
+
results.push(
|
|
984
|
+
await execute(b.flow, undefined, b.trigger as EveryTrigger),
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return results;
|
|
989
|
+
},
|
|
990
|
+
async dispatchCdc(tableName, payload, column) {
|
|
991
|
+
const results: ExecuteResult[] = [];
|
|
992
|
+
for (const b of adopted) {
|
|
993
|
+
if (b.trigger.kind !== "cdc") continue;
|
|
994
|
+
const t = b.trigger as CdcTrigger;
|
|
995
|
+
if (t.table !== tableName) continue;
|
|
996
|
+
if (column !== undefined && t.column !== undefined && t.column !== column) {
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
results.push(await execute(b.flow, payload, t));
|
|
1000
|
+
}
|
|
1001
|
+
return results;
|
|
1002
|
+
},
|
|
1003
|
+
async call(ref, input) {
|
|
1004
|
+
const flowDef = isFlow(ref)
|
|
1005
|
+
? (flowsByName.get(ref.name) ?? ref)
|
|
1006
|
+
: flowsByName.get(resolveName(ref as NamedRef));
|
|
1007
|
+
if (!flowDef) {
|
|
1008
|
+
return undefined;
|
|
1009
|
+
}
|
|
1010
|
+
registerFlow(flowDef);
|
|
1011
|
+
const trigger: Trigger =
|
|
1012
|
+
flowDef.triggers[0] ?? ({ kind: "internal" } satisfies InternalTrigger);
|
|
1013
|
+
const result = await execute(flowDef, input, trigger);
|
|
1014
|
+
if (result.failure) return result.failure;
|
|
1015
|
+
return result.output;
|
|
1016
|
+
},
|
|
1017
|
+
compiledFor(binding) {
|
|
1018
|
+
return compiled.get(binding);
|
|
1019
|
+
},
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
return app;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function compileHttpBinding(binding: Binding, aot: boolean): CompiledRoute {
|
|
1026
|
+
const trigger = binding.trigger as HttpTrigger;
|
|
1027
|
+
const hookFns: Array<(...args: never[]) => unknown> = [];
|
|
1028
|
+
for (const list of Object.values(binding.flow.hooks)) {
|
|
1029
|
+
if (!list) continue;
|
|
1030
|
+
for (const fn of list) {
|
|
1031
|
+
hookFns.push(fn as (...args: never[]) => unknown);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
return compileRoute(
|
|
1035
|
+
{
|
|
1036
|
+
method: trigger.method,
|
|
1037
|
+
path: trigger.path,
|
|
1038
|
+
handler: binding.flow.do as (...args: never[]) => unknown,
|
|
1039
|
+
hooks: hookFns,
|
|
1040
|
+
schema: binding.flow.in,
|
|
1041
|
+
},
|
|
1042
|
+
aot,
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/**
|
|
1047
|
+
* Pull personal fields from validated input for crypto-shred archival.
|
|
1048
|
+
*
|
|
1049
|
+
* @param input - Validated flow input
|
|
1050
|
+
* @param fields - Property names to archive
|
|
1051
|
+
*/
|
|
1052
|
+
function archiveFromInput(
|
|
1053
|
+
input: unknown,
|
|
1054
|
+
fields: readonly string[] | undefined,
|
|
1055
|
+
): Record<string, string> | undefined {
|
|
1056
|
+
if (!fields || fields.length === 0) return undefined;
|
|
1057
|
+
if (!input || typeof input !== "object") return undefined;
|
|
1058
|
+
const obj = input as Record<string, unknown>;
|
|
1059
|
+
const out: Record<string, string> = {};
|
|
1060
|
+
for (const name of fields) {
|
|
1061
|
+
const v = obj[name];
|
|
1062
|
+
if (typeof v === "string" && v.length > 0) out[name] = v;
|
|
1063
|
+
}
|
|
1064
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/** @internal expose smart router type for tests */
|
|
1068
|
+
export type { HttpTrigger, SmartRouter };
|