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/elements/store.ts
CHANGED
|
@@ -181,6 +181,56 @@ export {
|
|
|
181
181
|
} from "./store/files-policy.ts";
|
|
182
182
|
export type { FileKeyWarning } from "./store/files-policy.ts";
|
|
183
183
|
|
|
184
|
+
export {
|
|
185
|
+
SearchConfigError,
|
|
186
|
+
SEARCH_LOW_CORPUS_WARN_N,
|
|
187
|
+
RRF_DEFAULT_K,
|
|
188
|
+
LSH_DEFAULT_K,
|
|
189
|
+
BM25_K1,
|
|
190
|
+
BM25_B,
|
|
191
|
+
} from "./store/search-errors.ts";
|
|
192
|
+
export { bm25fScore, tokenize, termFrequencies } from "./store/search-bm25.ts";
|
|
193
|
+
export {
|
|
194
|
+
generateHyperplanes,
|
|
195
|
+
hyperplaneSeed,
|
|
196
|
+
lshBucket,
|
|
197
|
+
lshBucketToSql,
|
|
198
|
+
lshBucketFromSql,
|
|
199
|
+
cosineSimilarity,
|
|
200
|
+
neighborBuckets,
|
|
201
|
+
serializePlanes,
|
|
202
|
+
deserializePlanes,
|
|
203
|
+
} from "./store/search-lsh.ts";
|
|
204
|
+
export { fuseRrf, fuseWeighted, fuseLists } from "./store/search-fusion.ts";
|
|
205
|
+
export type { FuseOptions, FuseStrategy, RankedHit } from "./store/search-fusion.ts";
|
|
206
|
+
export { runSqlSearch } from "./store/search-runtime.ts";
|
|
207
|
+
export type {
|
|
208
|
+
SqlSearchOptions,
|
|
209
|
+
SqlSearchResult,
|
|
210
|
+
SearchColumnMeta,
|
|
211
|
+
} from "./store/search-runtime.ts";
|
|
212
|
+
export {
|
|
213
|
+
searchDdlForTable,
|
|
214
|
+
ensureHyperplaneInserts,
|
|
215
|
+
embColumn,
|
|
216
|
+
lshColumn,
|
|
217
|
+
OKE_TSV_COL,
|
|
218
|
+
SEARCH_PG_MIN_MAJOR,
|
|
219
|
+
parsePgMajor,
|
|
220
|
+
} from "./store/search-ddl.ts";
|
|
221
|
+
export { runSearchBackfill } from "./store/search-backfill.ts";
|
|
222
|
+
export type { SearchBackfillOptions, SearchBackfillResult } from "./store/search-backfill.ts";
|
|
223
|
+
export {
|
|
224
|
+
searchEmbedFlowName,
|
|
225
|
+
tablesNeedingSearchEmbed,
|
|
226
|
+
applySearchEmbedCdc,
|
|
227
|
+
} from "./store/search-embed-flow.ts";
|
|
228
|
+
export { bindSearchEmbedFlows } from "./store/search-bind.ts";
|
|
229
|
+
export type {
|
|
230
|
+
SearchableOptions,
|
|
231
|
+
EmbedFieldOptions,
|
|
232
|
+
ColumnSearchDecl,
|
|
233
|
+
} from "./store/schema-decl.ts";
|
|
184
234
|
export {
|
|
185
235
|
createFilesImagePipeline,
|
|
186
236
|
putImageToBucket,
|
package/src/full.ts
CHANGED
|
@@ -12,9 +12,7 @@ export {
|
|
|
12
12
|
on,
|
|
13
13
|
flow,
|
|
14
14
|
http,
|
|
15
|
-
every,
|
|
16
15
|
internal,
|
|
17
|
-
table,
|
|
18
16
|
fail,
|
|
19
17
|
createFx,
|
|
20
18
|
isFlow,
|
|
@@ -86,7 +84,11 @@ export {
|
|
|
86
84
|
signal,
|
|
87
85
|
createSignalRuntime,
|
|
88
86
|
type SignalDecl,
|
|
87
|
+
type SignalLiveOptions,
|
|
88
|
+
type SignalNamespace,
|
|
89
89
|
type SignalOptions,
|
|
90
|
+
type SignalRetention,
|
|
91
|
+
type SignalSharedOptions,
|
|
90
92
|
type SignalRuntime,
|
|
91
93
|
type DeadLetter,
|
|
92
94
|
type SignalFailureReason,
|
package/src/http.ts
CHANGED
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
|
|
19
19
|
export { on, listBindings, resetBindings, type Binding } from "./kernel/on.ts";
|
|
20
20
|
export { flow, isFlow, type FlowDef } from "./kernel/flow.ts";
|
|
21
|
-
export {
|
|
21
|
+
export { call } from "./kernel/call.ts";
|
|
22
|
+
export { http, internal } from "./kernel/triggers.ts";
|
|
23
|
+
export type { BoundaryContract } from "./kernel/boundary-contract.ts";
|
|
24
|
+
export { clock } from "./elements/clock/declare.ts";
|
|
22
25
|
export { gate, GATE_PUBLIC_NAME } from "./elements/gate/declare.ts";
|
|
23
26
|
export { fail, type FlowFailure } from "./kernel/errors.ts";
|
|
24
27
|
export { isFlowFailure } from "./kernel/hooks.ts";
|
package/src/i18n/catalogs/ar.ts
CHANGED
|
@@ -32,27 +32,35 @@ export const builtinAr = {
|
|
|
32
32
|
cause: 'التدفق "{flow}" يستدعي التدفق "{resource}" دون الإعلان عنه.',
|
|
33
33
|
fix: 'أضف "{resource}" إلى effects.calls لهذا التدفق.',
|
|
34
34
|
},
|
|
35
|
-
"
|
|
35
|
+
"1008": {
|
|
36
|
+
cause: 'التدفق "{flow}" يجلب "{resource}" دون الإعلان عنه.',
|
|
37
|
+
fix: 'أضف "{resource}" إلى effects.fetches لهذا التدفق.',
|
|
38
|
+
},
|
|
39
|
+
"1009": {
|
|
40
|
+
cause: 'التدفق "{flow}" يضمّن عبر "{resource}" دون الإعلان عنه.',
|
|
41
|
+
fix: 'أضف "{resource}" إلى effects.embeds لهذا التدفق.',
|
|
42
|
+
},
|
|
43
|
+
"1240": {
|
|
36
44
|
cause: 'التدفق "{flow}" يُصدِر الإشارة "{resource}" بلا مشترك.',
|
|
37
45
|
fix: "أضف on({resource}, …) أو عيّن الإشارة '{'optional: true'}'.",
|
|
38
46
|
},
|
|
39
|
-
"
|
|
47
|
+
"1210": {
|
|
40
48
|
cause: 'مؤشر البث "{afterId}" ليس على شريط "{signal}".',
|
|
41
49
|
fix: "أعد الاتصال بدون Last-Event-ID لإعادة تشغيل الشريط المتبقي.",
|
|
42
50
|
},
|
|
43
|
-
"
|
|
51
|
+
"1810": {
|
|
44
52
|
cause: "هذه العملية تحتاج مستأجراً، ولم يُحدَّد مستأجر لهذا الطلب.",
|
|
45
53
|
fix: "استدعِ fx.auth.switchTenant(id) أو أرسل مطالبة tid موقّعة أو رأس المستأجر.",
|
|
46
54
|
},
|
|
47
|
-
"
|
|
55
|
+
"1820": {
|
|
48
56
|
cause: 'المتصل ليس عضواً في المستأجر "{tenant}".',
|
|
49
57
|
fix: "اختر مستأجراً من fx.auth.listTenants() أو أضف المستخدم كعضو.",
|
|
50
58
|
},
|
|
51
|
-
"
|
|
59
|
+
"1830": {
|
|
52
60
|
cause: 'نطاق دور المستأجر "{scope}" ليس نطاقاً تطبيقياً معلناً.',
|
|
53
61
|
fix: "استخدم اسماً من كتالوج البيان لهذا التطبيق (وليس console:*).",
|
|
54
62
|
},
|
|
55
|
-
"
|
|
63
|
+
"1110": {
|
|
56
64
|
cause: "جدول النطاق غير موجود — لم تُطبَّق الترحيلات.",
|
|
57
65
|
fix: "شغّل `oke db migrate` على هذه البيئة.",
|
|
58
66
|
},
|
package/src/i18n/catalogs/en.ts
CHANGED
|
@@ -32,27 +32,35 @@ export const builtinEn = {
|
|
|
32
32
|
cause: 'Flow "{flow}" calls "{resource}" without declaring it.',
|
|
33
33
|
fix: "Add \"{resource}\" to this flow''s effects.calls.",
|
|
34
34
|
},
|
|
35
|
-
"
|
|
35
|
+
"1008": {
|
|
36
|
+
cause: 'Flow "{flow}" fetches "{resource}" without declaring it.',
|
|
37
|
+
fix: "Add \"{resource}\" to this flow''s effects.fetches.",
|
|
38
|
+
},
|
|
39
|
+
"1009": {
|
|
40
|
+
cause: 'Flow "{flow}" embeds with "{resource}" without declaring it.',
|
|
41
|
+
fix: "Add \"{resource}\" to this flow''s effects.embeds.",
|
|
42
|
+
},
|
|
43
|
+
"1240": {
|
|
36
44
|
cause: 'Flow "{flow}" emits signal "{resource}" with no subscriber.',
|
|
37
45
|
fix: "Add on({resource}, …) or mark the signal '{'optional: true'}'.",
|
|
38
46
|
},
|
|
39
|
-
"
|
|
47
|
+
"1210": {
|
|
40
48
|
cause: 'Live cursor "{afterId}" is not on the tape for "{signal}".',
|
|
41
49
|
fix: "Reconnect without Last-Event-ID to replay the remaining tape.",
|
|
42
50
|
},
|
|
43
|
-
"
|
|
51
|
+
"1810": {
|
|
44
52
|
cause: "This operation needs a tenant, but none is resolved for this request.",
|
|
45
53
|
fix: "Call fx.auth.switchTenant(id), send a signed tid claim, or pass the tenant header.",
|
|
46
54
|
},
|
|
47
|
-
"
|
|
55
|
+
"1820": {
|
|
48
56
|
cause: 'The caller is not a member of tenant "{tenant}".',
|
|
49
57
|
fix: "Pick a tenant from fx.auth.listTenants() or add the user as a member.",
|
|
50
58
|
},
|
|
51
|
-
"
|
|
59
|
+
"1830": {
|
|
52
60
|
cause: 'Tenant role scope "{scope}" is not a declared application scope.',
|
|
53
61
|
fix: "Use a name from this app's Manifest catalog (never console:*).",
|
|
54
62
|
},
|
|
55
|
-
"
|
|
63
|
+
"1110": {
|
|
56
64
|
cause: "domain table not found — migrations have not been applied.",
|
|
57
65
|
fix: "run `oke db migrate` against this environment.",
|
|
58
66
|
},
|
package/src/index.ts
CHANGED
|
@@ -24,7 +24,9 @@ export {
|
|
|
24
24
|
} from "./kernel/app.ts";
|
|
25
25
|
export { on, type Binding } from "./kernel/on.ts";
|
|
26
26
|
export { flow, isFlow, type FlowDef } from "./kernel/flow.ts";
|
|
27
|
-
export {
|
|
27
|
+
export { call } from "./kernel/call.ts";
|
|
28
|
+
export { http, internal, mcp } from "./kernel/triggers.ts";
|
|
29
|
+
export type { BoundaryContract } from "./kernel/boundary-contract.ts";
|
|
28
30
|
export { registerFlowUnits } from "./kernel/flow-units.ts";
|
|
29
31
|
export { stampFlowName, stampHttpPath } from "./kernel/stamp-http.ts";
|
|
30
32
|
export { fail, type FlowFailure, type FlowErrorValue } from "./kernel/errors.ts";
|
|
@@ -82,8 +84,11 @@ export {
|
|
|
82
84
|
signal,
|
|
83
85
|
createSignalRuntime,
|
|
84
86
|
type SignalDecl,
|
|
87
|
+
type SignalLiveOptions,
|
|
88
|
+
type SignalNamespace,
|
|
85
89
|
type SignalOptions,
|
|
86
90
|
type SignalRetention,
|
|
91
|
+
type SignalSharedOptions,
|
|
87
92
|
type SignalRuntime,
|
|
88
93
|
type DeadLetter,
|
|
89
94
|
type SignalFailureReason,
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Boot-level proof: a `src/flows/<unit>` folder on disk with no adopted
|
|
3
3
|
* flow under that unit — the stale/missing `.adopt()` barrel case.
|
|
4
4
|
*
|
|
5
|
-
* Mirrors `effects-stamping.test.ts`'s
|
|
5
|
+
* Mirrors `effects-stamping.test.ts`'s OKE1020 shape exactly: opt-in only
|
|
6
6
|
* via `rootDir`, `test` warns once (dev-loop stays unbroken),
|
|
7
|
-
* `dev`+compose / `prod` hard-fail (`
|
|
7
|
+
* `dev`+compose / `prod` hard-fail (`OKE1030`) — never a silently-incomplete route
|
|
8
8
|
* table in a deploy-shaped environment.
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -46,11 +46,11 @@ describe("assertAdoptBarrelFresh — direct", () => {
|
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
test("stale: unit on disk, zero adopted flows for it — dev+compose hard-fails
|
|
49
|
+
test("stale: unit on disk, zero adopted flows for it — dev+compose hard-fails OKE1030", async () => {
|
|
50
50
|
const root = await mkdtemp(join(tmpdir(), "oke-adopt-fresh-"));
|
|
51
51
|
try {
|
|
52
52
|
await makeUnitDir(root, "notes");
|
|
53
|
-
await expect(assertAdoptBarrelFresh([], "dev", root, true)).rejects.toThrow(/
|
|
53
|
+
await expect(assertAdoptBarrelFresh([], "dev", root, true)).rejects.toThrow(/OKE1030/);
|
|
54
54
|
} finally {
|
|
55
55
|
await rm(root, { recursive: true, force: true });
|
|
56
56
|
}
|
|
@@ -60,7 +60,7 @@ describe("assertAdoptBarrelFresh — direct", () => {
|
|
|
60
60
|
const root = await mkdtemp(join(tmpdir(), "oke-adopt-fresh-"));
|
|
61
61
|
try {
|
|
62
62
|
await makeUnitDir(root, "notes");
|
|
63
|
-
await expect(assertAdoptBarrelFresh([], "prod", root)).rejects.toThrow(/
|
|
63
|
+
await expect(assertAdoptBarrelFresh([], "prod", root)).rejects.toThrow(/OKE1030/);
|
|
64
64
|
} finally {
|
|
65
65
|
await rm(root, { recursive: true, force: true });
|
|
66
66
|
}
|
|
@@ -78,7 +78,7 @@ describe("assertAdoptBarrelFresh — direct", () => {
|
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
describe("boot-level: stale barrel through a real oke() boot", () => {
|
|
81
|
-
test("dev+compose: real app.boot() hard-fails
|
|
81
|
+
test("dev+compose: real app.boot() hard-fails OKE1030 when a unit folder was never adopted", async () => {
|
|
82
82
|
const root = await mkdtemp(join(tmpdir(), "oke-adopt-fresh-"));
|
|
83
83
|
try {
|
|
84
84
|
// "notes" exists on disk but this app only ever adopted "main" —
|
|
@@ -109,7 +109,7 @@ describe("boot-level: stale barrel through a real oke() boot", () => {
|
|
|
109
109
|
},
|
|
110
110
|
},
|
|
111
111
|
}),
|
|
112
|
-
).rejects.toThrow(/
|
|
112
|
+
).rejects.toThrow(/OKE1030/);
|
|
113
113
|
} finally {
|
|
114
114
|
await rm(root, { recursive: true, force: true });
|
|
115
115
|
}
|
|
@@ -186,6 +186,7 @@ export function runtimeRouteFromFlow(flowDef: AnyFlowDef): RuntimeFlowRoute {
|
|
|
186
186
|
if (trigger?.kind !== "http") return {};
|
|
187
187
|
const liveName = trigger.liveSignal?.name ?? flowDef.live;
|
|
188
188
|
const gates = trigger.gates.map((g) => (typeof g === "string" ? g : g.name));
|
|
189
|
+
const streamOnly = flowDef.stream === true && liveName === undefined;
|
|
189
190
|
return {
|
|
190
191
|
method: trigger.method,
|
|
191
192
|
path: trigger.path,
|
|
@@ -196,7 +197,14 @@ export function runtimeRouteFromFlow(flowDef: AnyFlowDef): RuntimeFlowRoute {
|
|
|
196
197
|
matchKey: httpPathParams(trigger.path),
|
|
197
198
|
...(gates.length > 0 ? { gates } : {}),
|
|
198
199
|
}
|
|
199
|
-
:
|
|
200
|
+
: streamOnly
|
|
201
|
+
? {
|
|
202
|
+
stream: true as const,
|
|
203
|
+
...(gates.length > 0 ? { gates } : {}),
|
|
204
|
+
}
|
|
205
|
+
: gates.length > 0
|
|
206
|
+
? { gates }
|
|
207
|
+
: {}),
|
|
200
208
|
};
|
|
201
209
|
}
|
|
202
210
|
|
package/src/kernel/app-auth.ts
CHANGED
|
@@ -10,6 +10,8 @@ import { auth as authPlugin } from "../auth/plugin.ts";
|
|
|
10
10
|
import { createAuthHttpBindings, type AuthHttpMaterialization } from "../auth/bindings.ts";
|
|
11
11
|
import { tokenFromCookieHeader } from "../auth/cookies.ts";
|
|
12
12
|
import { setActiveGateAuthContext } from "../auth/method-context.ts";
|
|
13
|
+
import { createPendingTwoFactorStore, createStepUpStore } from "../auth/two-factor-challenge.ts";
|
|
14
|
+
import { createVerificationStore } from "../auth/verification.ts";
|
|
13
15
|
import type { ResolvedGateConfig } from "../elements/gate/config.ts";
|
|
14
16
|
import {
|
|
15
17
|
createAppAuthBinding,
|
|
@@ -85,6 +87,9 @@ export function wireGateAuth(options: WireGateAuthOptions): WiredGateAuth {
|
|
|
85
87
|
passwordPolicy: auth.passwordPolicy,
|
|
86
88
|
password: auth.password,
|
|
87
89
|
breachCheck: auth.breachCheck,
|
|
90
|
+
pendingTwoFactor: createPendingTwoFactorStore(),
|
|
91
|
+
stepUp: createStepUpStore(),
|
|
92
|
+
twoFactorVerifications: createVerificationStore(),
|
|
88
93
|
});
|
|
89
94
|
|
|
90
95
|
return {
|
package/src/kernel/app.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type RoutesFromNamespace,
|
|
18
18
|
type RuntimeRouteMap,
|
|
19
19
|
} from "./adopt-routes.ts";
|
|
20
|
+
import { buildClientDescriptor } from "./client-descriptor.ts";
|
|
20
21
|
import { liveExposureKey, liveGatesKey, liveMatchKeyFromPath } from "./live-http.ts";
|
|
21
22
|
// `./boot.ts` pulls in every element + driver module (vault, store, signal,
|
|
22
23
|
// clock, gate, channel, ai, runs) — a type-only import here keeps that whole
|
|
@@ -37,6 +38,7 @@ import {
|
|
|
37
38
|
import { requirePackageModule } from "../shared/lazy-src.ts";
|
|
38
39
|
import { lazyRequire } from "./lazy-require.ts";
|
|
39
40
|
import type { DurableResult, RunDurableOptions } from "../elements/clock/durable.ts";
|
|
41
|
+
import { applyClockTimezoneDefaults } from "../elements/clock/declare.ts";
|
|
40
42
|
import type { TemplateCatalog } from "../elements/channel/runtime.ts";
|
|
41
43
|
import { parseAcceptLanguage } from "../elements/channel/locale.ts";
|
|
42
44
|
import { runWithLocale } from "../i18n/locale-context.ts";
|
|
@@ -62,7 +64,8 @@ import {
|
|
|
62
64
|
logDevRequest,
|
|
63
65
|
shouldLogDevRequests,
|
|
64
66
|
} from "../runtime/dev-request-log.ts";
|
|
65
|
-
import {
|
|
67
|
+
import { GATE_PUBLIC_NAME } from "../elements/gate/flatten.ts";
|
|
68
|
+
import type { JsonCodeAuth } from "../runtime/json-code-block.ts";
|
|
66
69
|
import { resolveDurationMs } from "./elapsed.ts";
|
|
67
70
|
import { fail, throwOke } from "./errors.ts";
|
|
68
71
|
import { consumeRegisteredFlowUnits, type FlowUnitBag } from "./flow-units.ts";
|
|
@@ -131,7 +134,6 @@ import { isPendingHttpPath } from "./http-path-pending.ts";
|
|
|
131
134
|
import { drainPendingResourceLiveMounts } from "./resource-live.ts";
|
|
132
135
|
import type {
|
|
133
136
|
CdcTrigger,
|
|
134
|
-
EveryTrigger,
|
|
135
137
|
HttpTrigger,
|
|
136
138
|
InternalTrigger,
|
|
137
139
|
SignalAsTrigger,
|
|
@@ -181,6 +183,15 @@ function loadDurable(): {
|
|
|
181
183
|
return lazyRequire(import.meta.dir, ["clock", "durable"].join("-"));
|
|
182
184
|
}
|
|
183
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Browser JSON page helpers — loaded on `app.fetch` only.
|
|
188
|
+
* A static import pins the traces-language HTML/CSS graph on every Store-only
|
|
189
|
+
* `oke()` construction.
|
|
190
|
+
*/
|
|
191
|
+
function loadJsonCodeBlock(): typeof import("../runtime/json-code-block.ts") {
|
|
192
|
+
return lazyRequire(`${import.meta.dir}/../runtime`, ["json", "code", "block"].join("-"));
|
|
193
|
+
}
|
|
194
|
+
|
|
184
195
|
/** Options for {@link oke}. */
|
|
185
196
|
export interface OkeOptions {
|
|
186
197
|
/** Application name (Manifest `app`). */
|
|
@@ -265,6 +276,14 @@ export interface OkeOptions {
|
|
|
265
276
|
readonly signals?: BootOptions["signals"];
|
|
266
277
|
/** Named clock declarations. */
|
|
267
278
|
readonly clocks?: BootOptions["clocks"];
|
|
279
|
+
/**
|
|
280
|
+
* Clock element defaults. `{ timezone: "Asia/Riyadh" }` applies to every
|
|
281
|
+
* `clock()` / helper that omits `timezone` (explicit per-clock wins).
|
|
282
|
+
* Prefer this over repeating `timezone` on each declaration.
|
|
283
|
+
*/
|
|
284
|
+
readonly clock?: {
|
|
285
|
+
readonly timezone?: string;
|
|
286
|
+
};
|
|
268
287
|
/** Store facet declarations to register. */
|
|
269
288
|
readonly stores?: BootOptions["stores"];
|
|
270
289
|
/**
|
|
@@ -275,8 +294,20 @@ export interface OkeOptions {
|
|
|
275
294
|
* `store.schema.live(false)`. Default `false` — today's explicit-only
|
|
276
295
|
* behavior, zero change to existing apps. Declaration ergonomics only;
|
|
277
296
|
* never the runtime cost model.
|
|
297
|
+
*
|
|
298
|
+
* `{ search: { embed: { model, dims } } }` sets the project default for
|
|
299
|
+
* field `.embed()` so columns can write bare `.embed()` and inherit;
|
|
300
|
+
* per-field `{ model?, dims? }` still overrides.
|
|
278
301
|
*/
|
|
279
|
-
readonly store?: {
|
|
302
|
+
readonly store?: {
|
|
303
|
+
readonly live?: boolean;
|
|
304
|
+
readonly search?: {
|
|
305
|
+
readonly embed?: {
|
|
306
|
+
readonly model: { readonly name: string } | string;
|
|
307
|
+
readonly dims: number;
|
|
308
|
+
};
|
|
309
|
+
};
|
|
310
|
+
};
|
|
280
311
|
/** Channel runtime options. */
|
|
281
312
|
readonly channel?: BootOptions["channel"];
|
|
282
313
|
/** AI runtime options. */
|
|
@@ -838,7 +869,12 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
838
869
|
const effectiveStores = mergeUnique(options.stores, registrySnapshot.stores);
|
|
839
870
|
const effectiveSecrets = mergeUnique(options.secrets, registrySnapshot.secrets);
|
|
840
871
|
const effectiveSignals = mergeUnique(options.signals, registrySnapshot.signals);
|
|
841
|
-
const
|
|
872
|
+
const mergedClocks = mergeUnique(options.clocks, registrySnapshot.clocks);
|
|
873
|
+
// Apply oke({ clock: { timezone } }) now; leave defaulted clocks intact when
|
|
874
|
+
// unset so bindClock can still honor defineConfig({ clock: { timezone } }).
|
|
875
|
+
const effectiveClocks = options.clock?.timezone
|
|
876
|
+
? applyClockTimezoneDefaults(mergedClocks, options.clock.timezone)
|
|
877
|
+
: mergedClocks;
|
|
842
878
|
const effectivePolicies = mergeUnique(options.gate?.policies, registrySnapshot.gates);
|
|
843
879
|
/**
|
|
844
880
|
* Fold registered `vault.env.required` names into the vault boot options so
|
|
@@ -954,6 +990,30 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
954
990
|
}
|
|
955
991
|
}
|
|
956
992
|
|
|
993
|
+
/** Once-per-app: system `_oke_search_embed_*` CDC bindings from Manifest. */
|
|
994
|
+
let searchEmbedFlowsBound = false;
|
|
995
|
+
/**
|
|
996
|
+
* Auto-register hybrid-search embed CDC flows when any `.embed()` column
|
|
997
|
+
* exists. Lazy-loads search-bind so edge graphs without search stay lean.
|
|
998
|
+
* Requires an explicit Manifest on `oke({ manifest })` / boot overrides —
|
|
999
|
+
* does not AoT-extract the project tree (that path is for capability stamping).
|
|
1000
|
+
*
|
|
1001
|
+
* @param manifest - Boot / options Manifest (optional)
|
|
1002
|
+
*/
|
|
1003
|
+
async function ensureSearchEmbedFlows(
|
|
1004
|
+
manifest: BootOptions["manifest"] | undefined,
|
|
1005
|
+
): Promise<void> {
|
|
1006
|
+
if (searchEmbedFlowsBound || !manifest) return;
|
|
1007
|
+
const { tablesNeedingSearchEmbed } = await import("../elements/store/search-embed-flow.ts");
|
|
1008
|
+
if (tablesNeedingSearchEmbed(manifest).length === 0) {
|
|
1009
|
+
searchEmbedFlowsBound = true;
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
const { bindSearchEmbedFlows } = await import("../elements/store/search-bind.ts");
|
|
1013
|
+
bindSearchEmbedFlows(adoptBinding, manifest);
|
|
1014
|
+
searchEmbedFlowsBound = true;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
957
1017
|
// Project-wide `store.live` default — drain deferred resource live mounts
|
|
958
1018
|
// now that the flag is known. Off flag: pending state is discarded and the
|
|
959
1019
|
// behavior is 100% today's explicit-only. On flag: every pending resource
|
|
@@ -1206,6 +1266,17 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
1206
1266
|
|
|
1207
1267
|
const caps = pluginRegistry.capabilities();
|
|
1208
1268
|
const pluginNames = new Set(Object.keys(caps));
|
|
1269
|
+
|
|
1270
|
+
// Soft-require csrf when gate.auth.cookies.enabled (cookie sessions need CSRF).
|
|
1271
|
+
if (gateConfig.auth?.cookies.enabled && !pluginNames.has("csrf")) {
|
|
1272
|
+
const msg =
|
|
1273
|
+
"gate.auth.cookies.enabled requires the csrf plugin — .plug(csrf({ allowNoHeader: false })) and cors with credentials when cross-origin";
|
|
1274
|
+
if (bootEnv === "prod") {
|
|
1275
|
+
throw new Error(msg);
|
|
1276
|
+
}
|
|
1277
|
+
console.warn(`[okengine] ${msg}`);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1209
1280
|
const authGates = authMaterialization?.authGates ?? [];
|
|
1210
1281
|
const baseGates = overrides?.gates ?? gateConfig.policies ?? [];
|
|
1211
1282
|
const elementNeeds = resolveElementNeeds({
|
|
@@ -1290,6 +1361,7 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
1290
1361
|
unguardedHttp,
|
|
1291
1362
|
signals: [...baseSignals, ...pluginSignals],
|
|
1292
1363
|
clocks: [...baseClocks, ...pluginClocks],
|
|
1364
|
+
clock: overrides?.clock ?? options.clock,
|
|
1293
1365
|
stores: overrides?.stores ?? effectiveStores,
|
|
1294
1366
|
channel: {
|
|
1295
1367
|
...(baseChannel ?? {}),
|
|
@@ -1347,6 +1419,9 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
1347
1419
|
);
|
|
1348
1420
|
}
|
|
1349
1421
|
}
|
|
1422
|
+
// Built-in hybrid search — auto-register durable embed CDC flows when the
|
|
1423
|
+
// Manifest declares any `.embed()` column (writer flows stay embed-free).
|
|
1424
|
+
await ensureSearchEmbedFlows(overrides?.manifest ?? options.manifest);
|
|
1350
1425
|
// otp() provider / app mode capability — fail loud at boot, never silent downgrade.
|
|
1351
1426
|
if (result.channel) {
|
|
1352
1427
|
const { assertOtpPluginCapability } = await import("../auth/otp-capability.ts");
|
|
@@ -2127,6 +2202,7 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
2127
2202
|
let flowLabel: string | undefined;
|
|
2128
2203
|
let runLabel: string | undefined;
|
|
2129
2204
|
let cacheLabel: "hit" | "miss" | "none" | undefined;
|
|
2205
|
+
let authLabel: JsonCodeAuth | undefined;
|
|
2130
2206
|
const url = new URL(request.url);
|
|
2131
2207
|
const method = request.method.toUpperCase();
|
|
2132
2208
|
|
|
@@ -2146,13 +2222,15 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
2146
2222
|
detail: await failureDetailFromResponse(response),
|
|
2147
2223
|
});
|
|
2148
2224
|
}
|
|
2149
|
-
|
|
2225
|
+
const jcb = loadJsonCodeBlock();
|
|
2226
|
+
return jcb.asBrowserJsonCodeBlock(
|
|
2150
2227
|
request,
|
|
2151
2228
|
response,
|
|
2152
2229
|
options.name,
|
|
2153
|
-
httpNavGroups(adopted, request),
|
|
2230
|
+
jcb.httpNavGroups(adopted, request),
|
|
2154
2231
|
performance.now() - started,
|
|
2155
2232
|
cacheLabel ?? "none",
|
|
2233
|
+
authLabel ?? { kind: "none" },
|
|
2156
2234
|
);
|
|
2157
2235
|
};
|
|
2158
2236
|
|
|
@@ -2175,8 +2253,9 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
2175
2253
|
}
|
|
2176
2254
|
|
|
2177
2255
|
if (method === "GET" && url.pathname === "/_oke/client.json") {
|
|
2256
|
+
const descriptor = buildClientDescriptor(routes, flowsByName);
|
|
2178
2257
|
return respond(
|
|
2179
|
-
new Response(JSON.stringify(
|
|
2258
|
+
new Response(JSON.stringify(descriptor), {
|
|
2180
2259
|
headers: { "content-type": "application/json" },
|
|
2181
2260
|
}),
|
|
2182
2261
|
);
|
|
@@ -2207,6 +2286,11 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
2207
2286
|
);
|
|
2208
2287
|
runLabel = internalResult.runId;
|
|
2209
2288
|
cacheLabel = internalResult.cache;
|
|
2289
|
+
authLabel = loadJsonCodeBlock().jsonCodeAuthFrom({
|
|
2290
|
+
userId: internalResult.fx.auth.userId,
|
|
2291
|
+
apiKeyId: internalResult.fx.auth.apiKeyId,
|
|
2292
|
+
operatorId: internalResult.fx.operator.id,
|
|
2293
|
+
});
|
|
2210
2294
|
return respond(await encodeExecuteResult(internalResult));
|
|
2211
2295
|
}
|
|
2212
2296
|
}
|
|
@@ -2285,6 +2369,12 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
2285
2369
|
});
|
|
2286
2370
|
runLabel = result.runId;
|
|
2287
2371
|
cacheLabel = result.cache;
|
|
2372
|
+
authLabel = loadJsonCodeBlock().jsonCodeAuthFrom({
|
|
2373
|
+
userId: result.fx.auth.userId,
|
|
2374
|
+
apiKeyId: result.fx.auth.apiKeyId,
|
|
2375
|
+
operatorId: result.fx.operator.id,
|
|
2376
|
+
publicGate: gateNamesOf(binding.trigger).includes(GATE_PUBLIC_NAME),
|
|
2377
|
+
});
|
|
2288
2378
|
|
|
2289
2379
|
return respond(await encodeExecuteResult(result));
|
|
2290
2380
|
},
|
|
@@ -2305,8 +2395,11 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
2305
2395
|
async dispatchEvery(interval, extras) {
|
|
2306
2396
|
const results: ExecuteResult[] = [];
|
|
2307
2397
|
for (const b of adopted) {
|
|
2308
|
-
if (
|
|
2309
|
-
|
|
2398
|
+
if (
|
|
2399
|
+
(b.trigger.kind === "clock" && b.trigger.name === interval) ||
|
|
2400
|
+
(b.trigger.kind === "every" && b.trigger.interval === interval)
|
|
2401
|
+
) {
|
|
2402
|
+
results.push(await execute(b.flow, undefined, b.trigger, extras));
|
|
2310
2403
|
}
|
|
2311
2404
|
}
|
|
2312
2405
|
return results;
|
|
@@ -44,9 +44,8 @@ describe("automatic tier-1 cache from effects", () => {
|
|
|
44
44
|
const db = store.sql("app", { schema: { notes } });
|
|
45
45
|
let lists = 0;
|
|
46
46
|
const list = on(
|
|
47
|
-
http.get("/notes").public(),
|
|
47
|
+
http.get("/notes", { out: z.array(NoteOut) }).public(),
|
|
48
48
|
flow("notes.list", {
|
|
49
|
-
out: z.array(NoteOut),
|
|
50
49
|
effects: { reads: ["sql:notes"] },
|
|
51
50
|
do: () => {
|
|
52
51
|
lists += 1;
|
|
@@ -80,9 +79,8 @@ describe("automatic tier-1 cache from effects", () => {
|
|
|
80
79
|
resetFlowSeq();
|
|
81
80
|
const db = store.sql("app", { schema: { notes } });
|
|
82
81
|
const list = on(
|
|
83
|
-
http.get("/notes").public(),
|
|
82
|
+
http.get("/notes", { out: z.array(NoteOut) }).public(),
|
|
84
83
|
flow("notes.list", {
|
|
85
|
-
out: z.array(NoteOut),
|
|
86
84
|
effects: { reads: ["sql:notes"] },
|
|
87
85
|
do: () => [{ id: "n1", title: "Harbor" }],
|
|
88
86
|
}),
|
|
@@ -146,9 +144,8 @@ describe("automatic tier-1 cache from effects", () => {
|
|
|
146
144
|
const db = store.sql("app", { schema: { notes } });
|
|
147
145
|
let lists = 0;
|
|
148
146
|
const list = on(
|
|
149
|
-
http.get("/notes").public(),
|
|
147
|
+
http.get("/notes", { out: z.array(NoteOut) }).public(),
|
|
150
148
|
flow("notes.list", {
|
|
151
|
-
out: z.array(NoteOut),
|
|
152
149
|
do: async (_input, fx) => {
|
|
153
150
|
lists += 1;
|
|
154
151
|
const rows = await fx.store(db).select().from(notes);
|
|
@@ -201,9 +198,8 @@ describe("automatic tier-1 cache from effects", () => {
|
|
|
201
198
|
}),
|
|
202
199
|
);
|
|
203
200
|
const create = on(
|
|
204
|
-
http.post("/notes").public(),
|
|
201
|
+
http.post("/notes", { in: z.object({ title: z.string() }) }).public(),
|
|
205
202
|
flow("notes.create", {
|
|
206
|
-
in: z.object({ title: z.string() }),
|
|
207
203
|
effects: { writes: ["sql:notes"] },
|
|
208
204
|
do: (input) => ({ id: "n-new", title: input.title }),
|
|
209
205
|
}),
|
|
@@ -231,9 +227,8 @@ describe("automatic tier-1 cache from effects", () => {
|
|
|
231
227
|
const db = store.sql("app", { schema: { notes } });
|
|
232
228
|
let lists = 0;
|
|
233
229
|
const list = on(
|
|
234
|
-
http.get("/notes").public(),
|
|
230
|
+
http.get("/notes", { out: z.array(NoteOut) }).public(),
|
|
235
231
|
flow("notes.list", {
|
|
236
|
-
out: z.array(NoteOut),
|
|
237
232
|
do: async (_input, fx) => {
|
|
238
233
|
lists += 1;
|
|
239
234
|
const rows = await fx.store(db).select().from(notes);
|
|
@@ -242,9 +237,8 @@ describe("automatic tier-1 cache from effects", () => {
|
|
|
242
237
|
}),
|
|
243
238
|
);
|
|
244
239
|
const create = on(
|
|
245
|
-
http.post("/notes").public(),
|
|
240
|
+
http.post("/notes", { in: z.object({ title: z.string() }) }).public(),
|
|
246
241
|
flow("notes.create", {
|
|
247
|
-
in: z.object({ title: z.string() }),
|
|
248
242
|
do: async (input, fx) => {
|
|
249
243
|
const id = `n-${lists + 1}`;
|
|
250
244
|
await fx.store(db).insert(notes).values({ id, title: input.title });
|
|
@@ -33,7 +33,7 @@ describe("oke() auto-registry — stores/secrets/signals/clocks/gates/channel.te
|
|
|
33
33
|
const dbBefore = store.sql("before-app", { schema: {} });
|
|
34
34
|
const filesBefore = store.files("before-uploads");
|
|
35
35
|
const secretBefore = vault.secret("BEFORE_WEBHOOK_SECRET", { dev: "dev-secret-before" });
|
|
36
|
-
const signalBefore = signal("before-note-created", {
|
|
36
|
+
const signalBefore = signal.once("before-note-created", { optional: true });
|
|
37
37
|
const mailBefore = channel.email({ from: "Before <before@localhost>" });
|
|
38
38
|
const templateBefore = mailBefore.template("before-note-created");
|
|
39
39
|
|
|
@@ -71,7 +71,7 @@ describe("oke() auto-registry — stores/secrets/signals/clocks/gates/channel.te
|
|
|
71
71
|
store.sql("after-app", { schema: {} });
|
|
72
72
|
store.files("after-uploads");
|
|
73
73
|
const secretAfter = vault.secret("AFTER_WEBHOOK_SECRET", { dev: "dev-secret-after" });
|
|
74
|
-
const signalAfter = signal("after-note-created", {
|
|
74
|
+
const signalAfter = signal.once("after-note-created", { optional: true });
|
|
75
75
|
const mailAfter = channel.email({ from: "After <after@localhost>" });
|
|
76
76
|
const templateAfter = mailAfter.template("after-note-created");
|
|
77
77
|
|
|
@@ -204,7 +204,7 @@ describe("oke() auto-registry — stores/secrets/signals/clocks/gates/channel.te
|
|
|
204
204
|
test('registry: "consume" (default) drains stores/secrets/signals/channel.templates — a later app does not inherit them', async () => {
|
|
205
205
|
store.sql("leak-app", { schema: {} });
|
|
206
206
|
vault.secret("LEAK_SECRET", { dev: "dev-leak" });
|
|
207
|
-
signal("leak-signal"
|
|
207
|
+
signal.once("leak-signal");
|
|
208
208
|
channel.email().template("leak-template");
|
|
209
209
|
|
|
210
210
|
const appA = oke({
|