okengine 0.19.9 → 0.20.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 (105) hide show
  1. package/AGENTS.md +1 -1
  2. package/package.json +1 -1
  3. package/site/content/docs/client/auth.mdx +1 -2
  4. package/site/content/docs/client/calling.mdx +8 -8
  5. package/site/content/docs/client/index.mdx +4 -4
  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 +24 -8
  14. package/site/content/docs/elements/flow/index.mdx +15 -12
  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/store/index.mdx +11 -3
  18. package/site/content/docs/elements/store/search.mdx +5 -5
  19. package/site/content/docs/elements/store/sql.mdx +91 -33
  20. package/site/content/docs/elements/vault/index.mdx +3 -5
  21. package/site/content/docs/plugins/magic-link.mdx +4 -3
  22. package/site/content/docs/plugins/otp.mdx +4 -3
  23. package/site/content/docs/plugins/two-factor.mdx +4 -0
  24. package/site/content/docs/providers/index.mdx +1 -1
  25. package/site/content/docs/recipes/index.mdx +1 -1
  26. package/site/content/docs/reference/cli.mdx +7 -4
  27. package/site/content/docs/reference/configuration.mdx +7 -7
  28. package/site/content/docs/reference/errors.mdx +30 -30
  29. package/site/content/docs/reference/fx.mdx +16 -8
  30. package/site/content/docs/reference/i18n.mdx +4 -4
  31. package/site/content/docs/reference/plugins.mdx +4 -4
  32. package/site/content/docs/understand/try-it.mdx +2 -0
  33. package/src/cli/ai-setup/ai-setup.test.ts +40 -0
  34. package/src/cli/ai-setup/apply.ts +28 -46
  35. package/src/cli/build.test.ts +3 -3
  36. package/src/cli/build.ts +5 -5
  37. package/src/cli/db-auto-push.test.ts +11 -0
  38. package/src/cli/db-auto-push.ts +6 -2
  39. package/src/cli/db.test.ts +1 -1
  40. package/src/cli/db.ts +6 -6
  41. package/src/cli/dev-db-push.test.ts +6 -2
  42. package/src/cli/dev-schema-sync.ts +1 -1
  43. package/src/cli/dev.test.ts +10 -7
  44. package/src/cli/dev.ts +13 -11
  45. package/src/cli/ensure-drizzle-config.ts +4 -3
  46. package/src/client-react/browser.test.ts +23 -0
  47. package/src/client-react/use-live-query.ts +1 -1
  48. package/src/compiler/flow-path.test.ts +1 -0
  49. package/src/compiler/flow-path.ts +1 -1
  50. package/src/compiler/generate-adopt.test.ts +55 -1
  51. package/src/compiler/generate-adopt.ts +111 -21
  52. package/src/config/index.ts +6 -4
  53. package/src/console/ui-next/dist/assets/{access-page-BoC83Ubl.js → access-page-DFLu0wTA.js} +1 -1
  54. package/src/console/ui-next/dist/assets/{agent-disclosure-CKjAEOqA.js → agent-disclosure-DGscxaF5.js} +1 -1
  55. package/src/console/ui-next/dist/assets/{cache-glyph-Ceaq9pYh.js → cache-glyph-BGmRZk7d.js} +1 -1
  56. package/src/console/ui-next/dist/assets/{call-pii-button-C4lmY7ck.js → call-pii-button--feUYxvG.js} +1 -1
  57. package/src/console/ui-next/dist/assets/{collapsible-82y257sL.js → collapsible-JWvpaiGY.js} +1 -1
  58. package/src/console/ui-next/dist/assets/{duration-tone-W63jaMZ8.js → duration-tone-D9yCJG4n.js} +1 -1
  59. package/src/console/ui-next/dist/assets/{flows-page-KFTFj2rK.js → flows-page-Bs6MD9GB.js} +1 -1
  60. package/src/console/ui-next/dist/assets/{highlighted-json-yE9zNTqC.js → highlighted-json-xH8MrEnv.js} +1 -1
  61. package/src/console/ui-next/dist/assets/{http-method-CwFeFroN.js → http-method-C4vB6ZIw.js} +1 -1
  62. package/src/console/ui-next/dist/assets/{index-r7xXt_VV.js → index-yTCY4AcS.js} +3 -3
  63. package/src/console/ui-next/dist/assets/{observability-page-BDiliMiC.js → observability-page-BxJ3R6dU.js} +1 -1
  64. package/src/console/ui-next/dist/assets/{replica-lag-DYDzWUFT.js → replica-lag-QRKB_IE8.js} +1 -1
  65. package/src/console/ui-next/dist/assets/{request-meta-CzrOfgiz.js → request-meta-DqZ-fMu5.js} +1 -1
  66. package/src/console/ui-next/dist/assets/{store-page-CZC2cwaw.js → store-page-Dixb6L7a.js} +1 -1
  67. package/src/console/ui-next/dist/assets/{trace-detail-sheet-DgeeejW7.js → trace-detail-sheet-CazhjtiU.js} +1 -1
  68. package/src/console/ui-next/dist/assets/{tree-expand-toggle-CzGIyOPY.js → tree-expand-toggle-DlnqYKfr.js} +1 -1
  69. package/src/console/ui-next/dist/assets/{units-page-DBiDCLIB.js → units-page-BXTLjU2-.js} +1 -1
  70. package/src/console/ui-next/dist/assets/{vault-page-CcHsthPe.js → vault-page-39KR__bc.js} +1 -1
  71. package/src/console/ui-next/dist/index.html +1 -1
  72. package/src/drivers/clock-postgres.test.ts +10 -2
  73. package/src/drivers/clock-postgres.ts +18 -2
  74. package/src/drivers/vault-driver-removal.test.ts +2 -2
  75. package/src/elements/channel/declare.ts +66 -3
  76. package/src/elements/channel/runtime.ts +9 -11
  77. package/src/elements/channel.test.ts +42 -0
  78. package/src/elements/channel.ts +4 -2
  79. package/src/elements/clock/reconcile.ts +45 -24
  80. package/src/elements/clock.test.ts +33 -0
  81. package/src/elements/store/emit-drizzle.ts +285 -65
  82. package/src/elements/store/load-plugin-tables.ts +1 -1
  83. package/src/elements/store/prepare-row.test.ts +57 -4
  84. package/src/elements/store/schema-decl.test.ts +178 -0
  85. package/src/elements/store/sql-session.ts +44 -2
  86. package/src/elements/store/table.ts +8 -6
  87. package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
  88. package/src/kernel/app.ts +26 -32
  89. package/src/kernel/auto-registry.test.ts +26 -1
  90. package/src/kernel/boot.ts +2 -2
  91. package/src/kernel/boundary-contract.ts +6 -1
  92. package/src/kernel/errors.ts +3 -3
  93. package/src/kernel/flow-units.ts +3 -3
  94. package/src/kernel/fx.ts +12 -2
  95. package/src/kernel/mutation-id.ts +8 -0
  96. package/src/kernel/plugin.ts +4 -3
  97. package/src/kernel/project-out.test.ts +176 -0
  98. package/src/kernel/project-out.ts +91 -0
  99. package/src/kernel/realtime-bind.ts +2 -3
  100. package/src/kernel/router/linear.ts +12 -6
  101. package/src/kernel/router.test.ts +13 -0
  102. package/src/plugins/magic-link.ts +25 -24
  103. package/src/plugins/otp.ts +35 -24
  104. package/src/plugins/two-factor.ts +15 -0
  105. package/src/runs/duckdb.test.ts +2 -2
@@ -299,12 +299,15 @@ export const get = on(
299
299
  );
300
300
  ```
301
301
 
302
+ Declared `out` projects the row — return it as-is (Date or `fx.clock.now()` epoch-ms → ISO-8601; extra columns strip).
303
+
302
304
  </Tab>
303
305
 
304
306
  <Tab value="POST">
305
307
 
306
- Create a resource or run a command. `fx.json.create(value)` returns `201 Created` with
307
- `{ data: value, error: null }`:
308
+ Create a resource or run a command. `fx.json.create(row)` returns `201 Created` with
309
+ `{ data: value, error: null }`. Declared `out` projects the store row (Date / epoch-ms → ISO).
310
+ HTTP `in` timestamps are ISO (`z.iso.datetime()`); store writes coerce them to `Date`.
308
311
 
309
312
  ```typescript title="src/flows/notes/create.ts"
310
313
  import { on, flow, http } from "okengine";
@@ -319,8 +322,8 @@ export const create = on(
319
322
  flow({
320
323
  do: async ({ title }, fx) => {
321
324
  const id = fx.id();
322
- await fx.store(db).insert(notes).values({ id, title });
323
- return fx.json.create({ id, title });
325
+ const [row] = await fx.store(db).insert(notes).values({ id, title }).returning();
326
+ return fx.json.create(row);
324
327
  },
325
328
  }),
326
329
  );
@@ -934,8 +937,9 @@ Every HTTP flow returns the same envelope shape. You choose status and optional
934
937
  custom wrapper.
935
938
 
936
939
  <Callout title="Envelope is fixed">
937
- Success and failure always use `{ data, error }` (optional top-level `meta`). There is no API to
938
- replace that shape. Use `fx.json.*` for status codes and `meta`; use `fx.fail` for typed errors.
940
+ Success and failure always use `{ data, error }` (optional top-level `meta`). Use `fx.json.*` for
941
+ status and `meta`; use `fx.fail` for typed errors. A raw `Response` from `do` is the exception —
942
+ `302` + `Location` for redirects.
939
943
  </Callout>
940
944
 
941
945
  **Success** — returning a value from `do` produces `200 OK`:
@@ -946,14 +950,21 @@ custom wrapper.
946
950
 
947
951
  Returning `undefined` produces a `204 No Content` response with an empty body.
948
952
 
949
- **Custom status** — `fx.json.create` for `201 Created`, or `fx.json.ok` with optional `meta`:
953
+ **Custom status** — `fx.json.create` for `201 Created`, or `fx.json.ok` with optional `meta`.
954
+ When `out` is set, pass the store row — the kernel projects it (Date → ISO-8601; extra keys strip):
950
955
 
951
956
  ```typescript
952
- return fx.json.create({ id: "ord_1" });
957
+ return fx.json.create(row);
953
958
  // or
954
959
  return fx.json.ok({ id: "ord_1" }, { meta: { traceId: fx.runId } });
955
960
  ```
956
961
 
962
+ **Redirects** — return a raw `Response` so the kernel does not wrap `{ data, error }`:
963
+
964
+ ```typescript
965
+ return new Response(null, { status: 302, headers: { Location: url } });
966
+ ```
967
+
957
968
  **Typed failures** — `fx.fail(code, data)` formats the error envelope and maps status:
958
969
 
959
970
  ```typescript
@@ -1004,6 +1015,11 @@ Standard status code mappings:
1004
1015
  `error.data.issues` array for the specific field validation failure.
1005
1016
  </Accordion>
1006
1017
 
1018
+ <Accordion title="I mapped Date columns to ISO by hand">
1019
+ Declared `out` already projects store rows. `fx.json.create(row)`, `return row`, and
1020
+ `fx.json.withQuery(rows, input)` are enough — extra columns strip. A miss is not a client error.
1021
+ </Accordion>
1022
+
1007
1023
  <Accordion title="Browser blocked by CORS / missing Access-Control-*">
1008
1024
  Cross-origin access is closed until you plug the [`cors`](/docs/plugins/cors) plugin with an
1009
1025
  explicit `origin`. Same-origin calls need no CORS headers. Preflight `OPTIONS` is answered by the
@@ -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
  );
@@ -238,11 +238,14 @@ Invoke contracts (`in` / `out` / `errors` / `breaking`) belong on the exposure
238
238
  ## Contracts
239
239
 
240
240
  <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`).
241
+ Invoke contracts live on the **exposure** (`http.post({ in, out, errors })`, `call("name", { in,
242
+ out, do })`, `mcp.tool("x", { in, out })`). Manifest `flows.*` is a projection from that bag.
243
+
244
+ `in` validates before `do`. After success, `out` projects the reply (Date → ISO-8601, extra keys
245
+ strip); a miss is not a client error. `fx.fail` skips `out`.
246
+
247
+ Signal / Channel `schema` is a separate **emit** contract (validated at `fx.emit` / `fx.send`).
248
+
246
249
  </Callout>
247
250
 
248
251
  <Tabs items={["Standard Schema", "Failures", "Envelope"]}>
@@ -344,8 +347,8 @@ A bare `404` with body `Not Found` means **no route matched** — not `fx.fail("
344
347
  </Accordion>
345
348
 
346
349
  <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 })`
350
+ Prefer nameless `flow({ do })` on HTTP tree files — `src/flows/links/[code]/get.ts` +
351
+ `export const get` stamps `links.get`. Signal / Clock workers pass `flow("name", { do })`
349
352
  (**OKE1072**; Clock inline is [Clock · Inline or named export](/docs/elements/clock#inline-or-named-export)).
350
353
  </Accordion>
351
354