okengine 0.18.4 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +18 -10
- package/README.md +2 -2
- package/package.json +51 -50
- package/site/content/docs/ai/index.mdx +25 -6
- package/site/content/docs/ai/llms-txt.mdx +1 -1
- package/site/content/docs/ai/meta.json +1 -1
- package/site/content/docs/ai/skills.mdx +4 -1
- package/site/content/docs/ai/try-it.mdx +58 -0
- package/site/content/docs/client/auth.mdx +208 -0
- package/site/content/docs/client/calling.mdx +451 -0
- package/site/content/docs/client/index.mdx +209 -0
- package/site/content/docs/client/live.mdx +234 -0
- package/site/content/docs/client/meta.json +5 -0
- package/site/content/docs/client/react.mdx +249 -0
- package/site/content/docs/elements/ai/agents.mdx +232 -0
- package/site/content/docs/elements/ai/index.mdx +341 -0
- package/site/content/docs/elements/ai/mcp.mdx +275 -0
- package/site/content/docs/elements/ai/meta.json +5 -0
- package/site/content/docs/elements/ai/models.mdx +283 -0
- package/site/content/docs/elements/ai/prompts.mdx +256 -0
- package/site/content/docs/elements/channel/email.mdx +266 -0
- package/site/content/docs/elements/channel/index.mdx +344 -0
- package/site/content/docs/elements/channel/meta.json +5 -0
- package/site/content/docs/elements/channel/push.mdx +218 -0
- package/site/content/docs/elements/channel/receipts.mdx +206 -0
- package/site/content/docs/elements/channel/sms.mdx +264 -0
- package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
- package/site/content/docs/elements/clock/index.mdx +300 -0
- package/site/content/docs/elements/clock/meta.json +5 -0
- package/site/content/docs/elements/clock/schedules.mdx +508 -0
- package/site/content/docs/elements/clock/sleep.mdx +445 -0
- package/site/content/docs/elements/flow/consumers.mdx +707 -0
- package/site/content/docs/elements/flow/http.mdx +1080 -0
- package/site/content/docs/elements/flow/index.mdx +618 -0
- package/site/content/docs/elements/flow/meta.json +5 -0
- package/site/content/docs/elements/flow/routing.mdx +559 -0
- package/site/content/docs/elements/flow/workflows.mdx +640 -0
- package/site/content/docs/elements/gate/auth.mdx +371 -0
- package/site/content/docs/elements/gate/authorization.mdx +298 -0
- package/site/content/docs/elements/gate/index.mdx +418 -0
- package/site/content/docs/elements/gate/meta.json +5 -0
- package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
- package/site/content/docs/elements/gate/rls.mdx +303 -0
- package/site/content/docs/elements/gate/tenancy.mdx +372 -0
- package/site/content/docs/elements/index.mdx +52 -11
- package/site/content/docs/elements/signal/broadcast.mdx +517 -0
- package/site/content/docs/elements/signal/index.mdx +352 -0
- package/site/content/docs/elements/signal/live.mdx +590 -0
- package/site/content/docs/elements/signal/meta.json +5 -0
- package/site/content/docs/elements/signal/once.mdx +596 -0
- package/site/content/docs/elements/store/files.mdx +659 -0
- package/site/content/docs/elements/store/index.mdx +310 -0
- package/site/content/docs/elements/store/kv.mdx +629 -0
- package/site/content/docs/elements/store/meta.json +5 -0
- package/site/content/docs/elements/store/search.mdx +946 -0
- package/site/content/docs/elements/store/sql.mdx +937 -0
- package/site/content/docs/elements/vault/config.mdx +256 -0
- package/site/content/docs/elements/vault/index.mdx +356 -0
- package/site/content/docs/elements/vault/meta.json +5 -0
- package/site/content/docs/elements/vault/rotation.mdx +273 -0
- package/site/content/docs/elements/vault/secrets.mdx +285 -0
- package/site/content/docs/index.mdx +78 -30
- package/site/content/docs/meta.json +11 -8
- package/site/content/docs/plugins/cors.mdx +2 -1
- package/site/content/docs/plugins/csrf.mdx +4 -1
- package/site/content/docs/plugins/headers.mdx +2 -1
- package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
- package/site/content/docs/plugins/magic-link.mdx +6 -0
- package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
- package/site/content/docs/plugins/oauth.mdx +1 -1
- package/site/content/docs/plugins/otp.mdx +12 -1
- package/site/content/docs/plugins/passkey.mdx +39 -15
- package/site/content/docs/plugins/two-factor.mdx +91 -34
- package/site/content/docs/plugins/username.mdx +1 -1
- package/site/content/docs/recipes/caddy.mdx +3 -3
- package/site/content/docs/recipes/index.mdx +18 -28
- package/site/content/docs/recipes/mailpit.mdx +1 -1
- package/site/content/docs/recipes/meilisearch.mdx +3 -3
- package/site/content/docs/recipes/meta.json +1 -4
- package/site/content/docs/recipes/nginx.mdx +2 -2
- package/site/content/docs/recipes/openrouter.mdx +283 -0
- package/site/content/docs/recipes/pgdog.mdx +3 -3
- package/site/content/docs/recipes/postgres.mdx +1 -2
- package/site/content/docs/recipes/rustfs.mdx +2 -2
- package/site/content/docs/recipes/traefik.mdx +3 -3
- package/site/content/docs/reference/cli.mdx +231 -0
- package/site/content/docs/reference/configuration.mdx +30 -10
- package/site/content/docs/reference/environment-variables.mdx +93 -20
- package/site/content/docs/reference/errors.mdx +132 -29
- package/site/content/docs/reference/fx.mdx +137 -73
- package/site/content/docs/reference/index.mdx +13 -21
- package/site/content/docs/reference/meta.json +4 -5
- package/site/content/docs/reference/okid.mdx +42 -17
- package/site/content/docs/reference/plugins.mdx +4 -3
- package/site/content/docs/reference/security.mdx +197 -0
- package/site/content/docs/understand/meta.json +5 -0
- package/site/content/docs/understand/the-anatomy.mdx +132 -0
- package/site/content/docs/understand/the-model.mdx +32 -0
- package/site/content/docs/understand/the-problem.mdx +74 -0
- package/site/content/docs/understand/the-vocabulary.mdx +26 -0
- package/src/auth/api-keys.ts +2 -1
- package/src/auth/bindings.ts +64 -16
- package/src/auth/gate-auth.test.ts +6 -1
- package/src/auth/identity.ts +152 -5
- package/src/auth/index.ts +29 -0
- package/src/auth/invites.ts +2 -1
- package/src/auth/method-context.ts +41 -0
- package/src/auth/oauth-as/crypto.ts +2 -1
- package/src/auth/oauth-as/stores.ts +2 -1
- package/src/auth/operator.ts +2 -1
- package/src/auth/sessions-jwt.test.ts +77 -0
- package/src/auth/sessions.ts +20 -4
- package/src/auth/tenants.ts +3 -2
- package/src/auth/two-factor-challenge.test.ts +61 -0
- package/src/auth/two-factor-challenge.ts +284 -0
- package/src/auth/verification.ts +5 -0
- package/src/bench/REPORT.md +49 -0
- package/src/bench/g03-signal-once.bench.ts +1 -1
- package/src/bench/g10-observability-contention.bench.ts +1 -1
- package/src/bench/g17-hybrid-search.bench.ts +572 -0
- package/src/bench/load-app.ts +2 -10
- package/src/cli/ai-setup/ai-setup.test.ts +331 -42
- package/src/cli/ai-setup/apply.ts +294 -52
- package/src/cli/ai-setup/catalog.ts +238 -1342
- package/src/cli/ai-setup/index.ts +14 -99
- package/src/cli/ai-setup/prompts.ts +60 -443
- package/src/cli/ask-seed.test.ts +96 -0
- package/src/cli/ask-seed.ts +82 -0
- package/src/cli/ask-vault-gaps.test.ts +84 -0
- package/src/cli/ask-vault-gaps.ts +146 -0
- package/src/cli/build.ts +2 -2
- package/src/cli/client-add.test.ts +26 -1
- package/src/cli/client-add.ts +106 -27
- package/src/cli/competitor-mention-removal.test.ts +1 -1
- package/src/cli/db-seed.ts +2 -0
- package/src/cli/db.ts +150 -11
- package/src/cli/dev.ts +119 -211
- package/src/cli/docker-clean.ts +2 -2
- package/src/cli/docker-cli.test.ts +1 -1
- package/src/cli/doctor-diff.ts +4 -2
- package/src/cli/doctor-pii.test.ts +1 -1
- package/src/cli/doctor.ts +14 -1
- package/src/cli/hero-meta.test.ts +4 -2
- package/src/cli/load-config.images.test.ts +8 -10
- package/src/cli/load-config.ts +1 -1
- package/src/cli/registry.ts +22 -10
- package/src/cli/replay.ts +3 -1
- package/src/cli/start.ts +1 -1
- package/src/cli/tui/keys.ts +2 -3
- package/src/client/auth/cookies.ts +74 -0
- package/src/client/auth/create-auth-client.ts +636 -0
- package/src/client/auth/denials.ts +99 -0
- package/src/client/auth/session.ts +283 -0
- package/src/client/auth.test.ts +251 -0
- package/src/client/auth.ts +39 -114
- package/src/client/create-with-session.ts +245 -0
- package/src/client/create.ts +51 -5
- package/src/client/index.ts +11 -1
- package/src/client/live.ts +11 -79
- package/src/client/notes-contract.test.ts +5 -10
- package/src/client/sse.ts +137 -0
- package/src/client/stream.ts +147 -0
- package/src/client/transport.test.ts +32 -0
- package/src/client/transport.ts +123 -26
- package/src/client/types.ts +118 -14
- package/src/client-react/index.ts +185 -24
- package/src/client-react/use-live-query.ts +13 -3
- package/src/compiler/aot.test.ts +7 -4
- package/src/compiler/effects-embed.test.ts +56 -0
- package/src/compiler/effects-fetch.test.ts +43 -0
- package/src/compiler/effects-infer.ts +58 -2
- package/src/compiler/extract.test.ts +258 -44
- package/src/compiler/extract.ts +566 -48
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
- package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
- package/src/compiler/fixtures/skyport.expected.json +4 -4
- package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
- package/src/compiler/response.ts +2 -2
- package/src/compiler/search-writer-isolation.test.ts +40 -0
- package/src/config/index.ts +11 -0
- package/src/console/server/app.ts +17 -23
- package/src/console/server/bind.ts +4 -0
- package/src/console/server/console.test.ts +2 -2
- package/src/console/server/flows-invoke.test.ts +50 -34
- package/src/console/server/flows.ts +436 -255
- package/src/console/server/runs-ingest.test.ts +6 -3
- package/src/console/server/serve.ts +1 -1
- package/src/console/server/signals.test.ts +1 -5
- package/src/console/server/signals.ts +8 -6
- package/src/console/server/state.ts +6 -1
- package/src/console/server/store.ts +1 -1
- package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
- package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
- package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
- package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
- package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
- package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
- package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
- package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
- package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
- package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
- package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
- package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
- package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
- package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
- package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
- package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
- package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
- package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
- package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
- package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
- package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
- package/src/console/ui-next/dist/index.html +6 -13
- package/src/console/ui-next/seed-invoke-host.ts +21 -18
- package/src/console/ui-next/src/client.ts +7 -2
- package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
- package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
- package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
- package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
- package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
- package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
- package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
- package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
- package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
- package/src/docker/ai-model-status.test.ts +1 -12
- package/src/docker/ai-model-status.ts +10 -38
- package/src/docker/compose-up.test.ts +100 -0
- package/src/docker/compose-up.ts +292 -0
- package/src/docker/compose.ts +4 -14
- package/src/docker/derive.ts +9 -34
- package/src/docker/docker.test.ts +16 -261
- package/src/docker/images-config.test.ts +13 -25
- package/src/docker/index.ts +1 -24
- package/src/docker/recipes/index.ts +0 -23
- package/src/docker/recipes/pgdog.ts +6 -4
- package/src/docker/stack-id.test.ts +2 -2
- package/src/docker/stack-id.ts +0 -1
- package/src/docker/types.ts +2 -2
- package/src/drivers/ai-anthropic.ts +10 -0
- package/src/drivers/ai-openai-compatible.ts +34 -7
- package/src/drivers/ai-providers.test.ts +0 -107
- package/src/drivers/ai-stream.test.ts +0 -34
- package/src/drivers/ai-types.ts +17 -8
- package/src/drivers/channel-fcm.ts +6 -1
- package/src/drivers/channel-msegat.ts +6 -1
- package/src/drivers/channel-resend.ts +5 -1
- package/src/drivers/channel-smtp.ts +5 -1
- package/src/drivers/channel-sndr.ts +10 -1
- package/src/drivers/channel-taqnyat-mail.ts +5 -1
- package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
- package/src/drivers/channel-taqnyat.ts +6 -1
- package/src/drivers/channel-types.ts +10 -0
- package/src/drivers/channel-unifonic.ts +6 -1
- package/src/drivers/channel-wa-cloud.ts +6 -1
- package/src/drivers/channel-webpush.ts +6 -1
- package/src/drivers/external.ts +33 -0
- package/src/drivers/index.ts +0 -10
- package/src/drivers/meilisearch.ts +6 -0
- package/src/drivers/oauth-types.ts +4 -0
- package/src/drivers/postgres.test.ts +74 -3
- package/src/drivers/postgres.ts +175 -6
- package/src/drivers/signal-types.ts +5 -5
- package/src/drivers/types.ts +7 -0
- package/src/drivers/vault-types.ts +5 -0
- package/src/elements/ai/declare.ts +17 -4
- package/src/elements/ai/eval.ts +1 -1
- package/src/elements/ai/mcp-http.ts +1 -1
- package/src/elements/ai/pii.ts +1 -1
- package/src/elements/ai/providers.test.ts +289 -0
- package/src/elements/ai/providers.ts +169 -0
- package/src/elements/ai/runtime.ts +68 -5
- package/src/elements/ai/schema.ts +2 -2
- package/src/elements/ai.test.ts +7 -7
- package/src/elements/ai.ts +17 -1
- package/src/elements/channel/runtime.ts +105 -12
- package/src/elements/clock/cron-fields.test.ts +144 -0
- package/src/elements/clock/cron-fields.ts +185 -0
- package/src/elements/clock/declare.ts +247 -3
- package/src/elements/clock.ts +21 -1
- package/src/elements/index.ts +15 -0
- package/src/elements/signal/chaos-child.ts +1 -2
- package/src/elements/signal/declare.ts +65 -37
- package/src/elements/signal/delivery-modes.test.ts +11 -31
- package/src/elements/signal/dry-run-replay.test.ts +1 -7
- package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
- package/src/elements/signal/key-ordering.test.ts +6 -26
- package/src/elements/signal/lease-reclaim.test.ts +2 -6
- package/src/elements/signal/optional-emit.test.ts +6 -9
- package/src/elements/signal/order-lifecycle.test.ts +4 -17
- package/src/elements/signal/orphan-messages.test.ts +4 -15
- package/src/elements/signal/reconcile.test.ts +2 -2
- package/src/elements/signal/runtime.ts +1 -1
- package/src/elements/signal/schema-emit.test.ts +4 -8
- package/src/elements/signal.test.ts +24 -49
- package/src/elements/signal.ts +9 -2
- package/src/elements/store/domain-ddl.test.ts +1 -1
- package/src/elements/store/live-http.test.ts +9 -4
- package/src/elements/store/prepare-row.test.ts +123 -0
- package/src/elements/store/resource.ts +43 -12
- package/src/elements/store/schema-decl.ts +117 -0
- package/src/elements/store/search-backfill.ts +205 -0
- package/src/elements/store/search-bind.ts +66 -0
- package/src/elements/store/search-bm25.ts +78 -0
- package/src/elements/store/search-ddl.ts +156 -0
- package/src/elements/store/search-embed-flow.ts +141 -0
- package/src/elements/store/search-errors.ts +34 -0
- package/src/elements/store/search-fusion.ts +112 -0
- package/src/elements/store/search-lsh.ts +179 -0
- package/src/elements/store/search-runtime.ts +300 -0
- package/src/elements/store/search.test.ts +144 -0
- package/src/elements/store/sql-session.test.ts +1 -0
- package/src/elements/store/sql-session.ts +83 -3
- package/src/elements/store/table.ts +23 -2
- package/src/elements/store/upsert-app.test.ts +1 -3
- package/src/elements/store.ts +50 -0
- package/src/full.ts +4 -2
- package/src/http.ts +4 -1
- package/src/i18n/catalogs/ar.ts +14 -6
- package/src/i18n/catalogs/en.ts +14 -6
- package/src/index.ts +6 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
- package/src/kernel/adopt-routes.ts +9 -1
- package/src/kernel/app-auth.ts +5 -0
- package/src/kernel/app.ts +102 -9
- package/src/kernel/auto-cache.test.ts +6 -12
- package/src/kernel/auto-registry.test.ts +3 -3
- package/src/kernel/boot-bind/ai.test.ts +22 -19
- package/src/kernel/boot-bind/ai.ts +5 -43
- package/src/kernel/boot-bind/clock.ts +23 -5
- package/src/kernel/boot-bind/honor-config.test.ts +4 -4
- package/src/kernel/boot.test.ts +11 -6
- package/src/kernel/boot.ts +66 -16
- package/src/kernel/boundary-contract.ts +93 -0
- package/src/kernel/budget.test.ts +1 -1
- package/src/kernel/call.ts +89 -0
- package/src/kernel/capability.ts +6 -0
- package/src/kernel/client-descriptor.ts +123 -0
- package/src/kernel/clock-timezone.test.ts +80 -0
- package/src/kernel/correlation.test.ts +1 -1
- package/src/kernel/dry-run.ts +11 -8
- package/src/kernel/effects-stamping.test.ts +42 -9
- package/src/kernel/effects.test.ts +4 -2
- package/src/kernel/effects.ts +51 -9
- package/src/kernel/errors-channel.ts +17 -0
- package/src/kernel/errors-live-resume.ts +3 -2
- package/src/kernel/errors-tenant.ts +7 -4
- package/src/kernel/errors.registry-helpers.ts +96 -0
- package/src/kernel/errors.registry.test.ts +139 -25
- package/src/kernel/errors.ts +102 -23
- package/src/kernel/external-effects.test.ts +196 -0
- package/src/kernel/flow.test.ts +13 -4
- package/src/kernel/flow.ts +69 -71
- package/src/kernel/fx-ask-telemetry.test.ts +2 -2
- package/src/kernel/fx-dead-letters.test.ts +3 -3
- package/src/kernel/fx-fetch.ts +55 -0
- package/src/kernel/fx-live-stream.ts +2 -2
- package/src/kernel/fx-live.test.ts +8 -8
- package/src/kernel/fx.test.ts +17 -11
- package/src/kernel/fx.ts +180 -57
- package/src/kernel/horizontal-child.ts +1 -1
- package/src/kernel/http-query.test.ts +3 -6
- package/src/kernel/index.ts +13 -3
- package/src/kernel/instance-id.ts +1 -1
- package/src/kernel/instances.test.ts +40 -0
- package/src/kernel/instances.ts +47 -9
- package/src/kernel/live-http.test.ts +6 -7
- package/src/kernel/live-http.ts +6 -2
- package/src/kernel/live-resume.test.ts +1 -1
- package/src/kernel/mcp-tool.test.ts +3 -3
- package/src/kernel/on.ts +98 -4
- package/src/kernel/pipeline.test.ts +12 -15
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/stamp-http.test.ts +3 -3
- package/src/kernel/triggers.ts +210 -95
- package/src/kernel-entry.ts +0 -1
- package/src/manifest/diff.ts +11 -1
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/types.ts +46 -2
- package/src/mcp/docs-index.ts +3 -3
- package/src/mcp/docs-mcp.test.ts +2 -2
- package/src/mcp/docs-tools.ts +1 -1
- package/src/okid.test.ts +56 -0
- package/src/okid.ts +55 -11
- package/src/plugins/anonymous.ts +8 -4
- package/src/plugins/auth/shared.ts +27 -3
- package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
- package/src/plugins/auth-methods.security.test.ts +561 -23
- package/src/plugins/config-source.ts +1 -1
- package/src/plugins/csrf.test.ts +65 -0
- package/src/plugins/index.ts +2 -0
- package/src/plugins/magic-link.ts +36 -16
- package/src/plugins/oauth/shared.ts +3 -1
- package/src/plugins/oauth.ts +22 -14
- package/src/plugins/otp.ts +37 -21
- package/src/plugins/passkey-webauthn.ts +9 -6
- package/src/plugins/passkey.ts +98 -26
- package/src/plugins/pre-account-hijack.test.ts +223 -0
- package/src/plugins/two-factor.ts +466 -37
- package/src/plugins/username.ts +19 -7
- package/src/release/build-lib.ts +1 -0
- package/src/release/limits.ts +2 -2
- package/src/release/measure.ts +1 -1
- package/src/runs/collect.test.ts +0 -2
- package/src/runtime/json-code-block.test.ts +250 -20
- package/src/runtime/json-code-block.ts +1261 -41
- package/src/term.test.ts +1 -1
- package/src/test/create-test-app.test.ts +12 -12
- package/src/test/live-signals.test.ts +6 -6
- package/src/test/provisions.integration.test.ts +10 -14
- package/src/test/reset-element-registries.ts +1 -1
- package/src/test/tenant-isolation.test.ts +12 -6
- package/site/content/docs/deployment/docker-swarm.mdx +0 -164
- package/site/content/docs/deployment/docker.mdx +0 -227
- package/site/content/docs/deployment/index.mdx +0 -83
- package/site/content/docs/deployment/kubernetes.mdx +0 -176
- package/site/content/docs/deployment/meta.json +0 -5
- package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
- package/site/content/docs/elements/ai.mdx +0 -385
- package/site/content/docs/elements/channel.mdx +0 -346
- package/site/content/docs/elements/clock.mdx +0 -244
- package/site/content/docs/elements/flow.mdx +0 -420
- package/site/content/docs/elements/gate.mdx +0 -436
- package/site/content/docs/elements/signal.mdx +0 -380
- package/site/content/docs/elements/store.mdx +0 -1099
- package/site/content/docs/elements/vault.mdx +0 -405
- package/site/content/docs/get-started/basic-usage.mdx +0 -173
- package/site/content/docs/get-started/index.mdx +0 -43
- package/site/content/docs/get-started/installation.mdx +0 -220
- package/site/content/docs/get-started/introduction.mdx +0 -132
- package/site/content/docs/get-started/meta.json +0 -13
- package/site/content/docs/get-started/project-structure.mdx +0 -925
- package/site/content/docs/get-started/testing.mdx +0 -328
- package/site/content/docs/get-started/why.mdx +0 -162
- package/site/content/docs/recipes/llama-cpp.mdx +0 -151
- package/site/content/docs/recipes/ollama.mdx +0 -142
- package/site/content/docs/recipes/sglang.mdx +0 -105
- package/site/content/docs/recipes/vllm.mdx +0 -106
- package/site/content/docs/reference/cli.md +0 -232
- package/site/content/docs/reference/client.mdx +0 -469
- package/site/content/docs/reference/security.md +0 -74
- package/src/cli/ai-setup/detect-ollama.ts +0 -213
- package/src/cli/ai-setup/recommend.test.ts +0 -225
- package/src/cli/ai-setup/recommend.ts +0 -225
- package/src/cli/dev-controls.test.ts +0 -122
- package/src/cli/dev-controls.ts +0 -164
- package/src/cli/tui/DevLive.tsx +0 -126
- package/src/console/ui-next/dist/assets/access-page-De7Lc2JC.js +0 -4
- package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
- package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
- package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
- package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
- package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
- package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
- package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
- package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
- package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-RGy7VEA_.js +0 -1
- package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
- package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
- package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.js +0 -4
- package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
- package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
- package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
- package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
- package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
- package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
- package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
- package/src/console/ui-next/dist/assets/units-page-4rHOePuE.js +0 -1
- package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
- package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-DISPgxLM.js +0 -2
- package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
- package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
- package/src/docker/ollama-pull.ts +0 -232
- package/src/docker/recipes/llama-cpp.ts +0 -298
- package/src/docker/recipes/ollama.ts +0 -44
- package/src/docker/recipes/sglang.ts +0 -55
- package/src/docker/recipes/vllm.ts +0 -44
- package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
- package/src/drivers/ai-ollama.integration.test.ts +0 -184
- package/src/drivers/ai-ollama.ts +0 -389
- package/src/drivers/ollama.ts +0 -14
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Schedules"
|
|
3
|
+
description: "Cron helpers, every-intervals, app timezones, per-tenant rows, leader locks, catch-up one, and Console overrides."
|
|
4
|
+
icon: "Calendar"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Named schedules trigger Flows on a calendar cron **or** a fixed duration. Declare with
|
|
9
|
+
`clock` / helpers, set the zone once on the app, then bind with `on(clockDecl, flow)`.
|
|
10
|
+
|
|
11
|
+
For developers running daily digests, health pings, and per-tenant billing loops on okengine.
|
|
12
|
+
|
|
13
|
+
<Callout title="The one rule">
|
|
14
|
+
Calendar work: prefer `oke({ clock: { timezone } })` (or `defineConfig`) and omit
|
|
15
|
+
`timezone` on each schedule. Intervals: durations are `ms|s|m|h|d` only — no weeks.
|
|
16
|
+
Both shapes share leader locks, catch-up `"one"`, and the Store.
|
|
17
|
+
</Callout>
|
|
18
|
+
|
|
19
|
+
<ClockSchedules />
|
|
20
|
+
|
|
21
|
+
## Smallest Example
|
|
22
|
+
|
|
23
|
+
<Steps>
|
|
24
|
+
|
|
25
|
+
<Step>
|
|
26
|
+
### Declare a schedule and bind a Flow
|
|
27
|
+
|
|
28
|
+
```typescript title="src/flows/reports/daily.ts"
|
|
29
|
+
import { on, flow, clock } from "okengine";
|
|
30
|
+
|
|
31
|
+
export const dailyReportClock = clock.daily("reports.daily", { at: "06:00" });
|
|
32
|
+
|
|
33
|
+
export const runDailyReport = on(
|
|
34
|
+
dailyReportClock,
|
|
35
|
+
flow("reports.runDaily", {
|
|
36
|
+
plane: "operator",
|
|
37
|
+
do: async (_, fx) => {
|
|
38
|
+
await fx.call(buildDailyReport, { at: fx.clock.now() });
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Set the zone once so each schedule stays short:
|
|
45
|
+
|
|
46
|
+
```typescript title="src/app.ts"
|
|
47
|
+
import { oke } from "okengine";
|
|
48
|
+
|
|
49
|
+
export const app = oke({
|
|
50
|
+
name: "notes",
|
|
51
|
+
clock: { timezone: "Asia/Riyadh" },
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or `defineConfig({ clock: { timezone: "Asia/Riyadh" } })`. `oke({ clock })` wins over
|
|
56
|
+
config; a per-clock `timezone` wins over both.
|
|
57
|
+
|
|
58
|
+
</Step>
|
|
59
|
+
|
|
60
|
+
<Step>
|
|
61
|
+
### What fires
|
|
62
|
+
|
|
63
|
+
Every morning at 06:00 in the declared zone, the leader instance runs `reports.runDaily`.
|
|
64
|
+
`do` receives no payload — use `fx.clock.now()` when you need the fire instant.
|
|
65
|
+
|
|
66
|
+
With `oke dev`, the scheduler reconciles `reports.daily` into the Store and leader-elects
|
|
67
|
+
before each tick. Under `drivers.clock.test = "frozen"`, advance time in tests instead of
|
|
68
|
+
waiting for dawn.
|
|
69
|
+
|
|
70
|
+
</Step>
|
|
71
|
+
|
|
72
|
+
</Steps>
|
|
73
|
+
|
|
74
|
+
<Callout title="Jobs are Flows">
|
|
75
|
+
`on(clockDecl, flow)` is the same species as HTTP handlers and Signal consumers. There is no
|
|
76
|
+
separate job runner — see [Consumers · Clock Jobs](/docs/elements/flow/consumers#clock-jobs).
|
|
77
|
+
</Callout>
|
|
78
|
+
|
|
79
|
+
## Progressive Patterns
|
|
80
|
+
|
|
81
|
+
From helpers and intervals to per-tenant rows and Console-tunable schedules:
|
|
82
|
+
|
|
83
|
+
<Tabs items={["Helpers", "Interval", "Per-tenant", "Complex fields", "Overridable"]}>
|
|
84
|
+
|
|
85
|
+
<Tab value="Helpers">
|
|
86
|
+
|
|
87
|
+
Named helpers compile to the same `ClockDecl` (a five-field `cron` or `every` string):
|
|
88
|
+
|
|
89
|
+
```typescript title="src/clocks/reports.ts"
|
|
90
|
+
import { clock } from "okengine";
|
|
91
|
+
|
|
92
|
+
export const daily = clock.daily("reports.daily", { at: "06:00" });
|
|
93
|
+
export const rollup = clock.hourly("metrics.rollup", { minute: 0 });
|
|
94
|
+
export const digest = clock.weekly("notes.digest", {
|
|
95
|
+
on: ["mon", "fri"],
|
|
96
|
+
at: "09:00",
|
|
97
|
+
});
|
|
98
|
+
export const close = clock.monthly("billing.close", { on: [1, 15], at: "00:00" });
|
|
99
|
+
export const ping = clock.every("health.ping", "30s");
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Raw strings still work: `clock("x", { cron: "0 6 * * *" })`, Bun nicknames (`@hourly`),
|
|
103
|
+
and `clock("x", { every: "30s" })`.
|
|
104
|
+
|
|
105
|
+
**Consequence:** Manifest / Store still see `cron: "0 6 * * *"` — helpers are declare-time sugar.
|
|
106
|
+
|
|
107
|
+
</Tab>
|
|
108
|
+
|
|
109
|
+
<Tab value="Interval">
|
|
110
|
+
|
|
111
|
+
Fixed duration loops — `"200ms"` · `"30s"` · `"5m"` · `"1h"` · `"7d"`:
|
|
112
|
+
|
|
113
|
+
```typescript title="src/flows/health/ping.ts"
|
|
114
|
+
import { on, flow, clock } from "okengine";
|
|
115
|
+
|
|
116
|
+
export const pingClock = clock.every("health.pingExternal", "30s");
|
|
117
|
+
|
|
118
|
+
export const pingExternal = on(
|
|
119
|
+
pingClock,
|
|
120
|
+
flow("health.pingExternal", {
|
|
121
|
+
plane: "operator",
|
|
122
|
+
do: async (_, fx) => {
|
|
123
|
+
await fx.call(pingUpstream);
|
|
124
|
+
},
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Combine with injectable offsets inside `do`:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
await fx.call(dropExpired, { before: fx.clock.ago("1h") });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Unknown duration strings parse as `0` ms and never become due. A `"d"` is exactly
|
|
136
|
+
86_400_000 ms — not a calendar day across DST. No week unit; no jitter on `clock()`
|
|
137
|
+
(retry jitter lives on Flow `retry`).
|
|
138
|
+
|
|
139
|
+
</Tab>
|
|
140
|
+
|
|
141
|
+
<Tab value="Per-tenant">
|
|
142
|
+
|
|
143
|
+
`clock.perTenant` expands one Store row per tenant (`{name}#{tenantId}`). The bare
|
|
144
|
+
template name is never ticked:
|
|
145
|
+
|
|
146
|
+
```typescript title="src/clocks/invoices.ts"
|
|
147
|
+
import { on, flow, clock } from "okengine";
|
|
148
|
+
|
|
149
|
+
export const invoicesClock = clock.perTenant("invoices", { every: "1h" });
|
|
150
|
+
|
|
151
|
+
export const runInvoices = on(
|
|
152
|
+
invoicesClock,
|
|
153
|
+
flow("billing.invoices", {
|
|
154
|
+
plane: "operator",
|
|
155
|
+
do: async (_, fx) => {
|
|
156
|
+
await fx.call(closeOpenInvoices);
|
|
157
|
+
},
|
|
158
|
+
}),
|
|
159
|
+
);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Equivalent: `clock("invoices", { every: "1h", perTenant: true })`.
|
|
163
|
+
|
|
164
|
+
**Consequence:** ten tenants → ten leader-elected rows. Catch-up still fires **once per
|
|
165
|
+
row** after downtime — not a burst of missed hours per tenant.
|
|
166
|
+
|
|
167
|
+
</Tab>
|
|
168
|
+
|
|
169
|
+
<Tab value="Complex fields">
|
|
170
|
+
|
|
171
|
+
`clock.cron` accepts a string **or** a field bag (lists, ranges, steps, weekday names):
|
|
172
|
+
|
|
173
|
+
```typescript title="src/clocks/ops.ts"
|
|
174
|
+
import { clock } from "okengine";
|
|
175
|
+
|
|
176
|
+
export const sweep = clock.cron("ops.sweep", {
|
|
177
|
+
minute: "*/15",
|
|
178
|
+
hour: [9, 12, 17],
|
|
179
|
+
dayOfWeek: "1-5",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
export const digest = clock.cron("ops.digest", {
|
|
183
|
+
at: "08:00",
|
|
184
|
+
dayOfWeek: ["mon", "wed", "fri"],
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
| Field | Examples |
|
|
189
|
+
| ------------------------------------------ | --------------------------------------------------- |
|
|
190
|
+
| `minute` / `hour` / `dayOfMonth` / `month` | `0` · `[9, 12, 17]` · `"1-5"` · step strings |
|
|
191
|
+
| `dayOfWeek` | `1` · `"mon"` · `["mon", "fri"]` · `"1-5"` |
|
|
192
|
+
| `at` | `"06:00"` — sets minute + hour when those are unset |
|
|
193
|
+
|
|
194
|
+
Only standard five-field cron (what Bun parses). No “last day of month” / “nth weekday”.
|
|
195
|
+
|
|
196
|
+
You may set both `cron` and `every` on one declaration; extract records the cron as the
|
|
197
|
+
Manifest trigger when both are present. Prefer one primary shape unless you want both.
|
|
198
|
+
|
|
199
|
+
</Tab>
|
|
200
|
+
|
|
201
|
+
<Tab value="Overridable">
|
|
202
|
+
|
|
203
|
+
`overridable: true` lets Console edit the effective cron/every in the Store:
|
|
204
|
+
|
|
205
|
+
```typescript title="src/clocks/digest.ts"
|
|
206
|
+
import { clock } from "okengine";
|
|
207
|
+
|
|
208
|
+
export const digestClock = clock.daily("notes.digest", {
|
|
209
|
+
at: "08:00",
|
|
210
|
+
overridable: true,
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Without it, a Console edit fails with `ScheduleNotOverridableError`
|
|
215
|
+
(`clock "{name}" is not overridable`).
|
|
216
|
+
|
|
217
|
+
</Tab>
|
|
218
|
+
|
|
219
|
+
</Tabs>
|
|
220
|
+
|
|
221
|
+
## Helper Reference
|
|
222
|
+
|
|
223
|
+
| Helper / form | Signature | Default / notes | Compiles to |
|
|
224
|
+
| ----------------- | ----------------------------------------- | ------------------------------------------------------------------ | --------------------- |
|
|
225
|
+
| `clock` | `clock(name, { cron? \| every?, … })` | Need `cron` and/or `every` | same |
|
|
226
|
+
| `clock.daily` | `clock.daily(name, { at?, … })` | `at` default `"00:00"`; invalid `at` → `cron at: expected "HH:MM"` | five-field cron |
|
|
227
|
+
| `clock.hourly` | `clock.hourly(name, { minute?, … })` | `minute` default `0` | five-field cron |
|
|
228
|
+
| `clock.weekly` | `clock.weekly(name, { on, at?, … })` | `on` required (`sun`…`sat` / `0–6`); `at` default `"00:00"` | five-field cron |
|
|
229
|
+
| `clock.monthly` | `clock.monthly(name, { on, at?, … })` | `on` required (day of month); no “last day” token | five-field cron |
|
|
230
|
+
| `clock.cron` | `clock.cron(name, expr \| fields, opts?)` | Empty field bag throws; invalid string → `Bun.cron.parse` | five-field / nickname |
|
|
231
|
+
| `clock.every` | `clock.every(name, duration, opts?)` | Same duration grammar as `fx.clock.ago` / sleep | `every` string |
|
|
232
|
+
| `clock.perTenant` | `clock.perTenant(name, opts)` | Expands `{name}#{tenantId}` rows | same + `perTenant` |
|
|
233
|
+
|
|
234
|
+
At least one of `cron` or `every` is required on the callable form. Empty options throw
|
|
235
|
+
`clock("name"): require cron or every`. Invalid cron throws at declare (`invalid cron …`).
|
|
236
|
+
|
|
237
|
+
An empty `clock.cron` field bag throws
|
|
238
|
+
`cron fields: require at least one of at, minute, hour, dayOfMonth, month, dayOfWeek`.
|
|
239
|
+
|
|
240
|
+
## Timezone Resolution
|
|
241
|
+
|
|
242
|
+
Cron math needs an IANA zone. Resolve it once; override only when a schedule must differ.
|
|
243
|
+
|
|
244
|
+
| Source | Wins when | Example |
|
|
245
|
+
| --------------------------------------- | ------------------- | ---------------------------------------------------------- |
|
|
246
|
+
| Per-clock `timezone` | Always | `clock.daily("x", { at: "06:00", timezone: "UTC" })` |
|
|
247
|
+
| `oke({ clock: { timezone } })` | No per-clock zone | `oke({ name: "app", clock: { timezone: "Asia/Riyadh" } })` |
|
|
248
|
+
| `defineConfig({ clock: { timezone } })` | No `oke({ clock })` | config default |
|
|
249
|
+
| Built-in default | Nothing else set | `"UTC"` |
|
|
250
|
+
|
|
251
|
+
```typescript title="src/app.ts"
|
|
252
|
+
import { oke } from "okengine";
|
|
253
|
+
import { clock } from "okengine";
|
|
254
|
+
|
|
255
|
+
export const app = oke({
|
|
256
|
+
name: "notes",
|
|
257
|
+
clock: { timezone: "Asia/Riyadh" },
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Inherits Asia/Riyadh
|
|
261
|
+
export const digest = clock.daily("notes.digest", { at: "08:00" });
|
|
262
|
+
|
|
263
|
+
// Explicit UTC wins over the app default
|
|
264
|
+
export const utcRollup = clock.hourly("metrics.utc", {
|
|
265
|
+
minute: 0,
|
|
266
|
+
timezone: "UTC",
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
`{ tz: "…" }` is not an option — it is ignored. Intervals (`every`) are duration-based and
|
|
271
|
+
do not consult the zone for tick spacing; the zone still lands on the Store row.
|
|
272
|
+
|
|
273
|
+
## Binding & Input
|
|
274
|
+
|
|
275
|
+
Bind the returned handle with `on(clockDecl, flow)`. The scheduler invokes `do` with **no
|
|
276
|
+
payload** — destructure `_` and read time through `fx.clock`:
|
|
277
|
+
|
|
278
|
+
```typescript title="src/flows/metrics/cleanup.ts"
|
|
279
|
+
import { on, flow, clock } from "okengine";
|
|
280
|
+
import { lt } from "drizzle-orm";
|
|
281
|
+
import { db, metricLogs } from "@/schema";
|
|
282
|
+
|
|
283
|
+
export const cleanupClock = clock.every("metrics.cleanup", "1h");
|
|
284
|
+
|
|
285
|
+
export const cleanup = on(
|
|
286
|
+
cleanupClock,
|
|
287
|
+
flow("metrics.cleanup", {
|
|
288
|
+
plane: "operator",
|
|
289
|
+
do: async (_, fx) => {
|
|
290
|
+
await fx
|
|
291
|
+
.store(db)
|
|
292
|
+
.delete(metricLogs)
|
|
293
|
+
.where(lt(metricLogs.timestamp, fx.clock.ago("7d")));
|
|
294
|
+
return { at: new Date(fx.clock.now()).toISOString() };
|
|
295
|
+
},
|
|
296
|
+
}),
|
|
297
|
+
);
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
| Concern | Rule |
|
|
301
|
+
| ------- | ------------------------------------------------------------------ |
|
|
302
|
+
| Trigger | `on(clockDecl, flow)` — same Flow species as HTTP / Signal |
|
|
303
|
+
| Input | none — use `_` |
|
|
304
|
+
| Time | `fx.clock.now` / `ago` / `fromNow` / `duration` only |
|
|
305
|
+
| Plane | Prefer `plane: "operator"` for background work |
|
|
306
|
+
| Import | The `clock(…)` module must load at boot so reconcile sees the decl |
|
|
307
|
+
|
|
308
|
+
## Options Reference
|
|
309
|
+
|
|
310
|
+
Shared options on `clock(name, options)` and helpers:
|
|
311
|
+
|
|
312
|
+
| Option / helper | Type | Default | Meaning |
|
|
313
|
+
| --------------------------------------- | ----------- | --------------------- | ------------------------------------------ |
|
|
314
|
+
| `cron` | `string` | — | Five-field cron or Bun nickname |
|
|
315
|
+
| `every` | `string` | — | Interval (`"30s"`, `"1h"`, `"7d"`, …) |
|
|
316
|
+
| `timezone` | IANA string | app default / `"UTC"` | Zone for cron math |
|
|
317
|
+
| `overridable` | `boolean` | `false` | Console may override schedule in the Store |
|
|
318
|
+
| `perTenant` | `boolean` | `false` | Expand `{name}#{tenantId}` rows |
|
|
319
|
+
| `description` | `string` | the name | Console / docs blurb |
|
|
320
|
+
| `oke({ clock: { timezone } })` | app option | — | Default zone when `timezone` omitted |
|
|
321
|
+
| `defineConfig({ clock: { timezone } })` | config | — | Same; overridden by `oke({ clock })` |
|
|
322
|
+
|
|
323
|
+
### Duration units (`every`)
|
|
324
|
+
|
|
325
|
+
| Unit | Example | Milliseconds |
|
|
326
|
+
| ---- | --------- | ------------ |
|
|
327
|
+
| `ms` | `"200ms"` | 200 |
|
|
328
|
+
| `s` | `"30s"` | 30_000 |
|
|
329
|
+
| `m` | `"5m"` | 300_000 |
|
|
330
|
+
| `h` | `"1h"` | 3_600_000 |
|
|
331
|
+
| `d` | `"7d"` | 604_800_000 |
|
|
332
|
+
|
|
333
|
+
## Per-tenant Schedules
|
|
334
|
+
|
|
335
|
+
`clock.perTenant` expands one Store row per tenant (`{name}#{tenantId}`). The bare
|
|
336
|
+
template name never ticks — `runNow("invoices")` returns `false`.
|
|
337
|
+
|
|
338
|
+
Tenant ids come from reconcile (`tenantIds`). New tenants get rows; deleted tenants mark
|
|
339
|
+
those rows `orphaned`.
|
|
340
|
+
|
|
341
|
+
| Shape | Example | Fires? |
|
|
342
|
+
| ------------------------ | --------------- | -------------------- |
|
|
343
|
+
| Template (declared name) | `invoices` | No — never ticked |
|
|
344
|
+
| Expanded row | `invoices#acme` | Yes — leader-elected |
|
|
345
|
+
|
|
346
|
+
**Consequence:** ten tenants → ten leases. Catch-up is `"one"` **per row**, so downtime
|
|
347
|
+
does not replay a burst of missed hours for each tenant.
|
|
348
|
+
|
|
349
|
+
## Leader Lock
|
|
350
|
+
|
|
351
|
+
Multi-instance deploys need a shared clock driver — otherwise every pod fires the same
|
|
352
|
+
tick. Dev/prod default is **postgres** (test is **frozen**). A short lease (default
|
|
353
|
+
**30s**) means only one instance runs each fire.
|
|
354
|
+
|
|
355
|
+
**Consequence:** three pods calling `runNow` still execute the Flow once. After the lease
|
|
356
|
+
expires, another instance may take the next tick.
|
|
357
|
+
|
|
358
|
+
| Driver | Cross-instance lock | Best for |
|
|
359
|
+
| ---------- | ----------------------------------------- | --------------------------------- |
|
|
360
|
+
| `postgres` | Yes — shared `oke_crons` | Dev/prod multi-instance (default) |
|
|
361
|
+
| `file` | Yes — on one host (`.oke/crons.json`) | Single-host multi-process |
|
|
362
|
+
| `memory` | No — single process only | Local single process |
|
|
363
|
+
| `frozen` | Test harness — you drive `tick` / advance | Tests |
|
|
364
|
+
|
|
365
|
+
## Catch-up Policy
|
|
366
|
+
|
|
367
|
+
<ClockCatchUp />
|
|
368
|
+
|
|
369
|
+
Catch-up is `"one"`: after 5 hours down on an hourly clock, the next tick fires **once**.
|
|
370
|
+
Missed slots are visible as `missedRuns` — they are not replayed as a burst.
|
|
371
|
+
|
|
372
|
+
**Consequence:** a digest that missed the night still runs once at boot, not 24 times.
|
|
373
|
+
|
|
374
|
+
Console health on each row exposes `driftMs`, `overdue`, `missedRuns`, and `catchUp: "one"`.
|
|
375
|
+
Flow `retry` is separate — it retries a failed fire, not missed calendar slots.
|
|
376
|
+
|
|
377
|
+
## Store Lifecycle & Console
|
|
378
|
+
|
|
379
|
+
Named clocks reconcile into the Store at boot. The scheduler reads **effective** state from
|
|
380
|
+
the Store, never the source declaration directly.
|
|
381
|
+
|
|
382
|
+
```text
|
|
383
|
+
Declared (Manifest / code) ← truth for names and defaults
|
|
384
|
+
Override (Store, overridable) ← operational drift
|
|
385
|
+
Effective = declared + override ← what actually runs
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
| Status | Meaning | Fires? |
|
|
389
|
+
| ---------- | ----------------------------- | ---------------------------- |
|
|
390
|
+
| `active` | Reconciled and enabled | Yes (when due + lease) |
|
|
391
|
+
| `paused` | Operator paused via Console | No — until `active` again |
|
|
392
|
+
| `orphaned` | Decl removed (or tenant gone) | No — row kept, never deleted |
|
|
393
|
+
|
|
394
|
+
Reconcile upserts still-declared clocks as `active` (preserving lease / `lastRunAt` /
|
|
395
|
+
overrides when `overridable`). Removed declarations become `orphaned`.
|
|
396
|
+
|
|
397
|
+
| Console action | Requirement | Error when missing |
|
|
398
|
+
| --------------- | ------------------- | ------------------------------------------------------ |
|
|
399
|
+
| Run now | Active row + lease | `ClockResourceNotFoundError` — `cron "{id}" not found` |
|
|
400
|
+
| Pause | Existing row | same |
|
|
401
|
+
| Edit cron/every | `overridable: true` | `ScheduleNotOverridableError` |
|
|
402
|
+
|
|
403
|
+
**Consequence:** without `overridable: true`, change the schedule in source and redeploy —
|
|
404
|
+
Console edits are refused. Redeploying a still-overridable clock **preserves** overrides.
|
|
405
|
+
|
|
406
|
+
DST gap / fall-back overlap attaches a Store warning (`gap` / `overlap`); UTC never
|
|
407
|
+
warns; the scheduler still ticks. Detection covers simple `M H * * *` / `M H * * DOW`
|
|
408
|
+
forms; on overlap days crontab fires **once** (first occurrence).
|
|
409
|
+
|
|
410
|
+
| Prefer | When |
|
|
411
|
+
| ----------------------------------------------- | ------------------------------------ |
|
|
412
|
+
| `timezone: "UTC"` | No DST warnings |
|
|
413
|
+
| Non-ambiguous local hour (`08:00`, not `02:00`) | Digests that stay silent on DST days |
|
|
414
|
+
|
|
415
|
+
## Troubleshooting
|
|
416
|
+
|
|
417
|
+
<Accordions>
|
|
418
|
+
|
|
419
|
+
<Accordion title='TypeError: clock("name"): require cron or every'>
|
|
420
|
+
Pass `{cron}` and/or `{every}`, or use a helper (`clock.daily`, `clock.every`, …). Declaration
|
|
421
|
+
throws before `on()`.
|
|
422
|
+
</Accordion>
|
|
423
|
+
|
|
424
|
+
<Accordion title="invalid cron at declare">
|
|
425
|
+
Cause: the expression failed `Bun.cron.parse`. Fix the five-field string / nickname, or fix
|
|
426
|
+
structured fields (`at` must be `"HH:MM"`). Error shape: `clock("…"): invalid cron "…" (…)`.
|
|
427
|
+
</Accordion>
|
|
428
|
+
|
|
429
|
+
<Accordion title='cron at: expected "HH:MM"'>
|
|
430
|
+
`at` on helpers / field bags must be `"HH:MM"` or `"H:MM"` with hour `0–23` and minute `0–59`.
|
|
431
|
+
`"6am"` and `"06:00:00"` fail.
|
|
432
|
+
</Accordion>
|
|
433
|
+
|
|
434
|
+
<Accordion title="Interval never fires">
|
|
435
|
+
Unknown duration strings parse as `0` and never become due. Use `ms|s|m|h|d` only. Confirm the
|
|
436
|
+
Flow is bound with `on(clockDecl, flow)` and the clock module is imported at boot.
|
|
437
|
+
</Accordion>
|
|
438
|
+
|
|
439
|
+
<Accordion title="tz is not a clock option">
|
|
440
|
+
Use `timezone: "Asia/Riyadh"` on the declaration, or set the app default with
|
|
441
|
+
`oke({ clock: { timezone: "Asia/Riyadh" } })` /
|
|
442
|
+
`defineConfig({ clock: { timezone: "Asia/Riyadh" } })`. `{ tz: "…" }` is ignored.
|
|
443
|
+
</Accordion>
|
|
444
|
+
|
|
445
|
+
<Accordion title="Cron / interval fired many times after downtime">
|
|
446
|
+
Catch-up is `"one"` per Store row. A burst usually means several clocks or `clock.perTenant` rows
|
|
447
|
+
(one per tenant), not a replay of missed slots.
|
|
448
|
+
</Accordion>
|
|
449
|
+
|
|
450
|
+
<Accordion title="Two pods ran the same job">
|
|
451
|
+
Need shared `drivers.clock` postgres (or `file` on one machine). `memory` does not elect a leader
|
|
452
|
+
across processes.
|
|
453
|
+
</Accordion>
|
|
454
|
+
|
|
455
|
+
<Accordion title="Bare template name never ticks">
|
|
456
|
+
Expected for `perTenant: true` — only `{name}#{tenantId}` rows fire. Ensure tenant ids are
|
|
457
|
+
available at reconcile.
|
|
458
|
+
</Accordion>
|
|
459
|
+
|
|
460
|
+
<Accordion title="ScheduleNotOverridableError from Console">
|
|
461
|
+
Cause: `clock "{name}" is not overridable`. Add `overridable: true` and redeploy, or change the
|
|
462
|
+
schedule in source.
|
|
463
|
+
</Accordion>
|
|
464
|
+
|
|
465
|
+
<Accordion title="ClockResourceNotFoundError">
|
|
466
|
+
Cause: `cron "{id}" not found` (or `run "{id}" not found`). The Console action targeted a name
|
|
467
|
+
that is not in the reconciled Store — check spelling against your `clock()` declarations.
|
|
468
|
+
</Accordion>
|
|
469
|
+
|
|
470
|
+
<Accordion title="DST gap / overlap warning on the row">
|
|
471
|
+
Informational. Pick a UTC cron, a non-ambiguous local hour, or accept the warning. The job still
|
|
472
|
+
schedules.
|
|
473
|
+
</Accordion>
|
|
474
|
+
|
|
475
|
+
<Accordion title="Paused schedule never fires">
|
|
476
|
+
Console pause sets `status: "paused"`. The scheduler skips it. A later reconcile of a
|
|
477
|
+
still-declared clock restores `active` (and keeps overrides when `overridable`).
|
|
478
|
+
</Accordion>
|
|
479
|
+
|
|
480
|
+
</Accordions>
|
|
481
|
+
|
|
482
|
+
## Learn more
|
|
483
|
+
|
|
484
|
+
- [Clock overview](/docs/elements/clock) — `fx.clock` helpers and drivers
|
|
485
|
+
- [Durable Sleep](/docs/elements/clock/sleep) — `fx.clock.sleep(label, duration)`
|
|
486
|
+
- [Consumers · Clock Jobs](/docs/elements/flow/consumers#clock-jobs) — bind with `on(clockDecl, flow)`
|
|
487
|
+
- [fx](/docs/reference/fx) — `fx.clock.now` / `ago` / `fromNow` / `duration`
|
|
488
|
+
- [Errors](/docs/reference/errors) — `ScheduleNotOverridableError` · `ClockResourceNotFoundError`
|
|
489
|
+
|
|
490
|
+
## Next
|
|
491
|
+
|
|
492
|
+
<Cards>
|
|
493
|
+
<Card
|
|
494
|
+
title="Durable Sleep"
|
|
495
|
+
description="Journaled pauses with fx.clock.sleep(label, duration)."
|
|
496
|
+
href="/docs/elements/clock/sleep"
|
|
497
|
+
/>
|
|
498
|
+
<Card
|
|
499
|
+
title="Clock Overview"
|
|
500
|
+
description="Return to the Clock element overview."
|
|
501
|
+
href="/docs/elements/clock"
|
|
502
|
+
/>
|
|
503
|
+
<Card
|
|
504
|
+
title="Consumers"
|
|
505
|
+
description="Signal workers, named Clock jobs, and SQL CDC."
|
|
506
|
+
href="/docs/elements/flow/consumers"
|
|
507
|
+
/>
|
|
508
|
+
</Cards>
|