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.
Files changed (512) 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/index.mdx +25 -6
  5. package/site/content/docs/ai/llms-txt.mdx +1 -1
  6. package/site/content/docs/ai/meta.json +1 -1
  7. package/site/content/docs/ai/skills.mdx +4 -1
  8. package/site/content/docs/ai/try-it.mdx +58 -0
  9. package/site/content/docs/client/auth.mdx +208 -0
  10. package/site/content/docs/client/calling.mdx +451 -0
  11. package/site/content/docs/client/index.mdx +209 -0
  12. package/site/content/docs/client/live.mdx +234 -0
  13. package/site/content/docs/client/meta.json +5 -0
  14. package/site/content/docs/client/react.mdx +249 -0
  15. package/site/content/docs/elements/ai/agents.mdx +232 -0
  16. package/site/content/docs/elements/ai/index.mdx +341 -0
  17. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  18. package/site/content/docs/elements/ai/meta.json +5 -0
  19. package/site/content/docs/elements/ai/models.mdx +283 -0
  20. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  21. package/site/content/docs/elements/channel/email.mdx +266 -0
  22. package/site/content/docs/elements/channel/index.mdx +344 -0
  23. package/site/content/docs/elements/channel/meta.json +5 -0
  24. package/site/content/docs/elements/channel/push.mdx +218 -0
  25. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  26. package/site/content/docs/elements/channel/sms.mdx +264 -0
  27. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  28. package/site/content/docs/elements/clock/index.mdx +300 -0
  29. package/site/content/docs/elements/clock/meta.json +5 -0
  30. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  31. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  32. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  33. package/site/content/docs/elements/flow/http.mdx +1080 -0
  34. package/site/content/docs/elements/flow/index.mdx +618 -0
  35. package/site/content/docs/elements/flow/meta.json +5 -0
  36. package/site/content/docs/elements/flow/routing.mdx +559 -0
  37. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  38. package/site/content/docs/elements/gate/auth.mdx +371 -0
  39. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  40. package/site/content/docs/elements/gate/index.mdx +418 -0
  41. package/site/content/docs/elements/gate/meta.json +5 -0
  42. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  43. package/site/content/docs/elements/gate/rls.mdx +303 -0
  44. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  45. package/site/content/docs/elements/index.mdx +52 -11
  46. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  47. package/site/content/docs/elements/signal/index.mdx +352 -0
  48. package/site/content/docs/elements/signal/live.mdx +590 -0
  49. package/site/content/docs/elements/signal/meta.json +5 -0
  50. package/site/content/docs/elements/signal/once.mdx +596 -0
  51. package/site/content/docs/elements/store/files.mdx +659 -0
  52. package/site/content/docs/elements/store/index.mdx +310 -0
  53. package/site/content/docs/elements/store/kv.mdx +629 -0
  54. package/site/content/docs/elements/store/meta.json +5 -0
  55. package/site/content/docs/elements/store/search.mdx +946 -0
  56. package/site/content/docs/elements/store/sql.mdx +937 -0
  57. package/site/content/docs/elements/vault/config.mdx +256 -0
  58. package/site/content/docs/elements/vault/index.mdx +356 -0
  59. package/site/content/docs/elements/vault/meta.json +5 -0
  60. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  61. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  62. package/site/content/docs/index.mdx +78 -30
  63. package/site/content/docs/meta.json +11 -8
  64. package/site/content/docs/plugins/cors.mdx +2 -1
  65. package/site/content/docs/plugins/csrf.mdx +4 -1
  66. package/site/content/docs/plugins/headers.mdx +2 -1
  67. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  68. package/site/content/docs/plugins/magic-link.mdx +6 -0
  69. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  70. package/site/content/docs/plugins/oauth.mdx +1 -1
  71. package/site/content/docs/plugins/otp.mdx +12 -1
  72. package/site/content/docs/plugins/passkey.mdx +39 -15
  73. package/site/content/docs/plugins/two-factor.mdx +91 -34
  74. package/site/content/docs/plugins/username.mdx +1 -1
  75. package/site/content/docs/recipes/caddy.mdx +3 -3
  76. package/site/content/docs/recipes/index.mdx +18 -28
  77. package/site/content/docs/recipes/mailpit.mdx +1 -1
  78. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  79. package/site/content/docs/recipes/meta.json +1 -4
  80. package/site/content/docs/recipes/nginx.mdx +2 -2
  81. package/site/content/docs/recipes/openrouter.mdx +283 -0
  82. package/site/content/docs/recipes/pgdog.mdx +3 -3
  83. package/site/content/docs/recipes/postgres.mdx +1 -2
  84. package/site/content/docs/recipes/rustfs.mdx +2 -2
  85. package/site/content/docs/recipes/traefik.mdx +3 -3
  86. package/site/content/docs/reference/cli.mdx +231 -0
  87. package/site/content/docs/reference/configuration.mdx +30 -10
  88. package/site/content/docs/reference/environment-variables.mdx +93 -20
  89. package/site/content/docs/reference/errors.mdx +132 -29
  90. package/site/content/docs/reference/fx.mdx +137 -73
  91. package/site/content/docs/reference/index.mdx +13 -21
  92. package/site/content/docs/reference/meta.json +4 -5
  93. package/site/content/docs/reference/okid.mdx +42 -17
  94. package/site/content/docs/reference/plugins.mdx +4 -3
  95. package/site/content/docs/reference/security.mdx +197 -0
  96. package/site/content/docs/understand/meta.json +5 -0
  97. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  98. package/site/content/docs/understand/the-model.mdx +32 -0
  99. package/site/content/docs/understand/the-problem.mdx +74 -0
  100. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  101. package/src/auth/api-keys.ts +2 -1
  102. package/src/auth/bindings.ts +64 -16
  103. package/src/auth/gate-auth.test.ts +6 -1
  104. package/src/auth/identity.ts +152 -5
  105. package/src/auth/index.ts +29 -0
  106. package/src/auth/invites.ts +2 -1
  107. package/src/auth/method-context.ts +41 -0
  108. package/src/auth/oauth-as/crypto.ts +2 -1
  109. package/src/auth/oauth-as/stores.ts +2 -1
  110. package/src/auth/operator.ts +2 -1
  111. package/src/auth/sessions-jwt.test.ts +77 -0
  112. package/src/auth/sessions.ts +20 -4
  113. package/src/auth/tenants.ts +3 -2
  114. package/src/auth/two-factor-challenge.test.ts +61 -0
  115. package/src/auth/two-factor-challenge.ts +284 -0
  116. package/src/auth/verification.ts +5 -0
  117. package/src/bench/REPORT.md +49 -0
  118. package/src/bench/g03-signal-once.bench.ts +1 -1
  119. package/src/bench/g10-observability-contention.bench.ts +1 -1
  120. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  121. package/src/bench/load-app.ts +2 -10
  122. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  123. package/src/cli/ai-setup/apply.ts +294 -52
  124. package/src/cli/ai-setup/catalog.ts +238 -1342
  125. package/src/cli/ai-setup/index.ts +14 -99
  126. package/src/cli/ai-setup/prompts.ts +60 -443
  127. package/src/cli/ask-seed.test.ts +96 -0
  128. package/src/cli/ask-seed.ts +82 -0
  129. package/src/cli/ask-vault-gaps.test.ts +84 -0
  130. package/src/cli/ask-vault-gaps.ts +146 -0
  131. package/src/cli/build.ts +2 -2
  132. package/src/cli/client-add.test.ts +26 -1
  133. package/src/cli/client-add.ts +106 -27
  134. package/src/cli/competitor-mention-removal.test.ts +1 -1
  135. package/src/cli/db-seed.ts +2 -0
  136. package/src/cli/db.ts +150 -11
  137. package/src/cli/dev.ts +119 -211
  138. package/src/cli/docker-clean.ts +2 -2
  139. package/src/cli/docker-cli.test.ts +1 -1
  140. package/src/cli/doctor-diff.ts +4 -2
  141. package/src/cli/doctor-pii.test.ts +1 -1
  142. package/src/cli/doctor.ts +14 -1
  143. package/src/cli/hero-meta.test.ts +4 -2
  144. package/src/cli/load-config.images.test.ts +8 -10
  145. package/src/cli/load-config.ts +1 -1
  146. package/src/cli/registry.ts +22 -10
  147. package/src/cli/replay.ts +3 -1
  148. package/src/cli/start.ts +1 -1
  149. package/src/cli/tui/keys.ts +2 -3
  150. package/src/client/auth/cookies.ts +74 -0
  151. package/src/client/auth/create-auth-client.ts +636 -0
  152. package/src/client/auth/denials.ts +99 -0
  153. package/src/client/auth/session.ts +283 -0
  154. package/src/client/auth.test.ts +251 -0
  155. package/src/client/auth.ts +39 -114
  156. package/src/client/create-with-session.ts +245 -0
  157. package/src/client/create.ts +51 -5
  158. package/src/client/index.ts +11 -1
  159. package/src/client/live.ts +11 -79
  160. package/src/client/notes-contract.test.ts +5 -10
  161. package/src/client/sse.ts +137 -0
  162. package/src/client/stream.ts +147 -0
  163. package/src/client/transport.test.ts +32 -0
  164. package/src/client/transport.ts +123 -26
  165. package/src/client/types.ts +118 -14
  166. package/src/client-react/index.ts +185 -24
  167. package/src/client-react/use-live-query.ts +13 -3
  168. package/src/compiler/aot.test.ts +7 -4
  169. package/src/compiler/effects-embed.test.ts +56 -0
  170. package/src/compiler/effects-fetch.test.ts +43 -0
  171. package/src/compiler/effects-infer.ts +58 -2
  172. package/src/compiler/extract.test.ts +258 -44
  173. package/src/compiler/extract.ts +566 -48
  174. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  175. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  176. package/src/compiler/fixtures/skyport.expected.json +4 -4
  177. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  178. package/src/compiler/response.ts +2 -2
  179. package/src/compiler/search-writer-isolation.test.ts +40 -0
  180. package/src/config/index.ts +11 -0
  181. package/src/console/server/app.ts +17 -23
  182. package/src/console/server/bind.ts +4 -0
  183. package/src/console/server/console.test.ts +2 -2
  184. package/src/console/server/flows-invoke.test.ts +50 -34
  185. package/src/console/server/flows.ts +436 -255
  186. package/src/console/server/runs-ingest.test.ts +6 -3
  187. package/src/console/server/serve.ts +1 -1
  188. package/src/console/server/signals.test.ts +1 -5
  189. package/src/console/server/signals.ts +8 -6
  190. package/src/console/server/state.ts +6 -1
  191. package/src/console/server/store.ts +1 -1
  192. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  193. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  194. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  195. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  196. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  197. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  198. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  199. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  200. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  201. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  202. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  203. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  204. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  205. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  206. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  207. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  208. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  209. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  210. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  211. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  212. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  213. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  214. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  215. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  216. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  217. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  218. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  219. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  220. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  221. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  222. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  223. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  224. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  225. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  226. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  227. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  228. package/src/console/ui-next/dist/index.html +6 -13
  229. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  230. package/src/console/ui-next/src/client.ts +7 -2
  231. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  232. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  233. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  235. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  236. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  237. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  238. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  239. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  240. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  241. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  242. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  243. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  244. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  245. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  246. package/src/docker/ai-model-status.test.ts +1 -12
  247. package/src/docker/ai-model-status.ts +10 -38
  248. package/src/docker/compose-up.test.ts +100 -0
  249. package/src/docker/compose-up.ts +292 -0
  250. package/src/docker/compose.ts +4 -14
  251. package/src/docker/derive.ts +9 -34
  252. package/src/docker/docker.test.ts +16 -261
  253. package/src/docker/images-config.test.ts +13 -25
  254. package/src/docker/index.ts +1 -24
  255. package/src/docker/recipes/index.ts +0 -23
  256. package/src/docker/recipes/pgdog.ts +6 -4
  257. package/src/docker/stack-id.test.ts +2 -2
  258. package/src/docker/stack-id.ts +0 -1
  259. package/src/docker/types.ts +2 -2
  260. package/src/drivers/ai-anthropic.ts +10 -0
  261. package/src/drivers/ai-openai-compatible.ts +34 -7
  262. package/src/drivers/ai-providers.test.ts +0 -107
  263. package/src/drivers/ai-stream.test.ts +0 -34
  264. package/src/drivers/ai-types.ts +17 -8
  265. package/src/drivers/channel-fcm.ts +6 -1
  266. package/src/drivers/channel-msegat.ts +6 -1
  267. package/src/drivers/channel-resend.ts +5 -1
  268. package/src/drivers/channel-smtp.ts +5 -1
  269. package/src/drivers/channel-sndr.ts +10 -1
  270. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  271. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  272. package/src/drivers/channel-taqnyat.ts +6 -1
  273. package/src/drivers/channel-types.ts +10 -0
  274. package/src/drivers/channel-unifonic.ts +6 -1
  275. package/src/drivers/channel-wa-cloud.ts +6 -1
  276. package/src/drivers/channel-webpush.ts +6 -1
  277. package/src/drivers/external.ts +33 -0
  278. package/src/drivers/index.ts +0 -10
  279. package/src/drivers/meilisearch.ts +6 -0
  280. package/src/drivers/oauth-types.ts +4 -0
  281. package/src/drivers/postgres.test.ts +74 -3
  282. package/src/drivers/postgres.ts +175 -6
  283. package/src/drivers/signal-types.ts +5 -5
  284. package/src/drivers/types.ts +7 -0
  285. package/src/drivers/vault-types.ts +5 -0
  286. package/src/elements/ai/declare.ts +17 -4
  287. package/src/elements/ai/eval.ts +1 -1
  288. package/src/elements/ai/mcp-http.ts +1 -1
  289. package/src/elements/ai/pii.ts +1 -1
  290. package/src/elements/ai/providers.test.ts +289 -0
  291. package/src/elements/ai/providers.ts +169 -0
  292. package/src/elements/ai/runtime.ts +68 -5
  293. package/src/elements/ai/schema.ts +2 -2
  294. package/src/elements/ai.test.ts +7 -7
  295. package/src/elements/ai.ts +17 -1
  296. package/src/elements/channel/runtime.ts +105 -12
  297. package/src/elements/clock/cron-fields.test.ts +144 -0
  298. package/src/elements/clock/cron-fields.ts +185 -0
  299. package/src/elements/clock/declare.ts +247 -3
  300. package/src/elements/clock.ts +21 -1
  301. package/src/elements/index.ts +15 -0
  302. package/src/elements/signal/chaos-child.ts +1 -2
  303. package/src/elements/signal/declare.ts +65 -37
  304. package/src/elements/signal/delivery-modes.test.ts +11 -31
  305. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  306. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  307. package/src/elements/signal/key-ordering.test.ts +6 -26
  308. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  309. package/src/elements/signal/optional-emit.test.ts +6 -9
  310. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  311. package/src/elements/signal/orphan-messages.test.ts +4 -15
  312. package/src/elements/signal/reconcile.test.ts +2 -2
  313. package/src/elements/signal/runtime.ts +1 -1
  314. package/src/elements/signal/schema-emit.test.ts +4 -8
  315. package/src/elements/signal.test.ts +24 -49
  316. package/src/elements/signal.ts +9 -2
  317. package/src/elements/store/domain-ddl.test.ts +1 -1
  318. package/src/elements/store/live-http.test.ts +9 -4
  319. package/src/elements/store/prepare-row.test.ts +123 -0
  320. package/src/elements/store/resource.ts +43 -12
  321. package/src/elements/store/schema-decl.ts +117 -0
  322. package/src/elements/store/search-backfill.ts +205 -0
  323. package/src/elements/store/search-bind.ts +66 -0
  324. package/src/elements/store/search-bm25.ts +78 -0
  325. package/src/elements/store/search-ddl.ts +156 -0
  326. package/src/elements/store/search-embed-flow.ts +141 -0
  327. package/src/elements/store/search-errors.ts +34 -0
  328. package/src/elements/store/search-fusion.ts +112 -0
  329. package/src/elements/store/search-lsh.ts +179 -0
  330. package/src/elements/store/search-runtime.ts +300 -0
  331. package/src/elements/store/search.test.ts +144 -0
  332. package/src/elements/store/sql-session.test.ts +1 -0
  333. package/src/elements/store/sql-session.ts +83 -3
  334. package/src/elements/store/table.ts +23 -2
  335. package/src/elements/store/upsert-app.test.ts +1 -3
  336. package/src/elements/store.ts +50 -0
  337. package/src/full.ts +4 -2
  338. package/src/http.ts +4 -1
  339. package/src/i18n/catalogs/ar.ts +14 -6
  340. package/src/i18n/catalogs/en.ts +14 -6
  341. package/src/index.ts +6 -1
  342. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  343. package/src/kernel/adopt-routes.ts +9 -1
  344. package/src/kernel/app-auth.ts +5 -0
  345. package/src/kernel/app.ts +102 -9
  346. package/src/kernel/auto-cache.test.ts +6 -12
  347. package/src/kernel/auto-registry.test.ts +3 -3
  348. package/src/kernel/boot-bind/ai.test.ts +22 -19
  349. package/src/kernel/boot-bind/ai.ts +5 -43
  350. package/src/kernel/boot-bind/clock.ts +23 -5
  351. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  352. package/src/kernel/boot.test.ts +11 -6
  353. package/src/kernel/boot.ts +66 -16
  354. package/src/kernel/boundary-contract.ts +93 -0
  355. package/src/kernel/budget.test.ts +1 -1
  356. package/src/kernel/call.ts +89 -0
  357. package/src/kernel/capability.ts +6 -0
  358. package/src/kernel/client-descriptor.ts +123 -0
  359. package/src/kernel/clock-timezone.test.ts +80 -0
  360. package/src/kernel/correlation.test.ts +1 -1
  361. package/src/kernel/dry-run.ts +11 -8
  362. package/src/kernel/effects-stamping.test.ts +42 -9
  363. package/src/kernel/effects.test.ts +4 -2
  364. package/src/kernel/effects.ts +51 -9
  365. package/src/kernel/errors-channel.ts +17 -0
  366. package/src/kernel/errors-live-resume.ts +3 -2
  367. package/src/kernel/errors-tenant.ts +7 -4
  368. package/src/kernel/errors.registry-helpers.ts +96 -0
  369. package/src/kernel/errors.registry.test.ts +139 -25
  370. package/src/kernel/errors.ts +102 -23
  371. package/src/kernel/external-effects.test.ts +196 -0
  372. package/src/kernel/flow.test.ts +13 -4
  373. package/src/kernel/flow.ts +69 -71
  374. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  375. package/src/kernel/fx-dead-letters.test.ts +3 -3
  376. package/src/kernel/fx-fetch.ts +55 -0
  377. package/src/kernel/fx-live-stream.ts +2 -2
  378. package/src/kernel/fx-live.test.ts +8 -8
  379. package/src/kernel/fx.test.ts +17 -11
  380. package/src/kernel/fx.ts +180 -57
  381. package/src/kernel/horizontal-child.ts +1 -1
  382. package/src/kernel/http-query.test.ts +3 -6
  383. package/src/kernel/index.ts +13 -3
  384. package/src/kernel/instance-id.ts +1 -1
  385. package/src/kernel/instances.test.ts +40 -0
  386. package/src/kernel/instances.ts +47 -9
  387. package/src/kernel/live-http.test.ts +6 -7
  388. package/src/kernel/live-http.ts +6 -2
  389. package/src/kernel/live-resume.test.ts +1 -1
  390. package/src/kernel/mcp-tool.test.ts +3 -3
  391. package/src/kernel/on.ts +98 -4
  392. package/src/kernel/pipeline.test.ts +12 -15
  393. package/src/kernel/plugin-elements.test.ts +1 -1
  394. package/src/kernel/stamp-http.test.ts +3 -3
  395. package/src/kernel/triggers.ts +210 -95
  396. package/src/kernel-entry.ts +0 -1
  397. package/src/manifest/diff.ts +11 -1
  398. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  399. package/src/manifest/types.ts +46 -2
  400. package/src/mcp/docs-index.ts +3 -3
  401. package/src/mcp/docs-mcp.test.ts +2 -2
  402. package/src/mcp/docs-tools.ts +1 -1
  403. package/src/okid.test.ts +56 -0
  404. package/src/okid.ts +55 -11
  405. package/src/plugins/anonymous.ts +8 -4
  406. package/src/plugins/auth/shared.ts +27 -3
  407. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  408. package/src/plugins/auth-methods.security.test.ts +561 -23
  409. package/src/plugins/config-source.ts +1 -1
  410. package/src/plugins/csrf.test.ts +65 -0
  411. package/src/plugins/index.ts +2 -0
  412. package/src/plugins/magic-link.ts +36 -16
  413. package/src/plugins/oauth/shared.ts +3 -1
  414. package/src/plugins/oauth.ts +22 -14
  415. package/src/plugins/otp.ts +37 -21
  416. package/src/plugins/passkey-webauthn.ts +9 -6
  417. package/src/plugins/passkey.ts +98 -26
  418. package/src/plugins/pre-account-hijack.test.ts +223 -0
  419. package/src/plugins/two-factor.ts +466 -37
  420. package/src/plugins/username.ts +19 -7
  421. package/src/release/build-lib.ts +1 -0
  422. package/src/release/limits.ts +2 -2
  423. package/src/release/measure.ts +1 -1
  424. package/src/runs/collect.test.ts +0 -2
  425. package/src/runtime/json-code-block.test.ts +250 -20
  426. package/src/runtime/json-code-block.ts +1261 -41
  427. package/src/term.test.ts +1 -1
  428. package/src/test/create-test-app.test.ts +12 -12
  429. package/src/test/live-signals.test.ts +6 -6
  430. package/src/test/provisions.integration.test.ts +10 -14
  431. package/src/test/reset-element-registries.ts +1 -1
  432. package/src/test/tenant-isolation.test.ts +12 -6
  433. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  434. package/site/content/docs/deployment/docker.mdx +0 -227
  435. package/site/content/docs/deployment/index.mdx +0 -83
  436. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  437. package/site/content/docs/deployment/meta.json +0 -5
  438. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  439. package/site/content/docs/elements/ai.mdx +0 -385
  440. package/site/content/docs/elements/channel.mdx +0 -346
  441. package/site/content/docs/elements/clock.mdx +0 -244
  442. package/site/content/docs/elements/flow.mdx +0 -420
  443. package/site/content/docs/elements/gate.mdx +0 -436
  444. package/site/content/docs/elements/signal.mdx +0 -380
  445. package/site/content/docs/elements/store.mdx +0 -1099
  446. package/site/content/docs/elements/vault.mdx +0 -405
  447. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  448. package/site/content/docs/get-started/index.mdx +0 -43
  449. package/site/content/docs/get-started/installation.mdx +0 -220
  450. package/site/content/docs/get-started/introduction.mdx +0 -132
  451. package/site/content/docs/get-started/meta.json +0 -13
  452. package/site/content/docs/get-started/project-structure.mdx +0 -925
  453. package/site/content/docs/get-started/testing.mdx +0 -328
  454. package/site/content/docs/get-started/why.mdx +0 -162
  455. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  456. package/site/content/docs/recipes/ollama.mdx +0 -142
  457. package/site/content/docs/recipes/sglang.mdx +0 -105
  458. package/site/content/docs/recipes/vllm.mdx +0 -106
  459. package/site/content/docs/reference/cli.md +0 -232
  460. package/site/content/docs/reference/client.mdx +0 -469
  461. package/site/content/docs/reference/security.md +0 -74
  462. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  463. package/src/cli/ai-setup/recommend.test.ts +0 -225
  464. package/src/cli/ai-setup/recommend.ts +0 -225
  465. package/src/cli/dev-controls.test.ts +0 -122
  466. package/src/cli/dev-controls.ts +0 -164
  467. package/src/cli/tui/DevLive.tsx +0 -126
  468. package/src/console/ui-next/dist/assets/access-page-De7Lc2JC.js +0 -4
  469. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  470. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  471. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  472. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  473. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  474. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  475. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  476. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  477. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  478. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  479. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  480. package/src/console/ui-next/dist/assets/flows-page-RGy7VEA_.js +0 -1
  481. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  482. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  483. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  484. package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
  485. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  486. package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.js +0 -4
  487. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  488. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  489. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  490. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  491. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  492. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  493. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  494. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  495. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  496. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  497. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  498. package/src/console/ui-next/dist/assets/units-page-4rHOePuE.js +0 -1
  499. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  500. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  501. package/src/console/ui-next/dist/assets/vault-page-DISPgxLM.js +0 -2
  502. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  503. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  504. package/src/docker/ollama-pull.ts +0 -232
  505. package/src/docker/recipes/llama-cpp.ts +0 -298
  506. package/src/docker/recipes/ollama.ts +0 -44
  507. package/src/docker/recipes/sglang.ts +0 -55
  508. package/src/docker/recipes/vllm.ts +0 -44
  509. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  510. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  511. package/src/drivers/ai-ollama.ts +0 -389
  512. package/src/drivers/ollama.ts +0 -14
@@ -31,11 +31,7 @@ afterEach(async () => {
31
31
 
32
32
  describe("signal key ordering · memory", () => {
33
33
  test("same key: never concurrent; complete in emission order", async () => {
34
- const once = signal("order-events", {
35
- delivery: "once",
36
- retries: 3,
37
- deadLetter: true,
38
- });
34
+ const once = signal.once("order-events", { retries: 3, deadLetter: true });
39
35
  const bus = await memorySignalDriver.open({
40
36
  signals: new Map([[once.name, once]]),
41
37
  });
@@ -75,11 +71,7 @@ describe("signal key ordering · memory", () => {
75
71
  });
76
72
 
77
73
  test("different keys: may process concurrently", async () => {
78
- const once = signal("order-events", {
79
- delivery: "once",
80
- retries: 3,
81
- deadLetter: true,
82
- });
74
+ const once = signal.once("order-events", { retries: 3, deadLetter: true });
83
75
  const bus = await memorySignalDriver.open({
84
76
  signals: new Map([[once.name, once]]),
85
77
  });
@@ -110,11 +102,7 @@ describe("signal key ordering · memory", () => {
110
102
  });
111
103
 
112
104
  test("no key: unchanged competing-consumer (both may run concurrent)", async () => {
113
- const once = signal("order-events", {
114
- delivery: "once",
115
- retries: 3,
116
- deadLetter: true,
117
- });
105
+ const once = signal.once("order-events", { retries: 3, deadLetter: true });
118
106
  const bus = await memorySignalDriver.open({
119
107
  signals: new Map([[once.name, once]]),
120
108
  });
@@ -148,7 +136,7 @@ describe("signal key ordering · memory", () => {
148
136
  describe("signal key ordering · postgres", () => {
149
137
  test("same-key pending blocked while sibling holds unexpired lease", async () => {
150
138
  let t = 10_000;
151
- const once = signal("order-events", { delivery: "once", retries: 3 });
139
+ const once = signal.once("order-events", { retries: 3 });
152
140
  const sql = createPostgresSignalFake({ now: () => t });
153
141
  const runtime = createSignalRuntime({
154
142
  driver: postgresSignalDriver,
@@ -213,11 +201,7 @@ describe("signal key ordering · postgres", () => {
213
201
  });
214
202
 
215
203
  test("emit with key then drain preserves emission order", async () => {
216
- const once = signal("order-events", {
217
- delivery: "once",
218
- retries: 3,
219
- deadLetter: true,
220
- });
204
+ const once = signal.once("order-events", { retries: 3, deadLetter: true });
221
205
  const bus = await postgresSignalDriver.open({
222
206
  signals: new Map([[once.name, once]]),
223
207
  sql: createPostgresSignalFake(),
@@ -246,11 +230,7 @@ describe("signal key ordering · lease reclaim", () => {
246
230
  const marker = join(dir, "claimed");
247
231
  try {
248
232
  let t = 5_000;
249
- const once = signal("order-events", {
250
- delivery: "once",
251
- retries: 3,
252
- deadLetter: true,
253
- });
233
+ const once = signal.once("order-events", { retries: 3, deadLetter: true });
254
234
  const r1 = createSignalRuntime({
255
235
  driver: memorySignalDriver,
256
236
  durablePath,
@@ -30,11 +30,7 @@ describe("signal lease reclaim", () => {
30
30
  const marker = join(dir, "claimed");
31
31
  try {
32
32
  let t = 5_000;
33
- const once = signal("order-placed", {
34
- delivery: "once",
35
- retries: 3,
36
- deadLetter: true,
37
- });
33
+ const once = signal.once("order-placed", { retries: 3, deadLetter: true });
38
34
  const r1 = createSignalRuntime({
39
35
  driver: memorySignalDriver,
40
36
  durablePath,
@@ -83,7 +79,7 @@ describe("signal lease reclaim", () => {
83
79
 
84
80
  test("postgres: expired inflight reclaimed in the same claim query", async () => {
85
81
  let t = 10_000;
86
- const once = signal("order-placed", { delivery: "once", retries: 3 });
82
+ const once = signal.once("order-placed", { retries: 3 });
87
83
  const sql = createPostgresSignalFake({ now: () => t });
88
84
  const runtime = createSignalRuntime({
89
85
  driver: postgresSignalDriver,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Zero-subscriber emit — OKE1042 when optional is unset/false; allow when true.
2
+ * Zero-subscriber emit — OKE1240 when optional is unset/false; allow when true.
3
3
  */
4
4
 
5
5
  import { afterEach, describe, expect, test } from "bun:test";
@@ -44,8 +44,8 @@ for (const { label, driver, setup } of [
44
44
  },
45
45
  ] as const) {
46
46
  describe(`signal optional emit · ${label}`, () => {
47
- test("optional unset/false: emit with zero subscribers throws OKE1042", async () => {
48
- const once = signal("order-placed", { delivery: "once" });
47
+ test("optional unset/false: emit with zero subscribers throws OKE1240", async () => {
48
+ const once = signal.once("order-placed");
49
49
  expect(once.optional).toBe(false);
50
50
  const bus = await openBus(driver, [once], setup());
51
51
 
@@ -56,16 +56,13 @@ for (const { label, driver, setup } of [
56
56
  err = e;
57
57
  }
58
58
  expect(err).toBeInstanceOf(OkeError);
59
- expect((err as OkeError).code).toBe(1042);
60
- expect((err as Error).message).toContain("OKE1042");
59
+ expect((err as OkeError).code).toBe(1240);
60
+ expect((err as Error).message).toContain("OKE1240");
61
61
  expect((err as Error).message).toContain("no subscriber");
62
62
  });
63
63
 
64
64
  test("optional: true allows emit with zero subscribers", async () => {
65
- const once = signal("hook", {
66
- delivery: "once",
67
- optional: true,
68
- });
65
+ const once = signal.once("hook", { optional: true });
69
66
  const bus = await openBus(driver, [once], setup());
70
67
  await bus.emit("hook", { ok: true });
71
68
  await bus.drain();
@@ -42,20 +42,11 @@ afterEach(async () => {
42
42
  describe("signal order lifecycle · once + broadcast + live", () => {
43
43
  test("one placed order: exclusive fulfill, fan-out side effects, live status replay", async () => {
44
44
  // once — queued job: exactly one competing consumer fulfills
45
- const orderPlaced = signal("order-placed", {
46
- delivery: "once",
47
- retries: 2,
48
- deadLetter: true,
49
- });
45
+ const orderPlaced = signal.once("order-placed", { retries: 2, deadLetter: true });
50
46
  // broadcast — same domain event fan-out: every subscriber gets a copy
51
- const orderChanged = signal("order-changed", {
52
- delivery: "broadcast",
53
- });
47
+ const orderChanged = signal.broadcast("order-changed");
54
48
  // live — client-visible status feed (bus.live; createClient has no subscribe yet)
55
- const orderStatus = signal("order-status", {
56
- delivery: "live",
57
- optional: true,
58
- });
49
+ const orderStatus = signal.live("order-status", { optional: true });
59
50
 
60
51
  const bus = await memorySignalDriver.open({
61
52
  signals: new Map([
@@ -118,11 +109,7 @@ describe("signal order lifecycle · once + broadcast + live", () => {
118
109
 
119
110
  test("once retries then DLQ still apply inside the same domain", async () => {
120
111
  // Light reuse of once retry/DLQ — full matrix lives in delivery-modes.test.ts.
121
- const orderPlaced = signal("order-placed", {
122
- delivery: "once",
123
- retries: 1,
124
- deadLetter: true,
125
- });
112
+ const orderPlaced = signal.once("order-placed", { retries: 1, deadLetter: true });
126
113
  const bus = await memorySignalDriver.open({
127
114
  signals: new Map([[orderPlaced.name, orderPlaced]]),
128
115
  });
@@ -28,11 +28,7 @@ describe("orphan signal messages", () => {
28
28
  const dir = await mkdtemp(join(tmpdir(), "oke-signal-orphan-"));
29
29
  const durablePath = join(dir, "bus.json");
30
30
  try {
31
- const legacy = signal("legacy-shipped", {
32
- delivery: "once",
33
- retries: 0,
34
- deadLetter: true,
35
- });
31
+ const legacy = signal.once("legacy-shipped", { retries: 0, deadLetter: true });
36
32
  const runtime = createSignalRuntime({
37
33
  driver: memorySignalDriver,
38
34
  durablePath,
@@ -75,11 +71,7 @@ describe("orphan signal messages", () => {
75
71
  openRuntimes.pop();
76
72
 
77
73
  // Re-declare → DLQ processable again via replay.
78
- const restored = signal("legacy-shipped", {
79
- delivery: "once",
80
- retries: 0,
81
- deadLetter: true,
82
- });
74
+ const restored = signal.once("legacy-shipped", { retries: 0, deadLetter: true });
83
75
  const again = await reconcileSignals([restored], store);
84
76
  expect(again.active).toEqual(["legacy-shipped"]);
85
77
  expect(again.orphaned).toEqual([]);
@@ -111,10 +103,7 @@ describe("orphan signal messages", () => {
111
103
  const dir = await mkdtemp(join(tmpdir(), "oke-signal-orphan-pend-"));
112
104
  const durablePath = join(dir, "bus.json");
113
105
  try {
114
- const once = signal("legacy-shipped", {
115
- delivery: "once",
116
- optional: true,
117
- });
106
+ const once = signal.once("legacy-shipped", { optional: true });
118
107
  const runtime = createSignalRuntime({
119
108
  driver: memorySignalDriver,
120
109
  durablePath,
@@ -126,7 +115,7 @@ describe("orphan signal messages", () => {
126
115
  await runtime.close();
127
116
  openRuntimes.pop();
128
117
 
129
- const other = signal("order-placed", { delivery: "once" });
118
+ const other = signal.once("order-placed");
130
119
  const next = createSignalRuntime({
131
120
  driver: memorySignalDriver,
132
121
  durablePath,
@@ -5,8 +5,8 @@ import { createMemorySignalConfigStore, reconcileSignals } from "./reconcile.ts"
5
5
  describe("reconcileSignals", () => {
6
6
  test("marks a removed signal orphaned without deleting it", async () => {
7
7
  const store = createMemorySignalConfigStore();
8
- const a = signal("order-placed", { delivery: "once" });
9
- const b = signal("legacy-shipped", { delivery: "once" });
8
+ const a = signal.once("order-placed");
9
+ const b = signal.once("legacy-shipped");
10
10
 
11
11
  await reconcileSignals([a, b], store);
12
12
  const second = await reconcileSignals([a], store);
@@ -68,7 +68,7 @@ export interface SignalRuntime {
68
68
  */
69
69
  live(name: string, opts?: { readonly afterId?: string }): AsyncIterable<LiveEvent>;
70
70
  /**
71
- * Validate a live resume cursor (auto-starts). Throws OKE1014 when missing.
71
+ * Validate a live resume cursor (auto-starts). Throws OKE1210 when missing.
72
72
  *
73
73
  * @param name - Signal name
74
74
  * @param afterId - SSE cursor
@@ -49,9 +49,8 @@ const drivers: Array<{
49
49
 
50
50
  for (const { label, driver, setup } of drivers) {
51
51
  describe(`signal schema emit · ${label}`, () => {
52
- test("invalid payload rejected at emit with OKE1043; valid succeeds", async () => {
53
- const orderPlaced = signal("order-placed", {
54
- delivery: "once",
52
+ test("invalid payload rejected at emit with OKE1250; valid succeeds", async () => {
53
+ const orderPlaced = signal.once("order-placed", {
55
54
  schema: z.object({
56
55
  orderId: z.string(),
57
56
  total: z.number(),
@@ -74,7 +73,7 @@ for (const { label, driver, setup } of drivers) {
74
73
  expect(rejected).toBeInstanceOf(OkeError);
75
74
  const oke = rejected as OkeError;
76
75
  expect(oke.code).toBe(OKE_ERRORS.SIGNAL_SCHEMA.code);
77
- expect(oke.message).toContain("OKE1043");
76
+ expect(oke.message).toContain("OKE1250");
78
77
  expect(oke.message).toContain("order-placed");
79
78
 
80
79
  // Nothing staged while invalid.
@@ -90,10 +89,7 @@ for (const { label, driver, setup } of drivers) {
90
89
  });
91
90
 
92
91
  test("no schema: any payload still accepted", async () => {
93
- const loose = signal("hook", {
94
- delivery: "once",
95
- optional: true,
96
- });
92
+ const loose = signal.once("hook", { optional: true });
97
93
  const bus = await openBus(driver, [loose], setup?.() ?? {});
98
94
  const got: unknown[] = [];
99
95
  await bus.subscribe("hook", "c1", async (m) => {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Signal element acceptance:
3
- * - delivery is mandatory with no default
3
+ * - delivery helpers: signal.once / broadcast / live
4
4
  * - rolled-back transaction emits nothing
5
5
  * - crash between write and emit loses nothing (transactional outbox)
6
6
  * - competing consumers under once receive each message once
@@ -76,25 +76,20 @@ async function openBus(
76
76
  }
77
77
 
78
78
  describe("signal declaration", () => {
79
- test("delivery is mandatory with no default", () => {
80
- expect(() =>
81
- // @ts-expect-error delivery is required
82
- signal("x", { retries: 1 }),
83
- ).toThrow(/delivery is mandatory/);
84
-
85
- const s = signal("order-placed", { delivery: "once" });
86
- expect(s.delivery).toBe("once");
87
- expect(s.name).toBe("order-placed");
79
+ test("helpers stamp delivery physics", () => {
80
+ expect(signal.once("order-placed").delivery).toBe("once");
81
+ expect(signal.broadcast("news").delivery).toBe("broadcast");
82
+ expect(signal.live("seat-feed").delivery).toBe("live");
83
+ expect(signal.once("order-placed").name).toBe("order-placed");
88
84
  });
89
85
 
90
86
  test("retention is live-only", () => {
91
87
  expect(() =>
92
88
  // @ts-expect-error retention is live-only
93
- signal("order-placed", { delivery: "once", retention: { maxCount: 2 } }),
89
+ signal.once("order-placed", { retention: { maxCount: 2 } }),
94
90
  ).toThrow(/retention is only valid/);
95
91
 
96
- const live = signal("order-status", {
97
- delivery: "live",
92
+ const live = signal.live("order-status", {
98
93
  optional: true,
99
94
  retention: { maxAge: "24h", maxCount: 500 },
100
95
  });
@@ -105,7 +100,7 @@ describe("signal declaration", () => {
105
100
  for (const { label, driver, setup } of drivers) {
106
101
  describe(`signal driver · ${label}`, () => {
107
102
  test("rolled-back transaction emits nothing", async () => {
108
- const once = signal("order-placed", { delivery: "once" });
103
+ const once = signal.once("order-placed");
109
104
  const bus = await openBus(driver, [once], setup?.() ?? {});
110
105
  const seen: unknown[] = [];
111
106
  await bus.subscribe("order-placed", "c1", async (m) => {
@@ -123,7 +118,7 @@ for (const { label, driver, setup } of drivers) {
123
118
  });
124
119
 
125
120
  test("commit makes write + emit visible together", async () => {
126
- const once = signal("order-placed", { delivery: "once" });
121
+ const once = signal.once("order-placed");
127
122
  const bus = await openBus(driver, [once], setup?.() ?? {});
128
123
  const seen: unknown[] = [];
129
124
  await bus.subscribe("order-placed", "c1", async (m) => {
@@ -141,7 +136,7 @@ for (const { label, driver, setup } of drivers) {
141
136
  });
142
137
 
143
138
  test("competing consumers under once receive each message exactly once", async () => {
144
- const once = signal("order-placed", { delivery: "once", retries: 2 });
139
+ const once = signal.once("order-placed", { retries: 2 });
145
140
  const bus = await openBus(driver, [once], setup?.() ?? {});
146
141
  const got: string[] = [];
147
142
 
@@ -163,7 +158,7 @@ for (const { label, driver, setup } of drivers) {
163
158
  });
164
159
 
165
160
  test("once: a single emit is received by exactly one of two consumers", async () => {
166
- const once = signal("order-placed", { delivery: "once", retries: 2 });
161
+ const once = signal.once("order-placed", { retries: 2 });
167
162
  const bus = await openBus(driver, [once], setup?.() ?? {});
168
163
  const got: string[] = [];
169
164
  await bus.subscribe("order-placed", "worker-a", async () => {
@@ -179,7 +174,7 @@ for (const { label, driver, setup } of drivers) {
179
174
  });
180
175
 
181
176
  test("broadcast reaches every subscriber", async () => {
182
- const bcast = signal("news", { delivery: "broadcast" });
177
+ const bcast = signal.broadcast("news");
183
178
  const bus = await openBus(driver, [bcast], setup?.() ?? {});
184
179
  const a: unknown[] = [];
185
180
  const b: unknown[] = [];
@@ -198,7 +193,7 @@ for (const { label, driver, setup } of drivers) {
198
193
  });
199
194
 
200
195
  test("live is client-subscribable", async () => {
201
- const live = signal("seat-feed", { delivery: "live", optional: true });
196
+ const live = signal.live("seat-feed", { optional: true });
202
197
  const bus = await openBus(driver, [live], setup?.() ?? {});
203
198
  const it = bus.live("seat-feed")[Symbol.asyncIterator]();
204
199
  const pending = it.next();
@@ -211,11 +206,7 @@ for (const { label, driver, setup } of drivers) {
211
206
  });
212
207
 
213
208
  test("DLQ preserves typed failure reasons per attempt", async () => {
214
- const once = signal("flaky", {
215
- delivery: "once",
216
- retries: 2,
217
- deadLetter: true,
218
- });
209
+ const once = signal.once("flaky", { retries: 2, deadLetter: true });
219
210
  const bus = await openBus(driver, [once], setup?.() ?? {});
220
211
  let attempts = 0;
221
212
  await bus.subscribe("flaky", "c1", async () => {
@@ -241,7 +232,7 @@ for (const { label, driver, setup } of drivers) {
241
232
 
242
233
  describe("postgres transactional emit (dual-write fix)", () => {
243
234
  test("fx.emit enrols in the caller's transaction via runtime", async () => {
244
- const orderPlaced = signal("order-placed", { delivery: "once" });
235
+ const orderPlaced = signal.once("order-placed");
245
236
  const runtime = createSignalRuntime({
246
237
  driver: postgresSignalDriver,
247
238
  sql: createPostgresSignalFake(),
@@ -268,7 +259,7 @@ describe("postgres transactional emit (dual-write fix)", () => {
268
259
 
269
260
  test("LISTEN/NOTIFY wakes drain after commit", async () => {
270
261
  const sql = createPostgresSignalFake();
271
- const once = signal("order-placed", { delivery: "once" });
262
+ const once = signal.once("order-placed");
272
263
  const bus = await openBus(postgresSignalDriver, [once], { sql });
273
264
  const seen: unknown[] = [];
274
265
  await bus.subscribe("order-placed", "c1", async (m) => {
@@ -291,7 +282,7 @@ describe("postgres transactional emit (dual-write fix)", () => {
291
282
  describe("redis / nats outbox relay", () => {
292
283
  test("redis relay publishes after commit, not on rollback", async () => {
293
284
  const redis = createSignalRedisFake();
294
- const once = signal("order-placed", { delivery: "once", optional: true });
285
+ const once = signal.once("order-placed", { optional: true });
295
286
  const bus = await openBus(redisSignalDriver, [once], { redis });
296
287
 
297
288
  const tx = await bus.begin();
@@ -309,7 +300,7 @@ describe("redis / nats outbox relay", () => {
309
300
 
310
301
  test("nats relay publishes after commit, not on rollback", async () => {
311
302
  const nats = createSignalNatsFake();
312
- const once = signal("order-placed", { delivery: "once", optional: true });
303
+ const once = signal.once("order-placed", { optional: true });
313
304
  const bus = await openBus(natsSignalDriver, [once], { nats });
314
305
 
315
306
  const tx = await bus.begin();
@@ -338,11 +329,7 @@ describe("chaos — kill process mid-transaction", () => {
338
329
  expect(code).toBe(99);
339
330
 
340
331
  // Recover: new process opens the same durable path.
341
- const orderPlaced = signal("order-placed", {
342
- delivery: "once",
343
- retries: 3,
344
- deadLetter: true,
345
- });
332
+ const orderPlaced = signal.once("order-placed", { retries: 3, deadLetter: true });
346
333
  const runtime = createSignalRuntime({
347
334
  driver: memorySignalDriver,
348
335
  durablePath,
@@ -376,7 +363,7 @@ describe("chaos — kill process mid-transaction", () => {
376
363
  import { memorySignalDriver } from ${JSON.stringify(join(import.meta.dir, "../drivers/signal-memory.ts"))};
377
364
  import { signal } from ${JSON.stringify(join(import.meta.dir, "signal/declare.ts"))};
378
365
  import { createSignalRuntime } from ${JSON.stringify(join(import.meta.dir, "signal/runtime.ts"))};
379
- const orderPlaced = signal("order-placed", { delivery: "once", retries: 3, deadLetter: true, optional: true });
366
+ const orderPlaced = signal.once("order-placed", { retries: 3, deadLetter: true, optional: true });
380
367
  const runtime = createSignalRuntime({ driver: memorySignalDriver, durablePath: ${JSON.stringify(durablePath)} });
381
368
  runtime.register(orderPlaced);
382
369
  const bus = await runtime.start();
@@ -396,11 +383,7 @@ describe("chaos — kill process mid-transaction", () => {
396
383
  child.kill(9);
397
384
  await child.exited;
398
385
 
399
- const orderPlaced = signal("order-placed", {
400
- delivery: "once",
401
- retries: 3,
402
- deadLetter: true,
403
- });
386
+ const orderPlaced = signal.once("order-placed", { retries: 3, deadLetter: true });
404
387
  const runtime = createSignalRuntime({
405
388
  driver: memorySignalDriver,
406
389
  durablePath,
@@ -434,11 +417,7 @@ describe("chaos — kill process mid-transaction", () => {
434
417
  });
435
418
  expect(await child.exited).toBe(0);
436
419
 
437
- const orderPlaced = signal("order-placed", {
438
- delivery: "once",
439
- retries: 3,
440
- deadLetter: true,
441
- });
420
+ const orderPlaced = signal.once("order-placed", { retries: 3, deadLetter: true });
442
421
  const runtime = createSignalRuntime({
443
422
  driver: memorySignalDriver,
444
423
  durablePath,
@@ -501,11 +480,7 @@ describe("chaos — kill process mid-transaction", () => {
501
480
  await Bun.sleep(leaseMs + 40);
502
481
 
503
482
  let clock = Date.now() + leaseMs + 1_000;
504
- const orderPlaced = signal("order-placed", {
505
- delivery: "once",
506
- retries: 3,
507
- deadLetter: true,
508
- });
483
+ const orderPlaced = signal.once("order-placed", { retries: 3, deadLetter: true });
509
484
  const runtime = createSignalRuntime({
510
485
  driver: memorySignalDriver,
511
486
  durablePath,
@@ -1,13 +1,20 @@
1
1
  /**
2
2
  * Signal element — data in motion.
3
3
  *
4
- * Delivery physics: `once` · `broadcast` · `live` (mandatory, no default).
4
+ * Delivery physics via helpers: `signal.once` · `signal.broadcast` · `signal.live`.
5
5
  * Drivers: `memory` · `postgres` (default / transactional) · `redis` · `nats`.
6
6
  * @module
7
7
  */
8
8
 
9
9
  export { signal } from "./signal/declare.ts";
10
- export type { SignalDecl, SignalOptions, SignalRetention } from "./signal/declare.ts";
10
+ export type {
11
+ SignalDecl,
12
+ SignalLiveOptions,
13
+ SignalNamespace,
14
+ SignalOptions,
15
+ SignalRetention,
16
+ SignalSharedOptions,
17
+ } from "./signal/declare.ts";
11
18
  export type { DeadLetter, SignalFailureReason } from "../drivers/signal-types.ts";
12
19
 
13
20
  export { createSignalRuntime } from "./signal/runtime.ts";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Domain DDL policy — ensure vs migrate; missing schema → OKE1101.
2
+ * Domain DDL policy — ensure vs migrate; missing schema → OKE1110.
3
3
  */
4
4
 
5
5
  import { describe, expect, test } from "bun:test";
@@ -79,9 +79,11 @@ describe("manual live flow (.live(table) + liveQuery) — real boot", () => {
79
79
  const db = store.sql("app", { schema: { tasks, activity } });
80
80
 
81
81
  const tasksLive = on(
82
- http.get("/tasks/live").public().live(tasks),
82
+ http
83
+ .get("/tasks/live", { in: { unknown: true } })
84
+ .public()
85
+ .live(tasks),
83
86
  flow("tasks.live", {
84
- in: { unknown: true },
85
87
  effects: { reads: ["sql:app"] },
86
88
  do: async (input, fx) =>
87
89
  liveQuery(fx, tasks, input, {
@@ -93,9 +95,12 @@ describe("manual live flow (.live(table) + liveQuery) — real boot", () => {
93
95
  );
94
96
  // A plain flow writing to BOTH tables — the keel `writeActivity` shape.
95
97
  const createTask = on(
96
- http.post("/tasks").public(),
98
+ http
99
+ .post("/tasks", {
100
+ in: { title: { type: "string" }, status: { type: "string" } },
101
+ })
102
+ .public(),
97
103
  flow("tasks.create", {
98
- in: { title: { type: "string" }, status: { type: "string" } },
99
104
  effects: { writes: ["sql:app"] },
100
105
  do: async (input, fx) => {
101
106
  const s = fx.store(db);
@@ -0,0 +1,123 @@
1
+ /**
2
+ * prepareInsertRow / prepareUpdateRow — temporal bind coercion.
3
+ */
4
+
5
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
6
+ import { timestamp, text, pgTable, integer } from "drizzle-orm/pg-core";
7
+ import { pgliteDriver } from "../../drivers/pglite.ts";
8
+ import type { SqlConnection } from "../../drivers/types.ts";
9
+ import { field, store } from "../store.ts";
10
+ import { createSqlStoreHandle, type SqlStoreHandle } from "./sql-session.ts";
11
+ import { coerceTemporalBindValue, prepareInsertRow, prepareUpdateRow } from "./table.ts";
12
+
13
+ describe("coerceTemporalBindValue", () => {
14
+ test("epoch-ms number → Date for TIMESTAMP and DATE", () => {
15
+ const at = coerceTemporalBindValue("TIMESTAMP", 1_700_000_000_000);
16
+ expect(at).toBeInstanceOf(Date);
17
+ expect((at as Date).getTime()).toBe(1_700_000_000_000);
18
+
19
+ const day = coerceTemporalBindValue("DATE", 0);
20
+ expect(day).toBeInstanceOf(Date);
21
+ expect((day as Date).getTime()).toBe(0);
22
+ });
23
+
24
+ test("leaves non-temporal and non-number values alone", () => {
25
+ expect(coerceTemporalBindValue("INTEGER", 42)).toBe(42);
26
+ expect(coerceTemporalBindValue("TIMESTAMP", "2024-01-01T00:00:00.000Z")).toBe(
27
+ "2024-01-01T00:00:00.000Z",
28
+ );
29
+ const d = new Date(5);
30
+ expect(coerceTemporalBindValue("TIMESTAMP", d)).toBe(d);
31
+ expect(coerceTemporalBindValue("TIMESTAMP", null)).toBe(null);
32
+ expect(coerceTemporalBindValue("TIMESTAMP", Number.NaN)).toBe(Number.NaN);
33
+ });
34
+ });
35
+
36
+ describe("prepareInsertRow / prepareUpdateRow — timestamp columns", () => {
37
+ const notes = store.schema.table("notes", {
38
+ id: field.id().primaryKey(),
39
+ createdAt: field.timestamp().notNull(),
40
+ archivedAt: field.timestamp(),
41
+ });
42
+
43
+ test("abstract schema: insert coerces epoch-ms on timestamp columns", () => {
44
+ const prepared = prepareInsertRow(notes, {
45
+ id: "welcome",
46
+ createdAt: 1,
47
+ archivedAt: null,
48
+ });
49
+ expect(prepared.created_at).toBeInstanceOf(Date);
50
+ expect((prepared.created_at as Date).getTime()).toBe(1);
51
+ expect(prepared.archived_at).toBe(null);
52
+ expect(prepared.id).toBe("welcome");
53
+ });
54
+
55
+ test("abstract schema: update coerces epoch-ms on timestamp columns", () => {
56
+ const prepared = prepareUpdateRow(notes, { archivedAt: 99 });
57
+ expect(prepared.archived_at).toBeInstanceOf(Date);
58
+ expect((prepared.archived_at as Date).getTime()).toBe(99);
59
+ });
60
+
61
+ test("drizzle pgTable timestamp: insert coerces epoch-ms", () => {
62
+ const posts = pgTable("posts", {
63
+ id: text("id").primaryKey(),
64
+ createdAt: timestamp("created_at", { mode: "date" }).notNull(),
65
+ hits: integer("hits").notNull(),
66
+ });
67
+ const prepared = prepareInsertRow(posts, {
68
+ id: "p1",
69
+ createdAt: 42,
70
+ hits: 7,
71
+ });
72
+ expect(prepared.created_at).toBeInstanceOf(Date);
73
+ expect((prepared.created_at as Date).getTime()).toBe(42);
74
+ expect(prepared.hits).toBe(7);
75
+ });
76
+ });
77
+
78
+ describe("SqlStoreHandle upsert — epoch-ms into timestamp (Postgres)", () => {
79
+ const notesTs = pgTable("notes_ts", {
80
+ id: text("id").primaryKey(),
81
+ title: text("title").notNull(),
82
+ createdAt: timestamp("created_at", { mode: "date" }).notNull(),
83
+ });
84
+
85
+ let conn: SqlConnection;
86
+ let handle: SqlStoreHandle;
87
+
88
+ beforeAll(async () => {
89
+ conn = await pgliteDriver.connect({
90
+ url: "memory://prepare-row-ts",
91
+ role: "primary",
92
+ });
93
+ handle = createSqlStoreHandle("sql:app", {
94
+ connection: conn,
95
+ classifications: new Map(),
96
+ routedRole: "primary",
97
+ domainDdl: "ensure",
98
+ });
99
+ await handle.insert(notesTs).values({ id: "_warmup", title: "x", createdAt: new Date(0) });
100
+ await conn.exec(`TRUNCATE "notes_ts" RESTART IDENTITY CASCADE`);
101
+ }, 15_000);
102
+
103
+ afterAll(async () => {
104
+ await conn.close();
105
+ });
106
+
107
+ beforeEach(async () => {
108
+ await conn.exec(`TRUNCATE "notes_ts" RESTART IDENTITY CASCADE`);
109
+ });
110
+
111
+ test("upsert accepts fx.clock.now()-style epoch-ms without Postgres type error", async () => {
112
+ const first = await handle.upsert(
113
+ notesTs,
114
+ { id: "welcome" },
115
+ { id: "welcome", title: "Welcome", createdAt: 1 },
116
+ );
117
+ expect(first.status).toBe("upserted");
118
+
119
+ const rows = await handle.select().from(notesTs);
120
+ expect(rows).toHaveLength(1);
121
+ expect(Number(rows[0]!.createdAt)).toBe(1);
122
+ });
123
+ });