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,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `call(name, opts)` — call-only Flow sugar with invoke contract on the bag.
|
|
3
|
+
*
|
|
4
|
+
* Same species as `flow` + internal trigger; contracts live here (not on `flow()`).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
stampBoundaryContract,
|
|
9
|
+
type BoundaryContract,
|
|
10
|
+
type InferBoundaryErrors,
|
|
11
|
+
type InferBoundaryIn,
|
|
12
|
+
type InferBoundaryOut,
|
|
13
|
+
} from "./boundary-contract.ts";
|
|
14
|
+
import {
|
|
15
|
+
flow,
|
|
16
|
+
type FlowDef,
|
|
17
|
+
type FlowErrorMap,
|
|
18
|
+
type FlowHandler,
|
|
19
|
+
type FlowOptions,
|
|
20
|
+
} from "./flow.ts";
|
|
21
|
+
import { internal } from "./triggers.ts";
|
|
22
|
+
|
|
23
|
+
/** Options for {@link call} — boundary contract + flow runtime + `do`. */
|
|
24
|
+
export type CallOptions<
|
|
25
|
+
I = unknown,
|
|
26
|
+
O = unknown,
|
|
27
|
+
E extends FlowErrorMap = FlowErrorMap,
|
|
28
|
+
> = BoundaryContract<unknown, unknown, E> &
|
|
29
|
+
Omit<FlowOptions<I, O>, "do"> & {
|
|
30
|
+
readonly do: FlowHandler<I, O>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type InferCallIn<Opts> = Opts extends { readonly in: unknown }
|
|
34
|
+
? InferBoundaryIn<Opts>
|
|
35
|
+
: Opts extends { readonly do: FlowHandler<infer I, infer _O> }
|
|
36
|
+
? I
|
|
37
|
+
: unknown;
|
|
38
|
+
|
|
39
|
+
type InferCallOut<Opts> = Opts extends { readonly out: unknown }
|
|
40
|
+
? InferBoundaryOut<Opts>
|
|
41
|
+
: Opts extends { readonly do: FlowHandler<infer _I, infer O> }
|
|
42
|
+
? O
|
|
43
|
+
: unknown;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Define a call-only Flow with its invoke contract on the same bag.
|
|
47
|
+
*
|
|
48
|
+
* @param name - Stable Manifest / `fx.call` name
|
|
49
|
+
* @param options - Contract + runtime + `do`
|
|
50
|
+
*/
|
|
51
|
+
export function call<Opts extends CallOptions<any, any, any>>(
|
|
52
|
+
name: string,
|
|
53
|
+
options: Opts & { readonly name?: never },
|
|
54
|
+
): FlowDef<InferCallIn<Opts>, InferCallOut<Opts>, InferBoundaryErrors<Opts>> {
|
|
55
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
56
|
+
throw new TypeError("call(name, opts): name is required");
|
|
57
|
+
}
|
|
58
|
+
const {
|
|
59
|
+
in: inSchema,
|
|
60
|
+
out: outSchema,
|
|
61
|
+
errors,
|
|
62
|
+
breaking,
|
|
63
|
+
do: handler,
|
|
64
|
+
...runtime
|
|
65
|
+
} = options as CallOptions<any, any, any>;
|
|
66
|
+
|
|
67
|
+
const def = flow(name, {
|
|
68
|
+
...runtime,
|
|
69
|
+
do: handler as FlowHandler,
|
|
70
|
+
}) as FlowDef<InferCallIn<Opts>, InferCallOut<Opts>, InferBoundaryErrors<Opts>>;
|
|
71
|
+
|
|
72
|
+
const stamped = stampBoundaryContract({
|
|
73
|
+
in: inSchema,
|
|
74
|
+
out: outSchema,
|
|
75
|
+
errors,
|
|
76
|
+
breaking,
|
|
77
|
+
});
|
|
78
|
+
def.in = stamped.in;
|
|
79
|
+
def.out = stamped.out;
|
|
80
|
+
def.errors = stamped.errors as InferBoundaryErrors<Opts> | undefined;
|
|
81
|
+
def.breaking = stamped.breaking;
|
|
82
|
+
|
|
83
|
+
// Mark as internal / call-only exposure (addressable trigger kind).
|
|
84
|
+
const triggers = def.triggers as import("./triggers.ts").Trigger[];
|
|
85
|
+
triggers.push(internal);
|
|
86
|
+
(def as { $trigger: typeof internal }).$trigger = internal;
|
|
87
|
+
|
|
88
|
+
return def;
|
|
89
|
+
}
|
package/src/kernel/capability.ts
CHANGED
|
@@ -17,8 +17,10 @@ const UNDECLARED_KEY: Readonly<Record<EffectKind, keyof typeof OKE_ERRORS>> = {
|
|
|
17
17
|
emit: "UNDECLARED_EMIT",
|
|
18
18
|
send: "UNDECLARED_SEND",
|
|
19
19
|
ask: "UNDECLARED_ASK",
|
|
20
|
+
embed: "UNDECLARED_EMBED",
|
|
20
21
|
secret: "UNDECLARED_SECRET",
|
|
21
22
|
call: "UNDECLARED_CALL",
|
|
23
|
+
fetch: "UNDECLARED_FETCH",
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
/** Maps effect kind → the corresponding `Effects` field. */
|
|
@@ -28,8 +30,10 @@ const EFFECTS_FIELD: Readonly<Record<EffectKind, keyof Effects>> = {
|
|
|
28
30
|
emit: "emits",
|
|
29
31
|
send: "sends",
|
|
30
32
|
ask: "asks",
|
|
33
|
+
embed: "embeds",
|
|
31
34
|
secret: "secrets",
|
|
32
35
|
call: "calls",
|
|
36
|
+
fetch: "fetches",
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
/**
|
|
@@ -83,8 +87,10 @@ export function createCapabilityToken(flow: string, declared?: Effects): Capabil
|
|
|
83
87
|
emit: new Set(effects.emits ?? []),
|
|
84
88
|
send: new Set(effects.sends ?? []),
|
|
85
89
|
ask: new Set(effects.asks ?? []),
|
|
90
|
+
embed: new Set(effects.embeds ?? []),
|
|
86
91
|
secret: new Set(effects.secrets ?? []),
|
|
87
92
|
call: new Set(effects.calls ?? []),
|
|
93
|
+
fetch: new Set(effects.fetches ?? []),
|
|
88
94
|
};
|
|
89
95
|
|
|
90
96
|
return {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build {@link ClientDescriptor} for `GET /_oke/client.json` / `oke client add`.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ClientDescriptor } from "../client/types.ts";
|
|
8
|
+
import type { AnyFlowDef } from "./flow.ts";
|
|
9
|
+
import type { RuntimeRouteMap } from "./adopt-routes.ts";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Build a client descriptor from runtime routes + flow defs (type strings + stamps).
|
|
14
|
+
*
|
|
15
|
+
* @param routes - Runtime `$routes` map
|
|
16
|
+
* @param flowsByName - Flow name → definition (for schemas)
|
|
17
|
+
*/
|
|
18
|
+
export function buildClientDescriptor(
|
|
19
|
+
routes: RuntimeRouteMap,
|
|
20
|
+
flowsByName: ReadonlyMap<string, AnyFlowDef> | Readonly<Record<string, AnyFlowDef>>,
|
|
21
|
+
): ClientDescriptor {
|
|
22
|
+
const lookup =
|
|
23
|
+
flowsByName instanceof Map
|
|
24
|
+
? flowsByName
|
|
25
|
+
: new Map(Object.entries(flowsByName as Record<string, AnyFlowDef>));
|
|
26
|
+
|
|
27
|
+
type RouteEntry = ClientDescriptor["routes"][string][string];
|
|
28
|
+
const out: Record<string, Record<string, RouteEntry>> = {};
|
|
29
|
+
for (const [unit, flows] of Object.entries(routes)) {
|
|
30
|
+
const unitOut: Record<string, RouteEntry> = {};
|
|
31
|
+
for (const [flowName, stamp] of Object.entries(flows)) {
|
|
32
|
+
const full = `${unit}.${flowName}`;
|
|
33
|
+
const def = lookup.get(full) ?? lookup.get(flowName);
|
|
34
|
+
const errors: Record<string, string> = {};
|
|
35
|
+
if (def?.errors) {
|
|
36
|
+
for (const [code, schema] of Object.entries(def.errors)) {
|
|
37
|
+
errors[code] = schemaToTsString(schema);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
unitOut[flowName] = {
|
|
41
|
+
in: schemaToTsString(def?.in),
|
|
42
|
+
out: schemaToTsString(def?.out),
|
|
43
|
+
...(Object.keys(errors).length > 0 ? { errors } : { errors: {} }),
|
|
44
|
+
...(stamp.method !== undefined ? { method: stamp.method } : {}),
|
|
45
|
+
...(stamp.path !== undefined ? { path: stamp.path } : {}),
|
|
46
|
+
...(stamp.live !== undefined ? { live: stamp.live } : {}),
|
|
47
|
+
...(stamp.stream === true ? { stream: true as const } : {}),
|
|
48
|
+
...(stamp.matchKey !== undefined ? { matchKey: stamp.matchKey } : {}),
|
|
49
|
+
...(stamp.gates !== undefined && stamp.gates.length > 0 ? { gates: stamp.gates } : {}),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
out[unit] = unitOut;
|
|
53
|
+
}
|
|
54
|
+
return { routes: out };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Print a compact TypeScript type string from a schema (Zod / JSON Schema).
|
|
59
|
+
* Falls back to `unknown` when unprintable.
|
|
60
|
+
*
|
|
61
|
+
* @param schema - Flow in/out/error schema
|
|
62
|
+
*/
|
|
63
|
+
export function schemaToTsString(schema: unknown): string {
|
|
64
|
+
if (schema === undefined || schema === null) return "unknown";
|
|
65
|
+
try {
|
|
66
|
+
if (hasToJSONSchema(schema)) {
|
|
67
|
+
return jsonSchemaToTs(schema.toJSONSchema());
|
|
68
|
+
}
|
|
69
|
+
// Zod 4 schemas expose `~standard` — convert via z.toJSONSchema when possible.
|
|
70
|
+
const json = z.toJSONSchema(schema as never);
|
|
71
|
+
return jsonSchemaToTs(json);
|
|
72
|
+
} catch {
|
|
73
|
+
if (isObjectSchema(schema)) return jsonSchemaToTs(schema);
|
|
74
|
+
return "unknown";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function hasToJSONSchema(value: unknown): value is { toJSONSchema: () => unknown } {
|
|
79
|
+
return (
|
|
80
|
+
value !== null &&
|
|
81
|
+
typeof value === "object" &&
|
|
82
|
+
"toJSONSchema" in value &&
|
|
83
|
+
typeof (value as { toJSONSchema?: unknown }).toJSONSchema === "function"
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isObjectSchema(value: unknown): value is Record<string, unknown> {
|
|
88
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && "type" in value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function jsonSchemaToTs(schema: unknown): string {
|
|
92
|
+
if (schema === null || typeof schema !== "object") return "unknown";
|
|
93
|
+
const s = schema as Record<string, unknown>;
|
|
94
|
+
if (Array.isArray(s.anyOf) || Array.isArray(s.oneOf)) {
|
|
95
|
+
const parts = ((s.anyOf ?? s.oneOf) as unknown[]).map(jsonSchemaToTs);
|
|
96
|
+
return parts.join(" | ") || "unknown";
|
|
97
|
+
}
|
|
98
|
+
if (s.const !== undefined) return JSON.stringify(s.const);
|
|
99
|
+
if (Array.isArray(s.enum)) {
|
|
100
|
+
return s.enum.map((v) => JSON.stringify(v)).join(" | ") || "unknown";
|
|
101
|
+
}
|
|
102
|
+
const typ = s.type;
|
|
103
|
+
if (typ === "string") return "string";
|
|
104
|
+
if (typ === "number" || typ === "integer") return "number";
|
|
105
|
+
if (typ === "boolean") return "boolean";
|
|
106
|
+
if (typ === "null") return "null";
|
|
107
|
+
if (typ === "array") {
|
|
108
|
+
const items = s.items !== undefined ? jsonSchemaToTs(s.items) : "unknown";
|
|
109
|
+
return `readonly ${items}[]`;
|
|
110
|
+
}
|
|
111
|
+
if (typ === "object" || s.properties) {
|
|
112
|
+
const props = (s.properties ?? {}) as Record<string, unknown>;
|
|
113
|
+
const required = new Set(
|
|
114
|
+
Array.isArray(s.required) ? s.required.filter((k): k is string => typeof k === "string") : [],
|
|
115
|
+
);
|
|
116
|
+
const lines = Object.entries(props).map(([k, v]) => {
|
|
117
|
+
const opt = required.has(k) ? "" : "?";
|
|
118
|
+
return `${JSON.stringify(k)}${opt}: ${jsonSchemaToTs(v)}`;
|
|
119
|
+
});
|
|
120
|
+
return `{ ${lines.join("; ")} }`;
|
|
121
|
+
}
|
|
122
|
+
return "unknown";
|
|
123
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App-wide Clock timezone — `oke({ clock })` / `defineConfig({ clock })`.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
|
|
7
|
+
import { clock, resetClocks } from "../elements/clock/declare.ts";
|
|
8
|
+
import { defineConfig } from "../config/index.ts";
|
|
9
|
+
import { bootApplication } from "./boot.ts";
|
|
10
|
+
import { oke } from "./app.ts";
|
|
11
|
+
import { resetBindings } from "./on.ts";
|
|
12
|
+
|
|
13
|
+
describe("oke({ clock: { timezone } })", () => {
|
|
14
|
+
test("applies to clocks that omit timezone; explicit wins", async () => {
|
|
15
|
+
resetBindings();
|
|
16
|
+
resetClocks();
|
|
17
|
+
|
|
18
|
+
clock.daily("local", { at: "06:00" });
|
|
19
|
+
clock.daily("ny", { at: "09:00", timezone: "America/New_York" });
|
|
20
|
+
|
|
21
|
+
const app = oke({
|
|
22
|
+
name: "tz-oke",
|
|
23
|
+
autoBoot: false,
|
|
24
|
+
startScheduler: false,
|
|
25
|
+
clock: { timezone: "Asia/Riyadh" },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(app.$options.clocks?.find((c) => c.name === "local")?.timezone).toBe("Asia/Riyadh");
|
|
29
|
+
expect(app.$options.clocks?.find((c) => c.name === "ny")?.timezone).toBe("America/New_York");
|
|
30
|
+
|
|
31
|
+
await app.boot({ env: "test" });
|
|
32
|
+
expect(app.bootResult?.clock?.declarations.get("local")?.timezone).toBe("Asia/Riyadh");
|
|
33
|
+
expect(app.bootResult?.clock?.declarations.get("ny")?.timezone).toBe("America/New_York");
|
|
34
|
+
await app.bootResult?.close();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("defineConfig({ clock: { timezone } }) applies at bind when oke omits it", async () => {
|
|
38
|
+
resetBindings();
|
|
39
|
+
resetClocks();
|
|
40
|
+
|
|
41
|
+
const digest = clock.daily("digest", { at: "08:00" });
|
|
42
|
+
expect(digest.timezoneDefaulted).toBe(true);
|
|
43
|
+
|
|
44
|
+
const result = await bootApplication({
|
|
45
|
+
env: "test",
|
|
46
|
+
clocks: [digest],
|
|
47
|
+
config: defineConfig({
|
|
48
|
+
clock: { timezone: "Asia/Riyadh" },
|
|
49
|
+
drivers: { clock: { test: "frozen" } },
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
try {
|
|
53
|
+
expect(result.clock?.declarations.get("digest")?.timezone).toBe("Asia/Riyadh");
|
|
54
|
+
} finally {
|
|
55
|
+
await result.close();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("oke({ clock }) wins over defineConfig", async () => {
|
|
60
|
+
resetBindings();
|
|
61
|
+
resetClocks();
|
|
62
|
+
|
|
63
|
+
clock.daily("digest", { at: "08:00" });
|
|
64
|
+
|
|
65
|
+
const app = oke({
|
|
66
|
+
name: "tz-precedence",
|
|
67
|
+
autoBoot: false,
|
|
68
|
+
startScheduler: false,
|
|
69
|
+
clock: { timezone: "Europe/Berlin" },
|
|
70
|
+
config: defineConfig({
|
|
71
|
+
clock: { timezone: "Asia/Riyadh" },
|
|
72
|
+
}),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(app.$options.clocks?.[0]?.timezone).toBe("Europe/Berlin");
|
|
76
|
+
await app.boot({ env: "test" });
|
|
77
|
+
expect(app.bootResult?.clock?.declarations.get("digest")?.timezone).toBe("Europe/Berlin");
|
|
78
|
+
await app.bootResult?.close();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -13,7 +13,7 @@ import { http } from "./triggers.ts";
|
|
|
13
13
|
describe("correlation — parentId across Flow → Signal → Flow", () => {
|
|
14
14
|
test("consumer WideEvent.parentId equals producer run id", async () => {
|
|
15
15
|
resetBindings();
|
|
16
|
-
const orderPlaced = signal("corr-order-placed"
|
|
16
|
+
const orderPlaced = signal.once("corr-order-placed");
|
|
17
17
|
const runs = createRunsRuntime({ driver: "memory" });
|
|
18
18
|
await runs.open();
|
|
19
19
|
|
package/src/kernel/dry-run.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dry-run effect stubbing + write isolation (console §9.1 · §9.3 · §9.4).
|
|
3
3
|
*
|
|
4
|
-
* - Irreversible effects (`send` / `ask`) are intercepted and
|
|
5
|
-
* "would have fired" — never contact a real channel or
|
|
4
|
+
* - Irreversible effects (`send` / `ask` / `embed` / `fetch`) are intercepted and
|
|
5
|
+
* recorded as "would have fired" — never contact a real channel, model, or URL.
|
|
6
6
|
* - Store writes run against real data for an honest pass/fail verdict, then
|
|
7
7
|
* are always rolled back (snapshot / restore) when the dry-run scope exits.
|
|
8
8
|
* - Drivers that cannot isolate writes throw {@link DryRunWriteIsolationError};
|
|
@@ -14,8 +14,8 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
14
14
|
/** One intercepted irreversible effect during a dry run. */
|
|
15
15
|
export interface DryRunWouldHaveFired {
|
|
16
16
|
/** Irreversible kind. */
|
|
17
|
-
readonly kind: "send" | "ask";
|
|
18
|
-
/** Template / prompt name. */
|
|
17
|
+
readonly kind: "send" | "ask" | "embed" | "fetch";
|
|
18
|
+
/** Template / prompt / model / host name. */
|
|
19
19
|
readonly resource: string;
|
|
20
20
|
/** Optional correlating message id (signal dry-run). */
|
|
21
21
|
readonly messageId?: string;
|
|
@@ -23,7 +23,7 @@ export interface DryRunWouldHaveFired {
|
|
|
23
23
|
|
|
24
24
|
/** Active dry-run bag. */
|
|
25
25
|
export interface DryRunContext {
|
|
26
|
-
/** Intercepted send/ask calls in order. */
|
|
26
|
+
/** Intercepted send/ask/embed calls in order. */
|
|
27
27
|
readonly wouldHaveFired: DryRunWouldHaveFired[];
|
|
28
28
|
/** Optional message id stamped onto subsequent intercepts. */
|
|
29
29
|
messageId?: string;
|
|
@@ -89,12 +89,15 @@ export function setDryRunMessageId(messageId: string): void {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
* Record that a send/ask would have fired. No-op outside dry-run.
|
|
92
|
+
* Record that a send/ask/embed/fetch would have fired. No-op outside dry-run.
|
|
93
93
|
*
|
|
94
94
|
* @param kind - Irreversible kind
|
|
95
|
-
* @param resource - Template / prompt name
|
|
95
|
+
* @param resource - Template / prompt / embed-model / host name
|
|
96
96
|
*/
|
|
97
|
-
export function recordWouldHaveFired(
|
|
97
|
+
export function recordWouldHaveFired(
|
|
98
|
+
kind: "send" | "ask" | "embed" | "fetch",
|
|
99
|
+
resource: string,
|
|
100
|
+
): void {
|
|
98
101
|
const ctx = storage.getStore();
|
|
99
102
|
if (!ctx) return;
|
|
100
103
|
ctx.wouldHaveFired.push({
|
|
@@ -23,6 +23,7 @@ import { join } from "node:path";
|
|
|
23
23
|
import { z } from "zod";
|
|
24
24
|
import { gate } from "../elements/gate.ts";
|
|
25
25
|
import { field, id, now, store } from "../elements/store.ts";
|
|
26
|
+
import { passkey } from "../plugins/passkey.ts";
|
|
26
27
|
import { oke } from "./app.ts";
|
|
27
28
|
import { resetNoEffectsWarnForTests } from "./boot.ts";
|
|
28
29
|
import { flow, resetFlowSeq } from "./flow.ts";
|
|
@@ -47,10 +48,8 @@ const CreateOut = z.object({ ok: z.boolean() });
|
|
|
47
48
|
|
|
48
49
|
function buildUnannotatedCreateFlow(db: ReturnType<typeof store.sql>) {
|
|
49
50
|
return on(
|
|
50
|
-
http.post("/notes").public(),
|
|
51
|
+
http.post("/notes", { in: CreateIn, out: CreateOut }).public(),
|
|
51
52
|
flow("notes.create", {
|
|
52
|
-
in: CreateIn,
|
|
53
|
-
out: CreateOut,
|
|
54
53
|
// Deliberately no `effects` — the "let the compiler infer it" case.
|
|
55
54
|
do: async (input, fx) => {
|
|
56
55
|
await fx.store(db).insert(notes).values({ id: input.id, title: input.title, createdAt: 1 });
|
|
@@ -84,7 +83,7 @@ describe("boot-level: undeclared-effects flow, no manifest / rootDir", () => {
|
|
|
84
83
|
expect(res.status).toBe(200);
|
|
85
84
|
});
|
|
86
85
|
|
|
87
|
-
test("docker: hard fail at boot —
|
|
86
|
+
test("docker: hard fail at boot — OKE1020, never an open token in a deploy-shaped env", async () => {
|
|
88
87
|
resetBindings();
|
|
89
88
|
resetFlowSeq();
|
|
90
89
|
const db = store.sql("app", { schema: { notes } });
|
|
@@ -107,7 +106,7 @@ describe("boot-level: undeclared-effects flow, no manifest / rootDir", () => {
|
|
|
107
106
|
startScheduler: false,
|
|
108
107
|
config: { drivers: memoryDrivers },
|
|
109
108
|
}),
|
|
110
|
-
).rejects.toThrow(/
|
|
109
|
+
).rejects.toThrow(/OKE1020/);
|
|
111
110
|
});
|
|
112
111
|
|
|
113
112
|
test("prod: hard fail at boot — same posture as docker", async () => {
|
|
@@ -130,7 +129,7 @@ describe("boot-level: undeclared-effects flow, no manifest / rootDir", () => {
|
|
|
130
129
|
},
|
|
131
130
|
},
|
|
132
131
|
}),
|
|
133
|
-
).rejects.toThrow(/
|
|
132
|
+
).rejects.toThrow(/OKE1020/);
|
|
134
133
|
});
|
|
135
134
|
});
|
|
136
135
|
|
|
@@ -140,10 +139,8 @@ describe("boot-level: table-ref resolution stays backward compatible", () => {
|
|
|
140
139
|
resetFlowSeq();
|
|
141
140
|
const db = store.sql("app", { schema: { notes } });
|
|
142
141
|
const create = on(
|
|
143
|
-
http.post("/notes").public(),
|
|
142
|
+
http.post("/notes", { in: CreateIn, out: CreateOut }).public(),
|
|
144
143
|
flow("notes.create", {
|
|
145
|
-
in: CreateIn,
|
|
146
|
-
out: CreateOut,
|
|
147
144
|
// The convention every template/test predates Direction B with —
|
|
148
145
|
// store-level, not table-level. Must keep working unchanged.
|
|
149
146
|
effects: { writes: ["sql:app"] },
|
|
@@ -227,3 +224,39 @@ export const create = on(
|
|
|
227
224
|
}
|
|
228
225
|
});
|
|
229
226
|
});
|
|
227
|
+
|
|
228
|
+
describe("boot-level: built-in auth flows under docker", () => {
|
|
229
|
+
test("emailAndPassword + passkey boot with docker:true — empty stamp from Manifest, no hand-declared effects", async () => {
|
|
230
|
+
resetBindings();
|
|
231
|
+
resetFlowSeq();
|
|
232
|
+
const memoryDrivers = {
|
|
233
|
+
store: {
|
|
234
|
+
sql: { dev: "memory", prod: "memory" },
|
|
235
|
+
kv: { dev: "memory", prod: "memory" },
|
|
236
|
+
},
|
|
237
|
+
channel: { email: { dev: "console", prod: "console" } },
|
|
238
|
+
} as const;
|
|
239
|
+
const app = oke({
|
|
240
|
+
name: "auth-stamp-docker",
|
|
241
|
+
registry: "ignore",
|
|
242
|
+
gate: {
|
|
243
|
+
auth: {
|
|
244
|
+
secret: "test-secret-at-least-16",
|
|
245
|
+
emailAndPassword: { enabled: true },
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
}).plug(passkey({ origins: ["http://localhost"] }));
|
|
249
|
+
|
|
250
|
+
// Explicit Manifest (as `oke dev` extract would supply for app Flows).
|
|
251
|
+
// Framework auth Flows are absent → mintCapabilities stamps `{}`.
|
|
252
|
+
await app.boot({
|
|
253
|
+
env: "dev",
|
|
254
|
+
docker: true,
|
|
255
|
+
unguardedHttp: "allow",
|
|
256
|
+
startScheduler: false,
|
|
257
|
+
manifest: { oke: "1.0", app: "auth-stamp-docker", flows: {} },
|
|
258
|
+
config: { drivers: memoryDrivers },
|
|
259
|
+
});
|
|
260
|
+
await app.stop();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
@@ -10,18 +10,20 @@ import {
|
|
|
10
10
|
} from "./effects.ts";
|
|
11
11
|
|
|
12
12
|
describe("effects — reversibility tiers", () => {
|
|
13
|
-
test("all
|
|
13
|
+
test("all effect kinds have the correct reversibility tier", () => {
|
|
14
14
|
const expected: Record<EffectKind, ReversibilityTier> = {
|
|
15
15
|
read: "none",
|
|
16
16
|
write: "reversible",
|
|
17
17
|
emit: "deferred",
|
|
18
18
|
send: "irreversible",
|
|
19
19
|
ask: "irreversible",
|
|
20
|
+
embed: "irreversible",
|
|
20
21
|
secret: "capability",
|
|
21
22
|
call: "portal",
|
|
23
|
+
fetch: "irreversible",
|
|
22
24
|
};
|
|
23
25
|
|
|
24
|
-
expect(EFFECT_KIND_TIERS).toHaveLength(
|
|
26
|
+
expect(EFFECT_KIND_TIERS).toHaveLength(9);
|
|
25
27
|
for (const { kind, reversibility } of EFFECT_KIND_TIERS) {
|
|
26
28
|
expect(reversibility).toBe(expected[kind]);
|
|
27
29
|
expect(reversibilityOf(kind)).toBe(expected[kind]);
|
package/src/kernel/effects.ts
CHANGED
|
@@ -7,16 +7,25 @@
|
|
|
7
7
|
|
|
8
8
|
import { resolveDurationMs } from "./elapsed.ts";
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
export type EffectKind =
|
|
10
|
+
/** Load-bearing effect kinds (manifest `effects` keys, singular). */
|
|
11
|
+
export type EffectKind =
|
|
12
|
+
| "read"
|
|
13
|
+
| "write"
|
|
14
|
+
| "emit"
|
|
15
|
+
| "send"
|
|
16
|
+
| "ask"
|
|
17
|
+
| "embed"
|
|
18
|
+
| "secret"
|
|
19
|
+
| "call"
|
|
20
|
+
| "fetch";
|
|
12
21
|
|
|
13
22
|
/**
|
|
14
|
-
* Reversibility tier — console §9.1 ranking, applied to all
|
|
23
|
+
* Reversibility tier — console §9.1 ranking, applied to all effect kinds.
|
|
15
24
|
*
|
|
16
25
|
* - `none` — reads; no world change
|
|
17
26
|
* - `reversible` — writes; undoable in-transaction
|
|
18
27
|
* - `deferred` — emits; commit with the txn, then fan-out
|
|
19
|
-
* - `irreversible` — sends / asks; cannot be undone by the runtime
|
|
28
|
+
* - `irreversible` — sends / asks / embeds / fetches; cannot be undone by the runtime
|
|
20
29
|
* - `capability` — secrets; authority held, not an effect caused
|
|
21
30
|
* - `portal` — calls; expands to the callee's transitive effects
|
|
22
31
|
*/
|
|
@@ -28,11 +37,22 @@ export type ReversibilityTier =
|
|
|
28
37
|
| "capability"
|
|
29
38
|
| "portal";
|
|
30
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Optional egress identity for an effect that left the process.
|
|
42
|
+
*
|
|
43
|
+
* Driver-reported — never inferred from hostname patterns. Only `host` is
|
|
44
|
+
* required when present; `provider` and `kind` are additive precision.
|
|
45
|
+
*
|
|
46
|
+
* Re-exported from drivers so kernel and driver contracts share one shape.
|
|
47
|
+
*/
|
|
48
|
+
export type { DriverExternal as EffectExternal } from "../drivers/external.ts";
|
|
49
|
+
import type { DriverExternal as EffectExternal } from "../drivers/external.ts";
|
|
50
|
+
|
|
31
51
|
/** One recorded effect call. */
|
|
32
52
|
export interface EffectEntry {
|
|
33
|
-
/** Which
|
|
53
|
+
/** Which effect kind. */
|
|
34
54
|
readonly kind: EffectKind;
|
|
35
|
-
/** Resource / signal / template / prompt / secret / flow ref. */
|
|
55
|
+
/** Resource / signal / template / prompt / embed-model / secret / flow / host ref. */
|
|
36
56
|
readonly resource: string;
|
|
37
57
|
/** Epoch-ms when the call started. */
|
|
38
58
|
readonly timestamp: number;
|
|
@@ -40,6 +60,11 @@ export interface EffectEntry {
|
|
|
40
60
|
readonly duration: number;
|
|
41
61
|
/** Reversibility tier for {@link kind}. */
|
|
42
62
|
readonly reversibility: ReversibilityTier;
|
|
63
|
+
/**
|
|
64
|
+
* When set, this effect left the process and reached an external peer.
|
|
65
|
+
* Omitted for in-process work (PGlite, mock AI, local channel console).
|
|
66
|
+
*/
|
|
67
|
+
readonly external?: EffectExternal;
|
|
43
68
|
}
|
|
44
69
|
|
|
45
70
|
/** Append-only ledger of effect entries for one flow invocation. */
|
|
@@ -63,14 +88,16 @@ const TIER_BY_KIND: Readonly<Record<EffectKind, ReversibilityTier>> = {
|
|
|
63
88
|
emit: "deferred",
|
|
64
89
|
send: "irreversible",
|
|
65
90
|
ask: "irreversible",
|
|
91
|
+
embed: "irreversible",
|
|
66
92
|
secret: "capability",
|
|
67
93
|
call: "portal",
|
|
94
|
+
fetch: "irreversible",
|
|
68
95
|
};
|
|
69
96
|
|
|
70
97
|
/**
|
|
71
98
|
* Reversibility tier for an effect kind.
|
|
72
99
|
*
|
|
73
|
-
* @param kind - One of the
|
|
100
|
+
* @param kind - One of the effect kinds
|
|
74
101
|
*/
|
|
75
102
|
export function reversibilityOf(kind: EffectKind): ReversibilityTier {
|
|
76
103
|
return TIER_BY_KIND[kind];
|
|
@@ -105,6 +132,7 @@ export function createEffectLedger(): EffectLedger {
|
|
|
105
132
|
* @param resource - Resource ref
|
|
106
133
|
* @param now - Clock (injectable for tests)
|
|
107
134
|
* @param body - Work to time
|
|
135
|
+
* @param externalOf - Optional egress metadata (static or derived from result / error)
|
|
108
136
|
*/
|
|
109
137
|
export async function recordEffect<T>(
|
|
110
138
|
ledger: EffectLedger,
|
|
@@ -112,18 +140,29 @@ export async function recordEffect<T>(
|
|
|
112
140
|
resource: string,
|
|
113
141
|
now: () => number,
|
|
114
142
|
body: () => T | Promise<T>,
|
|
143
|
+
externalOf?:
|
|
144
|
+
| EffectExternal
|
|
145
|
+
| ((result: T | undefined, error: unknown) => EffectExternal | undefined),
|
|
115
146
|
): Promise<T> {
|
|
116
147
|
const timestamp = now();
|
|
117
148
|
const t0 = performance.now();
|
|
149
|
+
let result: T | undefined;
|
|
150
|
+
let error: unknown;
|
|
118
151
|
try {
|
|
119
|
-
|
|
152
|
+
result = await body();
|
|
153
|
+
return result;
|
|
154
|
+
} catch (e) {
|
|
155
|
+
error = e;
|
|
156
|
+
throw e;
|
|
120
157
|
} finally {
|
|
158
|
+
const external = typeof externalOf === "function" ? externalOf(result, error) : externalOf;
|
|
121
159
|
recordObservedEffect(
|
|
122
160
|
ledger,
|
|
123
161
|
kind,
|
|
124
162
|
resource,
|
|
125
163
|
timestamp,
|
|
126
164
|
resolveDurationMs(now() - timestamp, performance.now() - t0),
|
|
165
|
+
external,
|
|
127
166
|
);
|
|
128
167
|
}
|
|
129
168
|
}
|
|
@@ -139,6 +178,7 @@ export async function recordEffect<T>(
|
|
|
139
178
|
* @param resource - Resource ref (or kernel cache key)
|
|
140
179
|
* @param timestamp - Epoch-ms when the call started
|
|
141
180
|
* @param duration - Measured duration in milliseconds
|
|
181
|
+
* @param external - Optional egress identity
|
|
142
182
|
*/
|
|
143
183
|
export function recordObservedEffect(
|
|
144
184
|
ledger: EffectLedger,
|
|
@@ -146,6 +186,7 @@ export function recordObservedEffect(
|
|
|
146
186
|
resource: string,
|
|
147
187
|
timestamp: number,
|
|
148
188
|
duration: number,
|
|
189
|
+
external?: EffectExternal,
|
|
149
190
|
): void {
|
|
150
191
|
ledger.record({
|
|
151
192
|
kind,
|
|
@@ -153,11 +194,12 @@ export function recordObservedEffect(
|
|
|
153
194
|
timestamp,
|
|
154
195
|
duration,
|
|
155
196
|
reversibility: reversibilityOf(kind),
|
|
197
|
+
...(external !== undefined ? { external } : {}),
|
|
156
198
|
});
|
|
157
199
|
}
|
|
158
200
|
|
|
159
201
|
/**
|
|
160
|
-
* All
|
|
202
|
+
* All effect kinds with their tiers — useful for exhaustive tests.
|
|
161
203
|
*/
|
|
162
204
|
export const EFFECT_KIND_TIERS: ReadonlyArray<{
|
|
163
205
|
kind: EffectKind;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OKE1605 — kept off the kernel edge profile.
|
|
3
|
+
*
|
|
4
|
+
* Channel `fx.send` throws this when template `data` fails the template schema.
|
|
5
|
+
* Loaded via computed `import.meta.require` from {@link lookupOkeError}; the
|
|
6
|
+
* Channel runtime imports it directly (that graph is not in `budget-entry`).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { OkeErrorDefinition } from "./errors.ts";
|
|
10
|
+
|
|
11
|
+
/** Send payload failed the channel template's declared Standard Schema. */
|
|
12
|
+
export const CHANNEL_SCHEMA: OkeErrorDefinition = {
|
|
13
|
+
code: 1605,
|
|
14
|
+
domain: "channel",
|
|
15
|
+
cause: '"{resource}": {detail}',
|
|
16
|
+
fix: "Fix template data payload or the template schema.",
|
|
17
|
+
};
|