okengine 0.18.5 → 0.19.0
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 +18 -10
- package/README.md +2 -2
- package/package.json +51 -50
- package/site/content/docs/ai/llms-txt.mdx +1 -1
- package/site/content/docs/ai/meta.json +1 -1
- package/site/content/docs/ai/skills.mdx +4 -1
- package/site/content/docs/ai/try-it.mdx +58 -0
- package/site/content/docs/client/auth.mdx +208 -0
- package/site/content/docs/client/calling.mdx +451 -0
- package/site/content/docs/client/index.mdx +209 -0
- package/site/content/docs/client/live.mdx +234 -0
- package/site/content/docs/client/meta.json +5 -0
- package/site/content/docs/client/react.mdx +249 -0
- package/site/content/docs/elements/ai/agents.mdx +232 -0
- package/site/content/docs/elements/ai/index.mdx +341 -0
- package/site/content/docs/elements/ai/mcp.mdx +275 -0
- package/site/content/docs/elements/ai/meta.json +5 -0
- package/site/content/docs/elements/ai/models.mdx +283 -0
- package/site/content/docs/elements/ai/prompts.mdx +256 -0
- package/site/content/docs/elements/channel/email.mdx +266 -0
- package/site/content/docs/elements/channel/index.mdx +344 -0
- package/site/content/docs/elements/channel/meta.json +5 -0
- package/site/content/docs/elements/channel/push.mdx +218 -0
- package/site/content/docs/elements/channel/receipts.mdx +206 -0
- package/site/content/docs/elements/channel/sms.mdx +264 -0
- package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
- package/site/content/docs/elements/clock/index.mdx +300 -0
- package/site/content/docs/elements/clock/meta.json +5 -0
- package/site/content/docs/elements/clock/schedules.mdx +508 -0
- package/site/content/docs/elements/clock/sleep.mdx +445 -0
- package/site/content/docs/elements/flow/consumers.mdx +707 -0
- package/site/content/docs/elements/flow/http.mdx +1080 -0
- package/site/content/docs/elements/flow/index.mdx +618 -0
- package/site/content/docs/elements/flow/meta.json +5 -0
- package/site/content/docs/elements/flow/routing.mdx +559 -0
- package/site/content/docs/elements/flow/workflows.mdx +640 -0
- package/site/content/docs/elements/gate/auth.mdx +371 -0
- package/site/content/docs/elements/gate/authorization.mdx +298 -0
- package/site/content/docs/elements/gate/index.mdx +418 -0
- package/site/content/docs/elements/gate/meta.json +5 -0
- package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
- package/site/content/docs/elements/gate/rls.mdx +303 -0
- package/site/content/docs/elements/gate/tenancy.mdx +372 -0
- package/site/content/docs/elements/index.mdx +20 -10
- package/site/content/docs/elements/signal/broadcast.mdx +517 -0
- package/site/content/docs/elements/signal/index.mdx +352 -0
- package/site/content/docs/elements/signal/live.mdx +590 -0
- package/site/content/docs/elements/signal/meta.json +5 -0
- package/site/content/docs/elements/signal/once.mdx +596 -0
- package/site/content/docs/elements/store/files.mdx +659 -0
- package/site/content/docs/elements/store/index.mdx +310 -0
- package/site/content/docs/elements/store/kv.mdx +629 -0
- package/site/content/docs/elements/store/meta.json +5 -0
- package/site/content/docs/elements/store/search.mdx +946 -0
- package/site/content/docs/elements/store/sql.mdx +937 -0
- package/site/content/docs/elements/vault/config.mdx +256 -0
- package/site/content/docs/elements/vault/index.mdx +356 -0
- package/site/content/docs/elements/vault/meta.json +5 -0
- package/site/content/docs/elements/vault/rotation.mdx +273 -0
- package/site/content/docs/elements/vault/secrets.mdx +285 -0
- package/site/content/docs/index.mdx +63 -36
- package/site/content/docs/meta.json +8 -7
- package/site/content/docs/plugins/cors.mdx +2 -1
- package/site/content/docs/plugins/csrf.mdx +4 -1
- package/site/content/docs/plugins/headers.mdx +2 -1
- package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
- package/site/content/docs/plugins/magic-link.mdx +6 -0
- package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
- package/site/content/docs/plugins/oauth.mdx +1 -1
- package/site/content/docs/plugins/otp.mdx +12 -1
- package/site/content/docs/plugins/passkey.mdx +39 -15
- package/site/content/docs/plugins/two-factor.mdx +91 -34
- package/site/content/docs/plugins/username.mdx +1 -1
- package/site/content/docs/recipes/caddy.mdx +3 -3
- package/site/content/docs/recipes/index.mdx +18 -28
- package/site/content/docs/recipes/mailpit.mdx +1 -1
- package/site/content/docs/recipes/meilisearch.mdx +3 -3
- package/site/content/docs/recipes/meta.json +1 -4
- package/site/content/docs/recipes/nginx.mdx +2 -2
- package/site/content/docs/recipes/openrouter.mdx +283 -0
- package/site/content/docs/recipes/pgdog.mdx +3 -3
- package/site/content/docs/recipes/postgres.mdx +1 -2
- package/site/content/docs/recipes/rustfs.mdx +2 -2
- package/site/content/docs/recipes/traefik.mdx +3 -3
- package/site/content/docs/reference/cli.mdx +231 -0
- package/site/content/docs/reference/configuration.mdx +30 -10
- package/site/content/docs/reference/environment-variables.mdx +93 -20
- package/site/content/docs/reference/errors.mdx +132 -29
- package/site/content/docs/reference/fx.mdx +137 -73
- package/site/content/docs/reference/index.mdx +13 -21
- package/site/content/docs/reference/meta.json +4 -5
- package/site/content/docs/reference/okid.mdx +42 -17
- package/site/content/docs/reference/plugins.mdx +4 -3
- package/site/content/docs/reference/security.mdx +197 -0
- package/site/content/docs/understand/meta.json +5 -0
- package/site/content/docs/understand/the-anatomy.mdx +132 -0
- package/site/content/docs/understand/the-model.mdx +32 -0
- package/site/content/docs/understand/the-problem.mdx +74 -0
- package/site/content/docs/understand/the-vocabulary.mdx +26 -0
- package/src/auth/api-keys.ts +2 -1
- package/src/auth/bindings.ts +64 -16
- package/src/auth/gate-auth.test.ts +6 -1
- package/src/auth/identity.ts +152 -5
- package/src/auth/index.ts +29 -0
- package/src/auth/invites.ts +2 -1
- package/src/auth/method-context.ts +41 -0
- package/src/auth/oauth-as/crypto.ts +2 -1
- package/src/auth/oauth-as/stores.ts +2 -1
- package/src/auth/operator.ts +2 -1
- package/src/auth/sessions-jwt.test.ts +77 -0
- package/src/auth/sessions.ts +20 -4
- package/src/auth/tenants.ts +3 -2
- package/src/auth/two-factor-challenge.test.ts +61 -0
- package/src/auth/two-factor-challenge.ts +284 -0
- package/src/auth/verification.ts +5 -0
- package/src/bench/REPORT.md +49 -0
- package/src/bench/g03-signal-once.bench.ts +1 -1
- package/src/bench/g10-observability-contention.bench.ts +1 -1
- package/src/bench/g17-hybrid-search.bench.ts +572 -0
- package/src/bench/load-app.ts +2 -10
- package/src/cli/ai-setup/ai-setup.test.ts +331 -42
- package/src/cli/ai-setup/apply.ts +294 -52
- package/src/cli/ai-setup/catalog.ts +238 -1342
- package/src/cli/ai-setup/index.ts +14 -99
- package/src/cli/ai-setup/prompts.ts +60 -443
- package/src/cli/ask-seed.test.ts +96 -0
- package/src/cli/ask-seed.ts +82 -0
- package/src/cli/ask-vault-gaps.test.ts +84 -0
- package/src/cli/ask-vault-gaps.ts +146 -0
- package/src/cli/build.ts +2 -2
- package/src/cli/client-add.test.ts +26 -1
- package/src/cli/client-add.ts +106 -27
- package/src/cli/db-seed.ts +2 -0
- package/src/cli/db.ts +150 -11
- package/src/cli/dev.ts +119 -211
- package/src/cli/docker-clean.ts +2 -2
- package/src/cli/docker-cli.test.ts +1 -1
- package/src/cli/doctor-diff.ts +4 -2
- package/src/cli/doctor-pii.test.ts +1 -1
- package/src/cli/doctor.ts +14 -1
- package/src/cli/hero-meta.test.ts +4 -2
- package/src/cli/load-config.images.test.ts +8 -10
- package/src/cli/load-config.ts +1 -1
- package/src/cli/registry.ts +22 -10
- package/src/cli/replay.ts +3 -1
- package/src/cli/start.ts +1 -1
- package/src/cli/tui/keys.ts +2 -3
- package/src/client/auth/cookies.ts +74 -0
- package/src/client/auth/create-auth-client.ts +636 -0
- package/src/client/auth/denials.ts +99 -0
- package/src/client/auth/session.ts +283 -0
- package/src/client/auth.test.ts +251 -0
- package/src/client/auth.ts +39 -114
- package/src/client/create-with-session.ts +245 -0
- package/src/client/create.ts +51 -5
- package/src/client/index.ts +11 -1
- package/src/client/live.ts +11 -79
- package/src/client/notes-contract.test.ts +5 -10
- package/src/client/sse.ts +137 -0
- package/src/client/stream.ts +147 -0
- package/src/client/transport.test.ts +32 -0
- package/src/client/transport.ts +123 -26
- package/src/client/types.ts +118 -14
- package/src/client-react/index.ts +185 -24
- package/src/client-react/use-live-query.ts +13 -3
- package/src/compiler/aot.test.ts +7 -4
- package/src/compiler/effects-embed.test.ts +56 -0
- package/src/compiler/effects-fetch.test.ts +43 -0
- package/src/compiler/effects-infer.ts +58 -2
- package/src/compiler/extract.test.ts +257 -43
- package/src/compiler/extract.ts +566 -48
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
- package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
- package/src/compiler/fixtures/skyport.expected.json +4 -4
- package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
- package/src/compiler/response.ts +2 -2
- package/src/compiler/search-writer-isolation.test.ts +40 -0
- package/src/config/index.ts +11 -0
- package/src/console/server/app.ts +17 -23
- package/src/console/server/bind.ts +4 -0
- package/src/console/server/console.test.ts +2 -2
- package/src/console/server/flows-invoke.test.ts +50 -34
- package/src/console/server/flows.ts +436 -255
- package/src/console/server/runs-ingest.test.ts +6 -3
- package/src/console/server/serve.ts +1 -1
- package/src/console/server/signals.test.ts +1 -5
- package/src/console/server/signals.ts +8 -6
- package/src/console/server/state.ts +6 -1
- package/src/console/server/store.ts +1 -1
- package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
- package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
- package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
- package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
- package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
- package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
- package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
- package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
- package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
- package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
- package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
- package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
- package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
- package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
- package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
- package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
- package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
- package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
- package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
- package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
- package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
- package/src/console/ui-next/dist/index.html +6 -13
- package/src/console/ui-next/seed-invoke-host.ts +21 -18
- package/src/console/ui-next/src/client.ts +7 -2
- package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
- package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
- package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
- package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
- package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
- package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
- package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
- package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
- package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
- package/src/docker/ai-model-status.test.ts +1 -12
- package/src/docker/ai-model-status.ts +10 -38
- package/src/docker/compose-up.test.ts +100 -0
- package/src/docker/compose-up.ts +292 -0
- package/src/docker/compose.ts +4 -14
- package/src/docker/derive.ts +9 -34
- package/src/docker/docker.test.ts +16 -261
- package/src/docker/images-config.test.ts +13 -25
- package/src/docker/index.ts +1 -24
- package/src/docker/recipes/index.ts +0 -23
- package/src/docker/recipes/pgdog.ts +6 -4
- package/src/docker/stack-id.test.ts +2 -2
- package/src/docker/stack-id.ts +0 -1
- package/src/docker/types.ts +2 -2
- package/src/drivers/ai-anthropic.ts +10 -0
- package/src/drivers/ai-openai-compatible.ts +34 -7
- package/src/drivers/ai-providers.test.ts +0 -107
- package/src/drivers/ai-stream.test.ts +0 -34
- package/src/drivers/ai-types.ts +17 -8
- package/src/drivers/channel-fcm.ts +6 -1
- package/src/drivers/channel-msegat.ts +6 -1
- package/src/drivers/channel-resend.ts +5 -1
- package/src/drivers/channel-smtp.ts +5 -1
- package/src/drivers/channel-sndr.ts +10 -1
- package/src/drivers/channel-taqnyat-mail.ts +5 -1
- package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
- package/src/drivers/channel-taqnyat.ts +6 -1
- package/src/drivers/channel-types.ts +10 -0
- package/src/drivers/channel-unifonic.ts +6 -1
- package/src/drivers/channel-wa-cloud.ts +6 -1
- package/src/drivers/channel-webpush.ts +6 -1
- package/src/drivers/external.ts +33 -0
- package/src/drivers/index.ts +0 -10
- package/src/drivers/meilisearch.ts +6 -0
- package/src/drivers/oauth-types.ts +4 -0
- package/src/drivers/postgres.test.ts +74 -3
- package/src/drivers/postgres.ts +175 -6
- package/src/drivers/signal-types.ts +5 -5
- package/src/drivers/types.ts +7 -0
- package/src/drivers/vault-types.ts +5 -0
- package/src/elements/ai/declare.ts +17 -4
- package/src/elements/ai/eval.ts +1 -1
- package/src/elements/ai/mcp-http.ts +1 -1
- package/src/elements/ai/pii.ts +1 -1
- package/src/elements/ai/providers.test.ts +289 -0
- package/src/elements/ai/providers.ts +169 -0
- package/src/elements/ai/runtime.ts +68 -5
- package/src/elements/ai/schema.ts +2 -2
- package/src/elements/ai.test.ts +7 -7
- package/src/elements/ai.ts +17 -1
- package/src/elements/channel/runtime.ts +105 -12
- package/src/elements/clock/cron-fields.test.ts +144 -0
- package/src/elements/clock/cron-fields.ts +185 -0
- package/src/elements/clock/declare.ts +247 -3
- package/src/elements/clock.ts +21 -1
- package/src/elements/index.ts +15 -0
- package/src/elements/signal/chaos-child.ts +1 -2
- package/src/elements/signal/declare.ts +65 -37
- package/src/elements/signal/delivery-modes.test.ts +11 -31
- package/src/elements/signal/dry-run-replay.test.ts +1 -7
- package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
- package/src/elements/signal/key-ordering.test.ts +6 -26
- package/src/elements/signal/lease-reclaim.test.ts +2 -6
- package/src/elements/signal/optional-emit.test.ts +6 -9
- package/src/elements/signal/order-lifecycle.test.ts +4 -17
- package/src/elements/signal/orphan-messages.test.ts +4 -15
- package/src/elements/signal/reconcile.test.ts +2 -2
- package/src/elements/signal/runtime.ts +1 -1
- package/src/elements/signal/schema-emit.test.ts +4 -8
- package/src/elements/signal.test.ts +24 -49
- package/src/elements/signal.ts +9 -2
- package/src/elements/store/domain-ddl.test.ts +1 -1
- package/src/elements/store/live-http.test.ts +9 -4
- package/src/elements/store/prepare-row.test.ts +123 -0
- package/src/elements/store/resource.ts +43 -12
- package/src/elements/store/schema-decl.ts +117 -0
- package/src/elements/store/search-backfill.ts +205 -0
- package/src/elements/store/search-bind.ts +66 -0
- package/src/elements/store/search-bm25.ts +78 -0
- package/src/elements/store/search-ddl.ts +156 -0
- package/src/elements/store/search-embed-flow.ts +141 -0
- package/src/elements/store/search-errors.ts +34 -0
- package/src/elements/store/search-fusion.ts +112 -0
- package/src/elements/store/search-lsh.ts +179 -0
- package/src/elements/store/search-runtime.ts +300 -0
- package/src/elements/store/search.test.ts +144 -0
- package/src/elements/store/sql-session.test.ts +1 -0
- package/src/elements/store/sql-session.ts +83 -3
- package/src/elements/store/table.ts +23 -2
- package/src/elements/store/upsert-app.test.ts +1 -3
- package/src/elements/store.ts +50 -0
- package/src/full.ts +4 -2
- package/src/http.ts +4 -1
- package/src/i18n/catalogs/ar.ts +14 -6
- package/src/i18n/catalogs/en.ts +14 -6
- package/src/index.ts +6 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
- package/src/kernel/adopt-routes.ts +9 -1
- package/src/kernel/app-auth.ts +5 -0
- package/src/kernel/app.ts +102 -9
- package/src/kernel/auto-cache.test.ts +6 -12
- package/src/kernel/auto-registry.test.ts +3 -3
- package/src/kernel/boot-bind/ai.test.ts +22 -19
- package/src/kernel/boot-bind/ai.ts +5 -43
- package/src/kernel/boot-bind/clock.ts +23 -5
- package/src/kernel/boot-bind/honor-config.test.ts +4 -4
- package/src/kernel/boot.test.ts +11 -6
- package/src/kernel/boot.ts +66 -16
- package/src/kernel/boundary-contract.ts +93 -0
- package/src/kernel/budget.test.ts +1 -1
- package/src/kernel/call.ts +89 -0
- package/src/kernel/capability.ts +6 -0
- package/src/kernel/client-descriptor.ts +123 -0
- package/src/kernel/clock-timezone.test.ts +80 -0
- package/src/kernel/correlation.test.ts +1 -1
- package/src/kernel/dry-run.ts +11 -8
- package/src/kernel/effects-stamping.test.ts +42 -9
- package/src/kernel/effects.test.ts +4 -2
- package/src/kernel/effects.ts +51 -9
- package/src/kernel/errors-channel.ts +17 -0
- package/src/kernel/errors-live-resume.ts +3 -2
- package/src/kernel/errors-tenant.ts +7 -4
- package/src/kernel/errors.registry-helpers.ts +96 -0
- package/src/kernel/errors.registry.test.ts +139 -25
- package/src/kernel/errors.ts +102 -23
- package/src/kernel/external-effects.test.ts +196 -0
- package/src/kernel/flow.test.ts +13 -4
- package/src/kernel/flow.ts +69 -71
- package/src/kernel/fx-ask-telemetry.test.ts +2 -2
- package/src/kernel/fx-dead-letters.test.ts +3 -3
- package/src/kernel/fx-fetch.ts +55 -0
- package/src/kernel/fx-live-stream.ts +2 -2
- package/src/kernel/fx-live.test.ts +8 -8
- package/src/kernel/fx.test.ts +17 -11
- package/src/kernel/fx.ts +180 -57
- package/src/kernel/horizontal-child.ts +1 -1
- package/src/kernel/http-query.test.ts +3 -6
- package/src/kernel/index.ts +13 -3
- package/src/kernel/instance-id.ts +1 -1
- package/src/kernel/instances.test.ts +40 -0
- package/src/kernel/instances.ts +47 -9
- package/src/kernel/live-http.test.ts +6 -7
- package/src/kernel/live-http.ts +6 -2
- package/src/kernel/live-resume.test.ts +1 -1
- package/src/kernel/mcp-tool.test.ts +3 -3
- package/src/kernel/on.ts +98 -4
- package/src/kernel/pipeline.test.ts +12 -15
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/stamp-http.test.ts +3 -3
- package/src/kernel/triggers.ts +210 -95
- package/src/kernel-entry.ts +0 -1
- package/src/manifest/diff.ts +11 -1
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/types.ts +46 -2
- package/src/mcp/docs-index.ts +3 -3
- package/src/mcp/docs-mcp.test.ts +2 -2
- package/src/mcp/docs-tools.ts +1 -1
- package/src/okid.test.ts +56 -0
- package/src/okid.ts +55 -11
- package/src/plugins/anonymous.ts +8 -4
- package/src/plugins/auth/shared.ts +27 -3
- package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
- package/src/plugins/auth-methods.security.test.ts +561 -23
- package/src/plugins/config-source.ts +1 -1
- package/src/plugins/csrf.test.ts +65 -0
- package/src/plugins/index.ts +2 -0
- package/src/plugins/magic-link.ts +36 -16
- package/src/plugins/oauth/shared.ts +3 -1
- package/src/plugins/oauth.ts +22 -14
- package/src/plugins/otp.ts +37 -21
- package/src/plugins/passkey-webauthn.ts +9 -6
- package/src/plugins/passkey.ts +98 -26
- package/src/plugins/pre-account-hijack.test.ts +223 -0
- package/src/plugins/two-factor.ts +466 -37
- package/src/plugins/username.ts +19 -7
- package/src/release/build-lib.ts +1 -0
- package/src/release/limits.ts +2 -2
- package/src/release/measure.ts +1 -1
- package/src/runs/collect.test.ts +0 -2
- package/src/runtime/json-code-block.test.ts +250 -20
- package/src/runtime/json-code-block.ts +1261 -41
- package/src/term.test.ts +1 -1
- package/src/test/create-test-app.test.ts +12 -12
- package/src/test/live-signals.test.ts +6 -6
- package/src/test/provisions.integration.test.ts +10 -14
- package/src/test/reset-element-registries.ts +1 -1
- package/src/test/tenant-isolation.test.ts +12 -6
- package/site/content/docs/deployment/docker-swarm.mdx +0 -164
- package/site/content/docs/deployment/docker.mdx +0 -227
- package/site/content/docs/deployment/index.mdx +0 -83
- package/site/content/docs/deployment/kubernetes.mdx +0 -176
- package/site/content/docs/deployment/meta.json +0 -5
- package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
- package/site/content/docs/elements/ai.mdx +0 -385
- package/site/content/docs/elements/channel.mdx +0 -346
- package/site/content/docs/elements/clock.mdx +0 -244
- package/site/content/docs/elements/flow.mdx +0 -420
- package/site/content/docs/elements/gate.mdx +0 -436
- package/site/content/docs/elements/signal.mdx +0 -380
- package/site/content/docs/elements/store.mdx +0 -1099
- package/site/content/docs/elements/vault.mdx +0 -405
- package/site/content/docs/get-started/basic-usage.mdx +0 -173
- package/site/content/docs/get-started/index.mdx +0 -43
- package/site/content/docs/get-started/installation.mdx +0 -220
- package/site/content/docs/get-started/introduction.mdx +0 -144
- package/site/content/docs/get-started/meta.json +0 -13
- package/site/content/docs/get-started/project-structure.mdx +0 -925
- package/site/content/docs/get-started/testing.mdx +0 -328
- package/site/content/docs/get-started/why.mdx +0 -114
- package/site/content/docs/recipes/llama-cpp.mdx +0 -151
- package/site/content/docs/recipes/ollama.mdx +0 -142
- package/site/content/docs/recipes/sglang.mdx +0 -105
- package/site/content/docs/recipes/vllm.mdx +0 -106
- package/site/content/docs/reference/cli.md +0 -232
- package/site/content/docs/reference/client.mdx +0 -469
- package/site/content/docs/reference/security.md +0 -74
- package/src/cli/ai-setup/detect-ollama.ts +0 -213
- package/src/cli/ai-setup/recommend.test.ts +0 -225
- package/src/cli/ai-setup/recommend.ts +0 -225
- package/src/cli/dev-controls.test.ts +0 -122
- package/src/cli/dev-controls.ts +0 -164
- package/src/cli/tui/DevLive.tsx +0 -126
- package/src/console/ui-next/dist/assets/access-page-3-EFj-2G.js +0 -4
- package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
- package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
- package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
- package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
- package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
- package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
- package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
- package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
- package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-cVFnA4HH.js +0 -1
- package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
- package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
- package/src/console/ui-next/dist/assets/index-CMIgUbD0.js +0 -66
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.js +0 -4
- package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
- package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
- package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
- package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
- package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
- package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
- package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
- package/src/console/ui-next/dist/assets/units-page-l8FeKfnP.js +0 -1
- package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
- package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-CL-d_mLE.js +0 -2
- package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
- package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
- package/src/docker/ollama-pull.ts +0 -232
- package/src/docker/recipes/llama-cpp.ts +0 -298
- package/src/docker/recipes/ollama.ts +0 -44
- package/src/docker/recipes/sglang.ts +0 -55
- package/src/docker/recipes/vllm.ts +0 -44
- package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
- package/src/drivers/ai-ollama.integration.test.ts +0 -184
- package/src/drivers/ai-ollama.ts +0 -389
- package/src/drivers/ollama.ts +0 -14
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Overview"
|
|
3
|
+
description: "One Flow species for HTTP, Signal, Clock, CDC, and call-only work — contracts, fx, and options in one place."
|
|
4
|
+
icon: "Workflow"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Flow is the element for **behavior**. An HTTP endpoint, a Signal worker, a named Clock tick, a SQL change handler, and a multi-step checkout are the same shape: `on(trigger, flow)`. Only the trigger changes.
|
|
9
|
+
|
|
10
|
+
For developers writing backend work on okengine — put the invoke contract on the exposure
|
|
11
|
+
(`http.*` / `call` / `mcp.tool`), keep `do` on `fx`.
|
|
12
|
+
|
|
13
|
+
<Callout title="The one rule">
|
|
14
|
+
All world access goes through `fx`. A direct `fetch`, `Date.now()`, or `node:` import inside `do`
|
|
15
|
+
is a defect. Effects are inferred from what the Flow touches through `fx` — that inference powers
|
|
16
|
+
the Manifest, Console, cache, and durability.
|
|
17
|
+
</Callout>
|
|
18
|
+
|
|
19
|
+
<FlowShape />
|
|
20
|
+
|
|
21
|
+
## Smallest Example
|
|
22
|
+
|
|
23
|
+
<Steps>
|
|
24
|
+
|
|
25
|
+
<Step>
|
|
26
|
+
### Define a Flow
|
|
27
|
+
|
|
28
|
+
```typescript title="src/flows/main/health.ts"
|
|
29
|
+
import { on, flow, http } from "okengine";
|
|
30
|
+
|
|
31
|
+
export const health = on(
|
|
32
|
+
http.get().public(),
|
|
33
|
+
flow({
|
|
34
|
+
do: () => ({ ok: true }),
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
</Step>
|
|
40
|
+
|
|
41
|
+
<Step>
|
|
42
|
+
### Call it
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
curl -X GET http://localhost:6530/health -H "accept: application/json"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Response:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"data": { "ok": true },
|
|
53
|
+
"error": null
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
</Step>
|
|
58
|
+
|
|
59
|
+
</Steps>
|
|
60
|
+
|
|
61
|
+
<Callout title="Omit path and name">
|
|
62
|
+
Tree default: `http.get()` + `flow({ do })` — no path or name strings. Pass
|
|
63
|
+
either only for
|
|
64
|
+
[control](/docs/elements/flow/routing#when-to-omit--when-to-pass).
|
|
65
|
+
</Callout>
|
|
66
|
+
|
|
67
|
+
<Callout title="Call-only Flows">
|
|
68
|
+
Use `call("payments.charge", { in, out, do, … })` for internal callees — same species as
|
|
69
|
+
`flow`, with the invoke contract on the bag. Nothing outside your code can start it unless you
|
|
70
|
+
also bind a trigger. Other Flows invoke it with `fx.call(flowRef, input)`.
|
|
71
|
+
</Callout>
|
|
72
|
+
## Progressive Patterns
|
|
73
|
+
|
|
74
|
+
Same `on` + `flow` + `do` from a ping to a typed failure to a private callee:
|
|
75
|
+
|
|
76
|
+
<Tabs items={["Minimal", "Validated", "Failures", "Call-only"]}>
|
|
77
|
+
|
|
78
|
+
<Tab value="Minimal">
|
|
79
|
+
|
|
80
|
+
Return a value. HTTP wraps it as `{ data, error: null }`:
|
|
81
|
+
|
|
82
|
+
```typescript title="src/flows/main/ping.ts"
|
|
83
|
+
import { on, flow, http } from "okengine";
|
|
84
|
+
|
|
85
|
+
export const ping = on(
|
|
86
|
+
http.get().public(),
|
|
87
|
+
flow({
|
|
88
|
+
do: () => ({ status: "ok" }),
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
</Tab>
|
|
94
|
+
|
|
95
|
+
<Tab value="Validated">
|
|
96
|
+
|
|
97
|
+
`in` / `out` live on the HTTP bag. Invalid input never enters `do`:
|
|
98
|
+
|
|
99
|
+
```typescript title="src/flows/notes/create.ts"
|
|
100
|
+
import { on, flow, http } from "okengine";
|
|
101
|
+
import { z } from "zod";
|
|
102
|
+
|
|
103
|
+
export const create = on(
|
|
104
|
+
http.post({
|
|
105
|
+
in: z.object({ title: z.string().min(1) }),
|
|
106
|
+
out: z.object({ id: z.string(), title: z.string() }),
|
|
107
|
+
}),
|
|
108
|
+
flow({
|
|
109
|
+
do: async ({ title }, fx) => {
|
|
110
|
+
const id = fx.id();
|
|
111
|
+
return { id, title };
|
|
112
|
+
},
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
</Tab>
|
|
118
|
+
|
|
119
|
+
<Tab value="Failures">
|
|
120
|
+
|
|
121
|
+
Declare domain errors on the exposure bag and return `fx.fail` — do not throw for expected failures:
|
|
122
|
+
|
|
123
|
+
```typescript title="src/flows/orders/[id]/get.ts"
|
|
124
|
+
import { on, flow, http } from "okengine";
|
|
125
|
+
import { z } from "zod";
|
|
126
|
+
import { eq } from "drizzle-orm";
|
|
127
|
+
import { db, orders } from "@/schema";
|
|
128
|
+
|
|
129
|
+
export const get = on(
|
|
130
|
+
http.get({
|
|
131
|
+
in: z.object({ id: z.string() }),
|
|
132
|
+
out: z.object({ id: z.string(), sku: z.string(), qty: z.number() }),
|
|
133
|
+
errors: { NotFound: z.object({ id: z.string() }) },
|
|
134
|
+
}),
|
|
135
|
+
flow({
|
|
136
|
+
do: async ({ id }, fx) => {
|
|
137
|
+
const [order] = await fx.store(db).select().from(orders).where(eq(orders.id, id));
|
|
138
|
+
if (!order) return fx.fail("NotFound", { id });
|
|
139
|
+
return order;
|
|
140
|
+
},
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
</Tab>
|
|
146
|
+
|
|
147
|
+
<Tab value="Call-only">
|
|
148
|
+
|
|
149
|
+
Use `call(name, { … })` — contract and `do` on one bag. The parent records `calls: ["payments.charge"]`.
|
|
150
|
+
`fx.call` waits for the callee:
|
|
151
|
+
|
|
152
|
+
```typescript title="src/flows/payments/charge.ts"
|
|
153
|
+
import { call } from "okengine";
|
|
154
|
+
import { z } from "zod";
|
|
155
|
+
import { db, charges } from "@/schema";
|
|
156
|
+
|
|
157
|
+
export const chargeCard = call("payments.charge", {
|
|
158
|
+
in: z.object({ amount: z.number() }),
|
|
159
|
+
out: z.object({ chargeId: z.string() }),
|
|
160
|
+
do: async ({ amount }, fx) => {
|
|
161
|
+
const chargeId = fx.id();
|
|
162
|
+
await fx.store(db).insert(charges).values({ id: chargeId, amount });
|
|
163
|
+
return { chargeId };
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const { chargeId } = await fx.call(chargeCard, { amount: 50 });
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
</Tab>
|
|
173
|
+
|
|
174
|
+
</Tabs>
|
|
175
|
+
|
|
176
|
+
## Trigger Reference
|
|
177
|
+
|
|
178
|
+
`flow`, `do`, and `fx` never change. Bind a different trigger:
|
|
179
|
+
|
|
180
|
+
<FlowTriggers />
|
|
181
|
+
|
|
182
|
+
| Trigger | Bind | Starts when | `do` input |
|
|
183
|
+
| --------- | ---------------------------------------------- | ------------------- | -------------------------- |
|
|
184
|
+
| HTTP | `on(http.get(), flow)` | A request | Merged path / query / body |
|
|
185
|
+
| Signal | `on(signalHandle, flow)` | `fx.emit` | Payload (`schema`) |
|
|
186
|
+
| Clock | `on(clockDecl, flow)` | Scheduler tick | none (`_`) |
|
|
187
|
+
| CDC | `on(db.table(t).changed(), flow)` | Committed SQL write | `{ before, after }` |
|
|
188
|
+
| Call-only | `call("name", { in, out, do, … })` | `fx.call` | Callee `in` |
|
|
189
|
+
| MCP | `on(mcp.tool("x", { in, out }).gate(…), flow)` | MCP `tools/call` | Tool args |
|
|
190
|
+
|
|
191
|
+
`signal.live` is an HTTP SSE tape — bind it with [`http.live`](/docs/elements/flow/http#live-streams), not as a worker.
|
|
192
|
+
|
|
193
|
+
## The Capabilities of Flow
|
|
194
|
+
|
|
195
|
+
<Cards>
|
|
196
|
+
<Card
|
|
197
|
+
title="HTTP"
|
|
198
|
+
description="REST verbs, RFC 10008 QUERY, CRUD mounts, and live SSE."
|
|
199
|
+
href="/docs/elements/flow/http"
|
|
200
|
+
/>
|
|
201
|
+
<Card
|
|
202
|
+
title="Routing"
|
|
203
|
+
description="File-tree stamps for HTTP paths, Flow names, and client units."
|
|
204
|
+
href="/docs/elements/flow/routing"
|
|
205
|
+
/>
|
|
206
|
+
<Card
|
|
207
|
+
title="Consumers"
|
|
208
|
+
description="Signal workers, named Clock jobs, and SQL CDC — one species."
|
|
209
|
+
href="/docs/elements/flow/consumers"
|
|
210
|
+
/>
|
|
211
|
+
<Card
|
|
212
|
+
title="Durable Workflows"
|
|
213
|
+
description="fx.step replay, LIFO undo, durable sleep, and crash resume."
|
|
214
|
+
href="/docs/elements/flow/workflows"
|
|
215
|
+
/>
|
|
216
|
+
</Cards>
|
|
217
|
+
|
|
218
|
+
## Options Reference
|
|
219
|
+
|
|
220
|
+
Second argument to `flow(name, options)` — or the only argument to a nameless `flow({ do })`.
|
|
221
|
+
Invoke contracts (`in` / `out` / `errors` / `breaking`) belong on the exposure — see
|
|
222
|
+
[Contracts](#contracts) below.
|
|
223
|
+
|
|
224
|
+
| Option | Type | Default | Meaning |
|
|
225
|
+
| -------------- | -------------------------------------- | ------------------------- | ------------------------------------------------------------------------------- |
|
|
226
|
+
| `do` | `(input, fx) => output \| FlowFailure` | _(required)_ | Handler. Missing `do` throws `flow() expected an options bag with a do handler` |
|
|
227
|
+
| `durable` | `boolean` | `false` | Journal `fx.step` / sleep / gated `fx` calls |
|
|
228
|
+
| `retry` | `FxRetryOptions` | omitted | Whole-`do` retry on throw (same journal when durable) |
|
|
229
|
+
| `cache` | `boolean \| string` | omitted (auto) | Read-only Flows cache automatically; `false` opts out; `"30s"` adds TTL |
|
|
230
|
+
| `compensate` | `(ctx, fx) => unknown` | omitted | After LIFO `{ undo }`, before the run commits `failed` |
|
|
231
|
+
| `plane` | `"user" \| "operator"` | `"user"` | Operator bypasses RLS; user must not `fx.call` operator |
|
|
232
|
+
| `effects` | `Effects` | inferred | Capability token — write this only when inference cannot see the body |
|
|
233
|
+
| `slo` | `{ availability?, latency? }` | omitted | Manifest metadata (Console / docs) |
|
|
234
|
+
| `tenantScoped` | `boolean` | `true` when tenancy is on | `false` skips tenant-role scope union |
|
|
235
|
+
|
|
236
|
+
**Consequence:** `durable: true` disables automatic read-cache for that Flow.
|
|
237
|
+
|
|
238
|
+
## Contracts
|
|
239
|
+
|
|
240
|
+
<Callout title="Detailed section">
|
|
241
|
+
Invoke contracts live on the **exposure** — `http.post({ in, out, errors })`, `call("name", {
|
|
242
|
+
in, out, do })`, or `mcp.tool("x", { in, out })`. The Manifest still shows flat
|
|
243
|
+
`flows.*.{in,out,errors,breaking}` as a projection from that exposure. `in` runs before `do`;
|
|
244
|
+
`out` runs after a successful return. `fx.fail` skips `out`. Signal / Channel `schema` is a
|
|
245
|
+
separate **emit** contract (validated at `fx.emit` / `fx.send`).
|
|
246
|
+
</Callout>
|
|
247
|
+
|
|
248
|
+
<Tabs items={["Standard Schema", "Failures", "Envelope"]}>
|
|
249
|
+
|
|
250
|
+
<Tab value="Standard Schema">
|
|
251
|
+
|
|
252
|
+
Any library with `~standard` (Standard Schema V1) works. Zod is the usual choice:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import { on, flow, http } from "okengine";
|
|
256
|
+
import { z } from "zod";
|
|
257
|
+
|
|
258
|
+
on(
|
|
259
|
+
http.post({
|
|
260
|
+
in: z.object({ sku: z.string(), qty: z.number().int().min(1) }),
|
|
261
|
+
out: z.object({ id: z.string() }),
|
|
262
|
+
}),
|
|
263
|
+
flow({
|
|
264
|
+
do: async (input, fx) => ({ id: fx.id() }),
|
|
265
|
+
}),
|
|
266
|
+
);
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Valibot (`v.object`) and ArkType (`type({…})`) bind the same way. Shared DTOs belong in
|
|
270
|
+
`shapes.ts` next to the unit — that filename is never a route.
|
|
271
|
+
|
|
272
|
+
</Tab>
|
|
273
|
+
|
|
274
|
+
<Tab value="Failures">
|
|
275
|
+
|
|
276
|
+
Errors at the Flow boundary are **values**. Throw only for bugs. Declare `errors` on the
|
|
277
|
+
exposure and return from `do`:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
call("orders.create", {
|
|
281
|
+
in: z.object({ sku: z.string(), qty: z.number().int().min(1) }),
|
|
282
|
+
out: z.object({ id: z.string() }),
|
|
283
|
+
errors: {
|
|
284
|
+
OutOfStock: z.object({ available: z.number() }),
|
|
285
|
+
},
|
|
286
|
+
do: async (input, fx) => {
|
|
287
|
+
const [row] = await fx.store(db).select().from(stock).where(eq(stock.sku, input.sku));
|
|
288
|
+
if (!row || row.available < input.qty) {
|
|
289
|
+
return fx.fail("OutOfStock", { available: row?.available ?? 0 });
|
|
290
|
+
}
|
|
291
|
+
return { id: fx.id() };
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
`fx.fail(code, data, { message? })` builds `{ data: null, error: { code, data, message? } }`.
|
|
297
|
+
The typed client narrows on `res.error.code`.
|
|
298
|
+
|
|
299
|
+
</Tab>
|
|
300
|
+
|
|
301
|
+
<Tab value="Envelope">
|
|
302
|
+
|
|
303
|
+
HTTP success from a returned value is `200` + `{ data, error: null }`. `undefined` is
|
|
304
|
+
`204` with an empty body. Typed failures use `{ data: null, error }`:
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"data": null,
|
|
309
|
+
"error": {
|
|
310
|
+
"code": "OutOfStock",
|
|
311
|
+
"data": { "available": 0 }
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Status for `error.code`:
|
|
317
|
+
|
|
318
|
+
| Code | Status |
|
|
319
|
+
| ----------------------------------------------------- | ------ |
|
|
320
|
+
| `ValidationError` | `422` |
|
|
321
|
+
| `Unauthorized` | `401` |
|
|
322
|
+
| `Forbidden` | `403` |
|
|
323
|
+
| `RateLimited` | `429` |
|
|
324
|
+
| Any other declared code (`NotFound`, `OutOfStock`, …) | `400` |
|
|
325
|
+
|
|
326
|
+
A bare `404` with body `Not Found` means **no route matched** — not `fx.fail("NotFound")`.
|
|
327
|
+
|
|
328
|
+
</Tab>
|
|
329
|
+
|
|
330
|
+
</Tabs>
|
|
331
|
+
|
|
332
|
+
<Accordions>
|
|
333
|
+
|
|
334
|
+
<Accordion title="store.resource schemas">
|
|
335
|
+
`store.resource(db, table, { in, out })` requires `in` (create body) and `out` (item
|
|
336
|
+
shape). List / get / update / remove Flows are built for you — contracts are stamped
|
|
337
|
+
from the resource factory. Handwritten invoke contracts go on `http.*` / `call` /
|
|
338
|
+
`mcp.tool` — see [HTTP · Resources](/docs/elements/flow/http#resources).
|
|
339
|
+
</Accordion>
|
|
340
|
+
|
|
341
|
+
<Accordion title="ValidationError payload">
|
|
342
|
+
Failed `in` (or `out`) is `ValidationError` with `error.data.issues` — each issue has `message`
|
|
343
|
+
and `path`. HTTP status is **422**. The handler never ran.
|
|
344
|
+
</Accordion>
|
|
345
|
+
|
|
346
|
+
<Accordion title="Name stamping">
|
|
347
|
+
Prefer nameless `flow({ do })` on tree files — the file stamps `unit.export`.
|
|
348
|
+
Pass `flow("notes.create", { … })` only for control. Nameless HTTP after adopt
|
|
349
|
+
fails **OKE1045** — see [Routing](/docs/elements/flow/routing#when-to-omit--when-to-pass).
|
|
350
|
+
</Accordion>
|
|
351
|
+
|
|
352
|
+
</Accordions>
|
|
353
|
+
|
|
354
|
+
## The fx door
|
|
355
|
+
|
|
356
|
+
<Callout title="Detailed section">
|
|
357
|
+
If you only need store / emit, jump to the table. `fx` is the only I/O surface inside `do`. The
|
|
358
|
+
compiler records what you touch as `effects` on the Manifest.
|
|
359
|
+
</Callout>
|
|
360
|
+
|
|
361
|
+
| Call | Records | Use |
|
|
362
|
+
| -------------------------- | -------------------------- | --------------------------------------------- |
|
|
363
|
+
| `fx.store(db)` | `reads` / `writes` `sql:…` | SQL (and other Store facets) |
|
|
364
|
+
| `fx.emit(signal, payload)` | `emits` | Signal outbox |
|
|
365
|
+
| `fx.send(template, opts)` | `sends` | Channel template |
|
|
366
|
+
| `fx.ask(prompt, opts)` | `asks` | AI prompt |
|
|
367
|
+
| `fx.vault.get(secret)` | `secrets` | Declared secret (never a raw value in source) |
|
|
368
|
+
| `fx.call(flow, input?)` | `calls` | Another Flow — waits for return |
|
|
369
|
+
| `fx.id()` | — | UUID |
|
|
370
|
+
| `fx.clock.now()` | — | Deterministic time |
|
|
371
|
+
| `fx.fail(code, data)` | — | Typed failure value |
|
|
372
|
+
| `fx.step(name, fn)` | journal | Durable checkpoint |
|
|
373
|
+
|
|
374
|
+
<Accordions>
|
|
375
|
+
|
|
376
|
+
<Accordion title="Side channels">
|
|
377
|
+
`Date.now()`, `new Date()`, `setTimeout`, global `fetch`, and `node:fs` skip the ledger.
|
|
378
|
+
Tests cannot time-travel; durable replay cannot skip the work; cache cannot see the read.
|
|
379
|
+
|
|
380
|
+
**Fix:** `fx.clock.now()`, `fx.store`, `fx.send`, or wrap a provider in `fx.step`.
|
|
381
|
+
|
|
382
|
+
</Accordion>
|
|
383
|
+
|
|
384
|
+
<Accordion title="fx.call identity">
|
|
385
|
+
`fx.call` starts the callee with an **empty** `fx.auth` (fail-closed). `fx.tenant.id` propagates.
|
|
386
|
+
For audit only, read `fx.principal` — gates never consult it. See [fx](/docs/reference/fx).
|
|
387
|
+
</Accordion>
|
|
388
|
+
|
|
389
|
+
<Accordion title="Undeclared effects (OKE1001–1007)">
|
|
390
|
+
Explicit `effects` that drift from the body throw at runtime (`Flow "{flow}" writes "{resource}"
|
|
391
|
+
without declaring it.`). Most apps never write `effects` — inference covers them. **OKE1020** is
|
|
392
|
+
deploy-shaped boot with neither inference nor a block.
|
|
393
|
+
</Accordion>
|
|
394
|
+
|
|
395
|
+
</Accordions>
|
|
396
|
+
|
|
397
|
+
## Call-only
|
|
398
|
+
|
|
399
|
+
<Callout title="Detailed section">
|
|
400
|
+
Prefer `call("name", { in, out, do, … })`. `internal` exists so call-only is a trigger
|
|
401
|
+
*value* — `on(internal, flow)` — when you need all kinds addressable the same way.
|
|
402
|
+
</Callout>
|
|
403
|
+
|
|
404
|
+
```typescript title="src/flows/orders/checkout.ts"
|
|
405
|
+
import { on, flow, http } from "okengine";
|
|
406
|
+
import { z } from "zod";
|
|
407
|
+
import { chargeCard } from "@/flows/payments/charge";
|
|
408
|
+
|
|
409
|
+
export const checkout = on(
|
|
410
|
+
http.post({ in: z.object({ sku: z.string() }) }),
|
|
411
|
+
flow({
|
|
412
|
+
do: async ({ sku }, fx) => {
|
|
413
|
+
const { chargeId } = await fx.call(chargeCard, { amount: 50 });
|
|
414
|
+
return { sku, chargeId };
|
|
415
|
+
},
|
|
416
|
+
}),
|
|
417
|
+
);
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Do **not** `fx.call` a Flow that parks on `fx.clock.sleep` — the caller receives
|
|
421
|
+
`undefined` and continues; the child wakes later as its own run. Sleep on the root
|
|
422
|
+
durable Flow, or split with `fx.emit`. See [Workflows](/docs/elements/flow/workflows).
|
|
423
|
+
|
|
424
|
+
## Cache, retry, and plane
|
|
425
|
+
|
|
426
|
+
<Tabs items={["Cache", "Retry", "Plane"]}>
|
|
427
|
+
|
|
428
|
+
<Tab value="Cache">
|
|
429
|
+
|
|
430
|
+
Read-only Flows (Store `reads`, no `writes`, no `asks`, not durable) cache automatically.
|
|
431
|
+
No `cache:` option required. Mutations and `durable: true` stay uncached:
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
flow("catalog.get", {
|
|
435
|
+
cache: "30s",
|
|
436
|
+
do: async ({ id }, fx) => {
|
|
437
|
+
const [row] = await fx.store(db).select().from(products).where(eq(products.id, id));
|
|
438
|
+
return row;
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
`cache: false` opts out. A duration string adds TTL on top of write invalidation.
|
|
444
|
+
|
|
445
|
+
</Tab>
|
|
446
|
+
|
|
447
|
+
<Tab value="Retry">
|
|
448
|
+
|
|
449
|
+
`flow({ retry })` re-enters the whole `do`. `retries` is extra attempts after the first.
|
|
450
|
+
Prefer `fx.retry` **inside** `fx.step` so a completed charge never re-runs:
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
flow({
|
|
454
|
+
retry: { retries: 3, delay: "200ms", backoff: 2 },
|
|
455
|
+
do: async (input, fx) => {
|
|
456
|
+
return await fx.ask(flakyModel, { prompt: input.text });
|
|
457
|
+
},
|
|
458
|
+
});
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
| `retry` option | Default | Meaning |
|
|
462
|
+
| -------------- | ------------- | ---------------------------------- |
|
|
463
|
+
| `retries` | `0` | Extra attempts after the first |
|
|
464
|
+
| `delay` | `50` (ms) | Initial wait (`"200ms"` allowed) |
|
|
465
|
+
| `backoff` | `2` | Multiplier after each retry |
|
|
466
|
+
| `jitter` | `true` | Full jitter |
|
|
467
|
+
| `when` | thrown errors | Skips abort and durable-sleep park |
|
|
468
|
+
|
|
469
|
+
</Tab>
|
|
470
|
+
|
|
471
|
+
<Tab value="Plane">
|
|
472
|
+
|
|
473
|
+
`"user"` is the application default. `"operator"` is Console — RLS is bypassed,
|
|
474
|
+
`fx.operator` is the principal, `fx.auth` must not appear in that body:
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
flow("ops.allOrders", {
|
|
478
|
+
plane: "operator",
|
|
479
|
+
do: async (_, fx) => {
|
|
480
|
+
return await fx.store(db).select().from(orders);
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
A user-plane Flow that `fx.call`s an operator Flow fails compile:
|
|
486
|
+
`cross-plane call: user flow "…" calls operator flow "…"`.
|
|
487
|
+
|
|
488
|
+
</Tab>
|
|
489
|
+
|
|
490
|
+
</Tabs>
|
|
491
|
+
|
|
492
|
+
<Accordions>
|
|
493
|
+
|
|
494
|
+
<Accordion title="compensate context">
|
|
495
|
+
Durable-only. Runs after reverse `{ undo }`, never on success, retry attempts, or sleep
|
|
496
|
+
park. Context: `{ input, error, completedSteps }` — forward step names only. Full
|
|
497
|
+
physics: [Workflows · Compensation](/docs/elements/flow/workflows#compensation).
|
|
498
|
+
</Accordion>
|
|
499
|
+
|
|
500
|
+
<Accordion title="breaking and tenantScoped">
|
|
501
|
+
`breaking: true` on the exposure bag lets `oke doctor --diff` accept that Flow's contract
|
|
502
|
+
break. It does not cover a different Flow.
|
|
503
|
+
|
|
504
|
+
`tenantScoped: false` skips tenant-role scope union even when `fx.tenant.id` is set.
|
|
505
|
+
Default is `true` once `gate.auth.tenant` is on.
|
|
506
|
+
|
|
507
|
+
</Accordion>
|
|
508
|
+
|
|
509
|
+
<Accordion title="Hooks and plugins">
|
|
510
|
+
`flowDef.hook(stage, fn)` registers a per-Flow hook (`onRequest` · `onParse` · `onAuth` ·
|
|
511
|
+
`beforeHandle` · `afterHandle` · `onError` · `onResponse`). `flowDef.plug(plugin)` scopes a plugin
|
|
512
|
+
to that Flow — see [Plugins](/docs/reference/plugins).
|
|
513
|
+
</Accordion>
|
|
514
|
+
|
|
515
|
+
</Accordions>
|
|
516
|
+
|
|
517
|
+
## Troubleshooting
|
|
518
|
+
|
|
519
|
+
<Accordions>
|
|
520
|
+
|
|
521
|
+
<Accordion title='TypeError: flow() expected an options bag with a do handler'>
|
|
522
|
+
`flow()` requires `{ do }`. `flow("name")` with no options, or a bag without `do`,
|
|
523
|
+
throws at declaration — before `on()`.
|
|
524
|
+
</Accordion>
|
|
525
|
+
|
|
526
|
+
<Accordion title="TypeError: on() expected a trigger or signal handle">
|
|
527
|
+
First argument must be an HTTP trigger, Signal handle, Clock handle,
|
|
528
|
+
`db.table(…).changed()`, `internal`, or `mcp.tool(…)`. A bare interval string is not
|
|
529
|
+
a trigger — wrap it in `clock("name", { every: "1h" })`.
|
|
530
|
+
</Accordion>
|
|
531
|
+
|
|
532
|
+
<Accordion title="TypeError: on() expected a flow() definition as the second argument">
|
|
533
|
+
Second argument must be the object `flow()` returned. Passing a plain function or
|
|
534
|
+
forgetting `flow({ do })` fails here. Resource mounts take **no** second argument —
|
|
535
|
+
see [HTTP · Resources](/docs/elements/flow/http#resources).
|
|
536
|
+
</Accordion>
|
|
537
|
+
|
|
538
|
+
<Accordion title="Direct Date.now, fetch, or node: import inside do">
|
|
539
|
+
Side channels skip effect tracking and durable replay.
|
|
540
|
+
|
|
541
|
+
**Fix:** `fx.clock.now()`, `fx.store`, `fx.send`, or `fx.step` around the provider.
|
|
542
|
+
|
|
543
|
+
</Accordion>
|
|
544
|
+
|
|
545
|
+
<Accordion title="Thrown Error becomes a mystery 500 instead of a typed envelope">
|
|
546
|
+
Uncaught exceptions are defects. Declare the code in `errors` on the exposure and `return
|
|
547
|
+
fx.fail("OutOfStock", payload)` from `do`.
|
|
548
|
+
</Accordion>
|
|
549
|
+
|
|
550
|
+
<Accordion title="422 ValidationError — path param missing from in">
|
|
551
|
+
`http.get("/users/:id")` merges `{id}`. A schema that expects `userId` fails before `do`. Align
|
|
552
|
+
path keys with `in` object keys. See [HTTP · Request
|
|
553
|
+
Parsing](/docs/elements/flow/http#request-parsing).
|
|
554
|
+
</Accordion>
|
|
555
|
+
|
|
556
|
+
<Accordion title="fx.fail('NotFound') is 400, not 404">
|
|
557
|
+
Custom domain codes map to **400**. A bare `404` `Not Found` means the router found no method +
|
|
558
|
+
path. Use `fx.fail` for domain misses; fix the route for missing bindings.
|
|
559
|
+
</Accordion>
|
|
560
|
+
|
|
561
|
+
<Accordion title="Read-only Flow never cache-hits">
|
|
562
|
+
Auto-cache needs Store `reads`, no `writes`, no `asks`, and `durable` off. Empty effect sets stay
|
|
563
|
+
uncached. Opt in with a duration (`cache: "30s"`) only after a real read is inferred — or pass
|
|
564
|
+
`cache: false` to disable.
|
|
565
|
+
</Accordion>
|
|
566
|
+
|
|
567
|
+
<Accordion title='cross-plane call: user flow "…" calls operator flow "…"'>
|
|
568
|
+
User-plane Flows cannot `fx.call` operator Flows. Keep operator work on `plane: "operator"` and
|
|
569
|
+
invoke it from Console, or split a user-safe callee.
|
|
570
|
+
</Accordion>
|
|
571
|
+
|
|
572
|
+
<Accordion title="OKE1020 — no declared effects">
|
|
573
|
+
Cause: `Flow "{flow}" has no declared effects and no Manifest to derive them from.` Boot with `oke
|
|
574
|
+
build` / `oke dev` so extract can infer. Framework / plugin Flows outside the app tree get an
|
|
575
|
+
empty least-privilege stamp automatically when a Manifest is present — do not hand-write `effects:{" "}
|
|
576
|
+
{}`. Real `fx` usage still needs inference (app source) or an explicit non-empty `effects` block
|
|
577
|
+
when extract cannot see the body. If extract failed (`Manifest extract failed`), install
|
|
578
|
+
`oxc-parser` and retry.
|
|
579
|
+
</Accordion>
|
|
580
|
+
|
|
581
|
+
</Accordions>
|
|
582
|
+
|
|
583
|
+
## Learn more
|
|
584
|
+
|
|
585
|
+
- [The Anatomy](/docs/understand/the-anatomy) — `on`, trigger, `flow`, `do`, `fx`
|
|
586
|
+
- [HTTP](/docs/elements/flow/http) — verbs, envelopes, resources, live SSE
|
|
587
|
+
- [Routing](/docs/elements/flow/routing) — file-tree stamps, barrels, OKE1030 · OKE1040–1045
|
|
588
|
+
- [Consumers](/docs/elements/flow/consumers) — Signal / Clock / CDC
|
|
589
|
+
- [Workflows](/docs/elements/flow/workflows) — `durable: true` + `fx.step`
|
|
590
|
+
- [fx](/docs/reference/fx) — every method inside `do`
|
|
591
|
+
- [Gate](/docs/elements/gate) — `.gate(...)` / `.public()` on the trigger
|
|
592
|
+
- [Errors](/docs/reference/errors) — OKE1001–1009 · OKE1020 · ValidationError · denials
|
|
593
|
+
- [MCP](/docs/elements/ai/mcp) — `mcp.tool` exposure
|
|
594
|
+
|
|
595
|
+
## Next
|
|
596
|
+
|
|
597
|
+
<Cards>
|
|
598
|
+
<Card
|
|
599
|
+
title="HTTP"
|
|
600
|
+
description="Synchronous REST, QUERY, resources, and live SSE."
|
|
601
|
+
href="/docs/elements/flow/http"
|
|
602
|
+
/>
|
|
603
|
+
<Card
|
|
604
|
+
title="Consumers"
|
|
605
|
+
description="Signal workers, named Clock jobs, and SQL CDC — one Flow species."
|
|
606
|
+
href="/docs/elements/flow/consumers"
|
|
607
|
+
/>
|
|
608
|
+
<Card
|
|
609
|
+
title="Durable Workflows"
|
|
610
|
+
description="Step journaling and multi-step distributed execution."
|
|
611
|
+
href="/docs/elements/flow/workflows"
|
|
612
|
+
/>
|
|
613
|
+
<Card
|
|
614
|
+
title="The Anatomy"
|
|
615
|
+
description="Five pieces behind on(trigger, flow)."
|
|
616
|
+
href="/docs/understand/the-anatomy"
|
|
617
|
+
/>
|
|
618
|
+
</Cards>
|