okengine 0.18.5 → 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.
Files changed (510) hide show
  1. package/AGENTS.md +18 -10
  2. package/README.md +2 -2
  3. package/package.json +51 -50
  4. package/site/content/docs/ai/llms-txt.mdx +1 -1
  5. package/site/content/docs/ai/meta.json +1 -1
  6. package/site/content/docs/ai/skills.mdx +4 -1
  7. package/site/content/docs/ai/try-it.mdx +58 -0
  8. package/site/content/docs/client/auth.mdx +208 -0
  9. package/site/content/docs/client/calling.mdx +451 -0
  10. package/site/content/docs/client/index.mdx +209 -0
  11. package/site/content/docs/client/live.mdx +234 -0
  12. package/site/content/docs/client/meta.json +5 -0
  13. package/site/content/docs/client/react.mdx +249 -0
  14. package/site/content/docs/elements/ai/agents.mdx +232 -0
  15. package/site/content/docs/elements/ai/index.mdx +341 -0
  16. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  17. package/site/content/docs/elements/ai/meta.json +5 -0
  18. package/site/content/docs/elements/ai/models.mdx +283 -0
  19. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  20. package/site/content/docs/elements/channel/email.mdx +266 -0
  21. package/site/content/docs/elements/channel/index.mdx +344 -0
  22. package/site/content/docs/elements/channel/meta.json +5 -0
  23. package/site/content/docs/elements/channel/push.mdx +218 -0
  24. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  25. package/site/content/docs/elements/channel/sms.mdx +264 -0
  26. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  27. package/site/content/docs/elements/clock/index.mdx +300 -0
  28. package/site/content/docs/elements/clock/meta.json +5 -0
  29. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  30. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  31. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  32. package/site/content/docs/elements/flow/http.mdx +1080 -0
  33. package/site/content/docs/elements/flow/index.mdx +618 -0
  34. package/site/content/docs/elements/flow/meta.json +5 -0
  35. package/site/content/docs/elements/flow/routing.mdx +559 -0
  36. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  37. package/site/content/docs/elements/gate/auth.mdx +371 -0
  38. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  39. package/site/content/docs/elements/gate/index.mdx +418 -0
  40. package/site/content/docs/elements/gate/meta.json +5 -0
  41. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  42. package/site/content/docs/elements/gate/rls.mdx +303 -0
  43. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  44. package/site/content/docs/elements/index.mdx +20 -10
  45. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  46. package/site/content/docs/elements/signal/index.mdx +352 -0
  47. package/site/content/docs/elements/signal/live.mdx +590 -0
  48. package/site/content/docs/elements/signal/meta.json +5 -0
  49. package/site/content/docs/elements/signal/once.mdx +596 -0
  50. package/site/content/docs/elements/store/files.mdx +659 -0
  51. package/site/content/docs/elements/store/index.mdx +310 -0
  52. package/site/content/docs/elements/store/kv.mdx +629 -0
  53. package/site/content/docs/elements/store/meta.json +5 -0
  54. package/site/content/docs/elements/store/search.mdx +946 -0
  55. package/site/content/docs/elements/store/sql.mdx +937 -0
  56. package/site/content/docs/elements/vault/config.mdx +256 -0
  57. package/site/content/docs/elements/vault/index.mdx +356 -0
  58. package/site/content/docs/elements/vault/meta.json +5 -0
  59. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  60. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  61. package/site/content/docs/index.mdx +63 -36
  62. package/site/content/docs/meta.json +8 -7
  63. package/site/content/docs/plugins/cors.mdx +2 -1
  64. package/site/content/docs/plugins/csrf.mdx +4 -1
  65. package/site/content/docs/plugins/headers.mdx +2 -1
  66. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  67. package/site/content/docs/plugins/magic-link.mdx +6 -0
  68. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  69. package/site/content/docs/plugins/oauth.mdx +1 -1
  70. package/site/content/docs/plugins/otp.mdx +12 -1
  71. package/site/content/docs/plugins/passkey.mdx +39 -15
  72. package/site/content/docs/plugins/two-factor.mdx +91 -34
  73. package/site/content/docs/plugins/username.mdx +1 -1
  74. package/site/content/docs/recipes/caddy.mdx +3 -3
  75. package/site/content/docs/recipes/index.mdx +18 -28
  76. package/site/content/docs/recipes/mailpit.mdx +1 -1
  77. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  78. package/site/content/docs/recipes/meta.json +1 -4
  79. package/site/content/docs/recipes/nginx.mdx +2 -2
  80. package/site/content/docs/recipes/openrouter.mdx +283 -0
  81. package/site/content/docs/recipes/pgdog.mdx +3 -3
  82. package/site/content/docs/recipes/postgres.mdx +1 -2
  83. package/site/content/docs/recipes/rustfs.mdx +2 -2
  84. package/site/content/docs/recipes/traefik.mdx +3 -3
  85. package/site/content/docs/reference/cli.mdx +231 -0
  86. package/site/content/docs/reference/configuration.mdx +30 -10
  87. package/site/content/docs/reference/environment-variables.mdx +93 -20
  88. package/site/content/docs/reference/errors.mdx +132 -29
  89. package/site/content/docs/reference/fx.mdx +137 -73
  90. package/site/content/docs/reference/index.mdx +13 -21
  91. package/site/content/docs/reference/meta.json +4 -5
  92. package/site/content/docs/reference/okid.mdx +42 -17
  93. package/site/content/docs/reference/plugins.mdx +4 -3
  94. package/site/content/docs/reference/security.mdx +197 -0
  95. package/site/content/docs/understand/meta.json +5 -0
  96. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  97. package/site/content/docs/understand/the-model.mdx +32 -0
  98. package/site/content/docs/understand/the-problem.mdx +74 -0
  99. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  100. package/src/auth/api-keys.ts +2 -1
  101. package/src/auth/bindings.ts +64 -16
  102. package/src/auth/gate-auth.test.ts +6 -1
  103. package/src/auth/identity.ts +152 -5
  104. package/src/auth/index.ts +29 -0
  105. package/src/auth/invites.ts +2 -1
  106. package/src/auth/method-context.ts +41 -0
  107. package/src/auth/oauth-as/crypto.ts +2 -1
  108. package/src/auth/oauth-as/stores.ts +2 -1
  109. package/src/auth/operator.ts +2 -1
  110. package/src/auth/sessions-jwt.test.ts +77 -0
  111. package/src/auth/sessions.ts +20 -4
  112. package/src/auth/tenants.ts +3 -2
  113. package/src/auth/two-factor-challenge.test.ts +61 -0
  114. package/src/auth/two-factor-challenge.ts +284 -0
  115. package/src/auth/verification.ts +5 -0
  116. package/src/bench/REPORT.md +49 -0
  117. package/src/bench/g03-signal-once.bench.ts +1 -1
  118. package/src/bench/g10-observability-contention.bench.ts +1 -1
  119. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  120. package/src/bench/load-app.ts +2 -10
  121. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  122. package/src/cli/ai-setup/apply.ts +294 -52
  123. package/src/cli/ai-setup/catalog.ts +238 -1342
  124. package/src/cli/ai-setup/index.ts +14 -99
  125. package/src/cli/ai-setup/prompts.ts +60 -443
  126. package/src/cli/ask-seed.test.ts +96 -0
  127. package/src/cli/ask-seed.ts +82 -0
  128. package/src/cli/ask-vault-gaps.test.ts +84 -0
  129. package/src/cli/ask-vault-gaps.ts +146 -0
  130. package/src/cli/build.ts +2 -2
  131. package/src/cli/client-add.test.ts +26 -1
  132. package/src/cli/client-add.ts +106 -27
  133. package/src/cli/db-seed.ts +2 -0
  134. package/src/cli/db.ts +150 -11
  135. package/src/cli/dev.ts +119 -211
  136. package/src/cli/docker-clean.ts +2 -2
  137. package/src/cli/docker-cli.test.ts +1 -1
  138. package/src/cli/doctor-diff.ts +4 -2
  139. package/src/cli/doctor-pii.test.ts +1 -1
  140. package/src/cli/doctor.ts +14 -1
  141. package/src/cli/hero-meta.test.ts +4 -2
  142. package/src/cli/load-config.images.test.ts +8 -10
  143. package/src/cli/load-config.ts +1 -1
  144. package/src/cli/registry.ts +22 -10
  145. package/src/cli/replay.ts +3 -1
  146. package/src/cli/start.ts +1 -1
  147. package/src/cli/tui/keys.ts +2 -3
  148. package/src/client/auth/cookies.ts +74 -0
  149. package/src/client/auth/create-auth-client.ts +636 -0
  150. package/src/client/auth/denials.ts +99 -0
  151. package/src/client/auth/session.ts +283 -0
  152. package/src/client/auth.test.ts +251 -0
  153. package/src/client/auth.ts +39 -114
  154. package/src/client/create-with-session.ts +245 -0
  155. package/src/client/create.ts +51 -5
  156. package/src/client/index.ts +11 -1
  157. package/src/client/live.ts +11 -79
  158. package/src/client/notes-contract.test.ts +5 -10
  159. package/src/client/sse.ts +137 -0
  160. package/src/client/stream.ts +147 -0
  161. package/src/client/transport.test.ts +32 -0
  162. package/src/client/transport.ts +123 -26
  163. package/src/client/types.ts +118 -14
  164. package/src/client-react/index.ts +185 -24
  165. package/src/client-react/use-live-query.ts +13 -3
  166. package/src/compiler/aot.test.ts +7 -4
  167. package/src/compiler/effects-embed.test.ts +56 -0
  168. package/src/compiler/effects-fetch.test.ts +43 -0
  169. package/src/compiler/effects-infer.ts +58 -2
  170. package/src/compiler/extract.test.ts +257 -43
  171. package/src/compiler/extract.ts +566 -48
  172. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  173. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  174. package/src/compiler/fixtures/skyport.expected.json +4 -4
  175. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  176. package/src/compiler/response.ts +2 -2
  177. package/src/compiler/search-writer-isolation.test.ts +40 -0
  178. package/src/config/index.ts +11 -0
  179. package/src/console/server/app.ts +17 -23
  180. package/src/console/server/bind.ts +4 -0
  181. package/src/console/server/console.test.ts +2 -2
  182. package/src/console/server/flows-invoke.test.ts +50 -34
  183. package/src/console/server/flows.ts +436 -255
  184. package/src/console/server/runs-ingest.test.ts +6 -3
  185. package/src/console/server/serve.ts +1 -1
  186. package/src/console/server/signals.test.ts +1 -5
  187. package/src/console/server/signals.ts +8 -6
  188. package/src/console/server/state.ts +6 -1
  189. package/src/console/server/store.ts +1 -1
  190. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  191. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  192. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  193. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  194. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  195. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  196. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  197. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  198. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  199. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  200. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  201. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  202. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  203. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  204. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  205. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  206. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  207. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  208. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  209. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  210. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  211. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  212. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  213. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  214. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  215. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  216. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  217. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  218. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  219. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  220. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  221. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  222. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  223. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  224. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  225. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  226. package/src/console/ui-next/dist/index.html +6 -13
  227. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  228. package/src/console/ui-next/src/client.ts +7 -2
  229. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  230. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  231. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  232. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  233. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  235. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  236. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  237. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  238. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  239. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  240. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  241. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  242. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  243. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  244. package/src/docker/ai-model-status.test.ts +1 -12
  245. package/src/docker/ai-model-status.ts +10 -38
  246. package/src/docker/compose-up.test.ts +100 -0
  247. package/src/docker/compose-up.ts +292 -0
  248. package/src/docker/compose.ts +4 -14
  249. package/src/docker/derive.ts +9 -34
  250. package/src/docker/docker.test.ts +16 -261
  251. package/src/docker/images-config.test.ts +13 -25
  252. package/src/docker/index.ts +1 -24
  253. package/src/docker/recipes/index.ts +0 -23
  254. package/src/docker/recipes/pgdog.ts +6 -4
  255. package/src/docker/stack-id.test.ts +2 -2
  256. package/src/docker/stack-id.ts +0 -1
  257. package/src/docker/types.ts +2 -2
  258. package/src/drivers/ai-anthropic.ts +10 -0
  259. package/src/drivers/ai-openai-compatible.ts +34 -7
  260. package/src/drivers/ai-providers.test.ts +0 -107
  261. package/src/drivers/ai-stream.test.ts +0 -34
  262. package/src/drivers/ai-types.ts +17 -8
  263. package/src/drivers/channel-fcm.ts +6 -1
  264. package/src/drivers/channel-msegat.ts +6 -1
  265. package/src/drivers/channel-resend.ts +5 -1
  266. package/src/drivers/channel-smtp.ts +5 -1
  267. package/src/drivers/channel-sndr.ts +10 -1
  268. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  269. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  270. package/src/drivers/channel-taqnyat.ts +6 -1
  271. package/src/drivers/channel-types.ts +10 -0
  272. package/src/drivers/channel-unifonic.ts +6 -1
  273. package/src/drivers/channel-wa-cloud.ts +6 -1
  274. package/src/drivers/channel-webpush.ts +6 -1
  275. package/src/drivers/external.ts +33 -0
  276. package/src/drivers/index.ts +0 -10
  277. package/src/drivers/meilisearch.ts +6 -0
  278. package/src/drivers/oauth-types.ts +4 -0
  279. package/src/drivers/postgres.test.ts +74 -3
  280. package/src/drivers/postgres.ts +175 -6
  281. package/src/drivers/signal-types.ts +5 -5
  282. package/src/drivers/types.ts +7 -0
  283. package/src/drivers/vault-types.ts +5 -0
  284. package/src/elements/ai/declare.ts +17 -4
  285. package/src/elements/ai/eval.ts +1 -1
  286. package/src/elements/ai/mcp-http.ts +1 -1
  287. package/src/elements/ai/pii.ts +1 -1
  288. package/src/elements/ai/providers.test.ts +289 -0
  289. package/src/elements/ai/providers.ts +169 -0
  290. package/src/elements/ai/runtime.ts +68 -5
  291. package/src/elements/ai/schema.ts +2 -2
  292. package/src/elements/ai.test.ts +7 -7
  293. package/src/elements/ai.ts +17 -1
  294. package/src/elements/channel/runtime.ts +105 -12
  295. package/src/elements/clock/cron-fields.test.ts +144 -0
  296. package/src/elements/clock/cron-fields.ts +185 -0
  297. package/src/elements/clock/declare.ts +247 -3
  298. package/src/elements/clock.ts +21 -1
  299. package/src/elements/index.ts +15 -0
  300. package/src/elements/signal/chaos-child.ts +1 -2
  301. package/src/elements/signal/declare.ts +65 -37
  302. package/src/elements/signal/delivery-modes.test.ts +11 -31
  303. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  304. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  305. package/src/elements/signal/key-ordering.test.ts +6 -26
  306. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  307. package/src/elements/signal/optional-emit.test.ts +6 -9
  308. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  309. package/src/elements/signal/orphan-messages.test.ts +4 -15
  310. package/src/elements/signal/reconcile.test.ts +2 -2
  311. package/src/elements/signal/runtime.ts +1 -1
  312. package/src/elements/signal/schema-emit.test.ts +4 -8
  313. package/src/elements/signal.test.ts +24 -49
  314. package/src/elements/signal.ts +9 -2
  315. package/src/elements/store/domain-ddl.test.ts +1 -1
  316. package/src/elements/store/live-http.test.ts +9 -4
  317. package/src/elements/store/prepare-row.test.ts +123 -0
  318. package/src/elements/store/resource.ts +43 -12
  319. package/src/elements/store/schema-decl.ts +117 -0
  320. package/src/elements/store/search-backfill.ts +205 -0
  321. package/src/elements/store/search-bind.ts +66 -0
  322. package/src/elements/store/search-bm25.ts +78 -0
  323. package/src/elements/store/search-ddl.ts +156 -0
  324. package/src/elements/store/search-embed-flow.ts +141 -0
  325. package/src/elements/store/search-errors.ts +34 -0
  326. package/src/elements/store/search-fusion.ts +112 -0
  327. package/src/elements/store/search-lsh.ts +179 -0
  328. package/src/elements/store/search-runtime.ts +300 -0
  329. package/src/elements/store/search.test.ts +144 -0
  330. package/src/elements/store/sql-session.test.ts +1 -0
  331. package/src/elements/store/sql-session.ts +83 -3
  332. package/src/elements/store/table.ts +23 -2
  333. package/src/elements/store/upsert-app.test.ts +1 -3
  334. package/src/elements/store.ts +50 -0
  335. package/src/full.ts +4 -2
  336. package/src/http.ts +4 -1
  337. package/src/i18n/catalogs/ar.ts +14 -6
  338. package/src/i18n/catalogs/en.ts +14 -6
  339. package/src/index.ts +6 -1
  340. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  341. package/src/kernel/adopt-routes.ts +9 -1
  342. package/src/kernel/app-auth.ts +5 -0
  343. package/src/kernel/app.ts +102 -9
  344. package/src/kernel/auto-cache.test.ts +6 -12
  345. package/src/kernel/auto-registry.test.ts +3 -3
  346. package/src/kernel/boot-bind/ai.test.ts +22 -19
  347. package/src/kernel/boot-bind/ai.ts +5 -43
  348. package/src/kernel/boot-bind/clock.ts +23 -5
  349. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  350. package/src/kernel/boot.test.ts +11 -6
  351. package/src/kernel/boot.ts +66 -16
  352. package/src/kernel/boundary-contract.ts +93 -0
  353. package/src/kernel/budget.test.ts +1 -1
  354. package/src/kernel/call.ts +89 -0
  355. package/src/kernel/capability.ts +6 -0
  356. package/src/kernel/client-descriptor.ts +123 -0
  357. package/src/kernel/clock-timezone.test.ts +80 -0
  358. package/src/kernel/correlation.test.ts +1 -1
  359. package/src/kernel/dry-run.ts +11 -8
  360. package/src/kernel/effects-stamping.test.ts +42 -9
  361. package/src/kernel/effects.test.ts +4 -2
  362. package/src/kernel/effects.ts +51 -9
  363. package/src/kernel/errors-channel.ts +17 -0
  364. package/src/kernel/errors-live-resume.ts +3 -2
  365. package/src/kernel/errors-tenant.ts +7 -4
  366. package/src/kernel/errors.registry-helpers.ts +96 -0
  367. package/src/kernel/errors.registry.test.ts +139 -25
  368. package/src/kernel/errors.ts +102 -23
  369. package/src/kernel/external-effects.test.ts +196 -0
  370. package/src/kernel/flow.test.ts +13 -4
  371. package/src/kernel/flow.ts +69 -71
  372. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  373. package/src/kernel/fx-dead-letters.test.ts +3 -3
  374. package/src/kernel/fx-fetch.ts +55 -0
  375. package/src/kernel/fx-live-stream.ts +2 -2
  376. package/src/kernel/fx-live.test.ts +8 -8
  377. package/src/kernel/fx.test.ts +17 -11
  378. package/src/kernel/fx.ts +180 -57
  379. package/src/kernel/horizontal-child.ts +1 -1
  380. package/src/kernel/http-query.test.ts +3 -6
  381. package/src/kernel/index.ts +13 -3
  382. package/src/kernel/instance-id.ts +1 -1
  383. package/src/kernel/instances.test.ts +40 -0
  384. package/src/kernel/instances.ts +47 -9
  385. package/src/kernel/live-http.test.ts +6 -7
  386. package/src/kernel/live-http.ts +6 -2
  387. package/src/kernel/live-resume.test.ts +1 -1
  388. package/src/kernel/mcp-tool.test.ts +3 -3
  389. package/src/kernel/on.ts +98 -4
  390. package/src/kernel/pipeline.test.ts +12 -15
  391. package/src/kernel/plugin-elements.test.ts +1 -1
  392. package/src/kernel/stamp-http.test.ts +3 -3
  393. package/src/kernel/triggers.ts +210 -95
  394. package/src/kernel-entry.ts +0 -1
  395. package/src/manifest/diff.ts +11 -1
  396. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  397. package/src/manifest/types.ts +46 -2
  398. package/src/mcp/docs-index.ts +3 -3
  399. package/src/mcp/docs-mcp.test.ts +2 -2
  400. package/src/mcp/docs-tools.ts +1 -1
  401. package/src/okid.test.ts +56 -0
  402. package/src/okid.ts +55 -11
  403. package/src/plugins/anonymous.ts +8 -4
  404. package/src/plugins/auth/shared.ts +27 -3
  405. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  406. package/src/plugins/auth-methods.security.test.ts +561 -23
  407. package/src/plugins/config-source.ts +1 -1
  408. package/src/plugins/csrf.test.ts +65 -0
  409. package/src/plugins/index.ts +2 -0
  410. package/src/plugins/magic-link.ts +36 -16
  411. package/src/plugins/oauth/shared.ts +3 -1
  412. package/src/plugins/oauth.ts +22 -14
  413. package/src/plugins/otp.ts +37 -21
  414. package/src/plugins/passkey-webauthn.ts +9 -6
  415. package/src/plugins/passkey.ts +98 -26
  416. package/src/plugins/pre-account-hijack.test.ts +223 -0
  417. package/src/plugins/two-factor.ts +466 -37
  418. package/src/plugins/username.ts +19 -7
  419. package/src/release/build-lib.ts +1 -0
  420. package/src/release/limits.ts +2 -2
  421. package/src/release/measure.ts +1 -1
  422. package/src/runs/collect.test.ts +0 -2
  423. package/src/runtime/json-code-block.test.ts +250 -20
  424. package/src/runtime/json-code-block.ts +1261 -41
  425. package/src/term.test.ts +1 -1
  426. package/src/test/create-test-app.test.ts +12 -12
  427. package/src/test/live-signals.test.ts +6 -6
  428. package/src/test/provisions.integration.test.ts +10 -14
  429. package/src/test/reset-element-registries.ts +1 -1
  430. package/src/test/tenant-isolation.test.ts +12 -6
  431. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  432. package/site/content/docs/deployment/docker.mdx +0 -227
  433. package/site/content/docs/deployment/index.mdx +0 -83
  434. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  435. package/site/content/docs/deployment/meta.json +0 -5
  436. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  437. package/site/content/docs/elements/ai.mdx +0 -385
  438. package/site/content/docs/elements/channel.mdx +0 -346
  439. package/site/content/docs/elements/clock.mdx +0 -244
  440. package/site/content/docs/elements/flow.mdx +0 -420
  441. package/site/content/docs/elements/gate.mdx +0 -436
  442. package/site/content/docs/elements/signal.mdx +0 -380
  443. package/site/content/docs/elements/store.mdx +0 -1099
  444. package/site/content/docs/elements/vault.mdx +0 -405
  445. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  446. package/site/content/docs/get-started/index.mdx +0 -43
  447. package/site/content/docs/get-started/installation.mdx +0 -220
  448. package/site/content/docs/get-started/introduction.mdx +0 -144
  449. package/site/content/docs/get-started/meta.json +0 -13
  450. package/site/content/docs/get-started/project-structure.mdx +0 -925
  451. package/site/content/docs/get-started/testing.mdx +0 -328
  452. package/site/content/docs/get-started/why.mdx +0 -114
  453. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  454. package/site/content/docs/recipes/ollama.mdx +0 -142
  455. package/site/content/docs/recipes/sglang.mdx +0 -105
  456. package/site/content/docs/recipes/vllm.mdx +0 -106
  457. package/site/content/docs/reference/cli.md +0 -232
  458. package/site/content/docs/reference/client.mdx +0 -469
  459. package/site/content/docs/reference/security.md +0 -74
  460. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  461. package/src/cli/ai-setup/recommend.test.ts +0 -225
  462. package/src/cli/ai-setup/recommend.ts +0 -225
  463. package/src/cli/dev-controls.test.ts +0 -122
  464. package/src/cli/dev-controls.ts +0 -164
  465. package/src/cli/tui/DevLive.tsx +0 -126
  466. package/src/console/ui-next/dist/assets/access-page-3-EFj-2G.js +0 -4
  467. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  468. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  469. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  470. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  471. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  472. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  473. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  474. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  475. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  476. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  477. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  478. package/src/console/ui-next/dist/assets/flows-page-cVFnA4HH.js +0 -1
  479. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  480. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  481. package/src/console/ui-next/dist/assets/index-CMIgUbD0.js +0 -66
  482. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  483. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  484. package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.js +0 -4
  485. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  486. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  487. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  488. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  489. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  490. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  491. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  492. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  493. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  494. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  495. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  496. package/src/console/ui-next/dist/assets/units-page-l8FeKfnP.js +0 -1
  497. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  498. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  499. package/src/console/ui-next/dist/assets/vault-page-CL-d_mLE.js +0 -2
  500. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  501. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  502. package/src/docker/ollama-pull.ts +0 -232
  503. package/src/docker/recipes/llama-cpp.ts +0 -298
  504. package/src/docker/recipes/ollama.ts +0 -44
  505. package/src/docker/recipes/sglang.ts +0 -55
  506. package/src/docker/recipes/vllm.ts +0 -44
  507. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  508. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  509. package/src/drivers/ai-ollama.ts +0 -389
  510. package/src/drivers/ollama.ts +0 -14
@@ -10,6 +10,8 @@ import type {
10
10
  AuthApiKeysResourceRef,
11
11
  AuthTenantsResourceRef,
12
12
  Effects,
13
+ EmbedRef,
14
+ FetchHostRef,
13
15
  PromptRef,
14
16
  ResourceRef,
15
17
  SecretRef,
@@ -53,6 +55,7 @@ export interface InferBinding {
53
55
  readonly kind:
54
56
  | "store"
55
57
  | "signal"
58
+ | "clock"
56
59
  | "prompt"
57
60
  | "secret"
58
61
  | "template"
@@ -96,7 +99,7 @@ export interface InferredEffects {
96
99
  * the flow must opt out of auto-caching.
97
100
  */
98
101
  readonly cacheIneligible: boolean;
99
- /** True when any `fx.ask` was seen (implies nondeterministic). */
102
+ /** True when any `fx.ask` or `fx.embed` was seen (implies nondeterministic). */
100
103
  readonly nondeterministic: boolean;
101
104
  /** True when the body references `fx.auth.userId`. */
102
105
  readonly readsUserId: boolean;
@@ -112,6 +115,10 @@ const READ_METHODS = new Set([
112
115
  "find",
113
116
  "list",
114
117
  "ttlMs",
118
+ "count",
119
+ "page",
120
+ "search",
121
+ "raw",
115
122
  ]);
116
123
 
117
124
  const WRITE_METHODS = new Set([
@@ -139,6 +146,9 @@ const TABLE_ARG_METHODS = new Set([
139
146
  "upsert",
140
147
  "increment",
141
148
  "delete",
149
+ "count",
150
+ "page",
151
+ "search",
142
152
  ]);
143
153
 
144
154
  /**
@@ -154,8 +164,10 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
154
164
  const emits = new Set<SignalRef>();
155
165
  const sends = new Set<TemplateRef>();
156
166
  const asks = new Set<PromptRef>();
167
+ const embeds = new Set<EmbedRef>();
157
168
  const secrets = new Set<SecretRef>();
158
169
  const calls = new Set<FlowRef>();
170
+ const fetches = new Set<FetchHostRef>();
159
171
  const steps: string[] = [];
160
172
  let usesRaw = false;
161
173
 
@@ -211,6 +223,34 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
211
223
  continue;
212
224
  }
213
225
 
226
+ if (chain.rootMethod === "embed" && call === chain.rootCall) {
227
+ // fx.embed(model, text) — distinct from fx.ask; never classified as asks.
228
+ // Prefer model binding; also accept embed-pipeline and string literals.
229
+ const lit = stringArg(call.arguments[0]);
230
+ if (lit) {
231
+ embeds.add(lit);
232
+ } else {
233
+ const binding = resolveBinding(call.arguments[0], options.bindings);
234
+ if (binding && (binding.kind === "embed" || binding.kind === "unknown")) {
235
+ embeds.add(binding.ref);
236
+ } else {
237
+ const name = identifierName(call.arguments[0]);
238
+ if (name) embeds.add(name);
239
+ }
240
+ }
241
+ continue;
242
+ }
243
+
244
+ if (chain.rootMethod === "fetch" && call === chain.rootCall) {
245
+ // fx.fetch(url) — host becomes effects.fetches entry.
246
+ const lit = stringArg(call.arguments[0]);
247
+ if (lit) {
248
+ const host = hostFromUrlLiteral(lit);
249
+ if (host) fetches.add(host);
250
+ }
251
+ continue;
252
+ }
253
+
214
254
  if (chain.rootMethod === "vault" && call === chain.rootCall) {
215
255
  // `fx.vault.list()` / `fx.vault.status()` take no path — nothing to
216
256
  // declare. Every other method's first argument is the secret.
@@ -289,15 +329,17 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
289
329
  if (emits.size > 0) effects.emits = sortUnique([...emits]);
290
330
  if (sends.size > 0) effects.sends = sortUnique([...sends]);
291
331
  if (asks.size > 0) effects.asks = sortUnique([...asks]);
332
+ if (embeds.size > 0) effects.embeds = sortUnique([...embeds]);
292
333
  if (secrets.size > 0) effects.secrets = sortUnique([...secrets]);
293
334
  if (calls.size > 0) effects.calls = sortUnique([...calls]);
335
+ if (fetches.size > 0) effects.fetches = sortUnique([...fetches]);
294
336
 
295
337
  return {
296
338
  effects,
297
339
  steps,
298
340
  usesRaw,
299
341
  cacheIneligible: usesRaw && !options.hasExplicitEffects,
300
- nondeterministic: asks.size > 0,
342
+ nondeterministic: asks.size > 0 || embeds.size > 0,
301
343
  readsUserId: containsAuthUserId(options.doNode),
302
344
  };
303
345
  }
@@ -658,6 +700,20 @@ export function stringArg(node: AstNode | undefined): string | undefined {
658
700
  return undefined;
659
701
  }
660
702
 
703
+ /**
704
+ * Hostname from a URL string literal for `fx.fetch` inference.
705
+ *
706
+ * @param url - Absolute or protocol-relative URL string
707
+ */
708
+ export function hostFromUrlLiteral(url: string): string | undefined {
709
+ try {
710
+ const parsed = new URL(url);
711
+ return parsed.hostname || undefined;
712
+ } catch {
713
+ return undefined;
714
+ }
715
+ }
716
+
661
717
  /**
662
718
  * Identifier name.
663
719
  *
@@ -246,6 +246,133 @@ export const db = store.sql("app", { schema: { notes } });
246
246
  });
247
247
  });
248
248
 
249
+ test("extracts searchable / embed column meta and table.search engines", async () => {
250
+ const source = `
251
+ import { store, field, ai } from "okengine";
252
+
253
+ export const embedder = ai.model("embedder", { provider: "openai-compatible", model: "nomic-embed-text" });
254
+
255
+ export const articles = store.schema.table("articles", {
256
+ id: field.text().primaryKey(),
257
+ title: field.text().searchable({ weight: 2 }).notNull(),
258
+ body: field.text().searchable().embed({ dims: 8, model: embedder }),
259
+ });
260
+
261
+ export const db = store.sql("app", { schema: { articles } });
262
+ `;
263
+ const manifest = await extractFromSources({
264
+ "src/schema.decl.ts": source,
265
+ });
266
+ const table = manifest.stores?.app?.tables?.articles;
267
+ expect(table?.columns?.title).toMatchObject({
268
+ searchable: { weight: 2 },
269
+ });
270
+ expect(table?.columns?.body).toMatchObject({
271
+ searchable: { weight: 1 },
272
+ embed: { dims: 8, model: "embedder" },
273
+ });
274
+ expect(table?.search?.engines).toEqual(["bm25", "lsh"]);
275
+ });
276
+
277
+ test("embed without ai element fails SearchConfigError", async () => {
278
+ const source = `
279
+ import { store, field } from "okengine";
280
+
281
+ export const articles = store.schema.table("articles", {
282
+ id: field.text().primaryKey(),
283
+ body: field.text().searchable().embed({ dims: 8, model: "embedder" }),
284
+ });
285
+
286
+ export const db = store.sql("app", { schema: { articles } });
287
+ `;
288
+ await expect(extractFromSources({ "src/schema.decl.ts": source })).rejects.toThrow(
289
+ /SearchConfigError|ai element/,
290
+ );
291
+ });
292
+
293
+ test("project search.embed default stamps bare .embed()", async () => {
294
+ const source = `
295
+ import { store, field, ai, oke } from "okengine";
296
+
297
+ export const embedder = ai.model("embedder", { provider: "openai-compatible", model: "nomic-embed-text" });
298
+
299
+ oke({
300
+ store: {
301
+ search: {
302
+ embed: { model: embedder, dims: 768 },
303
+ },
304
+ },
305
+ });
306
+
307
+ export const articles = store.schema.table("articles", {
308
+ id: field.text().primaryKey(),
309
+ body: field.text().searchable().embed(),
310
+ });
311
+
312
+ export const db = store.sql("app", { schema: { articles } });
313
+ `;
314
+ const manifest = await extractFromSources({
315
+ "src/schema.decl.ts": source,
316
+ });
317
+ expect(manifest.store?.search?.embed).toEqual({ model: "embedder", dims: 768 });
318
+ expect(manifest.stores?.app?.tables?.articles?.columns?.body).toMatchObject({
319
+ searchable: { weight: 1 },
320
+ embed: { model: "embedder", dims: 768 },
321
+ });
322
+ expect(manifest.stores?.app?.tables?.articles?.search?.engines).toEqual(["bm25", "lsh"]);
323
+ });
324
+
325
+ test("field .embed() override wins over project search.embed default", async () => {
326
+ const source = `
327
+ import { store, field, ai, oke } from "okengine";
328
+
329
+ export const embedder = ai.model("embedder", { provider: "openai-compatible", model: "nomic-embed-text" });
330
+ export const other = ai.model("other", { provider: "openai-compatible", model: "nomic-embed-text" });
331
+
332
+ oke({
333
+ store: {
334
+ search: {
335
+ embed: { model: embedder, dims: 768 },
336
+ },
337
+ },
338
+ });
339
+
340
+ export const articles = store.schema.table("articles", {
341
+ id: field.text().primaryKey(),
342
+ body: field.text().searchable().embed(),
343
+ caption: field.text().searchable().embed({ model: other }),
344
+ alt: field.text().searchable().embed({ model: other, dims: 384 }),
345
+ });
346
+
347
+ export const db = store.sql("app", { schema: { articles } });
348
+ `;
349
+ const manifest = await extractFromSources({
350
+ "src/schema.decl.ts": source,
351
+ });
352
+ const cols = manifest.stores?.app?.tables?.articles?.columns;
353
+ expect(cols?.body).toMatchObject({ embed: { model: "embedder", dims: 768 } });
354
+ expect(cols?.caption).toMatchObject({ embed: { model: "other", dims: 768 } });
355
+ expect(cols?.alt).toMatchObject({ embed: { model: "other", dims: 384 } });
356
+ });
357
+
358
+ test("bare .embed() without project default fails SearchConfigError", async () => {
359
+ const source = `
360
+ import { store, field, ai } from "okengine";
361
+
362
+ export const embedder = ai.model("embedder", { provider: "openai-compatible", model: "nomic-embed-text" });
363
+
364
+ export const articles = store.schema.table("articles", {
365
+ id: field.text().primaryKey(),
366
+ body: field.text().searchable().embed(),
367
+ });
368
+
369
+ export const db = store.sql("app", { schema: { articles } });
370
+ `;
371
+ await expect(extractFromSources({ "src/schema.decl.ts": source })).rejects.toThrow(
372
+ /SearchConfigError|model and dims|store: \{ search: \{ embed/,
373
+ );
374
+ });
375
+
249
376
  test("extracts RLS helpers onto store.tables", async () => {
250
377
  const source = `
251
378
  import { store, field } from "okengine";
@@ -349,6 +476,69 @@ export const db = store.sql("app", { schema: { assets } });
349
476
  expect(cols?.createdAt).toMatchObject({ type: "integer", nullable: false, default: null });
350
477
  });
351
478
 
479
+ test("field.id() / field.okid() heads extract as text PK with dynamic default", async () => {
480
+ const source = `
481
+ import { store, field } from "okengine";
482
+
483
+ export const tasks = store.schema.table("tasks", {
484
+ id: field.id().primaryKey(),
485
+ title: field.text().notNull(),
486
+ updatedAt: field.timestamp().notNull().now(),
487
+ });
488
+
489
+ export const assets = store.schema.table("assets", {
490
+ id: field.okid().primaryKey(),
491
+ name: field.text().notNull(),
492
+ });
493
+
494
+ export const db = store.sql("app", { schema: { tasks, assets } });
495
+ `;
496
+ const manifest = await extractFromSources({
497
+ "src/schema.decl.ts": source,
498
+ });
499
+ expect(manifest.stores?.app?.tables?.tasks?.columns?.id).toMatchObject({
500
+ type: "text",
501
+ primaryKey: true,
502
+ default: null,
503
+ sqlName: "id",
504
+ });
505
+ expect(manifest.stores?.app?.tables?.assets?.columns?.id).toMatchObject({
506
+ type: "text",
507
+ primaryKey: true,
508
+ default: null,
509
+ });
510
+ });
511
+
512
+ test("live(.live(table)) accepts field.id().primaryKey() tables", async () => {
513
+ const source = `
514
+ import { on, flow, http, gate, store, field } from "okengine";
515
+
516
+ export const tasks = store.schema.table("tasks", {
517
+ id: field.id().primaryKey(),
518
+ status: field.text().notNull(),
519
+ updatedAt: field.timestamp().notNull().now(),
520
+ });
521
+
522
+ export const db = store.sql("app", { schema: { tasks } });
523
+ export const member = gate.policy("member", { require: ["member"] });
524
+
525
+ export const live = on(
526
+ http.get("/tasks/live").gate(member).live(tasks),
527
+ flow("tasks.live", {
528
+ do: async () => ({ ok: true }),
529
+ }),
530
+ );
531
+ `;
532
+ const manifest = await extractFromSources({
533
+ "src/schema.decl.ts": source,
534
+ "src/flows/live.ts": source,
535
+ });
536
+ expect(manifest.flows?.["tasks.live"]).toBeDefined();
537
+ expect(manifest.stores?.app?.tables?.tasks?.columns?.id).toMatchObject({
538
+ primaryKey: true,
539
+ });
540
+ });
541
+
352
542
  test("decimal head collapses to numeric in the manifest", async () => {
353
543
  const source = `
354
544
  import { store, field } from "okengine";
@@ -389,10 +579,7 @@ export const embeddings = store.index("embeddings", {
389
579
 
390
580
  export const sessions = store.kv("sessions", { description: "Session cache" });
391
581
 
392
- export const orderPlaced = signal("order-placed", {
393
- delivery: "once",
394
- description: "Order placed event",
395
- });
582
+ export const orderPlaced = signal.once("order-placed", { description: "Order placed event" });
396
583
 
397
584
  export const bookingConfirmed = channel.template("booking-confirmed", {
398
585
  medium: "email",
@@ -438,11 +625,8 @@ export const stripeKey = vault.secret("STRIPE_KEY", {
438
625
  const manifest = await extractFromSources({
439
626
  "src/signals.ts": `
440
627
  import { signal } from "okengine";
441
- export const orderStatus = signal("order-status", {
442
- delivery: "live",
443
- optional: true,
444
- retention: { maxAge: "24h", maxCount: 500 },
445
- });
628
+ export const orderStatus = signal.live("order-status", { optional: true,
629
+ retention: { maxAge: "24h", maxCount: 500 } });
446
630
  `,
447
631
  });
448
632
  expect(manifest.signals?.["order-status"]?.retention).toEqual({
@@ -802,9 +986,8 @@ export const tasksTable = store.schema.table(
802
986
  );
803
987
 
804
988
  export const tasksLive = on(
805
- http.get("/tasks/live").gate(member).live(tasksTable),
989
+ http.get("/tasks/live", { in: { unknown: true } }).gate(member).live(tasksTable),
806
990
  flow("tasks.live", {
807
- in: { unknown: true },
808
991
  do: async (_input, fx) => liveQuery(fx, tasksTable, _input),
809
992
  }),
810
993
  );
@@ -948,12 +1131,13 @@ export const attach = on(
948
1131
 
949
1132
  test("fx.store(files).list(...) infers a read, unannotated", async () => {
950
1133
  const source = `
951
- import { on, flow, every, store } from "okengine";
1134
+ import { on, flow, clock, store } from "okengine";
952
1135
 
953
1136
  export const files = store.files("uploads");
1137
+ export const sweepClock = clock("notes.sweep", { every: "1d" });
954
1138
 
955
1139
  export const sweep = on(
956
- every("1d"),
1140
+ sweepClock,
957
1141
  flow("notes.sweep", {
958
1142
  do: async (_input, fx) => {
959
1143
  const keys = await fx.store(files).list();
@@ -968,16 +1152,49 @@ export const sweep = on(
968
1152
  });
969
1153
  });
970
1154
 
1155
+ describe("extractManifest — clock convenience helpers", () => {
1156
+ test("daily / cron fields / every compile into clocks", async () => {
1157
+ const source = `
1158
+ import { clock } from "okengine";
1159
+
1160
+ export const daily = clock.daily("reports.daily", { at: "06:00", description: "Morning" });
1161
+ export const sweep = clock.cron("ops.sweep", {
1162
+ minute: "*/15",
1163
+ hour: [9, 12, 17],
1164
+ dayOfWeek: "1-5",
1165
+ overridable: true,
1166
+ });
1167
+ export const ping = clock.every("health.ping", "30s");
1168
+ export const nick = clock.cron("metrics.hourly", "@hourly");
1169
+ export const weekly = clock.weekly("digest", { on: ["mon", "fri"], at: "09:00" });
1170
+ `;
1171
+ const manifest = await extractFromSources({ "src/clocks.ts": source });
1172
+
1173
+ expect(manifest.clocks?.["reports.daily"]).toMatchObject({
1174
+ cron: "0 6 * * *",
1175
+ description: "Morning",
1176
+ });
1177
+ expect(manifest.clocks?.["ops.sweep"]).toMatchObject({
1178
+ cron: "*/15 9,12,17 * * 1-5",
1179
+ overridable: true,
1180
+ });
1181
+ expect(manifest.clocks?.["health.ping"]).toMatchObject({ every: "30s" });
1182
+ expect(manifest.clocks?.["metrics.hourly"]).toMatchObject({ cron: "@hourly" });
1183
+ expect(manifest.clocks?.digest).toMatchObject({ cron: "0 9 * * 1,5" });
1184
+ });
1185
+ });
1186
+
971
1187
  describe("extractManifest — kv key methods", () => {
972
1188
  test("fx.store(kv).delete(key) writes kv:<namespace>, not kv:<identifier>", async () => {
973
1189
  const source = `
974
- import { on, flow, every, store, signal } from "okengine";
1190
+ import { on, flow, clock, store, signal } from "okengine";
975
1191
 
976
1192
  export const draftsKv = store.kv("drafts");
977
- export const draftExpired = signal("draft-expired", { delivery: "broadcast", retries: 0, deadLetter: false });
1193
+ export const draftExpired = signal.broadcast("draft-expired", { retries: 0, deadLetter: false });
1194
+ export const expireClock = clock("drafts.expire", { every: "10m" });
978
1195
 
979
1196
  export const expire = on(
980
- every("10m"),
1197
+ expireClock,
981
1198
  flow("drafts.expire", {
982
1199
  do: async (_input, fx) => {
983
1200
  const keys = await fx.store(draftsKv).list();
@@ -1005,7 +1222,7 @@ describe("extractManifest — fx.deadLetters", () => {
1005
1222
  const source = `
1006
1223
  import { on, flow, http, signal } from "okengine";
1007
1224
 
1008
- export const notify = signal("notify", { delivery: "once" });
1225
+ export const notify = signal.once("notify");
1009
1226
 
1010
1227
  export const failed = on(
1011
1228
  http.get("/notifications/failed").public(),
@@ -1026,9 +1243,7 @@ import { on, http, gate, signal } from "okengine";
1026
1243
  import { z } from "zod";
1027
1244
 
1028
1245
  export const member = gate.policy("member", ({ auth }) => !!auth.verified);
1029
- export const orderStatus = signal("order-status", {
1030
- delivery: "live",
1031
- optional: true,
1246
+ export const orderStatus = signal.live("order-status", { optional: true,
1032
1247
  schema: z.object({ orderId: z.string(), status: z.string() }),
1033
1248
  });
1034
1249
 
@@ -1049,7 +1264,7 @@ export const events = on(http.get("/orders/:orderId/events").gate(member).live(o
1049
1264
  import { on, http, gate, signal } from "okengine";
1050
1265
 
1051
1266
  export const member = gate.policy("member", ({ auth }) => !!auth.verified);
1052
- export const orderStatus = signal("order-status", { delivery: "live", optional: true });
1267
+ export const orderStatus = signal.live("order-status", { optional: true });
1053
1268
 
1054
1269
  export const firehose = on(http.live(orderStatus).gate(member));
1055
1270
  `;
@@ -1065,7 +1280,7 @@ export const firehose = on(http.live(orderStatus).gate(member));
1065
1280
  const source = `
1066
1281
  import { on, flow, http, signal } from "okengine";
1067
1282
 
1068
- export const orderStatus = signal("order-status", { delivery: "live", optional: true });
1283
+ export const orderStatus = signal.live("order-status", { optional: true });
1069
1284
 
1070
1285
  export const events = on(
1071
1286
  http.get("/feed").public().live(orderStatus),
@@ -1130,12 +1345,12 @@ export const send = on(
1130
1345
  });
1131
1346
  });
1132
1347
 
1133
- describe("extractManifest — flow(name, options) positional signature", () => {
1134
- test("bare flow(name, {...}) extracts name, in, out, effects", async () => {
1348
+ describe("extractManifest — call(name, options) invoke contract", () => {
1349
+ test("bare call(name, {...}) extracts name, in, out, effects", async () => {
1135
1350
  const source = `
1136
- import { flow } from "okengine";
1351
+ import { call } from "okengine";
1137
1352
 
1138
- export const chargeOrder = flow("checkout.chargeOrder", {
1353
+ export const chargeOrder = call("checkout.chargeOrder", {
1139
1354
  in: ChargeIn,
1140
1355
  out: ChargeOut,
1141
1356
  effects: { reads: ["sql:orders"], writes: ["sql:orders"] },
@@ -1153,14 +1368,13 @@ export const chargeOrder = flow("checkout.chargeOrder", {
1153
1368
  expect(manifest.flows?.["checkout.chargeOrder"]?.effects?.writes).toEqual(["sql:orders"]);
1154
1369
  });
1155
1370
 
1156
- test("on(trigger, flow(name, {...})) extracts the same shape as bare flow()", async () => {
1371
+ test("on(trigger, flow(name, {...})) extracts HTTP contract from the trigger", async () => {
1157
1372
  const source = `
1158
1373
  import { on, http, flow } from "okengine";
1159
1374
 
1160
1375
  export const getOrder = on(
1161
- http.get("/orders/:id"),
1376
+ http.get("/orders/:id", { out: OrderOut }),
1162
1377
  flow("checkout.getOrder", {
1163
- out: OrderOut,
1164
1378
  effects: { reads: ["sql:orders"] },
1165
1379
  do: async (input, fx) => {
1166
1380
  return { id: input.id };
@@ -1338,7 +1552,7 @@ export const ping = flow({ do: () => ({ ok: true }) });
1338
1552
  import { on, flow, http, store, signal } from "okengine";
1339
1553
  export const db = store.sql("db");
1340
1554
  export const notes = { name: "notes" };
1341
- export const noteCreated = signal("note-created");
1555
+ export const noteCreated = signal.once("note-created");
1342
1556
  export const get = on(
1343
1557
  http.get("/notes/:id").public(),
1344
1558
  flow("notes.get", {
@@ -1350,7 +1564,7 @@ export const get = on(
1350
1564
  import { on, flow, http, store, signal } from "okengine";
1351
1565
  export const db = store.sql("db");
1352
1566
  export const notes = { name: "notes" };
1353
- export const noteCreated = signal("note-created");
1567
+ export const noteCreated = signal.once("note-created");
1354
1568
  export const get = on(
1355
1569
  http.get().public(),
1356
1570
  flow({
@@ -1372,10 +1586,10 @@ export const get = on(
1372
1586
  });
1373
1587
  });
1374
1588
 
1375
- describe("extractManifest — flow in/out schema expansion", () => {
1589
+ describe("extractManifest — invoke contract schema expansion", () => {
1376
1590
  test("expands z.object identifiers into JSON Schema fields", async () => {
1377
1591
  const source = `
1378
- import { flow } from "okengine";
1592
+ import { call } from "okengine";
1379
1593
  import { z } from "zod";
1380
1594
 
1381
1595
  export const TaskCreateIn = z.object({
@@ -1389,7 +1603,7 @@ export const TaskCreateOut = z.object({
1389
1603
  identifier: z.string(),
1390
1604
  });
1391
1605
 
1392
- export const create = flow("tasks.create", {
1606
+ export const create = call("tasks.create", {
1393
1607
  in: TaskCreateIn,
1394
1608
  out: TaskCreateOut,
1395
1609
  do: async () => ({ id: "1", identifier: "ENG-1" }),
@@ -1428,7 +1642,7 @@ export const users = store.schema.table("users", {
1428
1642
  });
1429
1643
  `;
1430
1644
  const flows = `
1431
- import { flow } from "okengine";
1645
+ import { call } from "okengine";
1432
1646
  import { createInsertSchema, createSelectSchema, createUpdateSchema } from "drizzle-orm/zod";
1433
1647
  import { users } from "../schema.decl.ts";
1434
1648
 
@@ -1436,13 +1650,13 @@ export const userSelect = createSelectSchema(users);
1436
1650
  export const userInsert = createInsertSchema(users);
1437
1651
  export const userUpdate = createUpdateSchema(users);
1438
1652
 
1439
- export const create = flow("users.create", {
1653
+ export const create = call("users.create", {
1440
1654
  in: userInsert,
1441
1655
  out: userSelect,
1442
1656
  do: async () => ({}),
1443
1657
  });
1444
1658
 
1445
- export const patch = flow("users.patch", {
1659
+ export const patch = call("users.patch", {
1446
1660
  in: userUpdate,
1447
1661
  do: async () => ({}),
1448
1662
  });
@@ -1503,8 +1717,8 @@ export function tableZod(table) {
1503
1717
  export const spacesZod = tableZod(spaces);
1504
1718
  `;
1505
1719
  const flows = `
1506
- import { flow } from "okengine";
1507
- export const create = flow("spaces.create", {
1720
+ import { call } from "okengine";
1721
+ export const create = call("spaces.create", {
1508
1722
  in: spacesZod.insert,
1509
1723
  out: spacesZod.select.pick({ id: true, key: true }),
1510
1724
  do: async () => ({}),
@@ -1580,19 +1794,19 @@ export const { list, create, get, update, remove } = bindCrud({
1580
1794
 
1581
1795
  test("same-named createIn stays file-local (does not leak across units)", async () => {
1582
1796
  const spaces = `
1583
- import { flow } from "okengine";
1797
+ import { call } from "okengine";
1584
1798
  import { z } from "zod";
1585
1799
  const createIn = z.object({ key: z.string(), name: z.string() });
1586
- export const create = flow("spaces.create", {
1800
+ export const create = call("spaces.create", {
1587
1801
  in: createIn,
1588
1802
  do: async () => ({}),
1589
1803
  });
1590
1804
  `;
1591
1805
  const views = `
1592
- import { flow } from "okengine";
1806
+ import { call } from "okengine";
1593
1807
  import { z } from "zod";
1594
1808
  const createIn = z.object({ kind: z.enum(["list", "board"]) });
1595
- export const create = flow("views.create", {
1809
+ export const create = call("views.create", {
1596
1810
  in: createIn,
1597
1811
  do: async () => ({}),
1598
1812
  });