okengine 0.18.4 → 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/index.mdx +25 -6
- 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 +52 -11
- 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 +78 -30
- package/site/content/docs/meta.json +11 -8
- 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/competitor-mention-removal.test.ts +1 -1
- 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 +258 -44
- 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 -132
- 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 -162
- 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-De7Lc2JC.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-RGy7VEA_.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-Ck88Jmv8.css +0 -2
- package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.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-4rHOePuE.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-DISPgxLM.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
|
@@ -1,420 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "Flow"
|
|
3
|
-
description: "Behavior — endpoints, jobs, consumers, and workflows as one species: a typed trigger, declared contracts, and a do that touches the world only through fx."
|
|
4
|
-
icon: "Workflow"
|
|
5
|
-
source: "docs/spec/unified-theory.md"
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
Flow is how your backend **does anything**. An HTTP endpoint, a queue consumer, a cron job, a multi-step payment workflow — in other stacks these are four frameworks; here they are one species with one shape: `on(trigger, flow)`. Learn the shape once, and only the trigger ever changes.
|
|
9
|
-
|
|
10
|
-
<Callout title="The one rule">
|
|
11
|
-
All world access goes through `fx`. A direct `fetch`, `Date.now()`, or `node:` import inside a
|
|
12
|
-
flow is a defect — effects are **inferred from what a flow touches through `fx`**, and that
|
|
13
|
-
inference is what powers the Manifest, the Console, caching, and durability.
|
|
14
|
-
</Callout>
|
|
15
|
-
|
|
16
|
-
## Quick start
|
|
17
|
-
|
|
18
|
-
<Steps>
|
|
19
|
-
|
|
20
|
-
<Step>
|
|
21
|
-
### Write the flow
|
|
22
|
-
|
|
23
|
-
Four declarations plus a `do`:
|
|
24
|
-
|
|
25
|
-
```typescript title="src/flows/orders/create.ts"
|
|
26
|
-
import { on, flow, http } from "okengine";
|
|
27
|
-
import { z } from "zod";
|
|
28
|
-
|
|
29
|
-
export const createOrder = on(
|
|
30
|
-
http.post("/orders"),
|
|
31
|
-
flow("orders.create", {
|
|
32
|
-
in: z.object({ sku: z.string(), qty: z.number().int().min(1) }),
|
|
33
|
-
out: z.object({ id: z.string() }),
|
|
34
|
-
errors: { OutOfStock: z.object({ left: z.number() }) },
|
|
35
|
-
do: async (input, fx) => {
|
|
36
|
-
const id = fx.id();
|
|
37
|
-
await fx
|
|
38
|
-
.store(db)
|
|
39
|
-
.insert(orders)
|
|
40
|
-
.values({ id, ...input, status: "pending" });
|
|
41
|
-
return { id };
|
|
42
|
-
},
|
|
43
|
-
}),
|
|
44
|
-
);
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
</Step>
|
|
48
|
-
|
|
49
|
-
<Step>
|
|
50
|
-
### Run it
|
|
51
|
-
|
|
52
|
-
`oke dev` picks up every exported flow — no route registration, no controller wiring:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
oke dev
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
</Step>
|
|
59
|
-
|
|
60
|
-
<Step>
|
|
61
|
-
### Call it
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
curl -X POST localhost:6530/orders -d '{"sku":"SKU-1","qty":2}' -H 'content-type: application/json'
|
|
65
|
-
# { "data": { "id": "…" }, "error": null }
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
</Step>
|
|
69
|
-
|
|
70
|
-
</Steps>
|
|
71
|
-
|
|
72
|
-
## Anatomy of a Flow
|
|
73
|
-
|
|
74
|
-
| Part | Role |
|
|
75
|
-
| -------------- | ------------------------------------------------------------------------------------ |
|
|
76
|
-
| trigger | What starts the flow — `http`, a signal, `every`, a row change |
|
|
77
|
-
| `in` | Input contract — validated before `do` runs; bad input is a 422 |
|
|
78
|
-
| `out` | Output contract — the return value is checked against it |
|
|
79
|
-
| `errors` | Typed failures — **returned** with `fx.fail`, never thrown |
|
|
80
|
-
| `retry` | Optional whole-`do` backoff on thrown errors (same journal) |
|
|
81
|
-
| `cache` | Optional — `false` opts out; `"30s"` adds a TTL. Read-only flows cache by default |
|
|
82
|
-
| `tenantScoped` | Default `true` when `gate.auth.tenant` is on — set `false` to skip tenant-role union |
|
|
83
|
-
| `do` | The work — every read, write, emit, and call goes through `fx` |
|
|
84
|
-
| `compensate` | Durable only — optional hook after auto per-step `{ undo }` (same journal) |
|
|
85
|
-
|
|
86
|
-
Failures are values, not exceptions:
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
do: async (input, fx) => {
|
|
90
|
-
const [product] = await fx.store(db).select().from(products).where(eq(products.sku, input.sku));
|
|
91
|
-
if (!product || product.stock < input.qty) {
|
|
92
|
-
return fx.fail("OutOfStock", { left: product?.stock ?? 0 }); // declared in errors
|
|
93
|
-
}
|
|
94
|
-
// …
|
|
95
|
-
};
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Every response follows one envelope — success `{ data, error: null }`, failure
|
|
99
|
-
`{ data: null, error: { code, data, message? } }` — clients switch on `error.code`.
|
|
100
|
-
Built-in codes attach a localized `message` ([Errors](/docs/reference/errors)); custom codes stay message-less unless registered or `{ message }` is passed.
|
|
101
|
-
|
|
102
|
-
<Callout title="List pages">
|
|
103
|
-
Prefer `out` as the item array and `return fx.json.withQuery(rows, input)` so the pager is
|
|
104
|
-
top-level `meta`. Do not put `{ data, meta }` on `out`. See
|
|
105
|
-
[Store](/docs/elements/store#handwritten-http-lists).
|
|
106
|
-
</Callout>
|
|
107
|
-
|
|
108
|
-
<Callout title="Helpers take the real Fx">
|
|
109
|
-
Type a helper's `fx` as the exported `Fx` (`import type { Fx } from
|
|
110
|
-
"okengine"`). A narrower `{ store: … }` will not match `store()` overloads
|
|
111
|
-
— that is the `as never` trap. See [fx](/docs/reference/fx).
|
|
112
|
-
</Callout>
|
|
113
|
-
|
|
114
|
-
## The triggers
|
|
115
|
-
|
|
116
|
-
<FlowTriggers />
|
|
117
|
-
|
|
118
|
-
### http — a request arrives
|
|
119
|
-
|
|
120
|
-
`http.get` · `http.post` · `http.put` · `http.patch` · `http.delete` · `http.head` · `http.options` · `http.query`. QUERY (RFC 10008) is a safe, idempotent read with a JSON body.
|
|
121
|
-
|
|
122
|
-
`http.get()` with no path is filled from the file tree (`flows/notes/[id]/get.ts` → `/notes/:id`). `flow({ do })` is named `unit.export`. Explicit `http.get("/me/tasks")` and `flow("notes.get")` still win. Folder conventions live on [Project structure](/docs/get-started/project-structure).
|
|
123
|
-
|
|
124
|
-
`Content-Type: application/json` is required — missing → **400** `InvalidQuery`, anything else → **415** `UnsupportedMediaType` with `Accept-Query: "application/json"`. Invalid JSON is **400** (no sniffing). Responses advertise `Accept-Query`. okengine does not assign `Location` / `Content-Location` result URIs.
|
|
125
|
-
|
|
126
|
-
A browser `GET` (`Accept` prefers `text/html`) paints the `{ data, error }` envelope in traces chrome — status, latency, and cache, Routes rail (static GET links), copy, line numbers, Pretty / Raw, Console on **6533**. `curl`, `Accept: application/json`, and `?format=json` stay JSON.
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
export const findOrder = on(
|
|
130
|
-
http.get("/orders/:id").gate(member), // gates evaluate before do runs
|
|
131
|
-
flow("orders.find", {
|
|
132
|
-
in: z.object({ id: z.string() }),
|
|
133
|
-
out: Order,
|
|
134
|
-
errors: { NotFound: z.object({}) },
|
|
135
|
-
do: async ({ id }, fx) => (await fx.store(db).findById(orders, id)) ?? fx.fail("NotFound", {}),
|
|
136
|
-
}),
|
|
137
|
-
);
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### signal — another flow emits
|
|
141
|
-
|
|
142
|
-
The producer emits through `fx`. For `once` and `broadcast` the consumer is the same species —
|
|
143
|
-
`on(signal, flow)`, no `subscribe()` registration. `live` replay is HTTP SSE: `.live(signal)` on a
|
|
144
|
-
GET trigger, `fx.live` as the stream carrier, `api.live` on the client.
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
await fx.emit(orderPlaced, { orderId: id }); // inside the producing flow
|
|
148
|
-
|
|
149
|
-
on(
|
|
150
|
-
orderPlaced,
|
|
151
|
-
flow("orders.sendReceipt", {
|
|
152
|
-
do: async ({ orderId }, fx) => {
|
|
153
|
-
/* … */
|
|
154
|
-
},
|
|
155
|
-
}),
|
|
156
|
-
);
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### clock — time passes
|
|
160
|
-
|
|
161
|
-
`every("1h")` is a trigger value, not a registration with a scheduler library:
|
|
162
|
-
|
|
163
|
-
```typescript
|
|
164
|
-
on(
|
|
165
|
-
every("1h"),
|
|
166
|
-
flow("sessions.sweepExpired", {
|
|
167
|
-
do: async (_, fx) => {
|
|
168
|
-
const cutoff = fx.clock.ago("30d");
|
|
169
|
-
await fx.store(db).delete(sessions).where(lt(sessions.createdAt, cutoff));
|
|
170
|
-
},
|
|
171
|
-
}),
|
|
172
|
-
);
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### store change — a row changes
|
|
176
|
-
|
|
177
|
-
CDC is built in; the flow receives `{ before, after }`:
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
on(
|
|
181
|
-
db.table(orders).changed("status"),
|
|
182
|
-
flow("orders.reverify", {
|
|
183
|
-
do: ({ before, after }, fx) => fx.log.info("status", { from: before.status, to: after.status }),
|
|
184
|
-
}),
|
|
185
|
-
);
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### no trigger — a callable flow
|
|
189
|
-
|
|
190
|
-
Drop `on()` and it is still a real Flow — contracts, Manifest entry, everything. Other flows call it through `fx.call`:
|
|
191
|
-
|
|
192
|
-
```typescript
|
|
193
|
-
export const getOrder = flow("orders.get", {
|
|
194
|
-
in: OrderRef,
|
|
195
|
-
out: Order,
|
|
196
|
-
do: async ({ id }, fx) => {
|
|
197
|
-
/* … */
|
|
198
|
-
},
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
const order = await fx.call(getOrder, { id: orderId }); // from any other flow
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### mcp — an agent calls a tool
|
|
205
|
-
|
|
206
|
-
`mcp.tool(name)` marks a flow for **OAuth-protected user-plane** exposure over MCP. Deny-by-default: a flow without this trigger is never listed over `tools/list`. A gate is required, exactly like a sensitive HTTP route:
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
export const createBooking = on(
|
|
210
|
-
mcp.tool("bookings.create").gate(member), // gates required — deny-by-default
|
|
211
|
-
flow("bookings.create", {
|
|
212
|
-
plane: "user",
|
|
213
|
-
in: BookingRef,
|
|
214
|
-
out: Booking,
|
|
215
|
-
do: async ({ id }, fx) => {
|
|
216
|
-
/* … */
|
|
217
|
-
},
|
|
218
|
-
}),
|
|
219
|
-
);
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
- The tool name is namespaced (`bookings.create`) and appears in the Manifest under `trigger.mcp.name` with its gates.
|
|
223
|
-
- Binding the **same tool name** twice fails boot with **OKE1018** (`MCP tool "{tool}" is bound twice`).
|
|
224
|
-
- Ungated `mcp.tool(...)` fails gate posture at boot (`GateBootError`) — the trigger never exposes an unauthenticated tool.
|
|
225
|
-
|
|
226
|
-
This is one direction of [MCP](/docs/ai/mcp): exposing your flows as tools. The other is consuming external servers as `ai.mcpServer` tools via `fx.ask` / `ai.agent`.
|
|
227
|
-
|
|
228
|
-
## fx — the only door
|
|
229
|
-
|
|
230
|
-
Everything a flow may touch, on one object:
|
|
231
|
-
|
|
232
|
-
| Surface | Effect recorded | What it does |
|
|
233
|
-
| ------------------------------------------- | --------------- | -------------------------------------------------- |
|
|
234
|
-
| `fx.store(db).select/insert/…` | read / write | SQL, KV, files, index sessions |
|
|
235
|
-
| `fx.emit(signal, payload)` | emit | Publish a signal (transactional with writes) |
|
|
236
|
-
| `fx.deadLetters(signal)` | read | Dead-lettered messages for that signal |
|
|
237
|
-
| `fx.live(signal, { match? })` | read | Live signal SSE carrier (`JsonStreamResult`) |
|
|
238
|
-
| `fx.send(template, opts)` | send | Reach a human (email · SMS · …) |
|
|
239
|
-
| `fx.ask(prompt, input)` | ask | Call a versioned AI prompt |
|
|
240
|
-
| `fx.run(agent, input)` | ask | Run a bounded agent |
|
|
241
|
-
| `fx.call(flow, input)` | call | Invoke another flow |
|
|
242
|
-
| `fx.vault.get(contract)` | secret | Read a secret (`Redacted`; logs masked) |
|
|
243
|
-
| `fx.clock.now/ago/fromNow/duration/sleep` | — | Injected instants, spans, durable sleep |
|
|
244
|
-
| `fx.cache.get/set` | — | Shared cache with effect-aware invalidation |
|
|
245
|
-
| `fx.step(name, fn, { undo? })` | — | Named durable step — optional LIFO undo on failure |
|
|
246
|
-
| `fx.all` / `fx.race` / `fx.retry` | — | Structured concurrency + backoff retry |
|
|
247
|
-
| `fx.signal` | — | Ambient `AbortSignal` for the current branch |
|
|
248
|
-
| `fx.id()` · `fx.log` · `fx.t` · `fx.locale` | — | UUIDs, redacting logger, ICU i18n |
|
|
249
|
-
| `fx.auth` · `fx.operator` · `fx.tenant` | — | Who is calling (user / operator / tenant) |
|
|
250
|
-
|
|
251
|
-
<Callout title="Why this strictness pays off">
|
|
252
|
-
Effects are inferred from `fx` usage, so the Manifest knows exactly which flows read `orders` or
|
|
253
|
-
send PII to a model — without you declaring any of it. That one graph drives the Console panels,
|
|
254
|
-
cache invalidation, least-privilege tokens, and durable replay.
|
|
255
|
-
</Callout>
|
|
256
|
-
|
|
257
|
-
## Cache — automatic for reads
|
|
258
|
-
|
|
259
|
-
Read-only flows cache automatically from those inferred `reads`. A write to the same
|
|
260
|
-
resource invalidates the entry. You do not declare `effects` or set `cache: "30s"`
|
|
261
|
-
unless you want a TTL or to opt out.
|
|
262
|
-
|
|
263
|
-
| On the flow | What happens |
|
|
264
|
-
| ---------------- | --------------------------------------- |
|
|
265
|
-
| omitted / `true` | Tier-1 cache when the flow only `reads` |
|
|
266
|
-
| `cache: "30s"` | Same, plus a TTL |
|
|
267
|
-
| `cache: false` | Skip cache (live SQL every time) |
|
|
268
|
-
|
|
269
|
-
Mutations, `fx.ask`, and `durable: true` stay uncached. Console Traces shows **hit** /
|
|
270
|
-
**miss** on cached reads, and **not applicable** only when the flow never used the cache.
|
|
271
|
-
|
|
272
|
-
A hit records the cache lookup on the waterfall (`computed:…` reads), not a replay of
|
|
273
|
-
the original store reads.
|
|
274
|
-
|
|
275
|
-
## Durability — flows that survive the process
|
|
276
|
-
|
|
277
|
-
<FlowDurable />
|
|
278
|
-
|
|
279
|
-
Set `durable: true` and every `fx` call is journaled. Wrap side effects in `fx.step` and they never re-run on replay:
|
|
280
|
-
|
|
281
|
-
```typescript
|
|
282
|
-
export const chargeOrder = flow("payments.chargeOrder", {
|
|
283
|
-
durable: true, // every fx call below is journaled
|
|
284
|
-
in: OrderRef,
|
|
285
|
-
out: z.boolean(),
|
|
286
|
-
do: async ({ orderId }, fx) => {
|
|
287
|
-
const key = await fx.vault.get(stripeKey);
|
|
288
|
-
const intent = await fx.step("create-intent", () => stripe(key.reveal()).create(orderId));
|
|
289
|
-
|
|
290
|
-
await fx.clock.sleep("verify-window", "2m"); // journals wakeAt in a durable flow
|
|
291
|
-
|
|
292
|
-
return fx.step("confirm", () => stripe(key.reveal()).confirm(intent));
|
|
293
|
-
},
|
|
294
|
-
});
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
**Consequence:** completed steps replay from the journal — on resume, `create-intent` does not re-run, so the card is not charged twice.
|
|
298
|
-
|
|
299
|
-
**Crash recovery needs a shared journal.** Set `drivers.journal: "postgres"` (the `dev`/`prod` template default) and every run persists in `oke_journal_runs` under a per-run lease: boot scans for orphaned `running` / `compensating` / due `sleeping` runs and resumes them, and the lease lets exactly one instance execute a run at a time. The default `memory` journal is per process — replay holds for that process only.
|
|
300
|
-
|
|
301
|
-
Replay is **at-least-once** for a step whose body never persisted (crash mid-step, lease overrun): that step re-runs on reclaim, so keep step bodies short or internally idempotent. A client transport retry starts a _new_ run with a fresh `runId` — request-level idempotency is an application concern, not the journal's.
|
|
302
|
-
|
|
303
|
-
For flaky sub-steps, wrap the work in `fx.retry` **inside** `fx.step` so a completed charge is never retried on resume. Coarse whole-body retry is also available as `flow(name, { retry: { retries, delay, backoff, jitter } })` and reuses the same journal session.
|
|
304
|
-
|
|
305
|
-
After a **terminal** failure (`fx.fail` or exhausted retries / uncaught throw), declare
|
|
306
|
-
undo next to each step. Only **persisted** steps undo (LIFO) — if `charge` throws before
|
|
307
|
-
it journals, `undo:charge` does not run:
|
|
308
|
-
|
|
309
|
-
```typescript
|
|
310
|
-
export const chargeOrder = flow("payments.chargeOrder", {
|
|
311
|
-
durable: true,
|
|
312
|
-
in: OrderRef,
|
|
313
|
-
out: z.boolean(),
|
|
314
|
-
do: async ({ orderId }, fx) => {
|
|
315
|
-
await fx.step("reserve", () => reserve(orderId), {
|
|
316
|
-
undo: () => release(orderId),
|
|
317
|
-
});
|
|
318
|
-
await fx.step("charge", () => charge(orderId), {
|
|
319
|
-
undo: (intent) => refund(intent), // journaled value — resume-safe
|
|
320
|
-
});
|
|
321
|
-
return true;
|
|
322
|
-
},
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
The runtime journals undos as `undo:<name>` under `compensating`, then commits `failed`.
|
|
327
|
-
Orphan reclaim resumes that phase; `flow.compensate` is optional after auto undos.
|
|
328
|
-
`JournalSuspend` is park (no undo). Prefer `fx.using` or `{ undo }` — not both for one release.
|
|
329
|
-
|
|
330
|
-
## Composition is just calls
|
|
331
|
-
|
|
332
|
-
Wiring is values flowing between flows — declared in code, never configured in a dashboard:
|
|
333
|
-
|
|
334
|
-
```typescript
|
|
335
|
-
on(http.post("/orders"), createOrder); // ① a request arrives
|
|
336
|
-
on(orderPlaced, sendReceipt); // ② its emit starts the consumer
|
|
337
|
-
on(every("1h"), sweepExpired); // ③ time passes
|
|
338
|
-
on(db.table(orders).changed("status"), reverify); // ④ a row changes
|
|
339
|
-
// ⑤ getOrder — nothing starts it; every flow above can fx.call it
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
## Troubleshooting
|
|
343
|
-
|
|
344
|
-
<Accordions>
|
|
345
|
-
<Accordion title="My flow used fetch / Date.now directly and weird things happened">
|
|
346
|
-
|
|
347
|
-
That bypasses the effect graph: the Manifest can't see the call, cache keys miss it, durable replay re-executes it, and tests can't freeze it. Move it behind `fx` — `fx.ask` / `fx.send` / `fx.clock.now()`, or a step inside a durable flow for true third-party calls.
|
|
348
|
-
|
|
349
|
-
</Accordion>
|
|
350
|
-
<Accordion title="Should I throw or fx.fail?">
|
|
351
|
-
|
|
352
|
-
`fx.fail(code, data)` for expected outcomes — anything in `errors`. A throw is a crash: the run fails with a 500-shaped error and no typed code for the client. Expected failures are values so clients can switch on `error.code`.
|
|
353
|
-
|
|
354
|
-
</Accordion>
|
|
355
|
-
<Accordion title="Promise.all left orphan work running after one branch failed">
|
|
356
|
-
|
|
357
|
-
Use `fx.all` (or `fx.race`) with thunks. The first rejection aborts siblings through `fx.signal`. Bare `Promise.all` / `Promise.race` do not cancel losers.
|
|
358
|
-
|
|
359
|
-
</Accordion>
|
|
360
|
-
<Accordion title="Durable flow retried a completed charge">
|
|
361
|
-
|
|
362
|
-
Put `fx.retry` **inside** `fx.step`, not around it. Completed steps replay from the journal and never re-run. `flow(name, { retry })` is for coarse whole-body retries on the same session.
|
|
363
|
-
|
|
364
|
-
</Accordion>
|
|
365
|
-
<Accordion title="When does undo / compensate run?">
|
|
366
|
-
|
|
367
|
-
Only after terminal failure for that journal attempt — not on sleep, not mid-retry, not on success. Per-step `{ undo }` runs first (LIFO, only for persisted steps), then optional `flow.compensate`. The run stays `compensating` until undos finish so a crash mid-undo resumes the undo phase, not forward `do`. Undo bodies must be idempotent. Irreversible effects (`send` / `ask`) need developer-declared undos or an explicit no-op.
|
|
368
|
-
|
|
369
|
-
</Accordion>
|
|
370
|
-
<Accordion title="How do I share logic between flows — a private function?">
|
|
371
|
-
|
|
372
|
-
Make it a plain flow with no trigger and `fx.call` it. You keep contracts, the Manifest entry, and tracing; a bare function would hide the work from the effect graph.
|
|
373
|
-
|
|
374
|
-
</Accordion>
|
|
375
|
-
<Accordion title="The browser shows a code block instead of JSON">
|
|
376
|
-
|
|
377
|
-
That is the HTML view for a navigated `GET` when `Accept` prefers `text/html`. Send `Accept: application/json`, add `?format=json`, or use `curl` — the envelope is unchanged. `?raw=1` is the compact view of the same page.
|
|
378
|
-
|
|
379
|
-
</Accordion>
|
|
380
|
-
<Accordion title="GET on a POST-only path">
|
|
381
|
-
|
|
382
|
-
The router returns **405 Method Not Allowed** with `Allow` listing every method bound to that path. A path that no flow owns is still 404. When QUERY is allowed, the 405 also carries `Accept-Query: "application/json"`.
|
|
383
|
-
|
|
384
|
-
</Accordion>
|
|
385
|
-
<Accordion title="QUERY without Content-Type">
|
|
386
|
-
|
|
387
|
-
RFC 10008 forbids sniffing. Send `Content-Type: application/json` and a JSON body. Missing header → `InvalidQuery` **400**; `text/plain` (or any non-JSON type) → `UnsupportedMediaType` **415**.
|
|
388
|
-
|
|
389
|
-
</Accordion>
|
|
390
|
-
<Accordion title="What happens if a cron flow throws?">
|
|
391
|
-
|
|
392
|
-
Only that run fails — the schedule keeps firing and the process does not exit. The failed run lands in Console → Flows (Traces) with its trace.
|
|
393
|
-
|
|
394
|
-
</Accordion>
|
|
395
|
-
<Accordion title="Console shows Cache not applicable on a GET">
|
|
396
|
-
|
|
397
|
-
The flow is not a read-only cache candidate — it writes, asks a model, is durable, or set
|
|
398
|
-
`cache: false`. Leave `cache` omitted. The first identical GET is a **miss**; the second is
|
|
399
|
-
a **hit**. A matching write turns the next read into a miss.
|
|
400
|
-
|
|
401
|
-
</Accordion>
|
|
402
|
-
</Accordions>
|
|
403
|
-
|
|
404
|
-
## Learn more
|
|
405
|
-
|
|
406
|
-
- [Project structure](/docs/get-started/project-structure) — folders are the URL; `unit.export` is the name
|
|
407
|
-
- [fx](/docs/reference/fx) — full `fx` surface, including ICU `fx.t` / `fx.locale`
|
|
408
|
-
- [Signal](/docs/elements/signal) — delivery physics (`once` · `broadcast` · `live`)
|
|
409
|
-
- [Clock](/docs/elements/clock) — schedules and durable sleep
|
|
410
|
-
|
|
411
|
-
## Next
|
|
412
|
-
|
|
413
|
-
<Cards>
|
|
414
|
-
<Card title="Signal" description="Continue to Signal." href="/docs/elements/signal" />
|
|
415
|
-
<Card
|
|
416
|
-
title="Introduction"
|
|
417
|
-
description="Eight elements overview."
|
|
418
|
-
href="/docs/get-started/introduction"
|
|
419
|
-
/>
|
|
420
|
-
</Cards>
|