okengine 0.14.1 → 0.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -4
- package/site/content/docs/elements/ai.mdx +2 -1
- package/site/content/docs/elements/channel.mdx +1 -0
- package/site/content/docs/elements/clock.mdx +3 -0
- package/site/content/docs/elements/flow.mdx +3 -0
- package/site/content/docs/elements/gate.mdx +25 -22
- package/site/content/docs/elements/signal.mdx +4 -4
- package/site/content/docs/elements/store.mdx +86 -27
- package/site/content/docs/elements/vault.mdx +1 -0
- package/site/content/docs/get-started/basic-usage.mdx +22 -13
- package/site/content/docs/get-started/index.mdx +5 -0
- package/site/content/docs/get-started/introduction.mdx +11 -3
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/project-structure.mdx +932 -0
- package/site/content/docs/reference/cli.md +13 -10
- package/site/content/docs/reference/client.mdx +25 -21
- package/site/content/docs/reference/security.md +1 -1
- package/src/auth/bindings.ts +14 -5
- package/src/auth/cross-plane.ts +10 -3
- package/src/cli/ai-setup/ai-setup.test.ts +62 -0
- package/src/cli/ai-setup/apply.ts +32 -1
- package/src/cli/attach-host-console.test.ts +3 -2
- package/src/cli/attach-host-console.ts +3 -0
- package/src/cli/build.test.ts +4 -2
- package/src/cli/db-seed.ts +48 -15
- package/src/cli/db.test.ts +104 -0
- package/src/cli/db.ts +122 -10
- package/src/cli/dev-controls.test.ts +20 -0
- package/src/cli/dev-controls.ts +13 -0
- package/src/cli/dev.test.ts +15 -4
- package/src/cli/dev.ts +102 -29
- package/src/cli/ensure-drizzle-config.ts +2 -1
- package/src/cli/tui/DevLive.tsx +12 -2
- package/src/cli/tui/keys.test.ts +2 -1
- package/src/cli/tui/keys.ts +3 -2
- package/src/compiler/aot.test.ts +1 -1
- package/src/compiler/extract.test.ts +409 -33
- package/src/compiler/extract.ts +318 -67
- package/src/compiler/flow-path.test.ts +96 -0
- package/src/compiler/flow-path.ts +173 -0
- package/src/compiler/generate-adopt.test.ts +112 -3
- package/src/compiler/generate-adopt.ts +280 -26
- package/src/compiler/schema-from-ast.ts +609 -0
- package/src/console/server/bind.ts +2 -2
- package/src/console/server/console.test.ts +3 -0
- package/src/console/server/flows.ts +120 -40
- package/src/console/server/invoke-user-flow.test.ts +3 -1
- package/src/console/server/invoke-user-flow.ts +17 -2
- package/src/console/server/iso-at.test.ts +9 -0
- package/src/console/server/iso-at.ts +11 -0
- package/src/console/server/security-headers.ts +6 -3
- package/src/console/server/spa-proxy.test.ts +1 -0
- package/src/console/server/sql-catalog.ts +147 -7
- package/src/console/server/sql-rls.test.ts +16 -0
- package/src/console/server/state.ts +8 -0
- package/src/console/server/store.test.ts +7 -4
- package/src/console/server/store.ts +142 -58
- package/src/console/ui-next/dist/assets/agent-disclosure-C0X1fbWF.js +1 -0
- package/src/console/ui-next/dist/assets/{cache-glyph-BanhLsEY.js → cache-glyph-F1FI122b.js} +1 -1
- package/src/console/ui-next/dist/assets/{call-pii-button-bqkxMrJH.js → call-pii-button-DEDCl_j3.js} +1 -1
- package/src/console/ui-next/dist/assets/collapsible-LPqGvfoz.js +1 -0
- package/src/console/ui-next/dist/assets/{confirm-sheet-DDCRmG62.js → confirm-sheet-3ptDzXbz.js} +1 -1
- package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
- package/src/console/ui-next/dist/assets/{explorer-empty-HjTnVQoR.js → explorer-empty-BvYrygyO.js} +1 -1
- package/src/console/ui-next/dist/assets/flows-page-Dluo1Bd7.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-CS_O8L-r.js} +1 -1
- package/src/console/ui-next/dist/assets/http-method--sWDdXSB.js +1 -0
- package/src/console/ui-next/dist/assets/index-Ca3HZMVq.js +66 -0
- package/src/console/ui-next/dist/assets/index-UtTDRQpS.css +2 -0
- package/src/console/ui-next/dist/assets/{link-DF7SZ9Ek.js → link-COeyggt-.js} +1 -1
- package/src/console/ui-next/dist/assets/{observability-page-BEZDzyYh.js → observability-page-HK9-BO8z.js} +2 -2
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
- package/src/console/ui-next/dist/assets/replica-lag-DBIFFf7d.js +18 -0
- package/src/console/ui-next/dist/assets/sheet-form-Dnwa5oOR.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-CoqwEIi0.js +1 -0
- package/src/console/ui-next/dist/assets/skeleton-BHcfDYcb.js +1 -0
- package/src/console/ui-next/dist/assets/store-page-BSohH3wM.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-CFIGRnnA.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-BgLl34w7.js +54 -0
- package/src/console/ui-next/dist/assets/units-page-B0cFE76A.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-BLbZhzyF.js} +1 -1
- package/src/console/ui-next/dist/assets/vault-page-B1SbYe10.js +2 -0
- package/src/console/ui-next/dist/index.html +6 -6
- package/src/console/ui-next/src/client.ts +35 -7
- package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
- package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
- package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
- package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +19 -1
- package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
- package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
- package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
- package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
- package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
- package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
- package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
- package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
- package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
- package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
- package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
- package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
- package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
- package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
- package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
- package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
- package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
- package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
- package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
- package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
- package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
- package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
- package/src/docker/compose-health.test.ts +27 -0
- package/src/docker/compose-health.ts +19 -3
- package/src/docker/docker.test.ts +3 -0
- package/src/docker/recipes/pgdog.ts +2 -0
- package/src/drivers/clock-postgres.ts +13 -9
- package/src/drivers/instances-postgres.ts +4 -6
- package/src/drivers/journal-postgres.ts +13 -9
- package/src/drivers/pg-rls.test.ts +87 -0
- package/src/drivers/pg-rls.ts +233 -7
- package/src/drivers/pglite.ts +26 -3
- package/src/drivers/postgres.test.ts +23 -0
- package/src/drivers/postgres.ts +133 -9
- package/src/drivers/types.ts +10 -0
- package/src/elements/clock/declare.ts +26 -1
- package/src/elements/gate/boot.ts +5 -5
- package/src/elements/gate/declare.ts +36 -4
- package/src/elements/gate/flatten.ts +1 -1
- package/src/elements/gate/runtime.ts +1 -1
- package/src/elements/store/declare.ts +9 -6
- package/src/elements/store/emit-drizzle.ts +44 -8
- package/src/elements/store/resource.test.ts +1 -1
- package/src/elements/store/rls-identity.test.ts +95 -0
- package/src/elements/store/rls-identity.ts +116 -0
- package/src/elements/store/runtime.ts +4 -0
- package/src/elements/store/schema-decl.test.ts +37 -0
- package/src/elements/store/schema-decl.ts +161 -0
- package/src/elements/store/seed.ts +1 -1
- package/src/elements/store/sql-condition.test.ts +12 -0
- package/src/elements/store/sql-condition.ts +26 -2
- package/src/elements/store/sql-rls-isolation.test.ts +165 -0
- package/src/elements/store/sql-rls-stamp.test.ts +117 -0
- package/src/elements/store/sql-session.ts +81 -3
- package/src/elements/store/upsert-app.test.ts +1 -1
- package/src/elements/store.ts +14 -0
- package/src/http.ts +20 -3
- package/src/index.ts +10 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
- package/src/kernel/adopt-routes.ts +30 -2
- package/src/kernel/app.ts +131 -14
- package/src/kernel/auto-cache.test.ts +8 -8
- package/src/kernel/auto-registry.test.ts +49 -9
- package/src/kernel/boot.test.ts +4 -4
- package/src/kernel/boot.ts +2 -2
- package/src/kernel/effects-stamping.test.ts +3 -3
- package/src/kernel/element-registries.ts +12 -5
- package/src/kernel/errors.ts +24 -0
- package/src/kernel/flow-units.ts +64 -0
- package/src/kernel/flow.ts +29 -17
- package/src/kernel/fx.ts +23 -0
- package/src/kernel/horizontal-child.ts +4 -4
- package/src/kernel/http-path-pending.ts +20 -0
- package/src/kernel/http-resource.ts +4 -0
- package/src/kernel/http-stream.test.ts +4 -4
- package/src/kernel/pipeline.test.ts +2 -2
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/plugin-needs.test.ts +1 -1
- package/src/kernel/ready.test.ts +2 -2
- package/src/kernel/resource-mount.test.ts +14 -0
- package/src/kernel/run-duration.test.ts +2 -2
- package/src/kernel/stamp-http.test.ts +79 -0
- package/src/kernel/stamp-http.ts +75 -0
- package/src/kernel/triggers.ts +52 -27
- package/src/manifest/types.ts +13 -0
- package/src/release/measure.ts +2 -2
- package/src/runtime/json-code-block.test.ts +2 -2
- package/src/runtime/serve.test.ts +4 -4
- package/src/term.test.ts +52 -4
- package/src/term.ts +125 -17
- package/src/test/reset-element-registries.ts +8 -3
- package/src/cli/ask-seed.test.ts +0 -96
- package/src/cli/ask-seed.ts +0 -82
- package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-DxDsOd4f.js +0 -1
- package/src/console/ui-next/dist/assets/http-method-CJCBYL2j.js +0 -1
- package/src/console/ui-next/dist/assets/index-Ce6WKWKM.js +0 -66
- package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
- package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
- package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
- package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
- package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
- package/src/console/ui-next/dist/assets/skeleton-CL_X0GCj.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
- package/src/console/ui-next/dist/assets/trace-detail-sheet-DFLFfUUX.js +0 -2
- package/src/console/ui-next/dist/assets/units-page-C0gW6Kdo.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-DuKzqwzW.js +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "okengine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"description": "One law. Eight elements. Ten exports. One package. One manifest. Every backend need is derived, never added.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -174,10 +174,9 @@
|
|
|
174
174
|
"d3-array": "^3.2.4",
|
|
175
175
|
"d3-shape": "^3.2.0",
|
|
176
176
|
"date-fns": "^4.1.0",
|
|
177
|
-
"drizzle-kit": "1.0.0-rc.
|
|
178
|
-
"drizzle-orm": "1.0.0-rc.
|
|
177
|
+
"drizzle-kit": "1.0.0-rc.5-ab785fc",
|
|
178
|
+
"drizzle-orm": "1.0.0-rc.5-169397b",
|
|
179
179
|
"drizzle-seed": "^0.3.1",
|
|
180
|
-
"drizzle-zod": "^0.8.3",
|
|
181
180
|
"gflows": "^1.2.1",
|
|
182
181
|
"happy-dom": "^20.11.1",
|
|
183
182
|
"hast-util-to-jsx-runtime": "^2.3.6",
|
|
@@ -182,7 +182,7 @@ Return those chunks on HTTP with `fx.json.stream(...)`. The kernel answers `text
|
|
|
182
182
|
|
|
183
183
|
```typescript
|
|
184
184
|
on(
|
|
185
|
-
http.post("/complete").
|
|
185
|
+
http.post("/complete").public(),
|
|
186
186
|
flow("chat.complete", {
|
|
187
187
|
do: (input, fx) => fx.json.stream(fx.stream(smart, { prompt: input.prompt })),
|
|
188
188
|
}),
|
|
@@ -364,6 +364,7 @@ A hang-up is `AbortError` — it does not retry or advance `via`.
|
|
|
364
364
|
|
|
365
365
|
## Learn more
|
|
366
366
|
|
|
367
|
+
- [Project structure](/docs/get-started/project-structure) — agent `tools` are Flow names (`notes.get`), not URLs
|
|
367
368
|
- [Flow](/docs/elements/flow) — `fx.ask` and `fx.search` inside `do`
|
|
368
369
|
- [Store](/docs/elements/store) — `store.index`, the home of embeddings
|
|
369
370
|
- [MCP](/docs/ai/mcp) — serve the app to agents, or consume external servers as tools
|
|
@@ -327,6 +327,7 @@ Confirm a visible OS notification titled `okengine webpush interop`.
|
|
|
327
327
|
|
|
328
328
|
## Learn more
|
|
329
329
|
|
|
330
|
+
- [Project structure](/docs/get-started/project-structure) — `flow({…})` names as `unit.export`; explicit `flow("unit.action")` still wins
|
|
330
331
|
- [Flow](/docs/elements/flow) — `fx.send` inside `do`
|
|
331
332
|
- [Environment Variables](/docs/reference/environment-variables) — Channel boot credentials
|
|
332
333
|
- [Configuration](/docs/reference/configuration) — `drivers.channel.*` maps
|
|
@@ -52,6 +52,8 @@ export const dailyReport = clock("daily-report", {
|
|
|
52
52
|
});
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
Import the module before `oke()` — named clocks auto-register. No `oke({ clocks })` list.
|
|
56
|
+
|
|
55
57
|
```typescript title="src/flows/reports/daily.ts"
|
|
56
58
|
export const sendDaily = on(
|
|
57
59
|
dailyReport,
|
|
@@ -218,6 +220,7 @@ The registry is off in `test` and when there is no shared SQL URL. `dev`/`prod`
|
|
|
218
220
|
|
|
219
221
|
## Learn more
|
|
220
222
|
|
|
223
|
+
- [Project structure](/docs/get-started/project-structure) — clock flows in the tree get a name, never a URL
|
|
221
224
|
- [Flow](/docs/elements/flow) — `on(trigger, flow)` and the `fx` surface
|
|
222
225
|
- [fx · Runs](/docs/reference/fx#runs-observability-read) — native SLO checkers via `on(every(…))` + `fx.runs`
|
|
223
226
|
- [Signal](/docs/elements/signal) — reacting to events instead of time
|
|
@@ -112,6 +112,8 @@ Built-in codes attach a localized `message` ([Errors](/docs/reference/errors));
|
|
|
112
112
|
|
|
113
113
|
`http.get` · `http.post` · `http.put` · `http.patch` · `http.delete` · `http.head` · `http.options` · `http.query`. QUERY (RFC 10008) is a safe, idempotent read with a JSON body.
|
|
114
114
|
|
|
115
|
+
`http.get()` with no path is filled from the file tree (`flows/notes/[id]/get.ts` → `/notes/:id`). `flow({ do })` is named `unit.export`. Explicit `http.get("/me/tasks")` and `flow("notes.get")` still win. Folder conventions live on [Project structure](/docs/get-started/project-structure).
|
|
116
|
+
|
|
115
117
|
`Content-Type: application/json` is required — missing → **400** `InvalidQuery`, anything else → **415** `UnsupportedMediaType` with `Accept-Query: "application/json"`. Invalid JSON is **400** (no sniffing). Responses advertise `Accept-Query`. okengine does not assign `Location` / `Content-Location` result URIs.
|
|
116
118
|
|
|
117
119
|
A browser `GET` (`Accept` prefers `text/html`) paints the `{ data, error }` envelope in traces chrome — status, latency, and cache, Routes rail (static GET links), copy, line numbers, Pretty / Raw, Console on **6533**. `curl`, `Accept: application/json`, and `?format=json` stay JSON.
|
|
@@ -369,6 +371,7 @@ a **hit**. A matching write turns the next read into a miss.
|
|
|
369
371
|
|
|
370
372
|
## Learn more
|
|
371
373
|
|
|
374
|
+
- [Project structure](/docs/get-started/project-structure) — folders are the URL; `unit.export` is the name
|
|
372
375
|
- [fx](/docs/reference/fx) — full `fx` surface, including ICU `fx.t` / `fx.locale`
|
|
373
376
|
- [Signal](/docs/elements/signal) — delivery physics (`once` · `broadcast` · `live`)
|
|
374
377
|
- [Clock](/docs/elements/clock) — schedules and durable sleep
|
|
@@ -10,7 +10,7 @@ burned its minute quota? Attach gates to the HTTP trigger — the pipeline check
|
|
|
10
10
|
store write, emit, or channel send runs.
|
|
11
11
|
|
|
12
12
|
<Callout title="The one rule">
|
|
13
|
-
Every HTTP trigger declares posture: attach a real gate, or attach `.
|
|
13
|
+
Every HTTP trigger declares posture: attach a real gate, or attach `.public()`. Omitting both
|
|
14
14
|
fails boot. Denial is a typed error value — never a thrown exception mid-`do`.
|
|
15
15
|
</Callout>
|
|
16
16
|
|
|
@@ -19,10 +19,10 @@ store write, emit, or channel send runs.
|
|
|
19
19
|
<Steps>
|
|
20
20
|
|
|
21
21
|
<Step>
|
|
22
|
-
### Declare policies
|
|
22
|
+
### Declare policies
|
|
23
23
|
|
|
24
|
-
Policies are named predicates; rate limits are declarative budgets.
|
|
25
|
-
`oke({ gate: { policies } })
|
|
24
|
+
Policies are named predicates; rate limits are declarative budgets. Import the module before
|
|
25
|
+
`oke()` — they auto-register. `oke({ gate: { policies } })` is still valid and additive.
|
|
26
26
|
|
|
27
27
|
```typescript title="src/gates.ts"
|
|
28
28
|
import { gate } from "okengine";
|
|
@@ -43,14 +43,10 @@ export const fair = gate.rate({
|
|
|
43
43
|
the scope checked.
|
|
44
44
|
|
|
45
45
|
```typescript title="src/app.ts"
|
|
46
|
+
import "./gates";
|
|
46
47
|
import { oke } from "okengine";
|
|
47
|
-
import { member, canBook, fair } from "./gates";
|
|
48
48
|
|
|
49
|
-
export const app = oke({
|
|
50
|
-
name: "shop",
|
|
51
|
-
env: "dev",
|
|
52
|
-
gate: { policies: [member, canBook, fair] },
|
|
53
|
-
});
|
|
49
|
+
export const app = oke({ name: "shop", env: "dev" });
|
|
54
50
|
```
|
|
55
51
|
|
|
56
52
|
</Step>
|
|
@@ -58,14 +54,14 @@ export const app = oke({
|
|
|
58
54
|
<Step>
|
|
59
55
|
### Attach them to triggers
|
|
60
56
|
|
|
61
|
-
`.gate(...)` composes left to right — first denial wins. Public routes must say so explicitly
|
|
57
|
+
`.gate(...)` composes left to right — first denial wins. Public routes must say so explicitly. Path and name can come from the file tree — see [Project structure](/docs/get-started/project-structure).
|
|
62
58
|
|
|
63
59
|
```typescript title="src/flows/links/shorten.ts"
|
|
64
60
|
import { on, flow, http, gate } from "okengine";
|
|
65
61
|
import { member, canBook, fair } from "../../gates";
|
|
66
62
|
|
|
67
63
|
export const health = on(
|
|
68
|
-
http.get("/health").
|
|
64
|
+
http.get("/health").public(),
|
|
69
65
|
flow("health.check", {
|
|
70
66
|
out: HealthOut,
|
|
71
67
|
do: () => ({ ok: true as const }),
|
|
@@ -88,8 +84,8 @@ export const shorten = on(
|
|
|
88
84
|
```
|
|
89
85
|
|
|
90
86
|
A single gate stays `.gate(member)`. Reuse two or more with `gate.all` — `.gate(write)`
|
|
91
|
-
flattens. `http.resource(path, ops).gate(...)` stamps every verb.
|
|
92
|
-
`gate.public` in `gate.policies
|
|
87
|
+
flattens. `http.resource(path, ops).gate(...)` stamps every verb. Public routes use
|
|
88
|
+
`.public()` — do **not** put `gate.public` in `gate.policies`.
|
|
93
89
|
|
|
94
90
|
</Step>
|
|
95
91
|
|
|
@@ -131,16 +127,16 @@ gates are skipped. `do` runs only when every gate passed.
|
|
|
131
127
|
| ------------------------------------- | ------------------------------------------- | ---------------------------------- |
|
|
132
128
|
| `gate.policy(name, check \| options)` | Is this principal allowed? (ABAC) | auth / operator / request metadata |
|
|
133
129
|
| `gate.scope(name)` | Does `auth.scopes` contain this exact name? | `auth.scopes` |
|
|
134
|
-
| `gate.public` / `.
|
|
130
|
+
| `gate.public` / `.public()` | Intentionally unauthenticated surface | always allows |
|
|
135
131
|
| `gate.rate(options)` | Is there budget left for this subject? | an atomic counter on the kv driver |
|
|
136
132
|
| `gate.all(...members)` | Reuse this chain on many triggers | flattens; each member is evaluated |
|
|
137
133
|
|
|
138
134
|
Pass `{ check, description }` on `gate.policy` when you want a human title in the Console (falls
|
|
139
|
-
back to the policy name). The name `"public"` is reserved —
|
|
140
|
-
`gate.policy("public", …)`.
|
|
135
|
+
back to the policy name). The name `"public"` is reserved — mark the trigger with
|
|
136
|
+
`.public()`, not `gate.policy("public", …)`.
|
|
141
137
|
|
|
142
138
|
`gate.all()` with no members throws. Nested `all` handles flatten.
|
|
143
|
-
`oke({ gate: { policies: [write] } })`
|
|
139
|
+
Members auto-register when declared; `oke({ gate: { policies: [write] } })` is additive.
|
|
144
140
|
|
|
145
141
|
### Policy context
|
|
146
142
|
|
|
@@ -178,11 +174,11 @@ driver in tests. Boot opens a dedicated `oke:gates` namespace on **`drivers.stor
|
|
|
178
174
|
|
|
179
175
|
## Auth posture at boot
|
|
180
176
|
|
|
181
|
-
Every HTTP trigger must carry a gate or `.
|
|
177
|
+
Every HTTP trigger must carry a gate or `.public()`. Omitting both fails boot with
|
|
182
178
|
`GateBootError` — message starts with `gate boot failed —` and lists every gap.
|
|
183
179
|
|
|
184
180
|
```typescript
|
|
185
|
-
on(http.get("/health").
|
|
181
|
+
on(http.get("/health").public(), flow("health.check", {/* … */}));
|
|
186
182
|
on(http.post("/bookings").gate(member, canBook), flow("bookings.create", {/* … */}));
|
|
187
183
|
```
|
|
188
184
|
|
|
@@ -191,7 +187,7 @@ on(http.post("/bookings").gate(member, canBook), flow("bookings.create", {/* …
|
|
|
191
187
|
posture but does **not** authenticate.
|
|
192
188
|
|
|
193
189
|
`gate: { unguardedHttp: "allow" }` skips the audit **only** when `env === "test"`.
|
|
194
|
-
In `dev` / `prod` migrate with per-trigger `.
|
|
190
|
+
In `dev` / `prod` migrate with per-trigger `.public()`.
|
|
195
191
|
|
|
196
192
|
## Built-in auth (`gate.auth`)
|
|
197
193
|
|
|
@@ -270,12 +266,18 @@ Pass **and** deny land on the run's `gates` dimension. Console (`:6533` → `/fl
|
|
|
270
266
|
chain on the flow contract. Rate `overridable` is a catalog flag only — Clock schedules are what the Console can
|
|
271
267
|
edit live today.
|
|
272
268
|
|
|
269
|
+
<Callout title="RLS reads the stamped principal">
|
|
270
|
+
After HTTP Gate passes, user-plane `fx.store` stamps `oke.gate()`, `oke.user()`, and
|
|
271
|
+
`oke.has_scope()`. Rate, cron, CDC, and signal stay unstamped. Helpers:
|
|
272
|
+
[Store](/docs/elements/store).
|
|
273
|
+
</Callout>
|
|
274
|
+
|
|
273
275
|
## Troubleshooting
|
|
274
276
|
|
|
275
277
|
<Accordions>
|
|
276
278
|
<Accordion title="Boot: gate boot failed — N HTTP trigger(s) missing auth posture">
|
|
277
279
|
|
|
278
|
-
Empty `.gate(...)` chain. Attach a policy/rate gate, or `.
|
|
280
|
+
Empty `.gate(...)` chain. Attach a policy/rate gate, or `.public()`. First `fetch`
|
|
279
281
|
auto-boots — do not rely on `gate.unguardedHttp: "allow"` outside `env: "test"`.
|
|
280
282
|
|
|
281
283
|
</Accordion>
|
|
@@ -313,6 +315,7 @@ Keying an authenticated endpoint by IP punishes shared NAT.
|
|
|
313
315
|
|
|
314
316
|
## Learn more
|
|
315
317
|
|
|
318
|
+
- [Project structure](/docs/get-started/project-structure) — `http.get()` and `flow({…})` fill path and name from the file tree
|
|
316
319
|
- [Flow](/docs/elements/flow) — the trigger pipeline gates plug into
|
|
317
320
|
- [fx](/docs/reference/fx) — `fx.auth`, `fx.operator`, `fx.principal`
|
|
318
321
|
- [Client](/docs/reference/client) — Bearer `createClient` + `okengine/client/auth`
|
|
@@ -131,7 +131,7 @@ A Flow **writes** all three tapes. `once` / `broadcast` **listen** with `on(sign
|
|
|
131
131
|
|
|
132
132
|
```typescript title="src/flows/orders/place.ts"
|
|
133
133
|
export const placeOrder = on(
|
|
134
|
-
http.post("/orders").
|
|
134
|
+
http.post("/orders").public(),
|
|
135
135
|
flow("orders.place", {
|
|
136
136
|
in: z.object({ id: z.string(), total: z.number() }),
|
|
137
137
|
do: async (input, fx) => {
|
|
@@ -256,9 +256,8 @@ Committed `once` messages survive process death. A claim sets `lockedBy` and a v
|
|
|
256
256
|
|
|
257
257
|
<Callout title="Client subscription is not shipped yet">
|
|
258
258
|
`delivery: "live"` means the **driver** retains and replays. Listen with `bus.live()` on the
|
|
259
|
-
server — not `on(signal, flow)`. `createClient` has no SSE
|
|
260
|
-
|
|
261
|
-
queries](/docs/reference/client#signal-and-live-queries).
|
|
259
|
+
server — not `on(signal, flow)`. `createClient` has no SSE yet — poll HTTP. See
|
|
260
|
+
[Client](/docs/reference/client#signal-and-live-queries).
|
|
262
261
|
</Callout>
|
|
263
262
|
|
|
264
263
|
## Orphaned signal config
|
|
@@ -335,6 +334,7 @@ At-least-once: crash-after-claim reclaims when the lease expires, and a handler
|
|
|
335
334
|
|
|
336
335
|
## Learn more
|
|
337
336
|
|
|
337
|
+
- [Project structure](/docs/get-started/project-structure) — signal files get a name, never a URL; `http.get("/…")` still wins
|
|
338
338
|
- [Flow](/docs/elements/flow) — `on(trigger, flow)` and `fx.emit`
|
|
339
339
|
- [fx](/docs/reference/fx) — `fx.emit` and `fx.deadLetters`
|
|
340
340
|
- [Clock](/docs/elements/clock) — scheduled and delayed work
|
|
@@ -19,6 +19,9 @@ only the driver changes.
|
|
|
19
19
|
Every facet accepts optional `description` — a human title in the Console (falls back to the store
|
|
20
20
|
name).
|
|
21
21
|
|
|
22
|
+
`store.sql` / `kv` / `files` / `index` auto-register when the declaring module is imported before
|
|
23
|
+
`oke()`. `oke({ stores })` is still valid and additive.
|
|
24
|
+
|
|
22
25
|
## SQL
|
|
23
26
|
|
|
24
27
|
<StoreFacetMark facet="sql" />
|
|
@@ -39,9 +42,9 @@ Manifest tables, columns, and keywords.
|
|
|
39
42
|
Classified PII stays masked unless the toolbar **PII** control is on
|
|
40
43
|
(`revealPii: true`, audited).
|
|
41
44
|
|
|
42
|
-
**Gate** views
|
|
43
|
-
|
|
44
|
-
`
|
|
45
|
+
**Gate** views as Operator (bypass), public, a user, or a policy. SQL,
|
|
46
|
+
browse, and grid edit send `{ asGate, asUserId }`. postgres / pglite stamp
|
|
47
|
+
`oke.gate()` / `oke.user()` / `oke.has_scope()`; memory is catalog-only.
|
|
45
48
|
|
|
46
49
|
The SQL band **Schema** control opens a visualizer of Manifest tables and
|
|
47
50
|
relations — declared `.references()` plus inferred `*_id` columns — not the
|
|
@@ -89,16 +92,25 @@ When a newer packaged version exists, the name chip shows **Upgrade**
|
|
|
89
92
|
(`ALTER EXTENSION … UPDATE`). Names link to the project or Postgres docs.
|
|
90
93
|
|
|
91
94
|
Each SQL table row shows whether Postgres RLS is on (emerald shield)
|
|
92
|
-
or off (muted shield).
|
|
93
|
-
RLS
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
or off (muted shield). The resource header repeats that as an **RLS**
|
|
96
|
+
chip before **PII** — **N RLS** when enabled and policies exist. KV,
|
|
97
|
+
files, and index have no badge. Enabling RLS on create flips that
|
|
98
|
+
shield.
|
|
99
|
+
|
|
100
|
+
**RLS Policies** lists `pg_policies` plus Manifest policies (`declared` /
|
|
101
|
+
`live`). Two-finger click a row to open its sheet (Edit / Save on editable
|
|
102
|
+
columns); two-finger click empty grid to create. Create policy pins
|
|
103
|
+
SQL | Code (`store.schema.policy` + `pgPolicy`); drag the handle to
|
|
104
|
+
resize the preview.
|
|
105
|
+
**Templates** use `oke.user()` / `oke.gate()` / `oke.has_scope()`.
|
|
106
|
+
Owner cards open a **Column** select of the table fields (guessed
|
|
107
|
+
`creator_email` / `owner_email` when present) with the same PK / FK
|
|
108
|
+
marks as schema cards. Create stays blocked until a real column is
|
|
109
|
+
picked.
|
|
98
110
|
|
|
99
111
|
Create policy picks a Gate posture (Read / Write / Both), or **Policy &
|
|
100
|
-
scope
|
|
101
|
-
|
|
112
|
+
scope**. Those picks fill `USING` / `WITH CHECK`. `TO` stays `public` —
|
|
113
|
+
Gate names are not Postgres roles.
|
|
102
114
|
|
|
103
115
|
### Quick start
|
|
104
116
|
|
|
@@ -192,7 +204,7 @@ const notesR = store.resource(db, notes, {
|
|
|
192
204
|
},
|
|
193
205
|
});
|
|
194
206
|
|
|
195
|
-
const mounted = on(http.resource("/notes", notesR.all()).
|
|
207
|
+
const mounted = on(http.resource("/notes", notesR.all()).public().live());
|
|
196
208
|
```
|
|
197
209
|
|
|
198
210
|
`.gate(...)` / `.live()` chain like `http.get` — gates on every verb, live on list and get.
|
|
@@ -240,7 +252,7 @@ list: { mode: "offset", filter: "none", limit: 20 },
|
|
|
240
252
|
|
|
241
253
|
```typescript
|
|
242
254
|
export const list = on(
|
|
243
|
-
http.get("/notes").
|
|
255
|
+
http.get("/notes").public(),
|
|
244
256
|
flow("notes.list", {
|
|
245
257
|
out: z.array(Note),
|
|
246
258
|
do: async (input, fx) => {
|
|
@@ -251,6 +263,8 @@ export const list = on(
|
|
|
251
263
|
);
|
|
252
264
|
```
|
|
253
265
|
|
|
266
|
+
`list.ts` + `http.get()` fills `GET /notes` named `notes.list` — explicit strings still win. See [Project structure](/docs/get-started/project-structure).
|
|
267
|
+
|
|
254
268
|
Zero-config: `?q=` searches every string field, extra keys auto-eq except path `id`, and PostgREST `?col=op.value` / `?order=` / `?select=` are open. Nested lists filter the parent in the load — `input.id` is not a column eq.
|
|
255
269
|
|
|
256
270
|
<Callout title="Preferred, not required">
|
|
@@ -292,15 +306,44 @@ await fx.store(db).delete(notes).where(lt(notes.createdAt, cutoff));
|
|
|
292
306
|
|
|
293
307
|
The recommended path: declare tables ORM-agnostically, then let `oke db` emit real Drizzle (`pgTable` for Postgres / PGLite) into `src/db/schema.drizzle.ts`.
|
|
294
308
|
|
|
295
|
-
| Field API | Meaning
|
|
296
|
-
| -------------------------------------------- |
|
|
297
|
-
| `field.text()` / `field.integer()` | v1 column primitives
|
|
298
|
-
| `.primaryKey()` · `.notNull()` · `.unique()` | constraints
|
|
299
|
-
| `.default(v)` · `.defaultFn(id \| now)` | defaults
|
|
300
|
-
| `.pii()` · `.sensitive()` · `.retain("30d")` | privacy classification
|
|
301
|
-
| `.as("sql_name")` | override the automatic `camelCase → snake_case`
|
|
302
|
-
| `.describe("…")` | human title in the Console (falls back to key)
|
|
303
|
-
| `.references(() => col, { onDelete })` | foreign key
|
|
309
|
+
| Field API | Meaning |
|
|
310
|
+
| -------------------------------------------- | ---------------------------------------------------------------- |
|
|
311
|
+
| `field.text()` / `field.integer()` | v1 column primitives |
|
|
312
|
+
| `.primaryKey()` · `.notNull()` · `.unique()` | constraints |
|
|
313
|
+
| `.default(v)` · `.defaultFn(id \| now)` | defaults |
|
|
314
|
+
| `.pii()` · `.sensitive()` · `.retain("30d")` | privacy classification |
|
|
315
|
+
| `.as("sql_name")` | override the automatic `camelCase → snake_case` |
|
|
316
|
+
| `.describe("…")` | human title in the Console (falls back to key) |
|
|
317
|
+
| `.references(() => col, { onDelete })` | foreign key |
|
|
318
|
+
| `store.schema.rls()` | `pgTable.withRLS` when there are no policies |
|
|
319
|
+
| `store.schema.policy.gate/owner/scope` | happy-path RLS (`oke.gate()` / `oke.user()` / `oke.has_scope()`) |
|
|
320
|
+
|
|
321
|
+
Third argument on `store.schema.table` is Drizzle-shaped extras:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
export const bookings = store.schema.table(
|
|
325
|
+
"bookings",
|
|
326
|
+
{
|
|
327
|
+
id: field.text().primaryKey(),
|
|
328
|
+
owner: field.text().notNull(),
|
|
329
|
+
},
|
|
330
|
+
[
|
|
331
|
+
store.schema.policy.gate("member", { for: "select" }),
|
|
332
|
+
store.schema.policy.owner("owner", { for: "all" }),
|
|
333
|
+
store.schema.policy.scope("booking:create", { for: "insert" }),
|
|
334
|
+
],
|
|
335
|
+
);
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Helpers emit stable names (`gate_member_select`). Raw
|
|
339
|
+
`store.schema.policy("name", { as, to, for, using, withCheck })` is the
|
|
340
|
+
escape hatch — predicates use `oke.*`, never `current_setting`.
|
|
341
|
+
|
|
342
|
+
<Callout title="User-plane fx.store applies RLS">
|
|
343
|
+
HTTP / resource flows stamp Gate identity onto every postgres / pglite statement (`SET LOCAL ROLE
|
|
344
|
+
oke_app` + `set_config` in one pinned transaction). Table-owner bypass ends there; operator / cron
|
|
345
|
+
/ CDC / signal / catalog stay unstamped.
|
|
346
|
+
</Callout>
|
|
304
347
|
|
|
305
348
|
#### Foreign keys and relations
|
|
306
349
|
|
|
@@ -356,9 +399,9 @@ Staging/prod accumulate versioned SQL under `drizzle/` (`oke db generate`).
|
|
|
356
399
|
| Staging / prod | `oke db generate` → review files → `oke db migrate` on that DB |
|
|
357
400
|
|
|
358
401
|
<Callout title="Connection pooling is infrastructure, not app code" type="info">
|
|
359
|
-
Bun.SQL
|
|
360
|
-
|
|
361
|
-
|
|
402
|
+
One shared Bun.SQL pool (**8** connections) per URL. `dev`/`prod` put **PgDog** in front
|
|
403
|
+
(`DATABASE_URL` → port `6432`, `pool_size` 20). RLS stamps `reserve` one slot, then `SET LOCAL` in
|
|
404
|
+
that transaction — a leaked `BEGIN` exhausts checkout.
|
|
362
405
|
</Callout>
|
|
363
406
|
|
|
364
407
|
**Why PgDog (not PgBouncer as the default).** Transaction pooling fixes the
|
|
@@ -371,8 +414,9 @@ Not wired this round — readiness only.
|
|
|
371
414
|
|
|
372
415
|
#### Seeding
|
|
373
416
|
|
|
374
|
-
`oke db seed` loads `defineSeed` from `src/db/seed/index.ts` —
|
|
375
|
-
|
|
417
|
+
`oke db seed` loads `defineSeed` from `src/db/seed/index.ts` — a separate
|
|
418
|
+
command, never at boot. Not every project has a seed. In `oke dev`, `s` runs
|
|
419
|
+
the same command. `name` is this app's seed (template or example).
|
|
376
420
|
|
|
377
421
|
<StoreSeeding />
|
|
378
422
|
|
|
@@ -642,8 +686,12 @@ Default `store.kv("sessions")` is cache-shaped — a Redis recreate drops keys.
|
|
|
642
686
|
```typescript
|
|
643
687
|
export const sessions = store.kv("sessions", { description: "Session cache" });
|
|
644
688
|
export const ledger = store.kv("ledger", { durable: true, description: "Idempotency keys" });
|
|
689
|
+
export const drafts = store.kv("drafts", { durable: true, description: "Compose drafts" });
|
|
645
690
|
```
|
|
646
691
|
|
|
692
|
+
**Consequence:** `oke db seed` into cache Redis looks fine until compose recreates the
|
|
693
|
+
container — Console Store then shows **No rows.** Seeded namespaces need `{ durable: true }`.
|
|
694
|
+
|
|
647
695
|
Gate rates and Signal stay on `REDIS_URL`. Missing `DATABASE_URL` with the postgres driver
|
|
648
696
|
fails boot: `oke boot: durable store.kv needs DATABASE_URL`.
|
|
649
697
|
|
|
@@ -1014,6 +1062,11 @@ Inserts and selects now resolve it from the declaration. Keep the `name` column.
|
|
|
1014
1062
|
|
|
1015
1063
|
Schema DDL never runs automatically in `prod`. Under `oke dev`, `db.autoPush` (default `true`) runs `oke db push`. For production use `oke db generate` + `oke db migrate`.
|
|
1016
1064
|
|
|
1065
|
+
</Accordion>
|
|
1066
|
+
<Accordion title="oke db push: missing_hints">
|
|
1067
|
+
|
|
1068
|
+
New tables are created automatically. Push only manages `public` — it will not drop schema `oke` (RLS helpers) or `oke_console`. Remaining `missing_hints` are destructive (drop a non-empty public table, type change). Review with `oke db generate`.
|
|
1069
|
+
|
|
1017
1070
|
</Accordion>
|
|
1018
1071
|
<Accordion title="I need a join — with: is not supported">
|
|
1019
1072
|
|
|
@@ -1064,11 +1117,17 @@ Unlike KV/Files, unset `drivers.store.index` does **not** promote to `pgvector`
|
|
|
1064
1117
|
|
|
1065
1118
|
`.oke/state.json` stores `seededAt` plus this app's `defineSeed({ name })` — notes vs keel are different ids. A new name re-asks. Clear those keys to prompt again.
|
|
1066
1119
|
|
|
1120
|
+
</Accordion>
|
|
1121
|
+
<Accordion title="Console KV shows No rows after oke db seed">
|
|
1122
|
+
|
|
1123
|
+
Default `store.kv` is cache Redis with no AOF — a recreate drops keys. SQL seed still looks full. Mark seeded namespaces `{ durable: true }` so they live in `oke_kv`, then restart and seed again.
|
|
1124
|
+
|
|
1067
1125
|
</Accordion>
|
|
1068
1126
|
</Accordions>
|
|
1069
1127
|
|
|
1070
1128
|
## Learn more
|
|
1071
1129
|
|
|
1130
|
+
- [Project structure](/docs/get-started/project-structure) — `list.ts` + `http.get()` is `GET /notes` named `notes.list`
|
|
1072
1131
|
- [Flow](/docs/elements/flow) — the `fx.store` session inside `do`
|
|
1073
1132
|
- [AI](/docs/elements/ai) — `ai.embed` into a vector `store.index`, searched via `fx.search`
|
|
1074
1133
|
- [Gate](/docs/elements/gate) — `pii:reveal` and other permissions on flows
|
|
@@ -363,6 +363,7 @@ Schedule the live command with cron when you want automatic cleanup.
|
|
|
363
363
|
|
|
364
364
|
## Learn more
|
|
365
365
|
|
|
366
|
+
- [Project structure](/docs/get-started/project-structure) — `flow({…})` names as `unit.export`; `flow("billing.charge")` still wins
|
|
366
367
|
- [Flow](/docs/elements/flow) — how `fx.vault.get` reads secrets inside `do`
|
|
367
368
|
- [CLI Reference](/docs/reference/cli) — `oke vault set` · `list` · `import`
|
|
368
369
|
|
|
@@ -45,19 +45,21 @@ lists HTTP routes — static GET paths are links. `curl` still gets `{ data, err
|
|
|
45
45
|
|
|
46
46
|
The standard starter already exports a named health Flow:
|
|
47
47
|
|
|
48
|
-
```typescript title="flows/main"
|
|
49
|
-
import { on, flow, http
|
|
48
|
+
```typescript title="flows/main/health.ts"
|
|
49
|
+
import { on, flow, http } from "okengine";
|
|
50
50
|
import { z } from "zod";
|
|
51
51
|
|
|
52
52
|
export const health = on(
|
|
53
|
-
http.get(
|
|
54
|
-
flow(
|
|
53
|
+
http.get().public(),
|
|
54
|
+
flow({
|
|
55
55
|
out: z.object({ ok: z.literal(true) }),
|
|
56
56
|
do: () => ({ ok: true as const }),
|
|
57
57
|
}),
|
|
58
58
|
);
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
`main` omits the URL prefix, so this file is `GET /health` named `main.health`. See [Project structure](/docs/get-started/project-structure).
|
|
62
|
+
|
|
61
63
|
Named flows show up as `main.health` (not `flow_1`) in `oke dev` logs, with the run id after the timestamp.
|
|
62
64
|
Change the route or output and save — App and Console update together from the
|
|
63
65
|
same Manifest.
|
|
@@ -67,20 +69,19 @@ same Manifest.
|
|
|
67
69
|
<Step>
|
|
68
70
|
### Call it from a typed client
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
Generated units become namespaces on the client:
|
|
71
73
|
|
|
72
74
|
```typescript title="app"
|
|
73
75
|
import "@/core";
|
|
74
|
-
|
|
76
|
+
import "@/flows/generated";
|
|
75
77
|
import { oke } from "okengine/http";
|
|
76
|
-
import * as routes from "@/flows/generated";
|
|
77
78
|
|
|
78
|
-
export const app = oke({ name: "notes" })
|
|
79
|
+
export const app = oke({ name: "notes" });
|
|
79
80
|
export type App = typeof app;
|
|
80
81
|
```
|
|
81
82
|
|
|
82
|
-
`import "@/core"` loads stores, secrets, channels, and
|
|
83
|
-
(via `locales/index.ts`) so they auto-register before `oke()`.
|
|
83
|
+
`import "@/core"` loads stores, secrets, gates, clocks, channels, and
|
|
84
|
+
message catalogs (via `locales/index.ts`) so they auto-register before `oke()`.
|
|
84
85
|
|
|
85
86
|
```typescript title="client"
|
|
86
87
|
import { createClient } from "okengine/client";
|
|
@@ -107,7 +108,7 @@ Every backend behavior has one shape:
|
|
|
107
108
|
|
|
108
109
|
## From Flow to client
|
|
109
110
|
|
|
110
|
-
Export →
|
|
111
|
+
Export → generated barrel → call or test. Same App type — no separate codegen project.
|
|
111
112
|
|
|
112
113
|
<ClientLoop />
|
|
113
114
|
|
|
@@ -137,8 +138,10 @@ bun test
|
|
|
137
138
|
<Accordions>
|
|
138
139
|
|
|
139
140
|
<Accordion title="api.main.health is not a function / type error">
|
|
140
|
-
Confirm the Flow is `export`ed from
|
|
141
|
-
|
|
141
|
+
Confirm the Flow is `export`ed from a generated unit (`import "@/flows/generated"` then `oke({ name })`), or from a module you still `.adopt({ main })`.
|
|
142
|
+
|
|
143
|
+
Type `createClient` with your `App` type. Restart `oke dev` after renaming exports so the Manifest refreshes.
|
|
144
|
+
|
|
142
145
|
</Accordion>
|
|
143
146
|
|
|
144
147
|
<Accordion title="createTestApp fails to boot">
|
|
@@ -151,6 +154,7 @@ bun test
|
|
|
151
154
|
|
|
152
155
|
## Learn more
|
|
153
156
|
|
|
157
|
+
- [Project structure](/docs/get-started/project-structure) — folders are the URL
|
|
154
158
|
- [Flow](/docs/elements/flow) — triggers, contracts, effects, and composition
|
|
155
159
|
- [Store](/docs/elements/store) — SQL, KV, files, index, and the preferred list envelope
|
|
156
160
|
- [fx](/docs/reference/fx) — the full effect surface
|
|
@@ -158,6 +162,11 @@ bun test
|
|
|
158
162
|
## Next
|
|
159
163
|
|
|
160
164
|
<Cards>
|
|
165
|
+
<Card
|
|
166
|
+
title="Project structure"
|
|
167
|
+
description="Folders are the URL; unit plus export is the name."
|
|
168
|
+
href="/docs/get-started/project-structure"
|
|
169
|
+
/>
|
|
161
170
|
<Card
|
|
162
171
|
title="Flow"
|
|
163
172
|
description="Triggers, contracts, effects, and composition."
|
|
@@ -30,6 +30,11 @@ Learn the shape once, install on Bun, then write your first Flow from the standa
|
|
|
30
30
|
description="Health Flow, typed client, Console proof."
|
|
31
31
|
href="/docs/get-started/basic-usage"
|
|
32
32
|
/>
|
|
33
|
+
<Card
|
|
34
|
+
title="Project structure"
|
|
35
|
+
description="Folder and file conventions — URL, name, and the generated barrel."
|
|
36
|
+
href="/docs/get-started/project-structure"
|
|
37
|
+
/>
|
|
33
38
|
<Card
|
|
34
39
|
title="Deployment"
|
|
35
40
|
description="Compose, Swarm, Kubernetes, and reverse proxy."
|
|
@@ -23,19 +23,21 @@ shape. Learn the shape once; only the trigger changes.
|
|
|
23
23
|
|
|
24
24
|
Four contracts plus a `do`. This is the standard starter's health check:
|
|
25
25
|
|
|
26
|
-
```typescript title="health"
|
|
26
|
+
```typescript title="flows/main/health.ts"
|
|
27
27
|
import { on, flow, http } from "okengine";
|
|
28
28
|
import { z } from "zod";
|
|
29
29
|
|
|
30
30
|
export const health = on(
|
|
31
|
-
http.get(
|
|
32
|
-
flow(
|
|
31
|
+
http.get().public(),
|
|
32
|
+
flow({
|
|
33
33
|
out: z.object({ ok: z.literal(true) }),
|
|
34
34
|
do: () => ({ ok: true as const }),
|
|
35
35
|
}),
|
|
36
36
|
);
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
`main` omits the URL prefix, so this file is `GET /health` named `main.health`. See [Project structure](/docs/get-started/project-structure). Explicit `http.get("/health")` and `flow("main.health")` still win.
|
|
40
|
+
|
|
39
41
|
</Step>
|
|
40
42
|
|
|
41
43
|
<Step>
|
|
@@ -162,6 +164,7 @@ and `okengine/full`.
|
|
|
162
164
|
| Topic | Page |
|
|
163
165
|
| ------- | ------------------------------------------------------------------------------- |
|
|
164
166
|
| Why OKE | [Why OKE](/docs/get-started/why) |
|
|
167
|
+
| Folders | [Project structure](/docs/get-started/project-structure) |
|
|
165
168
|
| Flow | [Flow](/docs/elements/flow) |
|
|
166
169
|
| `fx` | [fx](/docs/reference/fx) |
|
|
167
170
|
| Agents | [MCP](/docs/ai/mcp) · [Skills](/docs/ai/skills) · [llms.txt](/docs/ai/llms-txt) |
|
|
@@ -184,4 +187,9 @@ and `okengine/full`.
|
|
|
184
187
|
description="Health Flow, typed client, and bun:test."
|
|
185
188
|
href="/docs/get-started/basic-usage"
|
|
186
189
|
/>
|
|
190
|
+
<Card
|
|
191
|
+
title="Project structure"
|
|
192
|
+
description="Folders are the URL; unit plus export is the name."
|
|
193
|
+
href="/docs/get-started/project-structure"
|
|
194
|
+
/>
|
|
187
195
|
</Cards>
|