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/auth.ts
CHANGED
|
@@ -1,120 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Optional auth helpers for {@link createClient} — not a second client factory.
|
|
3
3
|
*
|
|
4
|
+
* Prefer `createClient(url, { auth: { mode, … } })` so `api.auth` is attached.
|
|
5
|
+
* {@link createAuthClient} remains the compose / bind escape hatch.
|
|
6
|
+
*
|
|
4
7
|
* @module
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
} | null;
|
|
44
|
-
error: unknown;
|
|
45
|
-
}>;
|
|
46
|
-
};
|
|
47
|
-
}): Promise<string | null>;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Create an in-memory session helper for Bearer `createClient` wiring.
|
|
52
|
-
*/
|
|
53
|
-
export function memorySession(): MemorySession {
|
|
54
|
-
const state: {
|
|
55
|
-
accessToken: string | null;
|
|
56
|
-
refreshToken: string | null;
|
|
57
|
-
accessExpiresAt: number | null;
|
|
58
|
-
userId: string | null;
|
|
59
|
-
} = {
|
|
60
|
-
accessToken: null,
|
|
61
|
-
refreshToken: null,
|
|
62
|
-
accessExpiresAt: null,
|
|
63
|
-
userId: null,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
get accessToken() {
|
|
68
|
-
return state.accessToken;
|
|
69
|
-
},
|
|
70
|
-
set accessToken(v) {
|
|
71
|
-
state.accessToken = v;
|
|
72
|
-
},
|
|
73
|
-
get refreshToken() {
|
|
74
|
-
return state.refreshToken;
|
|
75
|
-
},
|
|
76
|
-
set refreshToken(v) {
|
|
77
|
-
state.refreshToken = v;
|
|
78
|
-
},
|
|
79
|
-
get accessExpiresAt() {
|
|
80
|
-
return state.accessExpiresAt;
|
|
81
|
-
},
|
|
82
|
-
set accessExpiresAt(v) {
|
|
83
|
-
state.accessExpiresAt = v;
|
|
84
|
-
},
|
|
85
|
-
get userId() {
|
|
86
|
-
return state.userId;
|
|
87
|
-
},
|
|
88
|
-
set userId(v) {
|
|
89
|
-
state.userId = v;
|
|
90
|
-
},
|
|
91
|
-
set(tokens) {
|
|
92
|
-
state.accessToken = tokens.accessToken;
|
|
93
|
-
state.refreshToken = tokens.refreshToken;
|
|
94
|
-
state.accessExpiresAt = tokens.accessExpiresAt ?? null;
|
|
95
|
-
state.userId = tokens.userId ?? null;
|
|
96
|
-
},
|
|
97
|
-
clear() {
|
|
98
|
-
state.accessToken = null;
|
|
99
|
-
state.refreshToken = null;
|
|
100
|
-
state.accessExpiresAt = null;
|
|
101
|
-
state.userId = null;
|
|
102
|
-
},
|
|
103
|
-
getToken() {
|
|
104
|
-
return state.accessToken;
|
|
105
|
-
},
|
|
106
|
-
async refresh(api) {
|
|
107
|
-
if (!state.refreshToken) return null;
|
|
108
|
-
const { data, error } = await api.auth.refresh({ refreshToken: state.refreshToken });
|
|
109
|
-
if (error || !data) {
|
|
110
|
-
state.accessToken = null;
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
state.accessToken = data.accessToken;
|
|
114
|
-
state.refreshToken = data.refreshToken;
|
|
115
|
-
state.accessExpiresAt = data.accessExpiresAt ?? null;
|
|
116
|
-
if (data.userId) state.userId = data.userId;
|
|
117
|
-
return data.accessToken;
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
}
|
|
10
|
+
export {
|
|
11
|
+
AUTH_ERROR_CODES,
|
|
12
|
+
forbiddenScopes,
|
|
13
|
+
isAuthRateLimited,
|
|
14
|
+
isCsrf,
|
|
15
|
+
isForbidden,
|
|
16
|
+
isSessionTokens,
|
|
17
|
+
isTwoFactorRequired,
|
|
18
|
+
isUnauthorized,
|
|
19
|
+
} from "./auth/denials.ts";
|
|
20
|
+
export {
|
|
21
|
+
createAuthClient,
|
|
22
|
+
type AuthApi,
|
|
23
|
+
type AuthClient,
|
|
24
|
+
type AuthMode,
|
|
25
|
+
type AuthPersist,
|
|
26
|
+
type AuthorizeQuery,
|
|
27
|
+
type AuthorizeResult,
|
|
28
|
+
type CreateAuthClientOptions,
|
|
29
|
+
type SignInResult,
|
|
30
|
+
} from "./auth/create-auth-client.ts";
|
|
31
|
+
export {
|
|
32
|
+
tokenFromRequestCookies,
|
|
33
|
+
type CookieSource,
|
|
34
|
+
type TokenFromCookiesOptions,
|
|
35
|
+
} from "./auth/cookies.ts";
|
|
36
|
+
export { createServerClient } from "./create-with-session.ts";
|
|
37
|
+
export {
|
|
38
|
+
memorySession,
|
|
39
|
+
persistSession,
|
|
40
|
+
type MemorySession,
|
|
41
|
+
type MemorySessionOptions,
|
|
42
|
+
type SessionListener,
|
|
43
|
+
type SessionTokens,
|
|
44
|
+
type SessionUser,
|
|
45
|
+
} from "./auth/session.ts";
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createClient` with optional session `auth` → `api.auth` ({@link AuthClient}).
|
|
3
|
+
*
|
|
4
|
+
* Bare transport stays in {@link ./create.ts} (budget entry). This wrapper is
|
|
5
|
+
* what `okengine/client` exports.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
createAuthClient,
|
|
12
|
+
type AuthApi,
|
|
13
|
+
type AuthClient,
|
|
14
|
+
type CreateAuthClientOptions,
|
|
15
|
+
} from "./auth/create-auth-client.ts";
|
|
16
|
+
import { tokenFromRequestCookies, type CookieSource } from "./auth/cookies.ts";
|
|
17
|
+
import {
|
|
18
|
+
createClient as createClientCore,
|
|
19
|
+
flattenRoutes,
|
|
20
|
+
transportOf,
|
|
21
|
+
type AppWithRoutes,
|
|
22
|
+
type TransportBag,
|
|
23
|
+
} from "./create.ts";
|
|
24
|
+
import type {
|
|
25
|
+
Client,
|
|
26
|
+
ClientHeaders,
|
|
27
|
+
ClientOptions,
|
|
28
|
+
ClientSessionAuthOptions,
|
|
29
|
+
ClientTransportAuth,
|
|
30
|
+
ResolveApp,
|
|
31
|
+
} from "./types.ts";
|
|
32
|
+
|
|
33
|
+
export { flattenRoutes, transportOf };
|
|
34
|
+
export type { AppWithRoutes, TransportBag };
|
|
35
|
+
|
|
36
|
+
/** Session-option keys that distinguish orchestration from bare transport hooks. */
|
|
37
|
+
const SESSION_AUTH_KEYS = [
|
|
38
|
+
"mode",
|
|
39
|
+
"persist",
|
|
40
|
+
"csrfConfigured",
|
|
41
|
+
"storageKey",
|
|
42
|
+
"session",
|
|
43
|
+
"tenantHeader",
|
|
44
|
+
"env",
|
|
45
|
+
] as const;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* True when `auth` is bare `{ getToken, refresh }` without session keys.
|
|
49
|
+
*
|
|
50
|
+
* @param auth - Client auth option
|
|
51
|
+
*/
|
|
52
|
+
export function isTransportAuthOnly(auth: unknown): auth is ClientTransportAuth {
|
|
53
|
+
if (auth === null || typeof auth !== "object") return false;
|
|
54
|
+
const bag = auth as Record<string, unknown>;
|
|
55
|
+
const hasHooks = typeof bag.getToken === "function" && typeof bag.refresh === "function";
|
|
56
|
+
if (!hasHooks) return false;
|
|
57
|
+
for (const key of SESSION_AUTH_KEYS) {
|
|
58
|
+
if (key in bag && bag[key] !== undefined) return false;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* True when `auth` should construct an {@link AuthClient} on the client.
|
|
65
|
+
*
|
|
66
|
+
* @param auth - Client auth option
|
|
67
|
+
*/
|
|
68
|
+
export function isSessionAuthOptions(auth: unknown): auth is ClientSessionAuthOptions {
|
|
69
|
+
return auth !== null && typeof auth === "object" && !isTransportAuthOnly(auth);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Create a fully typed client from an adopted app value (types + REST).
|
|
74
|
+
*
|
|
75
|
+
* @typeParam App - `typeof app` after `.adopt({ notes })`
|
|
76
|
+
*/
|
|
77
|
+
export function createClient<App extends AppWithRoutes>(
|
|
78
|
+
app: App,
|
|
79
|
+
url: string,
|
|
80
|
+
opts?: ClientOptions,
|
|
81
|
+
): Client<App>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Create a fully typed client from an App type argument / ambient Register.
|
|
85
|
+
*
|
|
86
|
+
* @typeParam App - `typeof app`, {@link AppOf} route map, or omit for {@link Register}
|
|
87
|
+
*/
|
|
88
|
+
export function createClient<App = never>(
|
|
89
|
+
url: string,
|
|
90
|
+
opts?: ClientOptions,
|
|
91
|
+
): Client<ResolveApp<App>>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @param appOrUrl - App instance or base URL
|
|
95
|
+
* @param urlOrOpts - Base URL (when app given) or options
|
|
96
|
+
* @param maybeOpts - Options when app + url form is used
|
|
97
|
+
*/
|
|
98
|
+
export function createClient(
|
|
99
|
+
appOrUrl: AppWithRoutes | string,
|
|
100
|
+
urlOrOpts?: string | ClientOptions,
|
|
101
|
+
maybeOpts?: ClientOptions,
|
|
102
|
+
): Client {
|
|
103
|
+
const { url, opts } = normalizeArgs(appOrUrl, urlOrOpts, maybeOpts);
|
|
104
|
+
if (!opts?.auth || isTransportAuthOnly(opts.auth)) {
|
|
105
|
+
return createClientCore(appOrUrl as never, urlOrOpts as never, maybeOpts as never);
|
|
106
|
+
}
|
|
107
|
+
return buildWithSession(url, opts);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function normalizeArgs(
|
|
111
|
+
appOrUrl: AppWithRoutes | string,
|
|
112
|
+
urlOrOpts?: string | ClientOptions,
|
|
113
|
+
maybeOpts?: ClientOptions,
|
|
114
|
+
): { url: string; opts: ClientOptions } {
|
|
115
|
+
if (typeof appOrUrl === "string") {
|
|
116
|
+
return { url: appOrUrl, opts: (urlOrOpts as ClientOptions | undefined) ?? {} };
|
|
117
|
+
}
|
|
118
|
+
const url = typeof urlOrOpts === "string" ? urlOrOpts : "";
|
|
119
|
+
const opts = (typeof urlOrOpts === "string" ? maybeOpts : urlOrOpts) ?? {};
|
|
120
|
+
return {
|
|
121
|
+
url,
|
|
122
|
+
opts: { ...opts, $routes: opts.$routes ?? appOrUrl.$routes },
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function buildWithSession(url: string, opts: ClientOptions): Client {
|
|
127
|
+
const sessionOpts = opts.auth as CreateAuthClientOptions;
|
|
128
|
+
const { auth: _drop, ...rest } = opts;
|
|
129
|
+
const shell = createClientCore(url, rest) as Client & AuthApi;
|
|
130
|
+
const authClient = createAuthClient(shell, sessionOpts);
|
|
131
|
+
const merged = mergeClientOptions(rest, authClient, sessionOpts);
|
|
132
|
+
const api = createClientCore(url, merged) as Client & AuthApi;
|
|
133
|
+
authClient.bind(api);
|
|
134
|
+
return attachAuth(api, authClient);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function mergeClientOptions(
|
|
138
|
+
base: ClientOptions,
|
|
139
|
+
authClient: AuthClient,
|
|
140
|
+
sessionOpts: CreateAuthClientOptions,
|
|
141
|
+
): ClientOptions {
|
|
142
|
+
const fromAuth = authClient.clientOptions;
|
|
143
|
+
const transportAuth: ClientTransportAuth = {
|
|
144
|
+
getToken: sessionOpts.getToken ?? fromAuth.auth?.getToken ?? (() => null),
|
|
145
|
+
refresh: sessionOpts.refresh ?? fromAuth.auth?.refresh ?? (async () => null),
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
...base,
|
|
149
|
+
credentials: fromAuth.credentials ?? base.credentials,
|
|
150
|
+
auth: transportAuth,
|
|
151
|
+
headers: mergeHeaderGetters(base.headers, fromAuth.headers),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function mergeHeaderGetters(
|
|
156
|
+
base: ClientOptions["headers"],
|
|
157
|
+
over: (() => Record<string, string>) | undefined,
|
|
158
|
+
): ClientOptions["headers"] {
|
|
159
|
+
if (!over) return base;
|
|
160
|
+
if (!base) return over;
|
|
161
|
+
return async () => {
|
|
162
|
+
const a = typeof base === "function" ? await base() : base;
|
|
163
|
+
const b = over();
|
|
164
|
+
return mergeHeaderBags(a, b);
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function mergeHeaderBags(a: ClientHeaders | undefined, b: Record<string, string>): ClientHeaders {
|
|
169
|
+
if (!a) return b;
|
|
170
|
+
if (!Array.isArray(a)) return { ...a, ...b };
|
|
171
|
+
return [...a, ...Object.entries(b)];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Attach {@link AuthClient} at `api.auth`, preserving unit Flow access
|
|
176
|
+
* (`api.auth.me`, `api.auth.signInEmail`, …) for unbound props.
|
|
177
|
+
*
|
|
178
|
+
* @param api - Typed client
|
|
179
|
+
* @param auth - Session helper
|
|
180
|
+
*/
|
|
181
|
+
function attachAuth(api: Client, auth: AuthClient): Client {
|
|
182
|
+
return new Proxy(api as object, {
|
|
183
|
+
get(target, prop, receiver) {
|
|
184
|
+
if (prop === "auth") {
|
|
185
|
+
const flows = Reflect.get(target, "auth", receiver);
|
|
186
|
+
return mergeAuthSurface(auth, flows);
|
|
187
|
+
}
|
|
188
|
+
return Reflect.get(target, prop, receiver);
|
|
189
|
+
},
|
|
190
|
+
}) as Client;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function mergeAuthSurface(auth: AuthClient, flows: unknown): AuthClient {
|
|
194
|
+
return new Proxy(auth as object, {
|
|
195
|
+
get(target, prop, receiver) {
|
|
196
|
+
if (typeof prop === "symbol") return Reflect.get(target, prop, receiver);
|
|
197
|
+
if (prop in (target as object)) return Reflect.get(target, prop, receiver);
|
|
198
|
+
if (flows !== null && (typeof flows === "object" || typeof flows === "function")) {
|
|
199
|
+
return Reflect.get(flows as object, prop, flows);
|
|
200
|
+
}
|
|
201
|
+
return undefined;
|
|
202
|
+
},
|
|
203
|
+
has(target, prop) {
|
|
204
|
+
if (prop in (target as object)) return true;
|
|
205
|
+
if (flows === null || (typeof flows !== "object" && typeof flows !== "function")) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
// Unit namespaces from createClient are Proxies that often implement get
|
|
209
|
+
// without has — treat a defined get as present so `"me" in api.auth` works.
|
|
210
|
+
if (prop in (flows as object)) return true;
|
|
211
|
+
return Reflect.get(flows as object, prop, flows) !== undefined;
|
|
212
|
+
},
|
|
213
|
+
}) as AuthClient;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Thin SSR helper — same {@link createClient} with cookie token from the request.
|
|
218
|
+
*
|
|
219
|
+
* @param req - Incoming request (cookies)
|
|
220
|
+
* @param url - API base URL
|
|
221
|
+
* @param opts - Client options
|
|
222
|
+
*/
|
|
223
|
+
export function createServerClient(
|
|
224
|
+
req: CookieSource,
|
|
225
|
+
url: string,
|
|
226
|
+
opts: ClientOptions = {},
|
|
227
|
+
): Client {
|
|
228
|
+
const prior = opts.auth;
|
|
229
|
+
const sessionBase =
|
|
230
|
+
prior && isSessionAuthOptions(prior)
|
|
231
|
+
? prior
|
|
232
|
+
: prior && isTransportAuthOnly(prior)
|
|
233
|
+
? {}
|
|
234
|
+
: ((prior as ClientSessionAuthOptions | undefined) ?? {});
|
|
235
|
+
return createClient(url, {
|
|
236
|
+
...opts,
|
|
237
|
+
credentials: opts.credentials ?? "include",
|
|
238
|
+
auth: {
|
|
239
|
+
...sessionBase,
|
|
240
|
+
mode: "cookie",
|
|
241
|
+
csrfConfigured: "csrfConfigured" in sessionBase ? sessionBase.csrfConfigured : true,
|
|
242
|
+
getToken: () => tokenFromRequestCookies(req),
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
}
|
package/src/client/create.ts
CHANGED
|
@@ -22,11 +22,13 @@ import {
|
|
|
22
22
|
type LiveByFlow,
|
|
23
23
|
type LiveRouteTable,
|
|
24
24
|
} from "./live.ts";
|
|
25
|
+
import { flattenStreamRoutes, openStream, type StreamByFlow } from "./stream.ts";
|
|
25
26
|
import type {
|
|
26
27
|
Client,
|
|
27
28
|
ClientHeaders,
|
|
28
29
|
ClientLive,
|
|
29
30
|
ClientOptions,
|
|
31
|
+
ClientResult,
|
|
30
32
|
ClientRouteMap,
|
|
31
33
|
LiveHandlers,
|
|
32
34
|
ResolveApp,
|
|
@@ -97,6 +99,7 @@ function buildClient(url: string, opts: ClientOptions = {}): Client {
|
|
|
97
99
|
const base = url.replace(/\/+$/, "");
|
|
98
100
|
const routes = opts.routes ?? flattenRoutes(opts.$routes);
|
|
99
101
|
const live = flattenLiveRoutes(opts.$routes);
|
|
102
|
+
const streamByFlow = flattenStreamRoutes(opts.$routes);
|
|
100
103
|
const transport = createTransport(base, { ...opts, routes });
|
|
101
104
|
const perCallHeaders = createPerCallHeaders();
|
|
102
105
|
return proxy(transport, [], {
|
|
@@ -104,6 +107,7 @@ function buildClient(url: string, opts: ClientOptions = {}): Client {
|
|
|
104
107
|
opts,
|
|
105
108
|
liveBySignal: live.bySignal,
|
|
106
109
|
liveByFlow: live.byFlow,
|
|
110
|
+
streamByFlow,
|
|
107
111
|
perCallHeaders,
|
|
108
112
|
}) as Client;
|
|
109
113
|
}
|
|
@@ -113,6 +117,7 @@ type ProxyCtx = {
|
|
|
113
117
|
readonly opts: ClientOptions;
|
|
114
118
|
readonly liveBySignal: LiveRouteTable;
|
|
115
119
|
readonly liveByFlow: LiveByFlow;
|
|
120
|
+
readonly streamByFlow: StreamByFlow;
|
|
116
121
|
/**
|
|
117
122
|
* Extra headers attached to the next transport call, then cleared — the
|
|
118
123
|
* `X-Oke-Mutation-Id` channel for optimistic dedupe (one-shot by design;
|
|
@@ -187,7 +192,7 @@ export function flattenRoutes(
|
|
|
187
192
|
* @param path - Accumulated property path
|
|
188
193
|
*/
|
|
189
194
|
function proxy(transport: Transport, path: readonly string[], ctx: ProxyCtx): unknown {
|
|
190
|
-
const invoke = async (input?: unknown) => {
|
|
195
|
+
const invoke = async (input?: unknown, callOpts?: CallOpts): Promise<ClientResult> => {
|
|
191
196
|
if (path.length < 2) {
|
|
192
197
|
return attachPager(
|
|
193
198
|
{
|
|
@@ -199,16 +204,20 @@ function proxy(transport: Transport, path: readonly string[], ctx: ProxyCtx): un
|
|
|
199
204
|
},
|
|
200
205
|
},
|
|
201
206
|
},
|
|
202
|
-
invoke,
|
|
207
|
+
(nextInput) => invoke(nextInput, callOpts),
|
|
203
208
|
input,
|
|
204
209
|
);
|
|
205
210
|
}
|
|
206
211
|
const unit = path[0]!;
|
|
207
212
|
const flow = path.slice(1).join(".");
|
|
208
|
-
const result = await transport.call(`${unit}/${flow}`, input,
|
|
209
|
-
|
|
213
|
+
const result = await transport.call(`${unit}/${flow}`, input, {
|
|
214
|
+
headers: ctx.perCallHeaders.read(),
|
|
215
|
+
...(callOpts?.response !== undefined ? { response: callOpts.response } : {}),
|
|
216
|
+
...(callOpts?.signal !== undefined ? { signal: callOpts.signal } : {}),
|
|
217
|
+
});
|
|
218
|
+
return attachPager(result, (nextInput) => invoke(nextInput, callOpts), input);
|
|
210
219
|
};
|
|
211
|
-
const call = (a?: unknown, b?: unknown) => {
|
|
220
|
+
const call = (a?: unknown, b?: unknown): unknown => {
|
|
212
221
|
if (path.length >= 2) {
|
|
213
222
|
const key = `${path[0]}.${path.slice(1).join(".")}`;
|
|
214
223
|
const exposure = ctx.liveByFlow[key];
|
|
@@ -217,6 +226,20 @@ function proxy(transport: Transport, path: readonly string[], ctx: ProxyCtx): un
|
|
|
217
226
|
const input = isLiveHandlers(a) ? undefined : a;
|
|
218
227
|
return subscribeLive(ctx.base, exposure, input, handlers, ctx.opts);
|
|
219
228
|
}
|
|
229
|
+
const streamRoute = ctx.streamByFlow[key];
|
|
230
|
+
if (streamRoute) {
|
|
231
|
+
const input = isCallOpts(a) ? undefined : a;
|
|
232
|
+
const opts = isCallOpts(a) ? a : isCallOpts(b) ? b : undefined;
|
|
233
|
+
const merged: ClientOptions = opts?.signal
|
|
234
|
+
? { ...ctx.opts, signal: opts.signal }
|
|
235
|
+
: ctx.opts;
|
|
236
|
+
return openStream(ctx.base, streamRoute, input, merged);
|
|
237
|
+
}
|
|
238
|
+
if (isCallOpts(a) || isCallOpts(b)) {
|
|
239
|
+
const input = isCallOpts(a) ? undefined : a;
|
|
240
|
+
const opts = (isCallOpts(a) ? a : b) as CallOpts;
|
|
241
|
+
return asThenableIterable((next) => invoke(next, opts), input);
|
|
242
|
+
}
|
|
220
243
|
}
|
|
221
244
|
return asThenableIterable(invoke, a);
|
|
222
245
|
};
|
|
@@ -313,3 +336,26 @@ function makeLive(ctx: ProxyCtx): ClientLive {
|
|
|
313
336
|
return subscribeLive(ctx.base, exposure, input, handlers, ctx.opts);
|
|
314
337
|
}) as ClientLive;
|
|
315
338
|
}
|
|
339
|
+
|
|
340
|
+
function isCallOpts(value: unknown): value is CallOpts {
|
|
341
|
+
if (value === null || typeof value !== "object" || isLiveHandlers(value)) return false;
|
|
342
|
+
const v = value as Record<string, unknown>;
|
|
343
|
+
if ("response" in v) {
|
|
344
|
+
return (
|
|
345
|
+
v.response === undefined ||
|
|
346
|
+
v.response === "json" ||
|
|
347
|
+
v.response === "blob" ||
|
|
348
|
+
v.response === "arrayBuffer"
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
if ("signal" in v) {
|
|
352
|
+
return v.signal === undefined || v.signal instanceof AbortSignal;
|
|
353
|
+
}
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** Per-call options on a Flow invoke (binary decode / abort). */
|
|
358
|
+
export interface CallOpts {
|
|
359
|
+
readonly response?: "json" | "blob" | "arrayBuffer";
|
|
360
|
+
readonly signal?: AbortSignal;
|
|
361
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -13,7 +13,12 @@ export {
|
|
|
13
13
|
nextResubscribeDelay,
|
|
14
14
|
pickLiveExposure,
|
|
15
15
|
} from "./live.ts";
|
|
16
|
-
export {
|
|
16
|
+
export {
|
|
17
|
+
createClient,
|
|
18
|
+
flattenRoutes,
|
|
19
|
+
transportOf,
|
|
20
|
+
createServerClient,
|
|
21
|
+
} from "./create-with-session.ts";
|
|
17
22
|
export type { AppWithRoutes, TransportBag } from "./create.ts";
|
|
18
23
|
export { isErrorCode, isFail, isOk, isTransportError } from "./errors.ts";
|
|
19
24
|
export { isPagerMeta, pagerLink } from "./pager.ts";
|
|
@@ -32,12 +37,17 @@ export type {
|
|
|
32
37
|
Client,
|
|
33
38
|
ClientApp,
|
|
34
39
|
ClientCall,
|
|
40
|
+
ClientCallOpts,
|
|
35
41
|
ClientDescriptor,
|
|
36
42
|
ClientError,
|
|
37
43
|
ClientFetch,
|
|
38
44
|
ClientFromRoutes,
|
|
39
45
|
ClientHeaders,
|
|
46
|
+
ClientBodyInit,
|
|
47
|
+
ClientCredentials,
|
|
40
48
|
ClientOptions,
|
|
49
|
+
ClientSessionAuthOptions,
|
|
50
|
+
ClientTransportAuth,
|
|
41
51
|
ClientEnvelope,
|
|
42
52
|
ClientListMeta,
|
|
43
53
|
ClientPagerLink,
|
package/src/client/live.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type { ClientFetch, ClientOptions, ClientRouteMap, FlowContract } from "./types.ts";
|
|
6
6
|
import type { LiveHandlers, LiveUnsubscribe } from "./types.ts";
|
|
7
|
+
import { readSse, sseError } from "./sse.ts";
|
|
7
8
|
|
|
8
9
|
/** One HTTP exposure of a live signal. */
|
|
9
10
|
export interface LiveExposure {
|
|
@@ -285,13 +286,21 @@ async function openSse(
|
|
|
285
286
|
} else if (extra) {
|
|
286
287
|
for (const [k, v] of Object.entries(extra)) headers.set(k, v);
|
|
287
288
|
}
|
|
288
|
-
const token =
|
|
289
|
+
const token =
|
|
290
|
+
opts.auth && "getToken" in opts.auth && typeof opts.auth.getToken === "function"
|
|
291
|
+
? await opts.auth.getToken()
|
|
292
|
+
: undefined;
|
|
289
293
|
if (token && !headers.has("authorization")) {
|
|
290
294
|
headers.set("authorization", `Bearer ${token}`);
|
|
291
295
|
}
|
|
292
296
|
if (lastSeenId) headers.set("last-event-id", lastSeenId);
|
|
293
297
|
const fetchFn: ClientFetch = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
294
|
-
return fetchFn(url, {
|
|
298
|
+
return fetchFn(url, {
|
|
299
|
+
method,
|
|
300
|
+
headers,
|
|
301
|
+
signal,
|
|
302
|
+
...(opts.credentials !== undefined ? { credentials: opts.credentials } : {}),
|
|
303
|
+
});
|
|
295
304
|
}
|
|
296
305
|
|
|
297
306
|
function restGet(base: string, path: string, input: unknown): { url: string; method: string } {
|
|
@@ -310,80 +319,3 @@ function restGet(base: string, path: string, input: unknown): { url: string; met
|
|
|
310
319
|
const qs = query.length ? `?${query.join("&")}` : "";
|
|
311
320
|
return { url: `${base}${pathOut}${qs}`, method: "GET" };
|
|
312
321
|
}
|
|
313
|
-
|
|
314
|
-
async function readSse(
|
|
315
|
-
res: Response,
|
|
316
|
-
onEvent: (event: unknown, id: string | undefined) => void,
|
|
317
|
-
signal: AbortSignal,
|
|
318
|
-
onOpen?: () => void,
|
|
319
|
-
): Promise<void> {
|
|
320
|
-
if (signal.aborted) return;
|
|
321
|
-
const ct = res.headers.get("content-type") ?? "";
|
|
322
|
-
if (!res.ok || !res.body) {
|
|
323
|
-
const text = await res.text().catch(() => "");
|
|
324
|
-
throw sseError(res.status, text);
|
|
325
|
-
}
|
|
326
|
-
if (!ct.includes("text/event-stream")) {
|
|
327
|
-
const text = await res.text().catch(() => "");
|
|
328
|
-
throw sseError(res.status, text || `Expected text/event-stream, got ${ct || "none"}`);
|
|
329
|
-
}
|
|
330
|
-
onOpen?.();
|
|
331
|
-
const reader = res.body.getReader();
|
|
332
|
-
const dec = new TextDecoder();
|
|
333
|
-
let buf = "";
|
|
334
|
-
try {
|
|
335
|
-
for (;;) {
|
|
336
|
-
if (signal.aborted) return;
|
|
337
|
-
const { done, value } = await reader.read();
|
|
338
|
-
if (done) break;
|
|
339
|
-
buf += dec.decode(value, { stream: true });
|
|
340
|
-
let sep = buf.indexOf("\n\n");
|
|
341
|
-
while (sep >= 0) {
|
|
342
|
-
const raw = buf.slice(0, sep);
|
|
343
|
-
buf = buf.slice(sep + 2);
|
|
344
|
-
const stop = dispatchFrame(raw, onEvent, signal);
|
|
345
|
-
if (stop || signal.aborted) return;
|
|
346
|
-
sep = buf.indexOf("\n\n");
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
} finally {
|
|
350
|
-
reader.releaseLock();
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function dispatchFrame(
|
|
355
|
-
raw: string,
|
|
356
|
-
onEvent: (event: unknown, id: string | undefined) => void,
|
|
357
|
-
signal: AbortSignal,
|
|
358
|
-
): boolean {
|
|
359
|
-
const dataLines: string[] = [];
|
|
360
|
-
let id: string | undefined;
|
|
361
|
-
for (const line of raw.split("\n")) {
|
|
362
|
-
if (line.startsWith("id:")) id = line.slice(3).replace(/^ /, "");
|
|
363
|
-
if (line.startsWith("data:")) dataLines.push(line.slice(5).replace(/^ /, ""));
|
|
364
|
-
}
|
|
365
|
-
if (dataLines.length === 0) return false;
|
|
366
|
-
const data = dataLines.join("\n");
|
|
367
|
-
if (data === "[DONE]") return true;
|
|
368
|
-
if (signal.aborted) return true;
|
|
369
|
-
onEvent(JSON.parse(data) as unknown, id);
|
|
370
|
-
return false;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function sseError(status: number, body: string): Error {
|
|
374
|
-
let message = `HTTP ${status}`;
|
|
375
|
-
if (body) {
|
|
376
|
-
try {
|
|
377
|
-
const json: unknown = JSON.parse(body);
|
|
378
|
-
if (json !== null && typeof json === "object" && "error" in json) {
|
|
379
|
-
const err = (json as { error?: { code?: string; data?: { message?: string } } }).error;
|
|
380
|
-
message = err?.data?.message ?? err?.code ?? message;
|
|
381
|
-
}
|
|
382
|
-
} catch {
|
|
383
|
-
message = body.slice(0, 200);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
const e = new Error(message);
|
|
387
|
-
(e as Error & { status?: number }).status = status;
|
|
388
|
-
return e;
|
|
389
|
-
}
|