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
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Broadcast"
|
|
3
|
+
description: "Ephemeral fan-out — every active subscriber gets a copy; offline listeners miss the event."
|
|
4
|
+
icon: "Share2"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
`signal.broadcast` delivers each emission to every subscribed Flow at once. Use it when every
|
|
9
|
+
active listener should react and you do not need a retained history.
|
|
10
|
+
|
|
11
|
+
For developers invalidating caches or syncing in-process state — declare the Signal, bind one or
|
|
12
|
+
more `on(signal, flow)` subscribers, emit with `fx.emit`.
|
|
13
|
+
|
|
14
|
+
<Callout title="The one rule">
|
|
15
|
+
Broadcast is ephemeral. If a subscriber is offline or restarts during the emit, it does not
|
|
16
|
+
receive past events. Use [`live`](/docs/elements/signal/live) when clients need replay. Use
|
|
17
|
+
[`once`](/docs/elements/signal/once) when exactly one worker must claim the job.
|
|
18
|
+
</Callout>
|
|
19
|
+
|
|
20
|
+
<SignalBroadcastFanout />
|
|
21
|
+
|
|
22
|
+
## Smallest Example
|
|
23
|
+
|
|
24
|
+
<Steps>
|
|
25
|
+
|
|
26
|
+
<Step>
|
|
27
|
+
### Define the broadcast signal
|
|
28
|
+
|
|
29
|
+
```typescript title="src/signals/cache.ts"
|
|
30
|
+
import { signal } from "okengine";
|
|
31
|
+
import { z } from "zod";
|
|
32
|
+
|
|
33
|
+
export const cacheInvalidated = signal.broadcast("cache.invalidated", {
|
|
34
|
+
schema: z.object({ key: z.string() }),
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
</Step>
|
|
39
|
+
|
|
40
|
+
<Step>
|
|
41
|
+
### Bind a subscriber
|
|
42
|
+
|
|
43
|
+
```typescript title="src/flows/cache/purge.ts"
|
|
44
|
+
import { on, flow } from "okengine";
|
|
45
|
+
import { cacheInvalidated } from "@/signals/cache";
|
|
46
|
+
|
|
47
|
+
export const purgeLocalCache = on(
|
|
48
|
+
cacheInvalidated,
|
|
49
|
+
flow("cache.purgeLocal", {
|
|
50
|
+
do: async ({ key }, fx) => {
|
|
51
|
+
await fx.store.kv.delete(key);
|
|
52
|
+
},
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
</Step>
|
|
58
|
+
|
|
59
|
+
<Step>
|
|
60
|
+
### Emit from any Flow
|
|
61
|
+
|
|
62
|
+
```typescript title="src/flows/skus/[sku]/update.ts"
|
|
63
|
+
import { on, flow, http } from "okengine";
|
|
64
|
+
import { z } from "zod";
|
|
65
|
+
import { cacheInvalidated } from "@/signals/cache";
|
|
66
|
+
|
|
67
|
+
export const update = on(
|
|
68
|
+
http.patch(),
|
|
69
|
+
flow({
|
|
70
|
+
in: z.object({ sku: z.string(), title: z.string().min(1) }),
|
|
71
|
+
do: async ({ sku, title }, fx) => {
|
|
72
|
+
// … persist the change …
|
|
73
|
+
await fx.emit(cacheInvalidated, { key: `sku:${sku}` });
|
|
74
|
+
return { sku, title };
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The compiler records `emits: ["cache.invalidated"]` on the producer. Emit resolves when the
|
|
81
|
+
outbox commits — the HTTP request does not wait for every subscriber to finish.
|
|
82
|
+
|
|
83
|
+
</Step>
|
|
84
|
+
|
|
85
|
+
</Steps>
|
|
86
|
+
|
|
87
|
+
<Callout title="Same handle everywhere">
|
|
88
|
+
Import the declared Signal handle (or the same name) in every subscriber and producer. A typo in
|
|
89
|
+
the name creates a different Manifest entry — fan-out never crosses names.
|
|
90
|
+
</Callout>
|
|
91
|
+
|
|
92
|
+
## Progressive Patterns
|
|
93
|
+
|
|
94
|
+
Explore broadcast from one listener to multi-Flow fan-out, optional hooks, and schema-checked
|
|
95
|
+
payloads:
|
|
96
|
+
|
|
97
|
+
<Tabs items={["Minimal", "Fan-out", "Optional", "Schema"]}>
|
|
98
|
+
|
|
99
|
+
<Tab value="Minimal">
|
|
100
|
+
|
|
101
|
+
One Flow subscribed — still broadcast physics (no competing claim, no once DLQ path):
|
|
102
|
+
|
|
103
|
+
```typescript title="src/signals/catalog.ts"
|
|
104
|
+
import { signal } from "okengine";
|
|
105
|
+
import { z } from "zod";
|
|
106
|
+
|
|
107
|
+
export const catalogChanged = signal.broadcast("catalog.changed", {
|
|
108
|
+
schema: z.object({ sku: z.string() }),
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```typescript title="src/flows/cache/invalidate.ts"
|
|
113
|
+
import { on, flow } from "okengine";
|
|
114
|
+
import { catalogChanged } from "@/signals/catalog";
|
|
115
|
+
|
|
116
|
+
export const invalidate = on(
|
|
117
|
+
catalogChanged,
|
|
118
|
+
flow("cache.invalidate", {
|
|
119
|
+
do: async ({ sku }, fx) => {
|
|
120
|
+
await fx.store.kv.delete(`sku:${sku}`);
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
</Tab>
|
|
127
|
+
|
|
128
|
+
<Tab value="Fan-out">
|
|
129
|
+
|
|
130
|
+
Bind a second Flow to the same Signal — both run. That is the fan-out:
|
|
131
|
+
|
|
132
|
+
```typescript title="src/flows/search/reindex.ts"
|
|
133
|
+
import { on, flow } from "okengine";
|
|
134
|
+
import { catalogChanged } from "@/signals/catalog";
|
|
135
|
+
|
|
136
|
+
export const reindexSku = on(
|
|
137
|
+
catalogChanged,
|
|
138
|
+
flow("search.reindexSku", {
|
|
139
|
+
do: async ({ sku }, fx) => {
|
|
140
|
+
await fx.call(rebuildSearchDoc, { sku });
|
|
141
|
+
},
|
|
142
|
+
}),
|
|
143
|
+
);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Consequence:** each subscribed Flow gets its own copy. A failure in one handler does not
|
|
147
|
+
claim-lock the message away from siblings the way `once` leases do.
|
|
148
|
+
|
|
149
|
+
</Tab>
|
|
150
|
+
|
|
151
|
+
<Tab value="Optional">
|
|
152
|
+
|
|
153
|
+
Hooks that may have no subscriber yet need `optional: true` or emit throws **OKE1240**:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
export const clusterHint = signal.broadcast("cluster.hint", {
|
|
157
|
+
optional: true,
|
|
158
|
+
schema: z.object({ nodeId: z.string() }),
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**OKE1240** cause: `Flow "{flow}" emits signal "{resource}" with no subscriber.`
|
|
163
|
+
Fix: bind at least one `on(signal, flow)` or set `{ optional: true }`.
|
|
164
|
+
|
|
165
|
+
</Tab>
|
|
166
|
+
|
|
167
|
+
<Tab value="Schema">
|
|
168
|
+
|
|
169
|
+
Invalid payloads fail at `fx.emit` with **OKE1250** before any subscriber runs:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
export const priceTick = signal.broadcast("prices.tick", {
|
|
173
|
+
schema: z.object({
|
|
174
|
+
sku: z.string(),
|
|
175
|
+
cents: z.number().int().nonnegative(),
|
|
176
|
+
}),
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Cause shape: `"{resource}": {detail}`. Align the payload with `schema` — no subscriber started.
|
|
181
|
+
|
|
182
|
+
</Tab>
|
|
183
|
+
|
|
184
|
+
</Tabs>
|
|
185
|
+
|
|
186
|
+
## Delivery Reference
|
|
187
|
+
|
|
188
|
+
How broadcast sits next to the other helpers when you are choosing physics:
|
|
189
|
+
|
|
190
|
+
| Helper | Pattern | Who runs | Replay | Typical use |
|
|
191
|
+
| ------------------ | ---------- | ---------------------------- | ---------------------- | ------------------------------------- |
|
|
192
|
+
| `signal.once` | Work queue | Exactly one competing worker | Retries + DLQ | Emails, fulfillment, sync |
|
|
193
|
+
| `signal.broadcast` | Pub/sub | Every active subscriber | None (miss if offline) | Cache bust, multi-side-effect fan-out |
|
|
194
|
+
| `signal.live` | SSE tape | HTTP clients via `http.live` | `Last-Event-ID` resume | Status feeds, progress |
|
|
195
|
+
|
|
196
|
+
| Guarantee | Broadcast | Once | Live |
|
|
197
|
+
| ---------------------------------- | ---------------------- | ------------------ | ----------------- |
|
|
198
|
+
| Competing claim / visibility lease | No | Yes (default 30s) | No |
|
|
199
|
+
| Dead-letter queue path | No | Yes (`deadLetter`) | No |
|
|
200
|
+
| Retained history for late joiners | No | No | Yes |
|
|
201
|
+
| Multiple Flows on one emit | Yes — each gets a copy | One claimer | Not a Flow worker |
|
|
202
|
+
|
|
203
|
+
## Options for broadcast
|
|
204
|
+
|
|
205
|
+
Second argument to `signal.broadcast(name, options?)`. Delivery is the helper name — not an
|
|
206
|
+
option.
|
|
207
|
+
|
|
208
|
+
| Option | Type | Default | Meaning |
|
|
209
|
+
| ------------- | --------------- | -------- | -------------------------------------------------------- |
|
|
210
|
+
| `schema` | Standard Schema | omitted | **Emit** contract — validated at `fx.emit` (**OKE1250**) |
|
|
211
|
+
| `optional` | `boolean` | `false` | Allow emit with zero subscribers |
|
|
212
|
+
| `description` | `string` | the name | Console / docs blurb |
|
|
213
|
+
| `retries` | `number` | `3` | Declared; not the `once` lease / DLQ path |
|
|
214
|
+
| `deadLetter` | `boolean` | `true` | Declared; broadcast does not use once DLQ |
|
|
215
|
+
|
|
216
|
+
`retention` is a type error on `signal.broadcast` — that option is live-only:
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
signal.broadcast("…"): retention is only valid with signal.live
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Fan-out Physics
|
|
223
|
+
|
|
224
|
+
<Callout title="Detailed section">
|
|
225
|
+
If you only need one subscriber, jump to Emit. Fan-out is the distinctive physics: one emit, N
|
|
226
|
+
independent Flow runs, no competing claim.
|
|
227
|
+
</Callout>
|
|
228
|
+
|
|
229
|
+
Bind any number of Flows with `on(theSameSignal, flow(...))`. At dispatch, every matching
|
|
230
|
+
binding runs with the same payload.
|
|
231
|
+
|
|
232
|
+
```typescript title="src/flows/orders/side-effects.ts"
|
|
233
|
+
import { on, flow } from "okengine";
|
|
234
|
+
import { orderChanged } from "@/signals/orders";
|
|
235
|
+
|
|
236
|
+
export const bustOrderCache = on(
|
|
237
|
+
orderChanged,
|
|
238
|
+
flow("orders.bustCache", {
|
|
239
|
+
do: async ({ orderId }, fx) => {
|
|
240
|
+
await fx.store.kv.delete(`order:${orderId}`);
|
|
241
|
+
},
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
export const notifyOrderWatchers = on(
|
|
246
|
+
orderChanged,
|
|
247
|
+
flow("orders.notifyWatchers", {
|
|
248
|
+
do: async ({ orderId }, fx) => {
|
|
249
|
+
await fx.call(pushOrderWatchers, { orderId });
|
|
250
|
+
},
|
|
251
|
+
}),
|
|
252
|
+
);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
await fx.emit(orderChanged, { orderId: "ord_42", kind: "placed" });
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
| Concept | Meaning |
|
|
260
|
+
| ---------- | ---------------------------------------------------------- |
|
|
261
|
+
| Subscriber | An adopted Flow bound with `on(broadcastSignal, flow)` |
|
|
262
|
+
| Copy | Each subscriber receives the payload independently |
|
|
263
|
+
| Isolation | One handler's failure does not lease-lock siblings |
|
|
264
|
+
| Offline | A process that was down at emit time does not get a replay |
|
|
265
|
+
|
|
266
|
+
**Consequence:** treat broadcast handlers as best-effort side effects. If the work must run
|
|
267
|
+
exactly once with retries and a DLQ, declare a separate `signal.once` for that job.
|
|
268
|
+
|
|
269
|
+
## Emit and Effects
|
|
270
|
+
|
|
271
|
+
<Callout title="Detailed section">
|
|
272
|
+
If you only need `fx.emit(signal, payload)`, jump to the example. Emit commits the outbox when the
|
|
273
|
+
call resolves. Subscribers run asynchronously afterward.
|
|
274
|
+
</Callout>
|
|
275
|
+
|
|
276
|
+
| Call | Records | Use |
|
|
277
|
+
| ----------------------------------- | ------- | ----------------------------------------------------------- |
|
|
278
|
+
| `fx.emit(signal, payload?)` | `emits` | Publish to every active subscriber |
|
|
279
|
+
| `fx.emit(signal, payload, { key })` | `emits` | `key` is for `once` ordering — ignore for broadcast fan-out |
|
|
280
|
+
|
|
281
|
+
```typescript title="src/flows/catalog/create.ts"
|
|
282
|
+
import { on, flow, http } from "okengine";
|
|
283
|
+
import { z } from "zod";
|
|
284
|
+
import { catalogChanged } from "@/signals/catalog";
|
|
285
|
+
|
|
286
|
+
export const create = on(
|
|
287
|
+
http.post(),
|
|
288
|
+
flow({
|
|
289
|
+
in: z.object({ sku: z.string(), title: z.string().min(1) }),
|
|
290
|
+
do: async ({ sku, title }, fx) => {
|
|
291
|
+
// … write …
|
|
292
|
+
await fx.emit(catalogChanged, { sku });
|
|
293
|
+
return { sku, title };
|
|
294
|
+
},
|
|
295
|
+
}),
|
|
296
|
+
);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
The producer WideEvent / run id is stamped as `parentRunId` for Console trace chains. Schema
|
|
300
|
+
mismatches fail at emit (**OKE1250**) before any subscriber runs.
|
|
301
|
+
|
|
302
|
+
## Subscribers
|
|
303
|
+
|
|
304
|
+
Each verb of fan-out is a normal Flow. Bind with `on(signal, flow(...))` — same species as
|
|
305
|
+
[Consumers](/docs/elements/flow/consumers).
|
|
306
|
+
|
|
307
|
+
<Tabs items={["Cache", "Notify", "Reindex", "Compose"]}>
|
|
308
|
+
|
|
309
|
+
<Tab value="Cache">
|
|
310
|
+
|
|
311
|
+
Drop hot keys when the source of truth changes:
|
|
312
|
+
|
|
313
|
+
```typescript title="src/flows/cache/on-catalog.ts"
|
|
314
|
+
import { on, flow } from "okengine";
|
|
315
|
+
import { catalogChanged } from "@/signals/catalog";
|
|
316
|
+
|
|
317
|
+
export const onCatalogCache = on(
|
|
318
|
+
catalogChanged,
|
|
319
|
+
flow("cache.onCatalog", {
|
|
320
|
+
do: async ({ sku }, fx) => {
|
|
321
|
+
await fx.store.kv.delete(`sku:${sku}`);
|
|
322
|
+
await fx.store.kv.delete(`sku:${sku}:meta`);
|
|
323
|
+
},
|
|
324
|
+
}),
|
|
325
|
+
);
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
</Tab>
|
|
329
|
+
|
|
330
|
+
<Tab value="Notify">
|
|
331
|
+
|
|
332
|
+
Fan a domain event into a Channel or another Flow without competing for a lease:
|
|
333
|
+
|
|
334
|
+
```typescript title="src/flows/notify/on-order.ts"
|
|
335
|
+
import { on, flow } from "okengine";
|
|
336
|
+
import { orderChanged } from "@/signals/orders";
|
|
337
|
+
import { orderWatchers } from "@/channels/orders";
|
|
338
|
+
|
|
339
|
+
export const onOrderNotify = on(
|
|
340
|
+
orderChanged,
|
|
341
|
+
flow("notify.onOrder", {
|
|
342
|
+
do: async ({ orderId }, fx) => {
|
|
343
|
+
await fx.send(orderWatchers, {
|
|
344
|
+
to: "ops@example.com",
|
|
345
|
+
data: { orderId },
|
|
346
|
+
});
|
|
347
|
+
},
|
|
348
|
+
}),
|
|
349
|
+
);
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
</Tab>
|
|
353
|
+
|
|
354
|
+
<Tab value="Reindex">
|
|
355
|
+
|
|
356
|
+
Kick search / projection work as a side effect of the same emit:
|
|
357
|
+
|
|
358
|
+
```typescript title="src/flows/search/on-catalog.ts"
|
|
359
|
+
import { on, flow } from "okengine";
|
|
360
|
+
import { catalogChanged } from "@/signals/catalog";
|
|
361
|
+
|
|
362
|
+
export const onCatalogReindex = on(
|
|
363
|
+
catalogChanged,
|
|
364
|
+
flow("search.onCatalog", {
|
|
365
|
+
do: async ({ sku }, fx) => {
|
|
366
|
+
await fx.call(rebuildSearchDoc, { sku });
|
|
367
|
+
},
|
|
368
|
+
}),
|
|
369
|
+
);
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
</Tab>
|
|
373
|
+
|
|
374
|
+
<Tab value="Compose">
|
|
375
|
+
|
|
376
|
+
Need durable retries for one side effect? Emit a `once` job from the broadcast handler (or from
|
|
377
|
+
the producer) — do not stretch broadcast into a queue:
|
|
378
|
+
|
|
379
|
+
```typescript title="src/flows/orders/on-changed.ts"
|
|
380
|
+
import { on, flow } from "okengine";
|
|
381
|
+
import { orderChanged, orderSyncJob } from "@/signals/orders";
|
|
382
|
+
|
|
383
|
+
export const onOrderChanged = on(
|
|
384
|
+
orderChanged,
|
|
385
|
+
flow("orders.onChanged", {
|
|
386
|
+
do: async ({ orderId }, fx) => {
|
|
387
|
+
await fx.store.kv.delete(`order:${orderId}`);
|
|
388
|
+
await fx.emit(orderSyncJob, { orderId });
|
|
389
|
+
},
|
|
390
|
+
}),
|
|
391
|
+
);
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
`orderSyncJob` is `signal.once(...)` — competing workers, retries, and DLQ live there.
|
|
395
|
+
|
|
396
|
+
</Tab>
|
|
397
|
+
|
|
398
|
+
</Tabs>
|
|
399
|
+
|
|
400
|
+
## Choosing Physics
|
|
401
|
+
|
|
402
|
+
<Callout title="Detailed section">
|
|
403
|
+
Pick physics from the guarantee you need. Broadcast is the wrong tool when work must be claimed
|
|
404
|
+
once, or when a late client must catch up.
|
|
405
|
+
</Callout>
|
|
406
|
+
|
|
407
|
+
| Need | Use instead |
|
|
408
|
+
| ------------------------------------- | ---------------------------------------------------- |
|
|
409
|
+
| Exactly one worker processes the job | [`once`](/docs/elements/signal/once) |
|
|
410
|
+
| Browser / SSE resume after disconnect | [`live`](/docs/elements/signal/live) |
|
|
411
|
+
| Durable multi-step work with journal | [Durable Workflows](/docs/elements/flow/workflows) |
|
|
412
|
+
| HTTP SSE firehose on a path | [`http.live`](/docs/elements/flow/http#live-streams) |
|
|
413
|
+
|
|
414
|
+
Broadcast does **not** use the `once` visibility lease. There is no competing claim and no
|
|
415
|
+
dead-letter queue for exhausted retries in the queue sense — see
|
|
416
|
+
[Once · Retries](/docs/elements/signal/once#retries-and-dead-letters).
|
|
417
|
+
|
|
418
|
+
<Accordions>
|
|
419
|
+
|
|
420
|
+
<Accordion title="Orphan emit (OKE1240)">
|
|
421
|
+
Cause: `Flow "{flow}" emits signal "{resource}" with no subscriber.`
|
|
422
|
+
Fix: bind at least one `on(signal, flow)` or set `{ optional: true }`.
|
|
423
|
+
</Accordion>
|
|
424
|
+
|
|
425
|
+
<Accordion title="Schema (OKE1250)">
|
|
426
|
+
Cause: `"{resource}": {detail}`. Fix the payload or the `schema`. No subscriber ran.
|
|
427
|
+
</Accordion>
|
|
428
|
+
|
|
429
|
+
<Accordion title="Missed while offline">
|
|
430
|
+
Expected. Restarted processes do not replay broadcast history. Emit again after boot if the
|
|
431
|
+
listener must refresh state, or switch to `live` for a retained tape.
|
|
432
|
+
</Accordion>
|
|
433
|
+
|
|
434
|
+
<Accordion title="retries / deadLetter on the declaration">
|
|
435
|
+
Those fields exist on the shared options type (defaults `3` / `true`) so Console and Manifest stay
|
|
436
|
+
uniform. Broadcast delivery does not follow the once lease + DLQ path — do not expect
|
|
437
|
+
`fx.deadLetters(broadcastSignal)` to behave like a queue operator surface.
|
|
438
|
+
</Accordion>
|
|
439
|
+
|
|
440
|
+
<Accordion title="retention on broadcast">
|
|
441
|
+
Throws at declare: `signal.broadcast("…"): retention is only valid with signal.live`. Cap a
|
|
442
|
+
client-visible tape with `signal.live({retention})` instead.
|
|
443
|
+
</Accordion>
|
|
444
|
+
|
|
445
|
+
</Accordions>
|
|
446
|
+
|
|
447
|
+
## Troubleshooting
|
|
448
|
+
|
|
449
|
+
<Accordions>
|
|
450
|
+
|
|
451
|
+
<Accordion title="Only one of two subscribers runs">
|
|
452
|
+
Confirm both Flows import the same Signal handle / name and are adopted into the app. Names must
|
|
453
|
+
match the Manifest entry exactly. A second `signal.broadcast("catalog.changed")` with a different
|
|
454
|
+
spelling is a different signal.
|
|
455
|
+
</Accordion>
|
|
456
|
+
|
|
457
|
+
<Accordion title="Subscriber missed an event after restart">
|
|
458
|
+
Broadcast has no tape. Re-emit on boot, hydrate from Store, or use
|
|
459
|
+
[`signal.live`](/docs/elements/signal/live).
|
|
460
|
+
</Accordion>
|
|
461
|
+
|
|
462
|
+
<Accordion title="OKE1240 on emit">
|
|
463
|
+
Cause: `Flow "{flow}" emits signal "{resource}" with no subscriber.`
|
|
464
|
+
No subscriber is bound. Add `on(signal, …)` or `{ optional: true }`.
|
|
465
|
+
</Accordion>
|
|
466
|
+
|
|
467
|
+
<Accordion title="OKE1250 on emit">
|
|
468
|
+
Cause: `"{resource}": {detail}`. Align the payload with `schema` — no subscriber started.
|
|
469
|
+
</Accordion>
|
|
470
|
+
|
|
471
|
+
<Accordion title="Expecting retries / DLQ like a queue">
|
|
472
|
+
Use `signal.once`. Broadcast fan-out is not the lease + DLQ path documented under
|
|
473
|
+
[Once](/docs/elements/signal/once).
|
|
474
|
+
</Accordion>
|
|
475
|
+
|
|
476
|
+
<Accordion title="TypeError: retention is only valid with signal.live">
|
|
477
|
+
You passed `retention` to `signal.broadcast`. Drop it, or switch the helper to `signal.live` if
|
|
478
|
+
you need a retained SSE tape.
|
|
479
|
+
</Accordion>
|
|
480
|
+
|
|
481
|
+
<Accordion title="HTTP client never sees the event">
|
|
482
|
+
Broadcast is for Flow subscribers, not browsers. Expose a
|
|
483
|
+
[`signal.live`](/docs/elements/signal/live) tape with
|
|
484
|
+
[`http.live`](/docs/elements/flow/http#live-streams) (or a gated GET `.live(signal)`).
|
|
485
|
+
</Accordion>
|
|
486
|
+
|
|
487
|
+
</Accordions>
|
|
488
|
+
|
|
489
|
+
## Learn more
|
|
490
|
+
|
|
491
|
+
- [Signal Overview](/docs/elements/signal) — delivery matrix and drivers
|
|
492
|
+
- [Once](/docs/elements/signal/once) — competing workers and DLQ
|
|
493
|
+
- [Live](/docs/elements/signal/live) — retained SSE
|
|
494
|
+
- [Consumers](/docs/elements/flow/consumers) — binding `on(signal)`
|
|
495
|
+
- [HTTP · Live Streams](/docs/elements/flow/http#live-streams) — when the consumer is a browser
|
|
496
|
+
- [fx](/docs/reference/fx) — `fx.emit`
|
|
497
|
+
- [Errors](/docs/reference/errors) — OKE1240 · OKE1250
|
|
498
|
+
|
|
499
|
+
## Next
|
|
500
|
+
|
|
501
|
+
<Cards>
|
|
502
|
+
<Card
|
|
503
|
+
title="Live"
|
|
504
|
+
description="Retained event tapes over HTTP SSE."
|
|
505
|
+
href="/docs/elements/signal/live"
|
|
506
|
+
/>
|
|
507
|
+
<Card
|
|
508
|
+
title="Once"
|
|
509
|
+
description="Competing workers with leases and dead letters."
|
|
510
|
+
href="/docs/elements/signal/once"
|
|
511
|
+
/>
|
|
512
|
+
<Card
|
|
513
|
+
title="Consumers"
|
|
514
|
+
description="Signal workers, Clock jobs, and SQL CDC."
|
|
515
|
+
href="/docs/elements/flow/consumers"
|
|
516
|
+
/>
|
|
517
|
+
</Cards>
|