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
package/src/client/types.ts
CHANGED
|
@@ -185,6 +185,59 @@ export type ClientFetch = (input: string | URL | Request, init?: RequestInit) =>
|
|
|
185
185
|
/** Header bag accepted by the client. */
|
|
186
186
|
export type ClientHeaders = Record<string, string> | [string, string][];
|
|
187
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Fetch credentials mode (avoids DOM `RequestCredentials` coupling).
|
|
190
|
+
* Use `"include"` for cookie sessions.
|
|
191
|
+
*/
|
|
192
|
+
export type ClientCredentials = "include" | "omit" | "same-origin";
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Raw request body accepted by the transport (avoids DOM `BodyInit` coupling).
|
|
196
|
+
* Mirrors what {@link createTransport} treats as non-JSON via `instanceof` checks.
|
|
197
|
+
*/
|
|
198
|
+
export type ClientBodyInit =
|
|
199
|
+
| Blob
|
|
200
|
+
| FormData
|
|
201
|
+
| ArrayBuffer
|
|
202
|
+
| ArrayBufferView
|
|
203
|
+
| ReadableStream
|
|
204
|
+
| string
|
|
205
|
+
| URLSearchParams;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Bare transport Bearer hooks (no {@link AuthClient}).
|
|
209
|
+
* Prefer session options (`mode` / `persist` / `csrfConfigured`) so
|
|
210
|
+
* `createClient` attaches `api.auth`.
|
|
211
|
+
*/
|
|
212
|
+
export interface ClientTransportAuth {
|
|
213
|
+
/** Current access token (Bearer), or null. */
|
|
214
|
+
readonly getToken: () => string | null | undefined | Promise<string | null | undefined>;
|
|
215
|
+
/** Obtain a new access token after 401. */
|
|
216
|
+
readonly refresh: () => Promise<string | null | undefined>;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Session orchestration options for `createClient({ auth })` → `api.auth`.
|
|
221
|
+
* Mirrors {@link CreateAuthClientOptions} plus optional SSR token overrides.
|
|
222
|
+
*/
|
|
223
|
+
export interface ClientSessionAuthOptions {
|
|
224
|
+
readonly mode?: "bearer" | "cookie";
|
|
225
|
+
readonly persist?: "memory" | "sessionStorage" | "localStorage";
|
|
226
|
+
readonly storageKey?: string;
|
|
227
|
+
readonly tenantHeader?: string;
|
|
228
|
+
readonly csrfConfigured?: boolean;
|
|
229
|
+
readonly session?: import("./auth/session.ts").MemorySession;
|
|
230
|
+
readonly env?: {
|
|
231
|
+
readonly localStorage?: Storage;
|
|
232
|
+
readonly sessionStorage?: Storage;
|
|
233
|
+
readonly warn?: (message: string) => void;
|
|
234
|
+
};
|
|
235
|
+
/** SSR / custom: override Bearer getToken (cookie mode still uses credentials). */
|
|
236
|
+
readonly getToken?: ClientTransportAuth["getToken"];
|
|
237
|
+
/** SSR / custom: override refresh after 401. */
|
|
238
|
+
readonly refresh?: ClientTransportAuth["refresh"];
|
|
239
|
+
}
|
|
240
|
+
|
|
188
241
|
/**
|
|
189
242
|
* Options for {@link createClient}.
|
|
190
243
|
*/
|
|
@@ -205,15 +258,20 @@ export interface ClientOptions {
|
|
|
205
258
|
readonly backoff?: number;
|
|
206
259
|
};
|
|
207
260
|
/**
|
|
208
|
-
*
|
|
209
|
-
*
|
|
261
|
+
* Session orchestration (`mode` / `csrfConfigured` / …) → attaches `api.auth`,
|
|
262
|
+
* or bare `{ getToken, refresh }` transport hooks only.
|
|
210
263
|
*/
|
|
211
|
-
readonly auth?:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
264
|
+
readonly auth?: ClientTransportAuth | ClientSessionAuthOptions;
|
|
265
|
+
/**
|
|
266
|
+
* Fetch credentials mode. Use `"include"` for cookie sessions
|
|
267
|
+
* (`gate.auth.cookies` + `createClient({ auth: { mode: "cookie" } })`).
|
|
268
|
+
*/
|
|
269
|
+
readonly credentials?: ClientCredentials;
|
|
270
|
+
/**
|
|
271
|
+
* Optional AbortSignal for all calls from this client (per-call override
|
|
272
|
+
* not yet exposed on the proxy).
|
|
273
|
+
*/
|
|
274
|
+
readonly signal?: AbortSignal;
|
|
217
275
|
/**
|
|
218
276
|
* Runtime REST table (optional). Keys are `unit.flow`.
|
|
219
277
|
* When absent, the client uses `POST /_oke/{unit}/{flow}` unless
|
|
@@ -234,6 +292,14 @@ export interface ClientOptions {
|
|
|
234
292
|
*/
|
|
235
293
|
export type ClientThenableIterable<T> = PromiseLike<T> & AsyncIterable<T>;
|
|
236
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Per-call options on a Flow invoke (binary decode / abort).
|
|
297
|
+
*/
|
|
298
|
+
export interface ClientCallOpts {
|
|
299
|
+
readonly response?: "json" | "blob" | "arrayBuffer";
|
|
300
|
+
readonly signal?: AbortSignal;
|
|
301
|
+
}
|
|
302
|
+
|
|
237
303
|
/**
|
|
238
304
|
* Call signature for one flow — thenable (one page) and async-iterable (walk).
|
|
239
305
|
*
|
|
@@ -241,11 +307,25 @@ export type ClientThenableIterable<T> = PromiseLike<T> & AsyncIterable<T>;
|
|
|
241
307
|
* @typeParam O - Output
|
|
242
308
|
* @typeParam E - Errors
|
|
243
309
|
*/
|
|
244
|
-
type
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
310
|
+
type IsAnyInput<T> = 0 extends 1 & T ? true : false;
|
|
311
|
+
|
|
312
|
+
type ClientCallFn<I, O, E extends Record<string, unknown>> =
|
|
313
|
+
IsAnyInput<I> extends true
|
|
314
|
+
? {
|
|
315
|
+
(input?: I, opts?: ClientCallOpts): ClientThenableIterable<ClientResult<O, E>>;
|
|
316
|
+
}
|
|
317
|
+
: [I] extends [void]
|
|
318
|
+
? {
|
|
319
|
+
(): ClientThenableIterable<ClientResult<O, E>>;
|
|
320
|
+
(opts: ClientCallOpts): ClientThenableIterable<ClientResult<O, E>>;
|
|
321
|
+
}
|
|
322
|
+
: Partial<I> extends I
|
|
323
|
+
? {
|
|
324
|
+
(input?: I, opts?: ClientCallOpts): ClientThenableIterable<ClientResult<O, E>>;
|
|
325
|
+
}
|
|
326
|
+
: {
|
|
327
|
+
(input: I, opts?: ClientCallOpts): ClientThenableIterable<ClientResult<O, E>>;
|
|
328
|
+
};
|
|
249
329
|
|
|
250
330
|
/**
|
|
251
331
|
* Call signature for one flow. No `.pages()` — iterate the call or the page.
|
|
@@ -311,6 +391,12 @@ type ClientLiveOverloads = {
|
|
|
311
391
|
|
|
312
392
|
type IsLiveContract<C> = C extends { readonly live: string } ? true : false;
|
|
313
393
|
|
|
394
|
+
type IsStreamOnlyContract<C> = C extends { readonly stream: true }
|
|
395
|
+
? IsLiveContract<C> extends true
|
|
396
|
+
? false
|
|
397
|
+
: true
|
|
398
|
+
: false;
|
|
399
|
+
|
|
314
400
|
type ClientLiveFlowCall<I, O> = [I] extends [void]
|
|
315
401
|
? (handlers: LiveHandlers<O>) => LiveUnsubscribe
|
|
316
402
|
: Partial<I> extends I
|
|
@@ -320,10 +406,18 @@ type ClientLiveFlowCall<I, O> = [I] extends [void]
|
|
|
320
406
|
}
|
|
321
407
|
: (input: I, handlers: LiveHandlers<O>) => LiveUnsubscribe;
|
|
322
408
|
|
|
409
|
+
type ClientStreamCall<I, O> = [I] extends [void]
|
|
410
|
+
? (opts?: { readonly signal?: AbortSignal }) => AsyncIterable<O>
|
|
411
|
+
: Partial<I> extends I
|
|
412
|
+
? (input?: I, opts?: { readonly signal?: AbortSignal }) => AsyncIterable<O>
|
|
413
|
+
: (input: I, opts?: { readonly signal?: AbortSignal }) => AsyncIterable<O>;
|
|
414
|
+
|
|
323
415
|
type ClientCallFor<C> =
|
|
324
416
|
IsLiveContract<C> extends true
|
|
325
417
|
? ClientLiveFlowCall<ContractIn<C>, ContractOut<C>>
|
|
326
|
-
:
|
|
418
|
+
: IsStreamOnlyContract<C> extends true
|
|
419
|
+
? ClientStreamCall<ContractIn<C>, ContractOut<C>>
|
|
420
|
+
: ClientCall<ContractIn<C>, ContractOut<C>, ContractErrors<C>>;
|
|
327
421
|
|
|
328
422
|
/**
|
|
329
423
|
* Pull input from a contract shape (supports required or phantom-optional `in`).
|
|
@@ -366,6 +460,12 @@ export type ClientFromRoutes<R extends ClientRouteMap> = {
|
|
|
366
460
|
*/
|
|
367
461
|
export type Client<App = never> = ClientFromRoutes<RoutesOf<ResolveApp<App>>> & {
|
|
368
462
|
readonly live: ClientLive<ResolveApp<App>>;
|
|
463
|
+
/**
|
|
464
|
+
* Present when `createClient` was constructed with session `auth` options
|
|
465
|
+
* (`mode` / `persist` / `csrfConfigured` / …). Merged over the `auth` unit
|
|
466
|
+
* Flows at runtime (`api.auth.me` still works).
|
|
467
|
+
*/
|
|
468
|
+
readonly auth?: import("./auth/create-auth-client.ts").AuthClient;
|
|
369
469
|
};
|
|
370
470
|
|
|
371
471
|
/**
|
|
@@ -388,6 +488,10 @@ export interface ClientDescriptor {
|
|
|
388
488
|
readonly errors?: Readonly<Record<string, string>>;
|
|
389
489
|
readonly method?: string;
|
|
390
490
|
readonly path?: string;
|
|
491
|
+
readonly live?: string;
|
|
492
|
+
readonly stream?: true;
|
|
493
|
+
readonly matchKey?: readonly string[];
|
|
494
|
+
readonly gates?: readonly string[];
|
|
391
495
|
};
|
|
392
496
|
};
|
|
393
497
|
};
|
|
@@ -18,11 +18,23 @@ export {
|
|
|
18
18
|
type UseLiveQueryState,
|
|
19
19
|
} from "./use-live-query.ts";
|
|
20
20
|
|
|
21
|
-
import {
|
|
22
|
-
|
|
21
|
+
import {
|
|
22
|
+
useCallback,
|
|
23
|
+
useEffect,
|
|
24
|
+
useMemo,
|
|
25
|
+
useState,
|
|
26
|
+
useSyncExternalStore,
|
|
27
|
+
type ReactNode,
|
|
28
|
+
} from "react";
|
|
29
|
+
import type {
|
|
30
|
+
AuthClient,
|
|
31
|
+
AuthorizeQuery,
|
|
32
|
+
AuthorizeResult,
|
|
33
|
+
} from "../client/auth/create-auth-client.ts";
|
|
34
|
+
import type { MemorySession, SessionUser } from "../client/auth/session.ts";
|
|
23
35
|
import type { ClientLive, LiveInput, LiveSignalHandle } from "../client/types.ts";
|
|
24
36
|
|
|
25
|
-
/** Minimal client surface for session reading. */
|
|
37
|
+
/** Minimal client surface for session reading (legacy). */
|
|
26
38
|
export interface SessionClient {
|
|
27
39
|
auth: {
|
|
28
40
|
me: (input?: unknown) => Promise<{
|
|
@@ -31,6 +43,10 @@ export interface SessionClient {
|
|
|
31
43
|
email: string;
|
|
32
44
|
name: string;
|
|
33
45
|
emailVerified?: boolean;
|
|
46
|
+
scopes?: readonly string[];
|
|
47
|
+
tenantId?: string | null;
|
|
48
|
+
apiKeyId?: string | null;
|
|
49
|
+
sessionFresh?: boolean;
|
|
34
50
|
} | null;
|
|
35
51
|
error: unknown;
|
|
36
52
|
}>;
|
|
@@ -40,38 +56,57 @@ export interface SessionClient {
|
|
|
40
56
|
/** Session snapshot returned by {@link useSession}. */
|
|
41
57
|
export interface SessionState {
|
|
42
58
|
readonly status: "loading" | "authenticated" | "unauthenticated";
|
|
43
|
-
readonly user:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
readonly name: string;
|
|
47
|
-
readonly emailVerified?: boolean;
|
|
48
|
-
} | null;
|
|
59
|
+
readonly user: SessionUser | null;
|
|
60
|
+
/** UI-only scopes from last `auth.me` — Gate on Flows is real authz. */
|
|
61
|
+
readonly scopes: readonly string[];
|
|
49
62
|
readonly accessToken: string | null;
|
|
50
63
|
refresh(): Promise<void>;
|
|
51
|
-
signOut(): void
|
|
64
|
+
signOut(): void | Promise<void>;
|
|
65
|
+
/** UI-only — never a security boundary. */
|
|
66
|
+
hasScope(scope: string): boolean;
|
|
67
|
+
can(...scopes: string[]): boolean;
|
|
52
68
|
}
|
|
53
69
|
|
|
54
70
|
/**
|
|
55
|
-
* React hook
|
|
71
|
+
* React hook over {@link AuthClient} (preferred) or legacy `api` + `memorySession`.
|
|
56
72
|
*
|
|
57
|
-
* @param
|
|
58
|
-
* @param session - Optional memory session
|
|
73
|
+
* @param apiOrAuth - AuthClient or typed client with `auth.me`
|
|
74
|
+
* @param session - Optional memory session when using raw client
|
|
59
75
|
*/
|
|
60
|
-
export function useSession(
|
|
76
|
+
export function useSession(
|
|
77
|
+
apiOrAuth: AuthClient | SessionClient,
|
|
78
|
+
session?: MemorySession,
|
|
79
|
+
): SessionState {
|
|
80
|
+
const authClient = isAuthClient(apiOrAuth) ? apiOrAuth : null;
|
|
81
|
+
const api = authClient ? null : (apiOrAuth as SessionClient);
|
|
82
|
+
const bag = authClient?.session ?? session;
|
|
83
|
+
|
|
61
84
|
const token = useSyncExternalStore(
|
|
62
85
|
(onStoreChange) => {
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
86
|
+
if (authClient) return authClient.subscribe(onStoreChange);
|
|
87
|
+
if (!bag?.subscribe) {
|
|
88
|
+
const id = setInterval(onStoreChange, 250);
|
|
89
|
+
return () => clearInterval(id);
|
|
90
|
+
}
|
|
91
|
+
return bag.subscribe(onStoreChange);
|
|
66
92
|
},
|
|
67
|
-
() =>
|
|
68
|
-
() =>
|
|
93
|
+
() => bag?.accessToken ?? null,
|
|
94
|
+
() => bag?.accessToken ?? null,
|
|
69
95
|
);
|
|
70
96
|
|
|
71
|
-
const [user, setUser] = useState<
|
|
97
|
+
const [user, setUser] = useState<SessionUser | null>(authClient?.session.user ?? null);
|
|
72
98
|
const [status, setStatus] = useState<SessionState["status"]>("loading");
|
|
73
99
|
|
|
74
100
|
const refresh = useCallback(async () => {
|
|
101
|
+
if (authClient) {
|
|
102
|
+
setStatus("loading");
|
|
103
|
+
const next = await authClient.getSession();
|
|
104
|
+
setUser(next);
|
|
105
|
+
setStatus(next ? "authenticated" : "unauthenticated");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!api) return;
|
|
109
|
+
// Legacy SessionClient path — cookie sessions always use AuthClient above.
|
|
75
110
|
if (!token) {
|
|
76
111
|
setUser(null);
|
|
77
112
|
setStatus("unauthenticated");
|
|
@@ -84,29 +119,155 @@ export function useSession(api: SessionClient, session?: MemorySession): Session
|
|
|
84
119
|
setStatus("unauthenticated");
|
|
85
120
|
return;
|
|
86
121
|
}
|
|
87
|
-
|
|
122
|
+
const next: SessionUser = {
|
|
123
|
+
userId: data.userId,
|
|
124
|
+
email: data.email,
|
|
125
|
+
name: data.name,
|
|
126
|
+
emailVerified: data.emailVerified,
|
|
127
|
+
scopes: data.scopes ?? [],
|
|
128
|
+
tenantId: data.tenantId ?? null,
|
|
129
|
+
apiKeyId: data.apiKeyId ?? null,
|
|
130
|
+
...(data.sessionFresh !== undefined ? { sessionFresh: data.sessionFresh } : {}),
|
|
131
|
+
};
|
|
132
|
+
bag?.setUser(next);
|
|
133
|
+
setUser(next);
|
|
88
134
|
setStatus("authenticated");
|
|
89
|
-
}, [api, token]);
|
|
135
|
+
}, [api, authClient, bag, token]);
|
|
90
136
|
|
|
91
137
|
useEffect(() => {
|
|
92
138
|
void refresh();
|
|
93
139
|
}, [refresh]);
|
|
94
140
|
|
|
95
141
|
const signOut = useCallback(() => {
|
|
96
|
-
|
|
142
|
+
if (authClient) return authClient.signOut();
|
|
143
|
+
bag?.clear();
|
|
97
144
|
setUser(null);
|
|
98
145
|
setStatus("unauthenticated");
|
|
99
|
-
}, [
|
|
146
|
+
}, [authClient, bag]);
|
|
147
|
+
|
|
148
|
+
const scopes = user?.scopes ?? bag?.scopes ?? [];
|
|
100
149
|
|
|
101
150
|
return {
|
|
102
151
|
status,
|
|
103
152
|
user,
|
|
153
|
+
scopes,
|
|
104
154
|
accessToken: token,
|
|
105
155
|
refresh,
|
|
106
156
|
signOut,
|
|
157
|
+
hasScope: (scope: string) => scopes.includes(scope),
|
|
158
|
+
can: (...needed: string[]) => {
|
|
159
|
+
const have = new Set(scopes);
|
|
160
|
+
return needed.every((s) => have.has(s));
|
|
161
|
+
},
|
|
107
162
|
};
|
|
108
163
|
}
|
|
109
164
|
|
|
165
|
+
function isAuthClient(value: unknown): value is AuthClient {
|
|
166
|
+
return (
|
|
167
|
+
value !== null &&
|
|
168
|
+
typeof value === "object" &&
|
|
169
|
+
"signIn" in value &&
|
|
170
|
+
"getSession" in value &&
|
|
171
|
+
"clientOptions" in value
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* UI-only scope check hook.
|
|
177
|
+
*
|
|
178
|
+
* @param auth - AuthClient or session state scopes
|
|
179
|
+
* @param scope - Required scope
|
|
180
|
+
*/
|
|
181
|
+
export function useScope(auth: AuthClient | SessionState, scope: string): boolean {
|
|
182
|
+
const scopes = useMemo(() => {
|
|
183
|
+
if (
|
|
184
|
+
"hasScope" in auth &&
|
|
185
|
+
typeof (auth as AuthClient).hasScope === "function" &&
|
|
186
|
+
"signIn" in auth
|
|
187
|
+
) {
|
|
188
|
+
return (auth as AuthClient).session.user?.scopes ?? (auth as AuthClient).session.scopes;
|
|
189
|
+
}
|
|
190
|
+
return (auth as SessionState).scopes;
|
|
191
|
+
}, [auth]);
|
|
192
|
+
return scopes.includes(scope);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* UI-only multi-scope check.
|
|
197
|
+
*
|
|
198
|
+
* @param auth - AuthClient or session state
|
|
199
|
+
* @param scopes - Required scopes (all)
|
|
200
|
+
*/
|
|
201
|
+
export function useCan(auth: AuthClient | SessionState, ...scopes: string[]): boolean {
|
|
202
|
+
const have = useMemo(() => {
|
|
203
|
+
if ("can" in auth && "signIn" in auth) {
|
|
204
|
+
return new Set(
|
|
205
|
+
(auth as AuthClient).session.user?.scopes ?? (auth as AuthClient).session.scopes,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
return new Set((auth as SessionState).scopes);
|
|
209
|
+
}, [auth]);
|
|
210
|
+
return scopes.every((s) => have.has(s));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* UI-only authorize hook — prefers {@link AuthClient.authorize}.
|
|
215
|
+
* Gate on Flows remains the security boundary.
|
|
216
|
+
*
|
|
217
|
+
* @param auth - AuthClient
|
|
218
|
+
* @param query - all / any scopes
|
|
219
|
+
*/
|
|
220
|
+
export function useAuthorize(auth: AuthClient, query: AuthorizeQuery): AuthorizeResult {
|
|
221
|
+
useSyncExternalStore(
|
|
222
|
+
(onStoreChange) => auth.subscribe(onStoreChange),
|
|
223
|
+
() => auth.session.user,
|
|
224
|
+
() => auth.session.user,
|
|
225
|
+
);
|
|
226
|
+
// Re-read on every render after subscribe notifies.
|
|
227
|
+
return auth.authorize(query);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Props for {@link Can} / {@link Cannot}. */
|
|
231
|
+
export interface CanProps {
|
|
232
|
+
readonly auth: AuthClient;
|
|
233
|
+
readonly all?: readonly string[];
|
|
234
|
+
readonly any?: readonly string[];
|
|
235
|
+
readonly children?: ReactNode;
|
|
236
|
+
readonly fallback?: ReactNode;
|
|
237
|
+
/** Rendered while authorize status is `loading` (default null). */
|
|
238
|
+
readonly loading?: ReactNode;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function authorizeQueryOf(props: CanProps): AuthorizeQuery {
|
|
242
|
+
if (props.all) return { all: props.all };
|
|
243
|
+
if (props.any) return { any: props.any };
|
|
244
|
+
return { all: [] };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* UI-only — render children when authorize status is `allowed`.
|
|
249
|
+
* Not a security boundary.
|
|
250
|
+
*/
|
|
251
|
+
export function Can(props: CanProps): ReactNode {
|
|
252
|
+
const query = authorizeQueryOf(props);
|
|
253
|
+
const result = useAuthorize(props.auth, query);
|
|
254
|
+
if (result.status === "loading") return props.loading ?? null;
|
|
255
|
+
if (result.status === "allowed") return props.children ?? null;
|
|
256
|
+
return props.fallback ?? null;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* UI-only — render children when authorize status is not `allowed`
|
|
261
|
+
* (denied / unauthenticated). Loading uses `loading` prop.
|
|
262
|
+
*/
|
|
263
|
+
export function Cannot(props: CanProps): ReactNode {
|
|
264
|
+
const query = authorizeQueryOf(props);
|
|
265
|
+
const result = useAuthorize(props.auth, query);
|
|
266
|
+
if (result.status === "loading") return props.loading ?? null;
|
|
267
|
+
if (result.status === "allowed") return props.fallback ?? null;
|
|
268
|
+
return props.children ?? null;
|
|
269
|
+
}
|
|
270
|
+
|
|
110
271
|
/** Options for {@link useLive}. */
|
|
111
272
|
export interface UseLiveOptions {
|
|
112
273
|
readonly autoResubscribe?: boolean;
|
|
@@ -123,17 +123,27 @@ export interface UseLiveQueryState<Row> {
|
|
|
123
123
|
* @param args.api - Typed client from `createClient`
|
|
124
124
|
* @param args.listFlow - The resource's list call (`api.tasks.list`)
|
|
125
125
|
* @param args.query - Same input as the list Flow (filters)
|
|
126
|
-
* @param args.live - SSE route from `$routes`
|
|
126
|
+
* @param args.live - SSE route from `$routes` (optional when `listPath` given)
|
|
127
|
+
* @param args.listPath - List REST path; live defaults to `` `${listPath}/live` ``
|
|
127
128
|
* @param args.options - PK/version extractors, refresh key, `enabled`
|
|
128
129
|
*/
|
|
129
130
|
export function useLiveQuery<Row extends Record<string, unknown>, I = void>(args: {
|
|
130
131
|
readonly api: object;
|
|
131
132
|
readonly listFlow: ClientCall<I, Row[], Record<string, never>>;
|
|
132
133
|
readonly query?: I;
|
|
133
|
-
readonly live
|
|
134
|
+
readonly live?: LiveRouteContract;
|
|
135
|
+
/** List HTTP path — when `live` is omitted, SSE uses `GET ${listPath}/live`. */
|
|
136
|
+
readonly listPath?: string;
|
|
134
137
|
readonly options?: UseLiveQueryOptions<Row>;
|
|
135
138
|
}): UseLiveQueryState<Row> {
|
|
136
|
-
const { api, listFlow, query,
|
|
139
|
+
const { api, listFlow, query, options } = args;
|
|
140
|
+
const live: LiveRouteContract = args.live ?? {
|
|
141
|
+
method: "GET",
|
|
142
|
+
path: `${(args.listPath ?? "").replace(/\/$/, "")}/live`,
|
|
143
|
+
};
|
|
144
|
+
if (!args.live && !args.listPath) {
|
|
145
|
+
throw new Error("useLiveQuery requires `live` or `listPath` to derive the SSE route");
|
|
146
|
+
}
|
|
137
147
|
const opts = options ?? {};
|
|
138
148
|
const enabled = opts.enabled ?? true;
|
|
139
149
|
const defaultIdOf = useCallback((row: Row) => String((row as Record<string, unknown>).id), []);
|
package/src/compiler/aot.test.ts
CHANGED
|
@@ -129,11 +129,14 @@ describe("typed error narrowing end-to-end", () => {
|
|
|
129
129
|
const FlightFull = z.object({ seatsLeft: z.number() });
|
|
130
130
|
|
|
131
131
|
on(
|
|
132
|
-
http
|
|
132
|
+
http
|
|
133
|
+
.post("/bookings", {
|
|
134
|
+
in: Booking,
|
|
135
|
+
out: z.object({ id: z.string() }),
|
|
136
|
+
errors: { FlightFull },
|
|
137
|
+
})
|
|
138
|
+
.public(),
|
|
133
139
|
flow("bookings.create", {
|
|
134
|
-
in: Booking,
|
|
135
|
-
out: z.object({ id: z.string() }),
|
|
136
|
-
errors: { FlightFull },
|
|
137
140
|
do: (input: { flightId: string; seats: number }, fx) => {
|
|
138
141
|
if (input.seats > 2) {
|
|
139
142
|
return fx.fail("FlightFull", { seatsLeft: 2 });
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fx.embed is a distinct effect kind from fx.ask — capability / Manifest honesty.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { parseSync } from "oxc-parser";
|
|
7
|
+
import { inferEffects, type AstNode, type InferBinding } from "./effects-infer.ts";
|
|
8
|
+
|
|
9
|
+
function doNodeFrom(source: string): AstNode {
|
|
10
|
+
const wrapped = `const __fn = ${source}`;
|
|
11
|
+
const result = parseSync("t.ts", wrapped);
|
|
12
|
+
const program = result.program as unknown as AstNode & { body: AstNode[] };
|
|
13
|
+
const decl = program.body[0] as AstNode & {
|
|
14
|
+
declarations: Array<{ init: AstNode }>;
|
|
15
|
+
};
|
|
16
|
+
return decl.declarations[0]!.init;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("inferEffects — fx.embed ≠ fx.ask", () => {
|
|
20
|
+
test("fx.embed alone → effects.embeds, never asks", () => {
|
|
21
|
+
const doNode = doNodeFrom(`async (_input, fx) => {
|
|
22
|
+
return fx.embed("embedder", "hello");
|
|
23
|
+
}`);
|
|
24
|
+
const bindings = new Map<string, InferBinding>([
|
|
25
|
+
["embedder", { kind: "unknown", ref: "embedder" }],
|
|
26
|
+
]);
|
|
27
|
+
const inferred = inferEffects({ doNode, bindings, hasExplicitEffects: false });
|
|
28
|
+
expect(inferred.effects.embeds).toEqual(["embedder"]);
|
|
29
|
+
expect(inferred.effects.asks).toBeUndefined();
|
|
30
|
+
expect(inferred.nondeterministic).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("fx.ask alone → effects.asks, never embeds", () => {
|
|
34
|
+
const doNode = doNodeFrom(`async (_input, fx) => {
|
|
35
|
+
return fx.ask("triage", { q: "x" });
|
|
36
|
+
}`);
|
|
37
|
+
const bindings = new Map<string, InferBinding>([["triage", { kind: "prompt", ref: "triage" }]]);
|
|
38
|
+
const inferred = inferEffects({ doNode, bindings, hasExplicitEffects: false });
|
|
39
|
+
expect(inferred.effects.asks).toEqual(["triage"]);
|
|
40
|
+
expect(inferred.effects.embeds).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("both ask and embed keep separate bags", () => {
|
|
44
|
+
const doNode = doNodeFrom(`async (_input, fx) => {
|
|
45
|
+
await fx.embed("embedder", "t");
|
|
46
|
+
return fx.ask("rerank", { query: "q", docs: [] });
|
|
47
|
+
}`);
|
|
48
|
+
const inferred = inferEffects({
|
|
49
|
+
doNode,
|
|
50
|
+
bindings: new Map(),
|
|
51
|
+
hasExplicitEffects: false,
|
|
52
|
+
});
|
|
53
|
+
expect(inferred.effects.embeds).toEqual(["embedder"]);
|
|
54
|
+
expect(inferred.effects.asks).toEqual(["rerank"]);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fx.fetch is a distinct effect kind — capability / Manifest honesty.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { inferEffects, type AstNode, type InferBinding } from "./effects-infer.ts";
|
|
7
|
+
|
|
8
|
+
function parseDo(src: string): AstNode {
|
|
9
|
+
// Minimal CallExpression-shaped tree for fx.fetch("https://…")
|
|
10
|
+
const match = /fx\.fetch\(\s*"([^"]+)"/.exec(src);
|
|
11
|
+
if (!match) throw new Error(`no fx.fetch in ${src}`);
|
|
12
|
+
return {
|
|
13
|
+
type: "BlockStatement",
|
|
14
|
+
body: [
|
|
15
|
+
{
|
|
16
|
+
type: "ExpressionStatement",
|
|
17
|
+
expression: {
|
|
18
|
+
type: "CallExpression",
|
|
19
|
+
callee: {
|
|
20
|
+
type: "MemberExpression",
|
|
21
|
+
object: { type: "Identifier", name: "fx" },
|
|
22
|
+
property: { type: "Identifier", name: "fetch" },
|
|
23
|
+
computed: false,
|
|
24
|
+
},
|
|
25
|
+
arguments: [{ type: "Literal", value: match[1] }],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
} as AstNode;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("inferEffects — fx.fetch → effects.fetches", () => {
|
|
33
|
+
test("fx.fetch URL literal → host in fetches", () => {
|
|
34
|
+
const inferred = inferEffects({
|
|
35
|
+
doNode: parseDo(`async (fx) => { await fx.fetch("https://api.stripe.com/v1"); }`),
|
|
36
|
+
bindings: new Map<string, InferBinding>(),
|
|
37
|
+
hasExplicitEffects: false,
|
|
38
|
+
});
|
|
39
|
+
expect(inferred.effects.fetches).toEqual(["api.stripe.com"]);
|
|
40
|
+
expect(inferred.effects.calls).toBeUndefined();
|
|
41
|
+
expect(inferred.effects.sends).toBeUndefined();
|
|
42
|
+
});
|
|
43
|
+
});
|