okengine 0.13.0 → 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/manifest.v1.schema.json +7 -1
- package/package.json +4 -5
- package/site/content/docs/ai/mcp.mdx +27 -2
- package/site/content/docs/elements/ai.mdx +59 -11
- package/site/content/docs/elements/channel.mdx +1 -0
- package/site/content/docs/elements/clock.mdx +30 -12
- package/site/content/docs/elements/flow.mdx +9 -3
- package/site/content/docs/elements/gate.mdx +25 -22
- package/site/content/docs/elements/signal.mdx +71 -25
- package/site/content/docs/elements/store.mdx +87 -28
- 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/installation.mdx +2 -2
- 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/fx.mdx +27 -22
- 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/ai-setup/recommend.test.ts +25 -0
- package/src/cli/ai-setup/recommend.ts +8 -3
- 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/effects-infer.ts +58 -3
- package/src/compiler/extract.test.ts +458 -29
- package/src/compiler/extract.ts +387 -69
- 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/response.ts +45 -1
- package/src/compiler/schema-from-ast.ts +609 -0
- package/src/console/server/ai.ts +5 -2
- package/src/console/server/bind.ts +2 -2
- package/src/console/server/console.test.ts +3 -0
- package/src/console/server/flows.ts +121 -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/serve.ts +17 -2
- 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-F1FI122b.js +1 -0
- 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-HK9-BO8z.js +4 -0
- 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/seed-invoke-host.ts +2 -0
- 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/graph/build-flow-graph.test.ts +18 -0
- package/src/console/ui-next/src/features/flows/graph/build-flow-graph.ts +14 -1
- package/src/console/ui-next/src/features/flows/graph/neighborhood.test.ts +17 -0
- package/src/console/ui-next/src/features/flows/graph/neighborhood.ts +16 -3
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +3 -1
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +24 -0
- 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-sheet.tsx +9 -3
- package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +29 -2
- package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
- package/src/console/ui-next/src/features/observability/lib/ask-count.test.ts +25 -0
- package/src/console/ui-next/src/features/observability/lib/ask-count.ts +4 -1
- 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/detail/effects-summary.tsx +12 -4
- 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 +4 -1
- package/src/docker/dockerfile.ts +1 -1
- package/src/docker/recipes/pgdog.ts +2 -0
- package/src/drivers/ai-anthropic.ts +5 -0
- package/src/drivers/ai-ollama.ts +49 -30
- package/src/drivers/ai-openai-compatible.ts +57 -46
- package/src/drivers/ai-providers.test.ts +3 -0
- package/src/drivers/bun-native-completeness.test.ts +7 -9
- 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/redis.ts +11 -4
- package/src/drivers/signal-redis.ts +24 -14
- package/src/drivers/signal-types.ts +2 -1
- package/src/drivers/types.ts +10 -0
- package/src/elements/ai/declare.ts +109 -0
- package/src/elements/ai/errors.test.ts +5 -1
- package/src/elements/ai/errors.ts +30 -2
- package/src/elements/ai/eval.ts +4 -6
- package/src/elements/ai/mcp-client.test.ts +206 -0
- package/src/elements/ai/mcp-client.ts +362 -0
- package/src/elements/ai/mcp-http.ts +159 -0
- package/src/elements/ai/mcp-mock.ts +134 -0
- package/src/elements/ai/mcp-protocol.ts +234 -0
- package/src/elements/ai/mcp-stdio.test.ts +50 -0
- package/src/elements/ai/mcp-stdio.ts +212 -0
- package/src/elements/ai/mcp-transport.ts +70 -0
- package/src/elements/ai/runtime.ts +159 -29
- package/src/elements/ai.test.ts +139 -0
- package/src/elements/ai.ts +16 -0
- package/src/elements/clock/declare.ts +26 -1
- package/src/elements/clock/health.test.ts +43 -0
- package/src/elements/clock/runtime.ts +55 -2
- package/src/elements/clock/schedule.ts +71 -142
- package/src/elements/clock.test.ts +9 -40
- package/src/elements/clock.ts +6 -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/index.ts +2 -0
- package/src/elements/signal/declare.ts +2 -1
- package/src/elements/signal/runtime.ts +16 -1
- package/src/elements/signal.ts +1 -0
- package/src/elements/store/cache.test.ts +2 -0
- package/src/elements/store/cache.ts +3 -3
- 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/elements/vault/builtin-adapter.ts +17 -0
- package/src/full.ts +2 -0
- package/src/http.ts +20 -3
- package/src/index.ts +14 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
- package/src/kernel/adopt-routes.ts +30 -2
- package/src/kernel/app.ts +227 -77
- package/src/kernel/auto-cache.test.ts +8 -8
- package/src/kernel/auto-registry.test.ts +54 -9
- package/src/kernel/boot-bind/ai.ts +24 -0
- package/src/kernel/boot-bind/clock.ts +2 -0
- package/src/kernel/boot.test.ts +4 -4
- package/src/kernel/boot.ts +5 -3
- package/src/kernel/effects-stamping.test.ts +3 -3
- package/src/kernel/element-registries.ts +15 -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-dead-letters.test.ts +77 -0
- package/src/kernel/fx.test.ts +22 -0
- package/src/kernel/fx.ts +135 -6
- 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 +174 -0
- package/src/kernel/index.ts +2 -0
- 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/mcp-ref.ts +88 -0
- package/src/manifest/types.ts +41 -2
- package/src/manifest/validate.test.ts +20 -0
- package/src/mcp/docs-server.ts +1 -1
- package/src/mcp/server.ts +1 -1
- package/src/plugins/compression.test.ts +21 -0
- package/src/plugins/compression.ts +1 -0
- package/src/release/measure.ts +2 -2
- package/src/runtime/bun.ts +41 -4
- 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 +10 -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/cache-glyph-CLPBqZeb.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-Dg8CTE29.js +0 -1
- package/src/console/ui-next/dist/assets/http-method-BJ92Z_ke.js +0 -1
- package/src/console/ui-next/dist/assets/index-Bp-R7jtM.js +0 -66
- package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
- package/src/console/ui-next/dist/assets/observability-page-DAnpEaq1.js +0 -4
- 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-D16lWQMt.js +0 -2
- package/src/console/ui-next/dist/assets/units-page-BtQ0bqMe.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-Ca-MvcmJ.js +0 -2
|
@@ -71,7 +71,8 @@ export const sendConfirmation = on(
|
|
|
71
71
|
|
|
72
72
|
</Steps>
|
|
73
73
|
|
|
74
|
-
That's the loop
|
|
74
|
+
That's the loop for `once` and `broadcast`: declare → `fx.emit` → `on(signal, flow)`.
|
|
75
|
+
`live` keeps the same emit; the listener is `bus.live()`, not a Flow.
|
|
75
76
|
|
|
76
77
|
## The three delivery physics
|
|
77
78
|
|
|
@@ -96,7 +97,7 @@ One placed order can need all three physics at once — pick a signal per job, n
|
|
|
96
97
|
|
|
97
98
|
| Signal | Mode | Job |
|
|
98
99
|
| --------------- | ----------- | ----------------------------------------------------------- |
|
|
99
|
-
| `order-placed` | `once` |
|
|
100
|
+
| `order-placed` | `once` | One job — competing workers, retries, DLQ |
|
|
100
101
|
| `order-changed` | `broadcast` | Fan-out — cache invalidation **and** customer notification |
|
|
101
102
|
| `order-status` | `live` | Status feed — retained history a late subscriber can replay |
|
|
102
103
|
|
|
@@ -107,7 +108,7 @@ import { z } from "zod";
|
|
|
107
108
|
export const orderPlaced = signal("order-placed", {
|
|
108
109
|
schema: z.object({ orderId: z.string(), total: z.number() }),
|
|
109
110
|
delivery: "once",
|
|
110
|
-
retries:
|
|
111
|
+
retries: 3,
|
|
111
112
|
deadLetter: true,
|
|
112
113
|
});
|
|
113
114
|
|
|
@@ -126,25 +127,43 @@ export const orderStatus = signal("order-status", {
|
|
|
126
127
|
});
|
|
127
128
|
```
|
|
128
129
|
|
|
129
|
-
|
|
130
|
+
A Flow **writes** all three tapes. `once` / `broadcast` **listen** with `on(signal, flow)`. `live` does not.
|
|
130
131
|
|
|
131
|
-
```typescript title="src/flows/
|
|
132
|
-
on(
|
|
132
|
+
```typescript title="src/flows/orders/place.ts"
|
|
133
|
+
export const placeOrder = on(
|
|
134
|
+
http.post("/orders").public(),
|
|
135
|
+
flow("orders.place", {
|
|
136
|
+
in: z.object({ id: z.string(), total: z.number() }),
|
|
137
|
+
do: async (input, fx) => {
|
|
138
|
+
await fx.emit(orderPlaced, { orderId: input.id, total: input.total });
|
|
139
|
+
await fx.emit(orderChanged, { orderId: input.id, kind: "placed" });
|
|
140
|
+
await fx.emit(orderStatus, { orderId: input.id, status: "placed" });
|
|
141
|
+
await fx.emit(orderStatus, { orderId: input.id, status: "fulfilling" });
|
|
142
|
+
await fx.emit(orderStatus, { orderId: input.id, status: "shipped" });
|
|
143
|
+
},
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Stacked status emits are so replay is visible in one request. A real app emits as the order moves.
|
|
149
|
+
|
|
150
|
+
`once` — the listener is `on(orderPlaced, flow)`. Two workers compete; **exactly one** claims each message (at-least-once; make the handler idempotent):
|
|
151
|
+
|
|
152
|
+
```typescript title="src/flows/orders/send-confirmation.ts"
|
|
153
|
+
export const sendConfirmation = on(
|
|
133
154
|
orderPlaced,
|
|
134
|
-
flow("
|
|
135
|
-
do: async (
|
|
136
|
-
await fx.
|
|
137
|
-
await fx.emit(orderStatus, { orderId, status: "fulfilling" });
|
|
138
|
-
await fx.emit(orderStatus, { orderId, status: "shipped" });
|
|
155
|
+
flow("orders.sendConfirmation", {
|
|
156
|
+
do: async (input, fx) => {
|
|
157
|
+
await fx.send(orderConfirmed, { to: "user-1", data: input });
|
|
139
158
|
},
|
|
140
159
|
}),
|
|
141
160
|
);
|
|
142
161
|
```
|
|
143
162
|
|
|
144
|
-
`broadcast` — every
|
|
163
|
+
`broadcast` — every `on(orderChanged, flow)` gets its own copy. Neither Flow knows the other exists:
|
|
145
164
|
|
|
146
165
|
```typescript title="src/flows/orders/side-effects.ts"
|
|
147
|
-
on(
|
|
166
|
+
export const invalidateCache = on(
|
|
148
167
|
orderChanged,
|
|
149
168
|
flow("cache.onOrderChanged", {
|
|
150
169
|
do: async ({ orderId }, fx) => {
|
|
@@ -153,7 +172,7 @@ on(
|
|
|
153
172
|
}),
|
|
154
173
|
);
|
|
155
174
|
|
|
156
|
-
on(
|
|
175
|
+
export const notifyCustomer = on(
|
|
157
176
|
orderChanged,
|
|
158
177
|
flow("notify.onOrderChanged", {
|
|
159
178
|
do: async ({ orderId }, fx) => {
|
|
@@ -163,27 +182,38 @@ on(
|
|
|
163
182
|
);
|
|
164
183
|
```
|
|
165
184
|
|
|
166
|
-
`live` — retain
|
|
185
|
+
`live` — retain the tape. A late subscriber replays **full history** (`placed → fulfilling → shipped`), then keeps going.
|
|
167
186
|
|
|
168
|
-
Today that
|
|
187
|
+
The listener is **`bus.live()`**, not `on(orderStatus, flow)`. `on(signal, flow)` does not replay retained live payloads. Today that API is server-side; `createClient` has no SSE / WebSocket / `client.live` yet — see [Client](/docs/reference/client#signal-and-live-queries).
|
|
169
188
|
|
|
170
189
|
```typescript
|
|
171
|
-
|
|
190
|
+
const bus = app.bootResult?.signal?.bus;
|
|
191
|
+
if (!bus) throw new Error("signal runtime not booted");
|
|
172
192
|
|
|
173
|
-
// Late subscriber (server / test harness) receives placed → fulfilling → shipped.
|
|
174
193
|
const unsub = await bus.live("order-status", (payload) => {
|
|
175
|
-
/*
|
|
194
|
+
/* placed → fulfilling → shipped */
|
|
176
195
|
});
|
|
177
196
|
```
|
|
178
197
|
|
|
179
|
-
**Why separate signals:** delivery is fixed per declaration. Competing work stays on `once`; fan-out stays on `broadcast`; the
|
|
198
|
+
**Why separate signals:** delivery is fixed per declaration. Competing work stays on `once`; fan-out stays on `broadcast`; the timeline stays on `live`. Switching a word later is cheap; mixing physics on one name is not.
|
|
180
199
|
|
|
181
200
|
## When delivery fails
|
|
182
201
|
|
|
183
202
|
`once` signals retry automatically. Every attempt keeps a **typed failure reason**, and the full attempt history survives into the DLQ — so when a message lands there you see _why_ each attempt failed, not just that it did.
|
|
184
203
|
|
|
204
|
+
Query that queue from a Flow with `fx.deadLetters(signal)` — same handle as `fx.emit`. The compiler stamps `reads: ["signal:<name>"]`; another signal throws **OKE1001**.
|
|
205
|
+
|
|
185
206
|
The **Console** (`:6533` → `/overview`) shows emit and consume on the graph, and delivery attempts in Traces — no separate broker UI to run.
|
|
186
207
|
|
|
208
|
+
```typescript
|
|
209
|
+
on(
|
|
210
|
+
http.get("/notifications/failed"),
|
|
211
|
+
flow("notifications.failed", {
|
|
212
|
+
do: async (input, fx) => fx.json.withQuery(await fx.deadLetters(orderPlaced), input),
|
|
213
|
+
}),
|
|
214
|
+
);
|
|
215
|
+
```
|
|
216
|
+
|
|
187
217
|
<Callout title="Orphan emits and schema fail loudly">
|
|
188
218
|
Zero subscribers throws **OKE1042** unless `optional: true`. A payload that fails the signal's
|
|
189
219
|
Standard Schema throws **OKE1043** before the message is staged — same `validate()` path as Flow
|
|
@@ -225,9 +255,9 @@ Committed `once` messages survive process death. A claim sets `lockedBy` and a v
|
|
|
225
255
|
`live` retains every delivered message and replays the **full history** to a late `bus.live()` subscriber. There is no TTL or max-count window on that retention today (the Console payload monitor shows only the newest 50 for display).
|
|
226
256
|
|
|
227
257
|
<Callout title="Client subscription is not shipped yet">
|
|
228
|
-
`delivery: "live"` means the **driver** retains and replays. `
|
|
229
|
-
|
|
230
|
-
|
|
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 yet — poll HTTP. See
|
|
260
|
+
[Client](/docs/reference/client#signal-and-live-queries).
|
|
231
261
|
</Callout>
|
|
232
262
|
|
|
233
263
|
## Orphaned signal config
|
|
@@ -258,7 +288,10 @@ Pairing an arbitrary Store insert with emit inside **one** shared SQL transactio
|
|
|
258
288
|
<Accordions>
|
|
259
289
|
<Accordion title="Emit fails with OKE1042 (no subscriber)">
|
|
260
290
|
|
|
261
|
-
You emitted a signal that nobody currently subscribes to.
|
|
291
|
+
You emitted a signal that nobody currently subscribes to. For `once` and `broadcast`, wire
|
|
292
|
+
`on(signal, flow)` before emit.
|
|
293
|
+
|
|
294
|
+
`live` listeners are `bus.live()`, not a Flow — set `optional: true` when they may connect later.
|
|
262
295
|
|
|
263
296
|
</Accordion>
|
|
264
297
|
<Accordion title="Emit fails with OKE1043 (schema)">
|
|
@@ -268,7 +301,18 @@ The payload failed the signal's Standard Schema at emit time — nothing was sta
|
|
|
268
301
|
</Accordion>
|
|
269
302
|
<Accordion title="A message keeps retrying and then disappears">
|
|
270
303
|
|
|
271
|
-
When `attempts > retries` the message moves to the DLQ (if `deadLetter: true`) — it is not lost.
|
|
304
|
+
When `attempts > retries` the message moves to the DLQ (if `deadLetter: true`) — it is not lost. List it from a Flow with `fx.deadLetters(signal)`, or open Console → Flows, inspect the typed failure, fix the consumer, then replay.
|
|
305
|
+
|
|
306
|
+
</Accordion>
|
|
307
|
+
<Accordion title="fx.deadLetters throws OKE1001">
|
|
308
|
+
|
|
309
|
+
The flow read a signal it did not declare. The compiler infers `reads: ["signal:<name>"]` from `fx.deadLetters(signal)`. A different handle needs its own read.
|
|
310
|
+
|
|
311
|
+
</Accordion>
|
|
312
|
+
<Accordion title="on(liveSignal, flow) never replays history">
|
|
313
|
+
|
|
314
|
+
`on(signal, flow)` listens for `once` and `broadcast`. `live` replay is `bus.live()` on the Signal
|
|
315
|
+
bus — a Flow trigger does not replay retained payloads.
|
|
272
316
|
|
|
273
317
|
</Accordion>
|
|
274
318
|
<Accordion title="once vs broadcast vs live — how do I choose?">
|
|
@@ -290,7 +334,9 @@ At-least-once: crash-after-claim reclaims when the lease expires, and a handler
|
|
|
290
334
|
|
|
291
335
|
## Learn more
|
|
292
336
|
|
|
337
|
+
- [Project structure](/docs/get-started/project-structure) — signal files get a name, never a URL; `http.get("/…")` still wins
|
|
293
338
|
- [Flow](/docs/elements/flow) — `on(trigger, flow)` and `fx.emit`
|
|
339
|
+
- [fx](/docs/reference/fx) — `fx.emit` and `fx.deadLetters`
|
|
294
340
|
- [Clock](/docs/elements/clock) — scheduled and delayed work
|
|
295
341
|
|
|
296
342
|
## Next
|
|
@@ -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
|
|
|
@@ -777,7 +825,7 @@ images: {
|
|
|
777
825
|
|
|
778
826
|
### Images — `image` / `putImage`
|
|
779
827
|
|
|
780
|
-
For photos, use Bun's built-in `Bun.Image` pipeline on the same handle (requires Bun `>=1.
|
|
828
|
+
For photos, use Bun's built-in `Bun.Image` pipeline on the same handle (requires Bun `>=1.4.0`).
|
|
781
829
|
`putImage` is one write that fans into several keys:
|
|
782
830
|
|
|
783
831
|
<StoreFilesVariants />
|
|
@@ -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."
|
|
@@ -6,7 +6,7 @@ icon: Download
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
This page gets you from zero to a running app on Bun. The engine targets
|
|
9
|
-
**Bun ≥ 1.
|
|
9
|
+
**Bun ≥ 1.4.0** — prefer it for install, scaffold, and `oke dev`.
|
|
10
10
|
|
|
11
11
|
<Callout title="The one rule">
|
|
12
12
|
The npm package is `okengine`; the CLI binary is `oke`. Use `bun add` in a project or `bun install
|
|
@@ -20,7 +20,7 @@ This page gets you from zero to a running app on Bun. The engine targets
|
|
|
20
20
|
<Step>
|
|
21
21
|
### Prerequisites
|
|
22
22
|
|
|
23
|
-
- [Bun](https://bun.sh) ≥ 1.
|
|
23
|
+
- [Bun](https://bun.sh) ≥ 1.4.0 (`bun --version`)
|
|
24
24
|
- [Docker](https://docs.docker.com/get-docker/) with a running daemon (`docker info`)
|
|
25
25
|
- A terminal and a code editor
|
|
26
26
|
|
|
@@ -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>
|