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/src/kernel/triggers.ts
CHANGED
|
@@ -7,8 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
import { flattenGateArgs, GATE_PUBLIC_NAME, type GateAllDecl } from "../elements/gate/flatten.ts";
|
|
9
9
|
import type { NamedRef } from "./fx.ts";
|
|
10
|
+
import { HTTP_PATH_PENDING, type HttpPathPending } from "./http-path-pending.ts";
|
|
10
11
|
import { lazyRequire } from "./lazy-require.ts";
|
|
11
12
|
|
|
13
|
+
export { HTTP_PATH_PENDING, isPendingHttpPath, type HttpPathPending } from "./http-path-pending.ts";
|
|
14
|
+
|
|
12
15
|
/** HTTP methods accepted by {@link http}. */
|
|
13
16
|
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD" | "QUERY";
|
|
14
17
|
|
|
@@ -18,15 +21,8 @@ export type GateRef = NamedRef;
|
|
|
18
21
|
/** One `.gate(...)` argument — a named ref, an `all` handle, or an array. */
|
|
19
22
|
export type GateArg = GateRef | GateAllDecl | readonly GateArg[];
|
|
20
23
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
* Call it with members, or use `.public` for the unauthenticated sentinel.
|
|
24
|
-
*/
|
|
25
|
-
export interface GateAttach<T> {
|
|
26
|
-
(...gates: GateArg[]): T;
|
|
27
|
-
/** Attach {@link gate.public} — same as `.gate(gate.public)`. */
|
|
28
|
-
readonly public: T;
|
|
29
|
-
}
|
|
24
|
+
/** Attach gates on an HTTP trigger / resource mount. */
|
|
25
|
+
export type GateAttach<T> = (...gates: GateArg[]) => T;
|
|
30
26
|
|
|
31
27
|
/**
|
|
32
28
|
* HTTP trigger value. Method and path are literal type parameters so
|
|
@@ -44,9 +40,12 @@ export interface HttpTrigger<M extends HttpMethod = HttpMethod, P extends string
|
|
|
44
40
|
readonly isLive: boolean;
|
|
45
41
|
/**
|
|
46
42
|
* Attach gates (registration order). `gate.all` handles and arrays flatten.
|
|
47
|
-
* `.gate.public` is the unauthenticated sentinel.
|
|
48
43
|
*/
|
|
49
44
|
readonly gate: GateAttach<HttpTrigger<M, P>>;
|
|
45
|
+
/**
|
|
46
|
+
* Attach the unauthenticated public sentinel.
|
|
47
|
+
*/
|
|
48
|
+
public(): HttpTrigger<M, P>;
|
|
50
49
|
/**
|
|
51
50
|
* Mark the HTTP trigger as live (push result to subscribers).
|
|
52
51
|
*/
|
|
@@ -93,7 +92,7 @@ export type Trigger = HttpTrigger | EveryTrigger | SignalAsTrigger | CdcTrigger
|
|
|
93
92
|
export type TriggerKind = Trigger["kind"];
|
|
94
93
|
|
|
95
94
|
/**
|
|
96
|
-
* Callable `.gate(...)`
|
|
95
|
+
* Callable `.gate(...)` on a trigger or resource mount.
|
|
97
96
|
*
|
|
98
97
|
* @param apply - Rebuild the host with the flattened gate list
|
|
99
98
|
* @param current - Gates already attached
|
|
@@ -102,19 +101,11 @@ export function createGateAttach<T>(
|
|
|
102
101
|
apply: (next: readonly GateRef[]) => T,
|
|
103
102
|
current: readonly GateRef[],
|
|
104
103
|
): GateAttach<T> {
|
|
105
|
-
|
|
106
|
-
apply([...current, ...flattenGateArgs(next)])) as GateAttach<T>;
|
|
107
|
-
Object.defineProperty(attach, "public", {
|
|
108
|
-
get() {
|
|
109
|
-
return apply([...current, GATE_PUBLIC_NAME]);
|
|
110
|
-
},
|
|
111
|
-
enumerable: true,
|
|
112
|
-
});
|
|
113
|
-
return attach;
|
|
104
|
+
return (...next: GateArg[]) => apply([...current, ...flattenGateArgs(next)]);
|
|
114
105
|
}
|
|
115
106
|
|
|
116
107
|
/**
|
|
117
|
-
* Build an HTTP trigger with `.gate` / `.live`.
|
|
108
|
+
* Build an HTTP trigger with `.gate` / `.public` / `.live`.
|
|
118
109
|
*
|
|
119
110
|
* @param method - HTTP verb
|
|
120
111
|
* @param path - Route path
|
|
@@ -134,6 +125,9 @@ export function createHttpTrigger<M extends HttpMethod, P extends string>(
|
|
|
134
125
|
gates,
|
|
135
126
|
isLive,
|
|
136
127
|
gate: createGateAttach((next) => createHttpTrigger(method, path, next, isLive), gates),
|
|
128
|
+
public() {
|
|
129
|
+
return createHttpTrigger(method, path, [...gates, GATE_PUBLIC_NAME], isLive);
|
|
130
|
+
},
|
|
137
131
|
live() {
|
|
138
132
|
return createHttpTrigger(method, path, gates, true);
|
|
139
133
|
},
|
|
@@ -156,7 +150,7 @@ export interface ResourceFlowBag {
|
|
|
156
150
|
/**
|
|
157
151
|
* A mounted resource — the single argument to the `on(http.resource(…))`
|
|
158
152
|
* overload. Branded so `on` can tell it apart from a plain trigger.
|
|
159
|
-
* Same chain as {@link HttpTrigger}: `.gate(...)` / `.live()`.
|
|
153
|
+
* Same chain as {@link HttpTrigger}: `.gate(...)` / `.public()` / `.live()`.
|
|
160
154
|
*/
|
|
161
155
|
export interface ResourceMount {
|
|
162
156
|
readonly [resourceMountBrand]: true;
|
|
@@ -166,9 +160,12 @@ export interface ResourceMount {
|
|
|
166
160
|
readonly isLive: boolean;
|
|
167
161
|
/**
|
|
168
162
|
* Attach gates to every verb (registration order). `gate.all` and arrays flatten.
|
|
169
|
-
* `.gate.public` is the unauthenticated sentinel.
|
|
170
163
|
*/
|
|
171
164
|
readonly gate: GateAttach<ResourceMount>;
|
|
165
|
+
/**
|
|
166
|
+
* Attach the unauthenticated public sentinel to every verb.
|
|
167
|
+
*/
|
|
168
|
+
public(): ResourceMount;
|
|
172
169
|
/**
|
|
173
170
|
* Mark list and get as live (GET mounts). Mutations stay request/response.
|
|
174
171
|
*/
|
|
@@ -200,34 +197,55 @@ function loadHttpResource(): typeof import("./http-resource.ts") {
|
|
|
200
197
|
* types for param extraction.
|
|
201
198
|
*/
|
|
202
199
|
export interface HttpTriggerNamespace {
|
|
200
|
+
/**
|
|
201
|
+
* Pathless — file-tree stamp fills the URL. Unresolved sentinel fails boot.
|
|
202
|
+
*/
|
|
203
|
+
get(): HttpTrigger<"GET", HttpPathPending>;
|
|
203
204
|
/**
|
|
204
205
|
* @param path - Route path (`/:id` params supported)
|
|
205
206
|
*/
|
|
206
207
|
get<P extends string>(path: P): HttpTrigger<"GET", P>;
|
|
208
|
+
/** Pathless — file-tree stamp fills the URL. */
|
|
209
|
+
post(): HttpTrigger<"POST", HttpPathPending>;
|
|
207
210
|
/**
|
|
208
211
|
* @param path - Route path
|
|
209
212
|
*/
|
|
210
213
|
post<P extends string>(path: P): HttpTrigger<"POST", P>;
|
|
214
|
+
/** Pathless — file-tree stamp fills the URL. */
|
|
215
|
+
put(): HttpTrigger<"PUT", HttpPathPending>;
|
|
211
216
|
/**
|
|
212
217
|
* @param path - Route path
|
|
213
218
|
*/
|
|
214
219
|
put<P extends string>(path: P): HttpTrigger<"PUT", P>;
|
|
220
|
+
/** Pathless — file-tree stamp fills the URL. */
|
|
221
|
+
patch(): HttpTrigger<"PATCH", HttpPathPending>;
|
|
215
222
|
/**
|
|
216
223
|
* @param path - Route path
|
|
217
224
|
*/
|
|
218
225
|
patch<P extends string>(path: P): HttpTrigger<"PATCH", P>;
|
|
226
|
+
/** Pathless — file-tree stamp fills the URL. */
|
|
227
|
+
delete(): HttpTrigger<"DELETE", HttpPathPending>;
|
|
219
228
|
/**
|
|
220
229
|
* @param path - Route path
|
|
221
230
|
*/
|
|
222
231
|
delete<P extends string>(path: P): HttpTrigger<"DELETE", P>;
|
|
232
|
+
/** Pathless — file-tree stamp fills the URL. */
|
|
233
|
+
options(): HttpTrigger<"OPTIONS", HttpPathPending>;
|
|
223
234
|
/**
|
|
224
235
|
* @param path - Route path
|
|
225
236
|
*/
|
|
226
237
|
options<P extends string>(path: P): HttpTrigger<"OPTIONS", P>;
|
|
238
|
+
/** Pathless — file-tree stamp fills the URL. */
|
|
239
|
+
head(): HttpTrigger<"HEAD", HttpPathPending>;
|
|
227
240
|
/**
|
|
228
241
|
* @param path - Route path
|
|
229
242
|
*/
|
|
230
243
|
head<P extends string>(path: P): HttpTrigger<"HEAD", P>;
|
|
244
|
+
/**
|
|
245
|
+
* Safe, idempotent read that carries a JSON body (RFC 10008).
|
|
246
|
+
* Pathless — file-tree stamp fills the URL.
|
|
247
|
+
*/
|
|
248
|
+
query(): HttpTrigger<"QUERY", HttpPathPending>;
|
|
231
249
|
/**
|
|
232
250
|
* Safe, idempotent read that carries a JSON body (RFC 10008).
|
|
233
251
|
*
|
|
@@ -236,8 +254,8 @@ export interface HttpTriggerNamespace {
|
|
|
236
254
|
query<P extends string>(path: P): HttpTrigger<"QUERY", P>;
|
|
237
255
|
/**
|
|
238
256
|
* Mount a CRUD resource (list/create on `path`, get/update/remove on
|
|
239
|
-
* `path/:id`) for the `on(http.resource(…))` overload. Chain `.gate(...)
|
|
240
|
-
* and `.live()` like {@link HttpTrigger}.
|
|
257
|
+
* `path/:id`) for the `on(http.resource(…))` overload. Chain `.gate(...)`,
|
|
258
|
+
* `.public()`, and `.live()` like {@link HttpTrigger}.
|
|
241
259
|
*/
|
|
242
260
|
resource<P extends string>(path: P, ops: ResourceFlowBag): ResourceMount;
|
|
243
261
|
}
|
|
@@ -245,8 +263,15 @@ export interface HttpTriggerNamespace {
|
|
|
245
263
|
/** Bind an HTTP verb constructor (`http.get`, `http.query`, …). */
|
|
246
264
|
function httpVerb<M extends HttpMethod>(
|
|
247
265
|
method: M,
|
|
248
|
-
):
|
|
249
|
-
|
|
266
|
+
): {
|
|
267
|
+
(): HttpTrigger<M, HttpPathPending>;
|
|
268
|
+
<P extends string>(path: P): HttpTrigger<M, P>;
|
|
269
|
+
} {
|
|
270
|
+
return ((path?: string) =>
|
|
271
|
+
createHttpTrigger(method, path === undefined ? HTTP_PATH_PENDING : path)) as {
|
|
272
|
+
(): HttpTrigger<M, HttpPathPending>;
|
|
273
|
+
<P extends string>(path: P): HttpTrigger<M, P>;
|
|
274
|
+
};
|
|
250
275
|
}
|
|
251
276
|
|
|
252
277
|
/**
|
package/src/manifest/types.ts
CHANGED
|
@@ -224,10 +224,23 @@ export interface DeclaredColumn extends ColumnClassification {
|
|
|
224
224
|
/** Field classification value forms. */
|
|
225
225
|
export type ClassificationValue = string | string[] | ColumnClassification;
|
|
226
226
|
|
|
227
|
+
/** Declared Postgres RLS policy on a {@link Table}. */
|
|
228
|
+
export interface TablePolicy {
|
|
229
|
+
as?: "permissive" | "restrictive";
|
|
230
|
+
to?: string | string[];
|
|
231
|
+
for?: "all" | "select" | "insert" | "update" | "delete";
|
|
232
|
+
using?: string;
|
|
233
|
+
withCheck?: string;
|
|
234
|
+
}
|
|
235
|
+
|
|
227
236
|
/** SQL table metadata. */
|
|
228
237
|
export interface Table {
|
|
229
238
|
columns?: Record<string, DeclaredColumn | ColumnClassification>;
|
|
230
239
|
classifications?: Record<string, ClassificationValue>;
|
|
240
|
+
/** RLS enabled (`pgTable.withRLS` or any policy). */
|
|
241
|
+
rls?: boolean;
|
|
242
|
+
/** Policy name → Drizzle-shaped spec. */
|
|
243
|
+
policies?: Record<string, TablePolicy>;
|
|
231
244
|
}
|
|
232
245
|
|
|
233
246
|
/** One Store declaration. */
|
package/src/release/measure.ts
CHANGED
|
@@ -201,7 +201,7 @@ export async function measureHttpPingAppBytes(): Promise<HttpPingBudgetSample> {
|
|
|
201
201
|
await Bun.write(
|
|
202
202
|
anchor,
|
|
203
203
|
`import { on, flow, http, gate, oke, createBunRuntime } from ${JSON.stringify(httpEntry)};\n` +
|
|
204
|
-
`on(http.get("/").
|
|
204
|
+
`on(http.get("/").public(), flow("ping", { do: () => "Hi" }));\n` +
|
|
205
205
|
`export const app = oke({ name: "ping" });\n` +
|
|
206
206
|
`export const rt = createBunRuntime;\n`,
|
|
207
207
|
);
|
|
@@ -362,7 +362,7 @@ const t0 = performance.now();
|
|
|
362
362
|
const { createBunRuntime, oke, on, flow, http, gate } = await import(${JSON.stringify(httpEntry)});
|
|
363
363
|
const { resetBindings } = await import(${JSON.stringify(`${import.meta.dir}/../kernel/on.ts`)});
|
|
364
364
|
resetBindings();
|
|
365
|
-
on(http.get("/ping").
|
|
365
|
+
on(http.get("/ping").public(), flow("ping", { do: () => ({ ok: true }) }));
|
|
366
366
|
const app = oke({ name: "cold-start" });
|
|
367
367
|
const rt = createBunRuntime();
|
|
368
368
|
const server = rt.serve(app, { port: 0, hostname: "127.0.0.1" });
|
|
@@ -255,7 +255,7 @@ describe("httpGetNavPaths", () => {
|
|
|
255
255
|
describe("asBrowserJsonCodeBlock — HTTP", () => {
|
|
256
256
|
test("Chrome GET / becomes the code block; curl stays JSON", async () => {
|
|
257
257
|
on(
|
|
258
|
-
http.get("/").
|
|
258
|
+
http.get("/").public(),
|
|
259
259
|
flow("main.root", {
|
|
260
260
|
do: () => ({ ok: true as const, app: "keel" }),
|
|
261
261
|
}),
|
|
@@ -286,7 +286,7 @@ describe("asBrowserJsonCodeBlock — HTTP", () => {
|
|
|
286
286
|
});
|
|
287
287
|
|
|
288
288
|
test("?raw=1 stays on the traces page; format=json and Accept stay the envelope", async () => {
|
|
289
|
-
on(http.get("/health").
|
|
289
|
+
on(http.get("/health").public(), flow("main.health", { do: () => ({ ok: true as const }) }));
|
|
290
290
|
const app = oke({ autoBoot: false, name: "keel" });
|
|
291
291
|
|
|
292
292
|
const raw = await app.fetch(
|
|
@@ -55,13 +55,13 @@ function rawHttp(port: number, payload: string): Promise<string> {
|
|
|
55
55
|
|
|
56
56
|
function buildApp() {
|
|
57
57
|
on(
|
|
58
|
-
http.get("/ping").
|
|
58
|
+
http.get("/ping").public(),
|
|
59
59
|
flow("ping", {
|
|
60
60
|
do: () => ({ ok: true as const, n: 1 }),
|
|
61
61
|
}),
|
|
62
62
|
);
|
|
63
63
|
on(
|
|
64
|
-
http.get("/notes/:id").
|
|
64
|
+
http.get("/notes/:id").public(),
|
|
65
65
|
flow("notes.get", {
|
|
66
66
|
do: ({ id }: { id: string }) => ({ id }),
|
|
67
67
|
}),
|
|
@@ -157,13 +157,13 @@ describe("Bun.serve — real HTTP", () => {
|
|
|
157
157
|
|
|
158
158
|
test("QUERY is omitted from native routes so Bun.serve still boots", async () => {
|
|
159
159
|
on(
|
|
160
|
-
http.query("/search").
|
|
160
|
+
http.query("/search").public(),
|
|
161
161
|
flow("search.query", {
|
|
162
162
|
do: () => ({ hits: 1 }),
|
|
163
163
|
}),
|
|
164
164
|
);
|
|
165
165
|
on(
|
|
166
|
-
http.get("/search/suggest").
|
|
166
|
+
http.get("/search/suggest").public(),
|
|
167
167
|
flow("search.suggest", {
|
|
168
168
|
do: () => ({ hits: 0 }),
|
|
169
169
|
}),
|
package/src/term.test.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { describe, expect, test } from "bun:test";
|
|
|
6
6
|
import {
|
|
7
7
|
formatAppReadyLine,
|
|
8
8
|
formatBootWarn,
|
|
9
|
+
formatBoundSurfaces,
|
|
9
10
|
formatClaimNote,
|
|
10
11
|
formatDevNote,
|
|
11
12
|
formatCliChrome,
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
formatDevLogSeparator,
|
|
15
16
|
formatOkeWordmark,
|
|
16
17
|
formatServiceLine,
|
|
18
|
+
formatSurfaceBox,
|
|
17
19
|
formatRequestLine,
|
|
18
20
|
formatStackSummary,
|
|
19
21
|
countTermLines,
|
|
@@ -61,8 +63,8 @@ describe("term", () => {
|
|
|
61
63
|
expect(out).toContain("elements");
|
|
62
64
|
expect(out).toContain("store");
|
|
63
65
|
expect(out).toContain("postgres");
|
|
64
|
-
expect(out).toContain("●
|
|
65
|
-
expect(out).toContain("●
|
|
66
|
+
expect(out).toContain("● flow");
|
|
67
|
+
expect(out).toContain("● ai");
|
|
66
68
|
expect(out).toContain("Starting");
|
|
67
69
|
expect(out).not.toContain("Ready");
|
|
68
70
|
expect(out).not.toContain("on(Trigger)");
|
|
@@ -134,6 +136,7 @@ describe("term", () => {
|
|
|
134
136
|
appUrl: "http://127.0.0.1:6530",
|
|
135
137
|
consoleUrl: "http://127.0.0.1:6533",
|
|
136
138
|
mcpUrl: "http://127.0.0.1:6535",
|
|
139
|
+
docsMcpUrl: "http://127.0.0.1:6536",
|
|
137
140
|
profile: "local",
|
|
138
141
|
runtimeEnv: "local",
|
|
139
142
|
system: "darwin 25.4.0 · bun 1.3.14",
|
|
@@ -145,11 +148,38 @@ describe("term", () => {
|
|
|
145
148
|
expect(out).toContain("Backend");
|
|
146
149
|
expect(out).toContain("Console");
|
|
147
150
|
expect(out).toContain("MCP");
|
|
151
|
+
expect(out).toContain("Docs MCP");
|
|
148
152
|
expect(out).toContain("http://127.0.0.1:6530");
|
|
153
|
+
expect(out).toContain("Your flows, served from the Manifest.");
|
|
149
154
|
expect(out).toContain("└");
|
|
150
155
|
expect(out).toContain("Logs");
|
|
151
156
|
});
|
|
152
157
|
|
|
158
|
+
test("formatSurfaceBox prints url and purpose", () => {
|
|
159
|
+
const out = formatSurfaceBox({
|
|
160
|
+
label: "Backend",
|
|
161
|
+
url: "http://127.0.0.1:6530",
|
|
162
|
+
color: false,
|
|
163
|
+
});
|
|
164
|
+
expect(out).toContain("Backend");
|
|
165
|
+
expect(out).toContain("http://127.0.0.1:6530");
|
|
166
|
+
expect(out).toContain("Your flows, served from the Manifest.");
|
|
167
|
+
expect(out).toMatch(/● {2}Backend\n│ {2}http:\/\/127\.0\.0\.1:6530\n│ {2}Your flows/);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("formatBoundSurfaces skips unbound MCP and Docs MCP", () => {
|
|
171
|
+
const out = formatBoundSurfaces({
|
|
172
|
+
appUrl: "http://127.0.0.1:6530",
|
|
173
|
+
consoleUrl: "http://127.0.0.1:6533",
|
|
174
|
+
color: false,
|
|
175
|
+
});
|
|
176
|
+
expect(out).toContain("Backend");
|
|
177
|
+
expect(out).toContain("Console");
|
|
178
|
+
expect(out).toContain("Panels, traces, and the effect graph.");
|
|
179
|
+
expect(out).not.toContain("Docs MCP");
|
|
180
|
+
expect(out).not.toContain("The same Manifest, for agents.");
|
|
181
|
+
});
|
|
182
|
+
|
|
153
183
|
test("formatDevLogSeparator closes hero and titles Logs", () => {
|
|
154
184
|
const out = formatDevLogSeparator(false);
|
|
155
185
|
expect(out).toContain("│");
|
|
@@ -280,10 +310,28 @@ describe("term", () => {
|
|
|
280
310
|
expect(out).toContain("Docker");
|
|
281
311
|
expect(out).toContain(":15975");
|
|
282
312
|
expect(out).toContain("gemma4:e4b-q4_K_M");
|
|
283
|
-
expect(out).toContain("●
|
|
284
|
-
expect(out).toContain("●
|
|
313
|
+
expect(out).toContain("● postgres");
|
|
314
|
+
expect(out).toContain("● ai");
|
|
285
315
|
// Blank │ row separates the Docker header from whatever sits above.
|
|
286
316
|
expect(out).toMatch(/│\n◇ {2}Docker/);
|
|
287
317
|
expect(out).not.toMatch(/\u001b\[/);
|
|
288
318
|
});
|
|
319
|
+
|
|
320
|
+
test("formatStackSummary aligns name and port columns", () => {
|
|
321
|
+
const out = formatStackSummary({
|
|
322
|
+
project: "oke-dev-a3f791",
|
|
323
|
+
services: [
|
|
324
|
+
{ label: "ai", hostPort: 23850, status: "ready" },
|
|
325
|
+
{ label: "store.index", hostPort: 8550, status: "ready" },
|
|
326
|
+
{ label: "postgres", hostPort: 15850, status: "ready" },
|
|
327
|
+
],
|
|
328
|
+
color: false,
|
|
329
|
+
});
|
|
330
|
+
const portCols = out
|
|
331
|
+
.split("\n")
|
|
332
|
+
.filter((line) => /●\s+\S/.test(line) && line.includes(":"))
|
|
333
|
+
.map((line) => line.search(/:\d/));
|
|
334
|
+
expect(portCols.length).toBe(3);
|
|
335
|
+
expect(new Set(portCols).size).toBe(1);
|
|
336
|
+
});
|
|
289
337
|
});
|
package/src/term.ts
CHANGED
|
@@ -112,6 +112,32 @@ export type DevHeroElement = {
|
|
|
112
112
|
readonly status?: DevStatus;
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
+
/** Name column for Ready / Docker / bind rows — fits `store.index`. */
|
|
116
|
+
const HERO_NAME_MIN = 12;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Widest name column that still fits every label (never below {@link HERO_NAME_MIN}).
|
|
120
|
+
*
|
|
121
|
+
* @param names - Row labels in the table
|
|
122
|
+
*/
|
|
123
|
+
function heroNameWidth(names: readonly string[]): number {
|
|
124
|
+
let width = HERO_NAME_MIN;
|
|
125
|
+
for (const name of names) {
|
|
126
|
+
if (name.length > width) width = name.length;
|
|
127
|
+
}
|
|
128
|
+
return width;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Left-align a table cell.
|
|
133
|
+
*
|
|
134
|
+
* @param value - Cell text
|
|
135
|
+
* @param width - Column width
|
|
136
|
+
*/
|
|
137
|
+
function padHeroCol(value: string, width: number): string {
|
|
138
|
+
return value.padEnd(width);
|
|
139
|
+
}
|
|
140
|
+
|
|
115
141
|
/** Shared options for the `oke dev` hero / banner. */
|
|
116
142
|
export type DevHeroMeta = {
|
|
117
143
|
/** `local` · `docker` · `test` · `production` */
|
|
@@ -158,18 +184,24 @@ export function formatOkeWordmark(color: boolean = termColorEnabled()): string {
|
|
|
158
184
|
export function formatDevHeroDetails(options: DevHeroMeta = {}): string {
|
|
159
185
|
const s = termStyle(options.color ?? termColorEnabled());
|
|
160
186
|
const bar = `${s.dim}│${s.reset}`;
|
|
161
|
-
const
|
|
187
|
+
const elements = options.elements ?? [];
|
|
188
|
+
const nameWidth = heroNameWidth([
|
|
189
|
+
...(options.profile ? ["profile"] : []),
|
|
190
|
+
...(options.runtimeEnv ? ["env"] : []),
|
|
191
|
+
...(options.system ? ["system"] : []),
|
|
192
|
+
...elements.map((row) => row.element),
|
|
193
|
+
]);
|
|
194
|
+
const label = (name: string) => `${s.dim}${padHeroCol(name, nameWidth)}${s.reset}`;
|
|
162
195
|
const lines: string[] = [];
|
|
163
196
|
if (options.profile) {
|
|
164
|
-
lines.push(`${bar} ${label("profile")}
|
|
197
|
+
lines.push(`${bar} ${label("profile")} ${s.cyan}${options.profile}${s.reset}`);
|
|
165
198
|
}
|
|
166
199
|
if (options.runtimeEnv) {
|
|
167
|
-
lines.push(`${bar} ${label("env")}
|
|
200
|
+
lines.push(`${bar} ${label("env")} ${s.cyan}${options.runtimeEnv}${s.reset}`);
|
|
168
201
|
}
|
|
169
202
|
if (options.system) {
|
|
170
|
-
lines.push(`${bar} ${label("system")}
|
|
203
|
+
lines.push(`${bar} ${label("system")} ${s.dim}${options.system}${s.reset}`);
|
|
171
204
|
}
|
|
172
|
-
const elements = options.elements ?? [];
|
|
173
205
|
if (elements.length > 0) {
|
|
174
206
|
lines.push(`${bar} ${s.dim}elements${s.reset}`);
|
|
175
207
|
const color = options.color ?? termColorEnabled();
|
|
@@ -182,8 +214,10 @@ export function formatDevHeroDetails(options: DevHeroMeta = {}): string {
|
|
|
182
214
|
? ""
|
|
183
215
|
: `${s.dim}—${s.reset}`
|
|
184
216
|
: `${s.cyan}${row.detail}${s.reset}`;
|
|
185
|
-
const detailPart = detail ? `
|
|
186
|
-
lines.push(
|
|
217
|
+
const detailPart = detail ? ` ${detail}` : "";
|
|
218
|
+
lines.push(
|
|
219
|
+
`${bar} ${dot} ${s.dim}${padHeroCol(row.element, nameWidth)}${s.reset}${detailPart}`,
|
|
220
|
+
);
|
|
187
221
|
}
|
|
188
222
|
}
|
|
189
223
|
return lines.length > 0 ? `${lines.join("\n")}\n` : "";
|
|
@@ -454,7 +488,19 @@ export function createAnchoredBoard(
|
|
|
454
488
|
}
|
|
455
489
|
|
|
456
490
|
/**
|
|
457
|
-
* One
|
|
491
|
+
* One-line purpose for each `oke dev` bind surface.
|
|
492
|
+
*
|
|
493
|
+
* Same wording as the site PORTS mnemonic — keep them in lockstep.
|
|
494
|
+
*/
|
|
495
|
+
export const DEV_SURFACE_DETAILS: Readonly<Record<string, string>> = {
|
|
496
|
+
Backend: "Your flows, served from the Manifest.",
|
|
497
|
+
Console: "Panels, traces, and the effect graph.",
|
|
498
|
+
MCP: "The same Manifest, for agents.",
|
|
499
|
+
"Docs MCP": "Read-only docs search for agents.",
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* One service URL line (Console / MCP after bind, before the ready board).
|
|
458
504
|
*
|
|
459
505
|
* @param label - Surface name
|
|
460
506
|
* @param url - Absolute URL
|
|
@@ -466,19 +512,73 @@ export function formatServiceLine(
|
|
|
466
512
|
color: boolean = termColorEnabled(),
|
|
467
513
|
): string {
|
|
468
514
|
const s = termStyle(color);
|
|
469
|
-
const pad = label
|
|
515
|
+
const pad = padHeroCol(label, HERO_NAME_MIN);
|
|
470
516
|
return `${s.dim}│${s.reset} ${s.dim}${pad}${s.reset} ${s.cyan}${url}${s.reset}\n`;
|
|
471
517
|
}
|
|
472
518
|
|
|
473
519
|
/**
|
|
474
520
|
* Backend child ready line (`bun --hot` soft reload).
|
|
475
521
|
*
|
|
522
|
+
* Stays one line so Logs do not fill with surface boxes on every reload.
|
|
523
|
+
*
|
|
476
524
|
* @param url - Backend base URL
|
|
477
525
|
* @param color - Color on/off
|
|
478
526
|
*/
|
|
479
527
|
export function formatAppReadyLine(url: string, color: boolean = termColorEnabled()): string {
|
|
480
528
|
const s = termStyle(color);
|
|
481
|
-
return `${s.green}●${s.reset} ${s.dim}${"Backend"
|
|
529
|
+
return `${s.green}●${s.reset} ${s.dim}${padHeroCol("Backend", HERO_NAME_MIN)}${s.reset} ${s.cyan}${url}${s.reset}\n`;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* One bound-surface box on the ready board — title, URL, purpose.
|
|
534
|
+
*
|
|
535
|
+
* @param options - Label, URL, optional detail override
|
|
536
|
+
*/
|
|
537
|
+
export function formatSurfaceBox(options: {
|
|
538
|
+
readonly label: string;
|
|
539
|
+
readonly url: string;
|
|
540
|
+
readonly detail?: string;
|
|
541
|
+
readonly color?: boolean;
|
|
542
|
+
}): string {
|
|
543
|
+
const s = termStyle(options.color ?? termColorEnabled());
|
|
544
|
+
const bar = `${s.dim}│${s.reset}`;
|
|
545
|
+
const detail = (options.detail ?? DEV_SURFACE_DETAILS[options.label] ?? "").trim();
|
|
546
|
+
const lines = [
|
|
547
|
+
`${s.green}●${s.reset} ${s.bold}${options.label}${s.reset}`,
|
|
548
|
+
`${bar} ${s.cyan}${options.url}${s.reset}`,
|
|
549
|
+
];
|
|
550
|
+
if (detail.length > 0) {
|
|
551
|
+
lines.push(`${bar} ${s.dim}${detail}${s.reset}`);
|
|
552
|
+
}
|
|
553
|
+
lines.push(bar);
|
|
554
|
+
return `${lines.join("\n")}\n`;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Backend · Console · MCP · Docs MCP boxes after bind.
|
|
559
|
+
*
|
|
560
|
+
* Pass `mcpUrl` / `docsMcpUrl` only when that surface came up — omitted
|
|
561
|
+
* URLs skip the box.
|
|
562
|
+
*
|
|
563
|
+
* @param options - Bound surface URLs
|
|
564
|
+
*/
|
|
565
|
+
export function formatBoundSurfaces(options: {
|
|
566
|
+
readonly appUrl: string;
|
|
567
|
+
readonly consoleUrl: string;
|
|
568
|
+
readonly mcpUrl?: string | null;
|
|
569
|
+
readonly docsMcpUrl?: string | null;
|
|
570
|
+
readonly color?: boolean;
|
|
571
|
+
}): string {
|
|
572
|
+
const color = options.color ?? termColorEnabled();
|
|
573
|
+
let out = formatSurfaceBox({ label: "Backend", url: options.appUrl, color });
|
|
574
|
+
out += formatSurfaceBox({ label: "Console", url: options.consoleUrl, color });
|
|
575
|
+
if (options.mcpUrl) {
|
|
576
|
+
out += formatSurfaceBox({ label: "MCP", url: options.mcpUrl, color });
|
|
577
|
+
}
|
|
578
|
+
if (options.docsMcpUrl) {
|
|
579
|
+
out += formatSurfaceBox({ label: "Docs MCP", url: options.docsMcpUrl, color });
|
|
580
|
+
}
|
|
581
|
+
return out;
|
|
482
582
|
}
|
|
483
583
|
|
|
484
584
|
/**
|
|
@@ -499,6 +599,7 @@ export function formatDevHero(
|
|
|
499
599
|
readonly appUrl: string;
|
|
500
600
|
readonly consoleUrl: string;
|
|
501
601
|
readonly mcpUrl: string;
|
|
602
|
+
readonly docsMcpUrl?: string;
|
|
502
603
|
},
|
|
503
604
|
): string {
|
|
504
605
|
const color = options.color ?? termColorEnabled();
|
|
@@ -511,9 +612,13 @@ export function formatDevHero(
|
|
|
511
612
|
`\n${bar}\n` +
|
|
512
613
|
formatDevHeroDetails({ ...options, color }) +
|
|
513
614
|
`${bar}\n` +
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
615
|
+
formatBoundSurfaces({
|
|
616
|
+
appUrl: options.appUrl,
|
|
617
|
+
consoleUrl: options.consoleUrl,
|
|
618
|
+
mcpUrl: options.mcpUrl,
|
|
619
|
+
docsMcpUrl: options.docsMcpUrl,
|
|
620
|
+
color,
|
|
621
|
+
}) +
|
|
517
622
|
formatDevLogSeparator(color)
|
|
518
623
|
);
|
|
519
624
|
}
|
|
@@ -767,25 +872,28 @@ export function formatStackSummary(options: {
|
|
|
767
872
|
}): string {
|
|
768
873
|
const s = termStyle(options.color ?? termColorEnabled());
|
|
769
874
|
const bar = `${s.dim}│${s.reset}`;
|
|
770
|
-
const
|
|
875
|
+
const nameWidth = heroNameWidth(["Docker", "app", ...options.services.map((svc) => svc.label)]);
|
|
876
|
+
const portWidth = Math.max(6, ...options.services.map((svc) => `:${svc.hostPort}`.length));
|
|
877
|
+
const pad = (label: string) => padHeroCol(label, nameWidth);
|
|
771
878
|
const color = options.color ?? termColorEnabled();
|
|
772
879
|
const lines: string[] = [
|
|
773
880
|
`${s.dim}│${s.reset}`,
|
|
774
|
-
`${s.green}◇${s.reset} ${s.bold}Docker${s.reset}
|
|
881
|
+
`${s.green}◇${s.reset} ${s.bold}${pad("Docker")}${s.reset} ${s.cyan}${options.project}${s.reset}`,
|
|
775
882
|
];
|
|
776
883
|
for (const svc of options.services) {
|
|
777
884
|
const detail = svc.detail?.trim();
|
|
778
885
|
const status = svc.status ?? "pending";
|
|
779
886
|
const dot = formatStatusDot(status, color);
|
|
887
|
+
const port = `:${svc.hostPort}`.padEnd(portWidth);
|
|
780
888
|
lines.push(
|
|
781
|
-
`${bar} ${dot}
|
|
889
|
+
`${bar} ${dot} ${s.dim}${pad(svc.label)}${s.reset} ${s.cyan}${port}${s.reset}` +
|
|
782
890
|
(detail ? ` ${s.dim}${detail}${s.reset}` : ""),
|
|
783
891
|
);
|
|
784
892
|
}
|
|
785
893
|
const drivers = options.appDrivers ?? [];
|
|
786
894
|
const appDetail = drivers.length > 0 ? `host Bun · ${drivers.join(" + ")}` : "host Bun";
|
|
787
895
|
const appDot = formatStatusDot("ready", color);
|
|
788
|
-
lines.push(`${bar} ${appDot}
|
|
896
|
+
lines.push(`${bar} ${appDot} ${s.dim}${pad("app")}${s.reset} ${s.dim}${appDetail}${s.reset}`);
|
|
789
897
|
lines.push(bar);
|
|
790
898
|
return `${lines.join("\n")}\n`;
|
|
791
899
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Global safety net for the `store
|
|
3
|
-
* `
|
|
4
|
-
* auto-registries
|
|
2
|
+
* Global safety net for the `store.*` / `vault.secret` / `signal()` /
|
|
3
|
+
* `clock()` / `gate.policy`·scope·rate / `channel.<medium>().template()` /
|
|
4
|
+
* `ai.model`·prompt·embed·agent auto-registries
|
|
5
|
+
* (`src/kernel/element-registries.ts`).
|
|
5
6
|
*
|
|
6
7
|
* Unlike `on()` bindings — created almost exclusively to wire a real app —
|
|
7
8
|
* these factories are called throughout the suite as bare value constructors,
|
|
@@ -23,6 +24,8 @@ import {
|
|
|
23
24
|
aiModelRegistry,
|
|
24
25
|
aiPromptRegistry,
|
|
25
26
|
channelTemplateRegistry,
|
|
27
|
+
clockRegistry,
|
|
28
|
+
gateRegistry,
|
|
26
29
|
requiredEnvRegistry,
|
|
27
30
|
secretRegistry,
|
|
28
31
|
signalRegistry,
|
|
@@ -34,6 +37,8 @@ afterEach(() => {
|
|
|
34
37
|
secretRegistry.length = 0;
|
|
35
38
|
requiredEnvRegistry.length = 0;
|
|
36
39
|
signalRegistry.length = 0;
|
|
40
|
+
clockRegistry.length = 0;
|
|
41
|
+
gateRegistry.length = 0;
|
|
37
42
|
channelTemplateRegistry.length = 0;
|
|
38
43
|
aiModelRegistry.length = 0;
|
|
39
44
|
aiPromptRegistry.length = 0;
|