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,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate tests for EffectEntry.external + fx.fetch + driver-reported egress.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { openOpenaiCompatible } from "../drivers/ai-openai-compatible.ts";
|
|
7
|
+
import { openAnthropic } from "../drivers/ai-anthropic.ts";
|
|
8
|
+
import { connectPglite } from "../drivers/pglite.ts";
|
|
9
|
+
import { createChannelRuntime } from "../elements/channel/runtime.ts";
|
|
10
|
+
import { createFxContext } from "./fx.ts";
|
|
11
|
+
import { withDryRun } from "./dry-run.ts";
|
|
12
|
+
|
|
13
|
+
describe("EffectEntry.external — optional / additive", () => {
|
|
14
|
+
test("ledger entries without external stay five-field shaped", async () => {
|
|
15
|
+
const { fx, ledger } = createFxContext({
|
|
16
|
+
flow: "local.read",
|
|
17
|
+
effects: { reads: ["sql:notes"] },
|
|
18
|
+
});
|
|
19
|
+
await (fx.store("sql:notes") as { get(key: string): Promise<unknown> }).get("n1");
|
|
20
|
+
const entry = ledger.entries[0];
|
|
21
|
+
expect(entry).toMatchObject({
|
|
22
|
+
kind: "read",
|
|
23
|
+
resource: "sql:notes",
|
|
24
|
+
reversibility: "none",
|
|
25
|
+
});
|
|
26
|
+
expect(entry?.external).toBeUndefined();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("fx.fetch", () => {
|
|
31
|
+
test("records fetch kind with third-party host from URL", async () => {
|
|
32
|
+
const originalFetch = globalThis.fetch;
|
|
33
|
+
globalThis.fetch = (async () => new Response("ok", { status: 200 })) as unknown as typeof fetch;
|
|
34
|
+
try {
|
|
35
|
+
const { fx, ledger } = createFxContext({
|
|
36
|
+
flow: "stripe.pull",
|
|
37
|
+
effects: { fetches: ["api.stripe.com"] },
|
|
38
|
+
});
|
|
39
|
+
const res = await fx.fetch("https://api.stripe.com/v1/charges");
|
|
40
|
+
expect(res.status).toBe(200);
|
|
41
|
+
expect(ledger.entries).toHaveLength(1);
|
|
42
|
+
expect(ledger.entries[0]).toMatchObject({
|
|
43
|
+
kind: "fetch",
|
|
44
|
+
resource: "api.stripe.com",
|
|
45
|
+
reversibility: "irreversible",
|
|
46
|
+
external: { host: "api.stripe.com", kind: "third-party" },
|
|
47
|
+
});
|
|
48
|
+
} finally {
|
|
49
|
+
globalThis.fetch = originalFetch;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("undeclared host throws UNDECLARED_FETCH", async () => {
|
|
54
|
+
const { fx } = createFxContext({
|
|
55
|
+
flow: "stripe.pull",
|
|
56
|
+
effects: { fetches: ["api.example.com"] },
|
|
57
|
+
});
|
|
58
|
+
await expect(fx.fetch("https://api.stripe.com/v1")).rejects.toMatchObject({
|
|
59
|
+
code: 1008,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("dry-run stubs fetch without contacting the network", async () => {
|
|
64
|
+
let called = false;
|
|
65
|
+
const originalFetch = globalThis.fetch;
|
|
66
|
+
globalThis.fetch = (async () => {
|
|
67
|
+
called = true;
|
|
68
|
+
return new Response("nope");
|
|
69
|
+
}) as unknown as typeof fetch;
|
|
70
|
+
try {
|
|
71
|
+
const { fx, ledger } = createFxContext({
|
|
72
|
+
flow: "stripe.pull",
|
|
73
|
+
effects: { fetches: ["api.stripe.com"] },
|
|
74
|
+
});
|
|
75
|
+
const { wouldHaveFired } = await withDryRun(async () => {
|
|
76
|
+
await fx.fetch("https://api.stripe.com/v1");
|
|
77
|
+
});
|
|
78
|
+
expect(called).toBe(false);
|
|
79
|
+
expect(wouldHaveFired).toEqual([{ kind: "fetch", resource: "api.stripe.com" }]);
|
|
80
|
+
expect(ledger.entries[0]?.external).toMatchObject({
|
|
81
|
+
host: "api.stripe.com",
|
|
82
|
+
kind: "third-party",
|
|
83
|
+
});
|
|
84
|
+
} finally {
|
|
85
|
+
globalThis.fetch = originalFetch;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("Channel failover — winning provider on EffectEntry", () => {
|
|
91
|
+
test("send stamps external.provider from the winning attempt, not the primary binding", async () => {
|
|
92
|
+
const { channel } = await import("../elements/channel.ts");
|
|
93
|
+
const { driverFromTransport, failingTransport, okTransport } =
|
|
94
|
+
await import("../elements/channel/test-helpers.ts");
|
|
95
|
+
const smtp = {
|
|
96
|
+
...driverFromTransport("smtp", failingTransport("smtp")),
|
|
97
|
+
external: {
|
|
98
|
+
host: "mail.example.internal",
|
|
99
|
+
provider: "smtp",
|
|
100
|
+
kind: "infrastructure" as const,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
const resend = {
|
|
104
|
+
...driverFromTransport("resend", okTransport("resend")),
|
|
105
|
+
external: {
|
|
106
|
+
host: "api.resend.com",
|
|
107
|
+
provider: "resend",
|
|
108
|
+
kind: "third-party" as const,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
const channelRuntime = createChannelRuntime({
|
|
112
|
+
templates: [channel.template("welcome", { medium: "email" })],
|
|
113
|
+
drivers: [smtp, resend],
|
|
114
|
+
catalog: { welcome: { en: { subject: "Hi", text: "Hello" } } },
|
|
115
|
+
});
|
|
116
|
+
const { fx, ledger } = createFxContext({
|
|
117
|
+
flow: "mail.send",
|
|
118
|
+
effects: { sends: ["welcome"] },
|
|
119
|
+
channelRuntime,
|
|
120
|
+
});
|
|
121
|
+
await fx.send("welcome", { to: "a@b.com", via: ["smtp", "resend"] });
|
|
122
|
+
const send = ledger.entries.find((e) => e.kind === "send");
|
|
123
|
+
expect(send?.external?.provider).toBe("resend");
|
|
124
|
+
expect(send?.external?.host).toBe("api.resend.com");
|
|
125
|
+
expect(send?.external?.kind).toBe("third-party");
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe("Store — in-process PGlite never carries external", () => {
|
|
130
|
+
test("pglite connection omits external", async () => {
|
|
131
|
+
const conn = await connectPglite({ url: "memory://" });
|
|
132
|
+
expect(conn.driverId).toBe("pglite");
|
|
133
|
+
expect(conn.external).toBeUndefined();
|
|
134
|
+
await conn.close?.();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("AI — infrastructure vs third-party", () => {
|
|
139
|
+
test("self-hosted openai-compatible reports infrastructure when opened with that kind", async () => {
|
|
140
|
+
const originalFetch = globalThis.fetch;
|
|
141
|
+
globalThis.fetch = (async () =>
|
|
142
|
+
new Response(
|
|
143
|
+
JSON.stringify({
|
|
144
|
+
choices: [{ message: { content: "hi" } }],
|
|
145
|
+
model: "llama3",
|
|
146
|
+
usage: { prompt_tokens: 1, completion_tokens: 1 },
|
|
147
|
+
}),
|
|
148
|
+
{ status: 200, headers: { "content-type": "application/json" } },
|
|
149
|
+
)) as unknown as typeof fetch;
|
|
150
|
+
try {
|
|
151
|
+
const client = await openOpenaiCompatible({
|
|
152
|
+
baseUrl: "http://127.0.0.1:11434/v1",
|
|
153
|
+
external: { kind: "infrastructure", provider: "local" },
|
|
154
|
+
});
|
|
155
|
+
const result = await client.complete({
|
|
156
|
+
messages: [{ role: "user", content: "hi" }],
|
|
157
|
+
});
|
|
158
|
+
expect(result.external).toEqual({
|
|
159
|
+
host: "127.0.0.1",
|
|
160
|
+
kind: "infrastructure",
|
|
161
|
+
provider: "local",
|
|
162
|
+
});
|
|
163
|
+
} finally {
|
|
164
|
+
globalThis.fetch = originalFetch;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("anthropic cloud reports third-party", async () => {
|
|
169
|
+
const originalFetch = globalThis.fetch;
|
|
170
|
+
globalThis.fetch = (async () =>
|
|
171
|
+
new Response(
|
|
172
|
+
JSON.stringify({
|
|
173
|
+
content: [{ type: "text", text: "hi" }],
|
|
174
|
+
model: "claude",
|
|
175
|
+
usage: { input_tokens: 1, output_tokens: 1 },
|
|
176
|
+
}),
|
|
177
|
+
{ status: 200, headers: { "content-type": "application/json" } },
|
|
178
|
+
)) as unknown as typeof fetch;
|
|
179
|
+
try {
|
|
180
|
+
const client = await openAnthropic({
|
|
181
|
+
apiKey: "sk-test",
|
|
182
|
+
baseUrl: "https://api.anthropic.com",
|
|
183
|
+
});
|
|
184
|
+
const result = await client.complete({
|
|
185
|
+
messages: [{ role: "user", content: "hi" }],
|
|
186
|
+
});
|
|
187
|
+
expect(result.external).toEqual({
|
|
188
|
+
host: "api.anthropic.com",
|
|
189
|
+
kind: "third-party",
|
|
190
|
+
provider: "anthropic",
|
|
191
|
+
});
|
|
192
|
+
} finally {
|
|
193
|
+
globalThis.fetch = originalFetch;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
});
|
package/src/kernel/flow.test.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { describe, expect, test, beforeEach } from "bun:test";
|
|
2
|
+
import { clock } from "../elements/clock/declare.ts";
|
|
3
|
+
import { field, store } from "../elements/store.ts";
|
|
2
4
|
import { oke } from "./app.ts";
|
|
3
5
|
import { flow, isFlow, resetFlowSeq } from "./flow.ts";
|
|
4
6
|
import { on, resetBindings } from "./on.ts";
|
|
5
|
-
import {
|
|
7
|
+
import { http, internal } from "./triggers.ts";
|
|
6
8
|
|
|
7
9
|
beforeEach(() => {
|
|
8
10
|
resetBindings();
|
|
@@ -71,8 +73,9 @@ describe("five trigger kinds", () => {
|
|
|
71
73
|
}),
|
|
72
74
|
);
|
|
73
75
|
|
|
76
|
+
const hourly = clock("hourly", { every: "1h" });
|
|
74
77
|
on(
|
|
75
|
-
|
|
78
|
+
hourly,
|
|
76
79
|
flow("t.every", {
|
|
77
80
|
do: () => {
|
|
78
81
|
seen.push("every");
|
|
@@ -89,8 +92,14 @@ describe("five trigger kinds", () => {
|
|
|
89
92
|
}),
|
|
90
93
|
);
|
|
91
94
|
|
|
95
|
+
const db = store.sql("main");
|
|
96
|
+
const orders = store.schema.table("orders", {
|
|
97
|
+
id: field.text().primaryKey(),
|
|
98
|
+
status: field.text(),
|
|
99
|
+
});
|
|
100
|
+
|
|
92
101
|
on(
|
|
93
|
-
table(
|
|
102
|
+
db.table(orders).changed("status"),
|
|
94
103
|
flow("t.cdc", {
|
|
95
104
|
do: (p: { before: { status: string }; after: { status: string } }) => {
|
|
96
105
|
seen.push(`cdc:${p.before.status}->${p.after.status}`);
|
|
@@ -114,7 +123,7 @@ describe("five trigger kinds", () => {
|
|
|
114
123
|
expect(res.status).toBe(200);
|
|
115
124
|
expect(await res.json()).toEqual({ data: { code: "abc" }, error: null });
|
|
116
125
|
|
|
117
|
-
await app.dispatchEvery("
|
|
126
|
+
await app.dispatchEvery("hourly");
|
|
118
127
|
await app.dispatchSignal("link-clicked", { code: "sa" });
|
|
119
128
|
await app.dispatchCdc(
|
|
120
129
|
"orders",
|
package/src/kernel/flow.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `flow` — the one species of backend behavior.
|
|
3
3
|
*
|
|
4
|
-
* A Flow is a
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* A Flow is a `do` body plus runtime options. Invoke contracts (`in` / `out` /
|
|
5
|
+
* `errors` / `breaking`) are authored on the exposure (`http.*`, `call`,
|
|
6
|
+
* `mcp.tool`) and stamped onto the FlowDef for Manifest / validation.
|
|
7
|
+
* Triggers are attached by {@link on}; {@link call} is the call-only sugar.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import type { Effects, FlowPlane, Slo } from "../manifest/types.ts";
|
|
10
|
-
import type {
|
|
11
|
+
import type { SchemaInput } from "../validation/standard-schema.ts";
|
|
11
12
|
import type { FxRetryOptions } from "./concurrency.ts";
|
|
12
13
|
import type { FlowFailure } from "./errors.ts";
|
|
13
14
|
import type { Fx } from "./fx.ts";
|
|
@@ -22,7 +23,7 @@ export type StandardSchemaResult<Output> =
|
|
|
22
23
|
| { readonly value: Output; readonly issues?: undefined }
|
|
23
24
|
| { readonly issues: ReadonlyArray<{ readonly message: string }> };
|
|
24
25
|
|
|
25
|
-
/** Map of declared
|
|
26
|
+
/** Map of declared boundary error names to their schemas. */
|
|
26
27
|
export type FlowErrorMap = Readonly<Record<string, SchemaInput>>;
|
|
27
28
|
|
|
28
29
|
/**
|
|
@@ -36,22 +37,12 @@ export type FlowHandler<I = unknown, O = unknown> = (
|
|
|
36
37
|
fx: Fx,
|
|
37
38
|
) => O | FlowFailure | Promise<O | FlowFailure>;
|
|
38
39
|
|
|
39
|
-
/**
|
|
40
|
-
export interface FlowOptions<I = unknown, O = unknown
|
|
41
|
-
/** Input schema (Standard Schema when present). */
|
|
42
|
-
readonly in?: SchemaInput;
|
|
43
|
-
/** Output schema (Standard Schema when present). */
|
|
44
|
-
readonly out?: SchemaInput;
|
|
45
|
-
/** Declared typed errors. */
|
|
46
|
-
readonly errors?: E;
|
|
40
|
+
/** Runtime / behavior options for {@link flow} (no invoke contract). */
|
|
41
|
+
export interface FlowOptions<I = unknown, O = unknown> {
|
|
47
42
|
/** Declared effects (capability token). Inferred later by AoT. */
|
|
48
43
|
readonly effects?: Effects;
|
|
49
44
|
/** Journal every effect call. */
|
|
50
45
|
readonly durable?: boolean;
|
|
51
|
-
/**
|
|
52
|
-
* Retry the whole `do` body on thrown errors (same journal session when
|
|
53
|
-
* durable). Prefer {@link Fx.retry} inside {@link Fx.step} for fine control.
|
|
54
|
-
*/
|
|
55
46
|
/**
|
|
56
47
|
* Retry the whole `do` body on thrown errors (same journal session when
|
|
57
48
|
* durable). Prefer {@link Fx.retry} inside {@link Fx.step} for fine control.
|
|
@@ -70,11 +61,6 @@ export interface FlowOptions<I = unknown, O = unknown, E extends FlowErrorMap =
|
|
|
70
61
|
* Cross-plane invocation is a compile error.
|
|
71
62
|
*/
|
|
72
63
|
readonly plane?: FlowPlane;
|
|
73
|
-
/**
|
|
74
|
-
* Acknowledge intentional Manifest contract breaks for this flow
|
|
75
|
-
* (`oke doctor --diff` / CI gate).
|
|
76
|
-
*/
|
|
77
|
-
readonly breaking?: boolean;
|
|
78
64
|
/**
|
|
79
65
|
* Compensation phase after terminal failure on a durable flow.
|
|
80
66
|
* Runs under the same journal session after auto per-step `{ undo }`
|
|
@@ -96,42 +82,32 @@ export interface FlowOptions<I = unknown, O = unknown, E extends FlowErrorMap =
|
|
|
96
82
|
* Default `true` when `gate.auth.tenant` is on.
|
|
97
83
|
*/
|
|
98
84
|
readonly tenantScoped?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Mark this HTTP flow as returning `text/event-stream` via `fx.json.stream`
|
|
87
|
+
* (not signal live). Stamps `$routes.stream` for the typed client.
|
|
88
|
+
*/
|
|
89
|
+
readonly stream?: true;
|
|
99
90
|
/** The behavior. */
|
|
100
91
|
readonly do: FlowHandler<I, O>;
|
|
101
92
|
}
|
|
102
93
|
|
|
103
94
|
/**
|
|
104
|
-
* Infer validated input from a flow options bag.
|
|
105
|
-
*
|
|
106
|
-
* @typeParam Opts - {@link FlowOptions} / call-site object
|
|
107
|
-
*/
|
|
108
|
-
export type InferFlowIn<Opts> = Opts extends { readonly in: infer S }
|
|
109
|
-
? InferSchemaOutput<S>
|
|
110
|
-
: Opts extends { readonly do: FlowHandler<infer I, infer _O> }
|
|
111
|
-
? I
|
|
112
|
-
: unknown;
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Infer success output from a flow options bag.
|
|
95
|
+
* Infer validated input from a flow options bag (handler signature).
|
|
116
96
|
*
|
|
117
97
|
* @typeParam Opts - {@link FlowOptions} / call-site object
|
|
118
98
|
*/
|
|
119
|
-
export type
|
|
120
|
-
?
|
|
121
|
-
:
|
|
122
|
-
? O
|
|
123
|
-
: unknown;
|
|
99
|
+
export type InferFlowIn<Opts> = Opts extends { readonly do: FlowHandler<infer I, infer _O> }
|
|
100
|
+
? I
|
|
101
|
+
: unknown;
|
|
124
102
|
|
|
125
103
|
/**
|
|
126
|
-
* Infer
|
|
104
|
+
* Infer success output from a flow options bag (handler signature).
|
|
127
105
|
*
|
|
128
106
|
* @typeParam Opts - {@link FlowOptions} / call-site object
|
|
129
107
|
*/
|
|
130
|
-
export type
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
? E
|
|
134
|
-
: {};
|
|
108
|
+
export type InferFlowOut<Opts> = Opts extends { readonly do: FlowHandler<infer _I, infer O> }
|
|
109
|
+
? O
|
|
110
|
+
: unknown;
|
|
135
111
|
|
|
136
112
|
/**
|
|
137
113
|
* Phantom brand for the bound trigger type parameter. Optional so
|
|
@@ -142,6 +118,9 @@ declare const triggerPhantom: unique symbol;
|
|
|
142
118
|
/**
|
|
143
119
|
* A Flow definition — one species, trigger-agnostic until {@link on} binds.
|
|
144
120
|
*
|
|
121
|
+
* `in` / `out` / `errors` / `breaking` are **projections** stamped from the
|
|
122
|
+
* exposure (`http.*` / `call` / `mcp.tool` / signal schema), not authored here.
|
|
123
|
+
*
|
|
145
124
|
* @typeParam I - Input type
|
|
146
125
|
* @typeParam O - Output type
|
|
147
126
|
* @typeParam E - Error map (schemas)
|
|
@@ -166,12 +145,12 @@ export interface FlowDef<
|
|
|
166
145
|
readonly name: string;
|
|
167
146
|
/** Unit scope — derived from `name`'s first dot segment (e.g. `"auth.refresh"` → `"auth"`). */
|
|
168
147
|
readonly unit: string | undefined;
|
|
169
|
-
/** Input schema. */
|
|
170
|
-
|
|
171
|
-
/** Output schema. */
|
|
172
|
-
|
|
173
|
-
/** Declared errors. */
|
|
174
|
-
|
|
148
|
+
/** Input schema (stamped from exposure). */
|
|
149
|
+
in: SchemaInput | undefined;
|
|
150
|
+
/** Output schema (stamped from exposure). */
|
|
151
|
+
out: SchemaInput | undefined;
|
|
152
|
+
/** Declared errors (stamped from exposure). */
|
|
153
|
+
errors: E | undefined;
|
|
175
154
|
/** Declared effects. */
|
|
176
155
|
readonly effects: Effects | undefined;
|
|
177
156
|
/** Durability flag. */
|
|
@@ -193,13 +172,17 @@ export interface FlowDef<
|
|
|
193
172
|
readonly slo: Slo | undefined;
|
|
194
173
|
/** Plane (user vs operator). */
|
|
195
174
|
readonly plane: FlowPlane | undefined;
|
|
196
|
-
/** Intentional contract-break acknowledgement
|
|
197
|
-
|
|
175
|
+
/** Intentional contract-break acknowledgement (stamped from exposure). */
|
|
176
|
+
breaking: boolean;
|
|
198
177
|
/**
|
|
199
178
|
* When `false`, this flow is tenant-unaware (no tenant-role scope union).
|
|
200
179
|
* Default `true` when `gate.auth.tenant` is on.
|
|
201
180
|
*/
|
|
202
181
|
readonly tenantScoped: boolean | undefined;
|
|
182
|
+
/**
|
|
183
|
+
* True when this HTTP flow returns SSE via `fx.json.stream` (not live signal).
|
|
184
|
+
*/
|
|
185
|
+
readonly stream: true | undefined;
|
|
203
186
|
/**
|
|
204
187
|
* Optional compensation phase after terminal durable failure.
|
|
205
188
|
* See {@link FlowOptions.compensate}.
|
|
@@ -253,38 +236,53 @@ export interface FlowDef<
|
|
|
253
236
|
export const flowBrand: unique symbol = Symbol("oke.flow");
|
|
254
237
|
|
|
255
238
|
/**
|
|
256
|
-
* Define a Flow —
|
|
257
|
-
*
|
|
258
|
-
* Input / output types are inferred from Standard Schema `in` / `out` when
|
|
259
|
-
* present; otherwise from the `do` handler signature.
|
|
239
|
+
* Define a Flow — behavior only. Author invoke contracts on `http.*` /
|
|
240
|
+
* {@link call} / `mcp.tool`, not here.
|
|
260
241
|
*
|
|
261
242
|
* A nameless `flow({ do })` is stamped `unit.export` by the file-tree
|
|
262
243
|
* generator, {@link unit}, or `.adopt()`. Explicit `flow("notes.get", {…})`
|
|
263
244
|
* still wins.
|
|
264
245
|
*
|
|
265
|
-
* @param options -
|
|
246
|
+
* @param options - Runtime options and handler (name stamped later)
|
|
266
247
|
*/
|
|
267
|
-
export function flow<Opts extends FlowOptions<any, any
|
|
248
|
+
export function flow<Opts extends FlowOptions<any, any>>(
|
|
268
249
|
options: Opts,
|
|
269
|
-
): FlowDef<InferFlowIn<Opts>, InferFlowOut<Opts
|
|
250
|
+
): FlowDef<InferFlowIn<Opts>, InferFlowOut<Opts>>;
|
|
270
251
|
/**
|
|
271
252
|
* @param name - Stable name (used by `fx.call` and the Manifest)
|
|
272
|
-
* @param options -
|
|
253
|
+
* @param options - Runtime options and handler
|
|
273
254
|
*/
|
|
274
|
-
export function flow<Opts extends FlowOptions<any, any
|
|
255
|
+
export function flow<Opts extends FlowOptions<any, any>>(
|
|
275
256
|
name: string,
|
|
276
257
|
options: Opts & { readonly name?: never },
|
|
277
|
-
): FlowDef<InferFlowIn<Opts>, InferFlowOut<Opts
|
|
258
|
+
): FlowDef<InferFlowIn<Opts>, InferFlowOut<Opts>>;
|
|
278
259
|
export function flow(
|
|
279
|
-
nameOrOptions: string | FlowOptions<any, any
|
|
280
|
-
maybeOptions?: FlowOptions<any, any
|
|
260
|
+
nameOrOptions: string | FlowOptions<any, any>,
|
|
261
|
+
maybeOptions?: FlowOptions<any, any> & { readonly name?: never },
|
|
281
262
|
): FlowDef {
|
|
282
263
|
const named = typeof nameOrOptions === "string";
|
|
283
264
|
const name = named ? nameOrOptions : "";
|
|
284
|
-
const options = (named ? maybeOptions : nameOrOptions) as FlowOptions<any, any
|
|
265
|
+
const options = (named ? maybeOptions : nameOrOptions) as FlowOptions<any, any> | undefined;
|
|
285
266
|
if (!options || typeof options.do !== "function") {
|
|
286
267
|
throw new TypeError("flow() expected an options bag with a do handler");
|
|
287
268
|
}
|
|
269
|
+
// Reject legacy contract keys so misuse fails loud at definition time.
|
|
270
|
+
const legacy = options as FlowOptions<any, any> & {
|
|
271
|
+
in?: unknown;
|
|
272
|
+
out?: unknown;
|
|
273
|
+
errors?: unknown;
|
|
274
|
+
breaking?: unknown;
|
|
275
|
+
};
|
|
276
|
+
if (
|
|
277
|
+
legacy.in !== undefined ||
|
|
278
|
+
legacy.out !== undefined ||
|
|
279
|
+
legacy.errors !== undefined ||
|
|
280
|
+
legacy.breaking !== undefined
|
|
281
|
+
) {
|
|
282
|
+
throw new TypeError(
|
|
283
|
+
"flow() no longer accepts in/out/errors/breaking — author the contract on http.*/call/mcp.tool",
|
|
284
|
+
);
|
|
285
|
+
}
|
|
288
286
|
|
|
289
287
|
const triggers: Trigger[] = [];
|
|
290
288
|
const hooks: Partial<Record<HookStage, HookFn[]>> = {};
|
|
@@ -297,9 +295,9 @@ export function flow(
|
|
|
297
295
|
[flowBrand]: true,
|
|
298
296
|
name,
|
|
299
297
|
unit,
|
|
300
|
-
in:
|
|
301
|
-
out:
|
|
302
|
-
errors:
|
|
298
|
+
in: undefined,
|
|
299
|
+
out: undefined,
|
|
300
|
+
errors: undefined,
|
|
303
301
|
effects: options.effects,
|
|
304
302
|
durable: options.durable ?? false,
|
|
305
303
|
retry: options.retry,
|
|
@@ -308,8 +306,9 @@ export function flow(
|
|
|
308
306
|
cache: options.cache,
|
|
309
307
|
slo: options.slo,
|
|
310
308
|
plane: options.plane,
|
|
311
|
-
breaking:
|
|
309
|
+
breaking: false,
|
|
312
310
|
tenantScoped: options.tenantScoped,
|
|
311
|
+
stream: options.stream,
|
|
313
312
|
compensate: options.compensate as FlowDef["compensate"],
|
|
314
313
|
do: options.do as FlowHandler,
|
|
315
314
|
triggers,
|
|
@@ -323,7 +322,6 @@ export function flow(
|
|
|
323
322
|
},
|
|
324
323
|
plug(pluginDef) {
|
|
325
324
|
pendingPlugins.push(pluginDef);
|
|
326
|
-
// Decorations accumulate on the interface; runtime object is unchanged.
|
|
327
325
|
return def as never;
|
|
328
326
|
},
|
|
329
327
|
};
|
|
@@ -8,12 +8,12 @@ import { z } from "zod";
|
|
|
8
8
|
import { ai, createAiRuntime } from "../elements/ai.ts";
|
|
9
9
|
import { collectWideEvent } from "../runs/collect.ts";
|
|
10
10
|
import { createEffectLedger } from "./effects.ts";
|
|
11
|
-
import {
|
|
11
|
+
import { call } from "./call.ts";
|
|
12
12
|
import { createFx } from "./fx.ts";
|
|
13
13
|
import { createRunTelemetry } from "./run-telemetry.ts";
|
|
14
14
|
import { internal } from "./triggers.ts";
|
|
15
15
|
|
|
16
|
-
const triageFlow =
|
|
16
|
+
const triageFlow = call("support.triage", {
|
|
17
17
|
in: z.object({}),
|
|
18
18
|
do: () => ({ ok: true }),
|
|
19
19
|
});
|
|
@@ -6,7 +6,7 @@ import { createFx, createFxContext, signalReadRef } from "./fx.ts";
|
|
|
6
6
|
|
|
7
7
|
describe("fx.deadLetters", () => {
|
|
8
8
|
test("returns dead-lettered messages for a declared signal read", async () => {
|
|
9
|
-
const notify = signal("notify", {
|
|
9
|
+
const notify = signal.once("notify", { retries: 0, deadLetter: true });
|
|
10
10
|
const runtime = openRuntime(notify);
|
|
11
11
|
const bus = await runtime.start();
|
|
12
12
|
await bus.subscribe("notify", "c1", async () => {
|
|
@@ -33,8 +33,8 @@ describe("fx.deadLetters", () => {
|
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
test("undeclared and cross-signal reads throw OKE1001", async () => {
|
|
36
|
-
const notify = signal("notify", {
|
|
37
|
-
const other = signal("other", {
|
|
36
|
+
const notify = signal.once("notify", { retries: 0, deadLetter: true });
|
|
37
|
+
const other = signal.once("other", { retries: 0, deadLetter: true });
|
|
38
38
|
const runtime = openRuntime(notify);
|
|
39
39
|
const fx = createFx({
|
|
40
40
|
flow: "notifications.failed",
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fx.fetch` body — lazy chunk so Store-only / edge graphs that never call
|
|
3
|
+
* outbound HTTP do not pay for host parsing + dry-run stub strings.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { EffectExternal } from "./effects.ts";
|
|
7
|
+
import { isDryRun, recordWouldHaveFired } from "./dry-run.ts";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hostname for `fx.fetch` capability / ledger resource.
|
|
11
|
+
*
|
|
12
|
+
* @param href - Absolute URL string
|
|
13
|
+
*/
|
|
14
|
+
export function hostFromFetchUrl(href: string): string {
|
|
15
|
+
try {
|
|
16
|
+
const host = new URL(href).hostname;
|
|
17
|
+
if (host) return host;
|
|
18
|
+
} catch {
|
|
19
|
+
/* fall through */
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`fx.fetch: invalid URL "${href}" — expected an absolute URL with a hostname`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type Gated = <T>(
|
|
25
|
+
kind: "fetch",
|
|
26
|
+
resource: string,
|
|
27
|
+
body: () => T | Promise<T>,
|
|
28
|
+
externalOf?:
|
|
29
|
+
| EffectExternal
|
|
30
|
+
| ((result: T | undefined, error: unknown) => EffectExternal | undefined),
|
|
31
|
+
) => Promise<T>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Capability-gated outbound HTTP (always `external.kind: "third-party"`).
|
|
35
|
+
*
|
|
36
|
+
* @param gated - Flow `gated` helper
|
|
37
|
+
* @param url - Absolute URL
|
|
38
|
+
* @param init - Fetch init
|
|
39
|
+
*/
|
|
40
|
+
export function runFxFetch(gated: Gated, url: string | URL, init?: RequestInit): Promise<Response> {
|
|
41
|
+
const href = typeof url === "string" ? url : url.href;
|
|
42
|
+
const host = hostFromFetchUrl(href);
|
|
43
|
+
return gated(
|
|
44
|
+
"fetch",
|
|
45
|
+
host,
|
|
46
|
+
async () => {
|
|
47
|
+
if (isDryRun()) {
|
|
48
|
+
recordWouldHaveFired("fetch", host);
|
|
49
|
+
return new Response(null, { status: 204 });
|
|
50
|
+
}
|
|
51
|
+
return globalThis.fetch(url, init);
|
|
52
|
+
},
|
|
53
|
+
{ host, kind: "third-party" },
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -108,13 +108,13 @@ export function createLiveStream(options: CreateLiveStreamOptions): LiveStreamRe
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
|
-
* Map
|
|
111
|
+
* Map OKE1210 to HTTP 410 `{ error: { code: "LiveResumeGap" } }`.
|
|
112
112
|
*
|
|
113
113
|
* @param err - Thrown value
|
|
114
114
|
*/
|
|
115
115
|
export function encodeGap(err: unknown): Response | undefined {
|
|
116
116
|
const o = err as { code?: unknown; params?: { signal?: string; afterId?: string } };
|
|
117
|
-
if (o?.code !==
|
|
117
|
+
if (o?.code !== 1210) return;
|
|
118
118
|
return Response.json(
|
|
119
119
|
{
|
|
120
120
|
data: null,
|
|
@@ -8,14 +8,14 @@ import { createFx, createFxContext, isSseFrame, signalReadRef } from "./fx.ts";
|
|
|
8
8
|
|
|
9
9
|
describe("fx.live", () => {
|
|
10
10
|
test("undeclared signal read throws OKE1001", async () => {
|
|
11
|
-
const orderStatus = signal("order-status", {
|
|
11
|
+
const orderStatus = signal.live("order-status", { optional: true });
|
|
12
12
|
const runtime = openRuntime(orderStatus);
|
|
13
13
|
const fx = createFx({
|
|
14
14
|
flow: "orders.events",
|
|
15
15
|
effects: { reads: [signalReadRef("order-status")] },
|
|
16
16
|
signalRuntime: runtime,
|
|
17
17
|
});
|
|
18
|
-
const other = signal("other-status", {
|
|
18
|
+
const other = signal.live("other-status", { optional: true });
|
|
19
19
|
let err: unknown;
|
|
20
20
|
try {
|
|
21
21
|
const stream = fx.live(other);
|
|
@@ -44,7 +44,7 @@ describe("fx.live", () => {
|
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
test("ALS abort unsubscribes the live handler", async () => {
|
|
47
|
-
const orderStatus = signal("order-status", {
|
|
47
|
+
const orderStatus = signal.live("order-status", { optional: true });
|
|
48
48
|
const runtime = openRuntime(orderStatus);
|
|
49
49
|
const bus = await runtime.start();
|
|
50
50
|
const { fx } = createFxContext({
|
|
@@ -74,7 +74,7 @@ describe("fx.live", () => {
|
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
test("yields branded SSE frames with id + payload", async () => {
|
|
77
|
-
const orderStatus = signal("order-status", {
|
|
77
|
+
const orderStatus = signal.live("order-status", { optional: true });
|
|
78
78
|
const runtime = openRuntime(orderStatus);
|
|
79
79
|
const bus = await runtime.start();
|
|
80
80
|
await runtime.emit("order-status", { orderId: "ord_1", status: "placed" });
|
|
@@ -102,7 +102,7 @@ describe("fx.live", () => {
|
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
test("afterId skips already-delivered events", async () => {
|
|
105
|
-
const orderStatus = signal("order-status", {
|
|
105
|
+
const orderStatus = signal.live("order-status", { optional: true });
|
|
106
106
|
const runtime = openRuntime(orderStatus);
|
|
107
107
|
const bus = await runtime.start();
|
|
108
108
|
await runtime.emit("order-status", { orderId: "ord_1", status: "placed" });
|
|
@@ -130,8 +130,8 @@ describe("fx.live", () => {
|
|
|
130
130
|
await runtime.close();
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
-
test("ready throws
|
|
134
|
-
const orderStatus = signal("order-status", {
|
|
133
|
+
test("ready throws OKE1210 for a missing afterId", async () => {
|
|
134
|
+
const orderStatus = signal.live("order-status", { optional: true });
|
|
135
135
|
const runtime = openRuntime(orderStatus);
|
|
136
136
|
const fx = createFx({
|
|
137
137
|
flow: "orders.events",
|
|
@@ -139,7 +139,7 @@ describe("fx.live", () => {
|
|
|
139
139
|
signalRuntime: runtime,
|
|
140
140
|
});
|
|
141
141
|
const stream = fx.live(orderStatus, { afterId: "missing" });
|
|
142
|
-
await expect(stream.ready?.()).rejects.toMatchObject({ code:
|
|
142
|
+
await expect(stream.ready?.()).rejects.toMatchObject({ code: 1210 });
|
|
143
143
|
const res = await encodeExecuteResult({ output: stream });
|
|
144
144
|
expect(res.status).toBe(410);
|
|
145
145
|
expect(await res.json()).toMatchObject({
|