okengine 0.19.9 → 0.21.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 (156) hide show
  1. package/AGENTS.md +1 -1
  2. package/package.json +2 -1
  3. package/site/content/docs/client/auth.mdx +1 -2
  4. package/site/content/docs/client/calling.mdx +125 -37
  5. package/site/content/docs/client/index.mdx +7 -7
  6. package/site/content/docs/client/react.mdx +5 -0
  7. package/site/content/docs/elements/channel/email.mdx +25 -36
  8. package/site/content/docs/elements/channel/index.mdx +88 -46
  9. package/site/content/docs/elements/channel/push.mdx +7 -9
  10. package/site/content/docs/elements/channel/sms.mdx +6 -4
  11. package/site/content/docs/elements/channel/whatsapp.mdx +11 -9
  12. package/site/content/docs/elements/clock/index.mdx +14 -25
  13. package/site/content/docs/elements/flow/http.mdx +42 -23
  14. package/site/content/docs/elements/flow/index.mdx +41 -30
  15. package/site/content/docs/elements/flow/routing.mdx +166 -122
  16. package/site/content/docs/elements/gate/rls.mdx +2 -2
  17. package/site/content/docs/elements/gate/tenancy.mdx +1 -1
  18. package/site/content/docs/elements/store/files.mdx +11 -5
  19. package/site/content/docs/elements/store/index.mdx +13 -6
  20. package/site/content/docs/elements/store/kv.mdx +8 -2
  21. package/site/content/docs/elements/store/search.mdx +5 -5
  22. package/site/content/docs/elements/store/sql.mdx +100 -39
  23. package/site/content/docs/elements/vault/index.mdx +14 -13
  24. package/site/content/docs/elements/vault/secrets.mdx +6 -3
  25. package/site/content/docs/index.mdx +1 -1
  26. package/site/content/docs/plugins/magic-link.mdx +4 -3
  27. package/site/content/docs/plugins/otp.mdx +4 -3
  28. package/site/content/docs/plugins/two-factor.mdx +4 -0
  29. package/site/content/docs/providers/index.mdx +1 -1
  30. package/site/content/docs/recipes/index.mdx +1 -1
  31. package/site/content/docs/recipes/rustfs.mdx +1 -1
  32. package/site/content/docs/reference/cli.mdx +7 -4
  33. package/site/content/docs/reference/configuration.mdx +8 -8
  34. package/site/content/docs/reference/errors.mdx +229 -55
  35. package/site/content/docs/reference/fx.mdx +22 -9
  36. package/site/content/docs/reference/i18n.mdx +4 -4
  37. package/site/content/docs/reference/plugins.mdx +4 -4
  38. package/site/content/docs/understand/the-architecture.mdx +2 -2
  39. package/site/content/docs/understand/try-it.mdx +759 -24
  40. package/src/cli/ai-setup/ai-setup.test.ts +40 -0
  41. package/src/cli/ai-setup/apply.ts +28 -46
  42. package/src/cli/build.test.ts +3 -3
  43. package/src/cli/build.ts +5 -5
  44. package/src/cli/db-auto-push.test.ts +11 -0
  45. package/src/cli/db-auto-push.ts +6 -2
  46. package/src/cli/db.test.ts +1 -1
  47. package/src/cli/db.ts +6 -6
  48. package/src/cli/dev-app-runner.ts +2 -1
  49. package/src/cli/dev-db-push.test.ts +6 -2
  50. package/src/cli/dev-schema-sync.ts +1 -1
  51. package/src/cli/dev.test.ts +10 -7
  52. package/src/cli/dev.ts +13 -11
  53. package/src/cli/ensure-drizzle-config.ts +4 -3
  54. package/src/cli/start.ts +2 -1
  55. package/src/client/create.ts +3 -3
  56. package/src/client/explain.test.ts +252 -0
  57. package/src/client/explain.ts +272 -0
  58. package/src/client/live.test.ts +44 -0
  59. package/src/client/notes-contract.test.ts +10 -0
  60. package/src/client/sse.ts +6 -2
  61. package/src/client/transport.test.ts +67 -0
  62. package/src/client/transport.ts +24 -40
  63. package/src/client/types.ts +17 -6
  64. package/src/client-react/browser.test.ts +23 -0
  65. package/src/client-react/live-resource.ts +6 -2
  66. package/src/client-react/use-live-query.ts +1 -1
  67. package/src/compiler/flow-path.test.ts +1 -0
  68. package/src/compiler/flow-path.ts +1 -1
  69. package/src/compiler/generate-adopt.test.ts +55 -1
  70. package/src/compiler/generate-adopt.ts +111 -21
  71. package/src/compiler/response.ts +17 -27
  72. package/src/config/index.ts +6 -4
  73. package/src/console/server/invoke-user-flow.test.ts +8 -2
  74. package/src/console/server/invoke-user-flow.ts +12 -18
  75. package/src/console/server/security.gate.test.ts +1 -1
  76. package/src/console/ui-next/dist/assets/{access-page-BoC83Ubl.js → access-page-tIbsiphz.js} +1 -1
  77. package/src/console/ui-next/dist/assets/{agent-disclosure-CKjAEOqA.js → agent-disclosure-CSKumwS2.js} +1 -1
  78. package/src/console/ui-next/dist/assets/{cache-glyph-Ceaq9pYh.js → cache-glyph-BCC-DxKT.js} +1 -1
  79. package/src/console/ui-next/dist/assets/{call-pii-button-C4lmY7ck.js → call-pii-button-jOmYISdc.js} +1 -1
  80. package/src/console/ui-next/dist/assets/{collapsible-82y257sL.js → collapsible-DC2xNaAb.js} +1 -1
  81. package/src/console/ui-next/dist/assets/{duration-tone-W63jaMZ8.js → duration-tone-CwoV56jn.js} +1 -1
  82. package/src/console/ui-next/dist/assets/{flows-page-KFTFj2rK.js → flows-page-gT1lsWQK.js} +1 -1
  83. package/src/console/ui-next/dist/assets/{highlighted-json-yE9zNTqC.js → highlighted-json-C2GZEJNI.js} +1 -1
  84. package/src/console/ui-next/dist/assets/{http-method-CwFeFroN.js → http-method-BdYjcIrD.js} +1 -1
  85. package/src/console/ui-next/dist/assets/{index-r7xXt_VV.js → index-Cul17AcV.js} +3 -3
  86. package/src/console/ui-next/dist/assets/{observability-page-BDiliMiC.js → observability-page-oY9vdYBk.js} +1 -1
  87. package/src/console/ui-next/dist/assets/{replica-lag-DYDzWUFT.js → replica-lag-C4QdAF7J.js} +1 -1
  88. package/src/console/ui-next/dist/assets/{request-meta-CzrOfgiz.js → request-meta-C43DHyld.js} +1 -1
  89. package/src/console/ui-next/dist/assets/{store-page-CZC2cwaw.js → store-page-CL0D9dOq.js} +1 -1
  90. package/src/console/ui-next/dist/assets/{trace-detail-sheet-DgeeejW7.js → trace-detail-sheet-Dcpo4Us_.js} +1 -1
  91. package/src/console/ui-next/dist/assets/{tree-expand-toggle-CzGIyOPY.js → tree-expand-toggle-CKJTuv43.js} +1 -1
  92. package/src/console/ui-next/dist/assets/{units-page-DBiDCLIB.js → units-page-1PlT19ft.js} +1 -1
  93. package/src/console/ui-next/dist/assets/{vault-page-CcHsthPe.js → vault-page-BwZ9YjTW.js} +1 -1
  94. package/src/console/ui-next/dist/index.html +1 -1
  95. package/src/docker/docker.test.ts +3 -3
  96. package/src/docker/images-config.test.ts +4 -4
  97. package/src/docker/stack-id.test.ts +1 -1
  98. package/src/drivers/clock-postgres.test.ts +10 -2
  99. package/src/drivers/clock-postgres.ts +18 -2
  100. package/src/drivers/vault-driver-removal.test.ts +2 -2
  101. package/src/elements/channel/declare.ts +66 -3
  102. package/src/elements/channel/runtime.ts +9 -11
  103. package/src/elements/channel.test.ts +42 -0
  104. package/src/elements/channel.ts +4 -2
  105. package/src/elements/clock/reconcile.ts +45 -24
  106. package/src/elements/clock.test.ts +33 -0
  107. package/src/elements/store/emit-drizzle.ts +285 -65
  108. package/src/elements/store/files-errors.test.ts +149 -0
  109. package/src/elements/store/files-errors.ts +189 -0
  110. package/src/elements/store/kv-errors.test.ts +98 -0
  111. package/src/elements/store/kv-errors.ts +139 -0
  112. package/src/elements/store/load-plugin-tables.ts +1 -1
  113. package/src/elements/store/prepare-row.test.ts +57 -4
  114. package/src/elements/store/resource.ts +11 -7
  115. package/src/elements/store/runtime.ts +18 -13
  116. package/src/elements/store/schema-decl.test.ts +178 -0
  117. package/src/elements/store/sql-errors.test.ts +197 -0
  118. package/src/elements/store/sql-errors.ts +294 -0
  119. package/src/elements/store/sql-session.test.ts +52 -0
  120. package/src/elements/store/sql-session.ts +50 -2
  121. package/src/elements/store/store-errors.ts +47 -0
  122. package/src/elements/store/table.ts +8 -6
  123. package/src/http.ts +9 -1
  124. package/src/i18n/catalogs/ar.ts +18 -0
  125. package/src/i18n/catalogs/en.ts +18 -0
  126. package/src/index.ts +9 -1
  127. package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
  128. package/src/kernel/app.ts +40 -34
  129. package/src/kernel/auto-registry.test.ts +26 -1
  130. package/src/kernel/boot.ts +2 -2
  131. package/src/kernel/boundary-contract.ts +6 -1
  132. package/src/kernel/builtin-errors.test.ts +117 -0
  133. package/src/kernel/builtin-errors.ts +129 -0
  134. package/src/kernel/call.test.ts +182 -0
  135. package/src/kernel/errors-vault.ts +16 -0
  136. package/src/kernel/errors.registry.test.ts +7 -0
  137. package/src/kernel/errors.ts +97 -24
  138. package/src/kernel/fail-helpers.ts +34 -0
  139. package/src/kernel/flow-units.ts +3 -3
  140. package/src/kernel/fx.test.ts +8 -0
  141. package/src/kernel/fx.ts +24 -8
  142. package/src/kernel/index.ts +12 -1
  143. package/src/kernel/mutation-id.ts +8 -0
  144. package/src/kernel/plugin.ts +4 -3
  145. package/src/kernel/project-out.test.ts +176 -0
  146. package/src/kernel/project-out.ts +91 -0
  147. package/src/kernel/realtime-bind.ts +2 -3
  148. package/src/kernel/router/linear.ts +12 -6
  149. package/src/kernel/router.test.ts +13 -0
  150. package/src/plugins/magic-link.ts +25 -24
  151. package/src/plugins/otp.ts +35 -24
  152. package/src/plugins/two-factor.ts +15 -0
  153. package/src/runs/duckdb.test.ts +2 -2
  154. package/src/runtime/dev-request-log.ts +29 -11
  155. package/src/term.test.ts +76 -0
  156. package/src/term.ts +166 -3
@@ -96,19 +96,19 @@ export const ping = on(
96
96
 
97
97
  `in` / `out` live on the HTTP bag. Invalid input never enters `do`:
98
98
 
99
- ```typescript title="src/flows/notes/create.ts"
99
+ ```typescript title="src/flows/links/create.ts"
100
100
  import { on, flow, http } from "okengine";
101
101
  import { z } from "zod";
102
102
 
103
103
  export const create = on(
104
104
  http.post({
105
- in: z.object({ title: z.string().min(1) }),
106
- out: z.object({ id: z.string(), title: z.string() }),
105
+ in: z.object({ url: z.string().url() }),
106
+ out: z.object({ id: z.string(), url: z.string() }),
107
107
  }),
108
108
  flow({
109
- do: async ({ title }, fx) => {
109
+ do: async ({ url }, fx) => {
110
110
  const id = fx.id();
111
- return { id, title };
111
+ return { id, url };
112
112
  },
113
113
  }),
114
114
  );
@@ -118,7 +118,7 @@ export const create = on(
118
118
 
119
119
  <Tab value="Failures">
120
120
 
121
- Declare domain errors on the exposure bag and return `fx.fail` — do not throw for expected failures:
121
+ Declare domain errors on the exposure bag. Built-in codes (`NotFound`, `Forbidden`, …) need no `errors:` — use the helpers:
122
122
 
123
123
  ```typescript title="src/flows/orders/[id]/get.ts"
124
124
  import { on, flow, http } from "okengine";
@@ -130,12 +130,11 @@ export const get = on(
130
130
  http.get({
131
131
  in: z.object({ id: z.string() }),
132
132
  out: z.object({ id: z.string(), sku: z.string(), qty: z.number() }),
133
- errors: { NotFound: z.object({ id: z.string() }) },
134
133
  }),
135
134
  flow({
136
135
  do: async ({ id }, fx) => {
137
136
  const [order] = await fx.store(db).select().from(orders).where(eq(orders.id, id));
138
- if (!order) return fx.fail("NotFound", { id });
137
+ if (!order) return fx.fail.notFound({ id });
139
138
  return order;
140
139
  },
141
140
  }),
@@ -238,11 +237,14 @@ Invoke contracts (`in` / `out` / `errors` / `breaking`) belong on the exposure
238
237
  ## Contracts
239
238
 
240
239
  <Callout title="Detailed section">
241
- Invoke contracts live on the **exposure** — `http.post({ in, out, errors })`, `call("name", {
242
- in, out, do })`, or `mcp.tool("x", { in, out })`. The Manifest still shows flat
243
- `flows.*.{in,out,errors,breaking}` as a projection from that exposure. `in` runs before `do`;
244
- `out` runs after a successful return. `fx.fail` skips `out`. Signal / Channel `schema` is a
245
- separate **emit** contract (validated at `fx.emit` / `fx.send`).
240
+ Invoke contracts live on the **exposure** (`http.post({ in, out, errors })`, `call("name", { in,
241
+ out, do })`, `mcp.tool("x", { in, out })`). Manifest `flows.*` is a projection from that bag.
242
+
243
+ `in` validates before `do`. After success, `out` projects the reply (Date → ISO-8601, extra keys
244
+ strip); a miss is not a client error. `fx.fail` skips `out`.
245
+
246
+ Signal / Channel `schema` is a separate **emit** contract (validated at `fx.emit` / `fx.send`).
247
+
246
248
  </Callout>
247
249
 
248
250
  <Tabs items={["Standard Schema", "Failures", "Envelope"]}>
@@ -315,15 +317,22 @@ HTTP success from a returned value is `200` + `{ data, error: null }`. `undefine
315
317
 
316
318
  Status for `error.code`:
317
319
 
318
- | Code | Status |
319
- | ----------------------------------------------------- | ------ |
320
- | `ValidationError` | `422` |
321
- | `Unauthorized` | `401` |
322
- | `Forbidden` | `403` |
323
- | `RateLimited` | `429` |
324
- | Any other declared code (`NotFound`, `OutOfStock`, …) | `400` |
325
-
326
- A bare `404` with body `Not Found` means **no route matched** — not `fx.fail("NotFound")`.
320
+ | Code | Status |
321
+ | ------------------------------------------------------ | ------ |
322
+ | `ValidationError` | `422` |
323
+ | `Unauthorized` | `401` |
324
+ | `Forbidden` | `403` |
325
+ | `NotFound` | `404` |
326
+ | `Conflict` / `ForeignKey` | `409` |
327
+ | `UnsupportedMediaType` | `415` |
328
+ | `RateLimited` / `AuthRateLimited` | `429` |
329
+ | `DatabaseError` (`not_null` / `check`) | `422` |
330
+ | `DatabaseError` (`retryable`) / `ServiceUnavailable` | `503` |
331
+ | `DatabaseError` (else) / `InternalError` | `500` |
332
+ | Domain codes (`OutOfStock`, `FlightFull`, `Duplicate`) | `400` |
333
+
334
+ A bare `404` with body `Not Found` means **no route matched** — not `fx.fail.notFound`. Clients
335
+ distinguish router miss (plain text) from domain miss (JSON envelope).
327
336
 
328
337
  </Tab>
329
338
 
@@ -344,8 +353,8 @@ A bare `404` with body `Not Found` means **no route matched** — not `fx.fail("
344
353
  </Accordion>
345
354
 
346
355
  <Accordion title="Name stamping">
347
- Prefer nameless `flow({ do })` on HTTP tree files — `src/flows/notes/[id]/get.ts` +
348
- `export const get` stamps `notes.get`. Signal / Clock workers pass `flow("name", { do })`
356
+ Prefer nameless `flow({ do })` on HTTP tree files — `src/flows/links/[code]/get.ts` +
357
+ `export const get` stamps `links.get`. Signal / Clock workers pass `flow("name", { do })`
349
358
  (**OKE1072**; Clock inline is [Clock · Inline or named export](/docs/elements/clock#inline-or-named-export)).
350
359
  </Accordion>
351
360
 
@@ -383,7 +392,9 @@ A bare `404` with body `Not Found` means **no route matched** — not `fx.fail("
383
392
 
384
393
  <Accordion title="fx.call identity">
385
394
  `fx.call` starts the callee with an **empty** `fx.auth` (fail-closed). `fx.tenant.id` propagates.
386
- For audit only, read `fx.principal` — gates never consult it. See [fx](/docs/reference/fx).
395
+ For audit only, read `fx.principal` — gates never consult it. Controlled `return fx.fail(...)`
396
+ comes back as a `FlowFailure` value; an unhandled `throw` rethrows to the caller (never silent
397
+ `undefined`). See [fx](/docs/reference/fx).
387
398
  </Accordion>
388
399
 
389
400
  <Accordion title="Undeclared effects (OKE1001–1007)">
@@ -543,8 +554,8 @@ Default is `true` once `gate.auth.tenant` is on.
543
554
  </Accordion>
544
555
 
545
556
  <Accordion title="Thrown Error becomes a mystery 500 instead of a typed envelope">
546
- Uncaught exceptions are defects. Declare the code in `errors` on the exposure and `return
547
- fx.fail("OutOfStock", payload)` from `do`.
557
+ Uncaught exceptions become `InternalError` (catalog message only — the thrown `message` is not
558
+ copied). For expected misses use `fx.fail.notFound` / domain `fx.fail("OutOfStock", payload)`.
548
559
  </Accordion>
549
560
 
550
561
  <Accordion title="422 ValidationError — path param missing from in">
@@ -553,9 +564,9 @@ Default is `true` once `gate.auth.tenant` is on.
553
564
  Parsing](/docs/elements/flow/http#request-parsing).
554
565
  </Accordion>
555
566
 
556
- <Accordion title="fx.fail('NotFound') is 400, not 404">
557
- Custom domain codes map to **400**. A bare `404` `Not Found` means the router found no method +
558
- path. Use `fx.fail` for domain misses; fix the route for missing bindings.
567
+ <Accordion title="Router 404 vs domain NotFound">
568
+ A bare `404` with body `Not Found` means **no route matched**. `fx.fail.notFound` is a JSON
569
+ envelope at **404** with `error.code: "NotFound"`. Clients distinguish by envelope, not status.
559
570
  </Accordion>
560
571
 
561
572
  <Accordion title="Read-only Flow never cache-hits">