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
|
@@ -89,6 +89,7 @@ export async function openOpenaiCompatible(options: AiOpenOptions = {}): Promise
|
|
|
89
89
|
const model = options.model ?? "gpt-4o-mini";
|
|
90
90
|
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
91
91
|
const extraHeaders = options.headers;
|
|
92
|
+
preconnectFetch(fetchFn, baseUrl);
|
|
92
93
|
|
|
93
94
|
return {
|
|
94
95
|
driverId: "openai-compatible",
|
|
@@ -162,10 +163,7 @@ export async function openOpenaiCompatible(options: AiOpenOptions = {}): Promise
|
|
|
162
163
|
const msg = raw.error?.message ?? `openai-compatible HTTP ${res.status}`;
|
|
163
164
|
throwHttp(`openai-compatible: ${msg}`, res.status);
|
|
164
165
|
}
|
|
165
|
-
|
|
166
|
-
throw new Error("openai-compatible: stream response has no body");
|
|
167
|
-
}
|
|
168
|
-
yield* readOpenaiSse(res.body, opts.signal);
|
|
166
|
+
yield* readOpenaiSse(res, opts.signal);
|
|
169
167
|
},
|
|
170
168
|
async embed(opts: AiEmbedOptions): Promise<AiEmbedResult> {
|
|
171
169
|
const resolvedModel = opts.model ?? model;
|
|
@@ -262,57 +260,70 @@ function parseToolCalls(
|
|
|
262
260
|
/**
|
|
263
261
|
* Parse OpenAI SSE chat.completion.chunk stream.
|
|
264
262
|
*
|
|
265
|
-
* @param
|
|
263
|
+
* @param res - Streaming response
|
|
266
264
|
* @param signal - Optional abort
|
|
267
265
|
*/
|
|
268
|
-
async function* readOpenaiSse(
|
|
269
|
-
body: ReadableStream<Uint8Array>,
|
|
270
|
-
signal?: AbortSignal,
|
|
271
|
-
): AsyncGenerator<AiStreamChunk> {
|
|
272
|
-
const reader = body.getReader();
|
|
273
|
-
const decoder = new TextDecoder();
|
|
266
|
+
async function* readOpenaiSse(res: Response, signal?: AbortSignal): AsyncGenerator<AiStreamChunk> {
|
|
274
267
|
let buffer = "";
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
268
|
+
for await (const piece of responseTextStream(res)) {
|
|
269
|
+
if (signal?.aborted) throw abortAsError(signal.reason);
|
|
270
|
+
buffer += piece;
|
|
271
|
+
const lines = buffer.split("\n");
|
|
272
|
+
buffer = lines.pop() ?? "";
|
|
273
|
+
for (const line of lines) {
|
|
274
|
+
const trimmed = line.trim();
|
|
275
|
+
if (!trimmed.startsWith("data:")) continue;
|
|
276
|
+
const data = trimmed.slice(5).trim();
|
|
277
|
+
if (data === "[DONE]") {
|
|
278
|
+
yield { text: "", done: true };
|
|
279
|
+
return;
|
|
279
280
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
281
|
+
try {
|
|
282
|
+
const chunk = JSON.parse(data) as {
|
|
283
|
+
choices?: readonly {
|
|
284
|
+
delta?: { content?: string | null };
|
|
285
|
+
finish_reason?: string | null;
|
|
286
|
+
}[];
|
|
287
|
+
};
|
|
288
|
+
const delta = chunk.choices?.[0]?.delta?.content;
|
|
289
|
+
if (typeof delta === "string" && delta.length > 0) {
|
|
290
|
+
yield { text: delta };
|
|
291
|
+
}
|
|
292
|
+
if (chunk.choices?.[0]?.finish_reason) {
|
|
290
293
|
yield { text: "", done: true };
|
|
291
294
|
return;
|
|
292
295
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
choices?: readonly {
|
|
296
|
-
delta?: { content?: string | null };
|
|
297
|
-
finish_reason?: string | null;
|
|
298
|
-
}[];
|
|
299
|
-
};
|
|
300
|
-
const delta = chunk.choices?.[0]?.delta?.content;
|
|
301
|
-
if (typeof delta === "string" && delta.length > 0) {
|
|
302
|
-
yield { text: delta };
|
|
303
|
-
}
|
|
304
|
-
if (chunk.choices?.[0]?.finish_reason) {
|
|
305
|
-
yield { text: "", done: true };
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
} catch {
|
|
309
|
-
// ignore malformed SSE lines
|
|
310
|
-
}
|
|
296
|
+
} catch {
|
|
297
|
+
// ignore malformed SSE lines
|
|
311
298
|
}
|
|
312
299
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
300
|
+
}
|
|
301
|
+
yield { text: "", done: true };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* UTF-8 text chunks from a Response (`textStream` on Bun ≥1.4).
|
|
306
|
+
*
|
|
307
|
+
* @param res - HTTP response
|
|
308
|
+
*/
|
|
309
|
+
function responseTextStream(res: Response): AsyncIterable<string> {
|
|
310
|
+
const stream = (res as Response & { textStream?: () => AsyncIterable<string> }).textStream;
|
|
311
|
+
if (typeof stream !== "function") {
|
|
312
|
+
throw new Error("openai-compatible: Response.textStream is required (Bun >= 1.4.0)");
|
|
313
|
+
}
|
|
314
|
+
return stream.call(res);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Warm DNS+TCP+TLS for a cloud origin. No-op when `fetch` is a test stub.
|
|
319
|
+
*
|
|
320
|
+
* @param fetchFn - Fetch implementation
|
|
321
|
+
* @param url - Provider base URL
|
|
322
|
+
*/
|
|
323
|
+
function preconnectFetch(fetchFn: typeof fetch, url: string): void {
|
|
324
|
+
const preconnect = (fetchFn as { preconnect?: (href: string) => void }).preconnect;
|
|
325
|
+
if (typeof preconnect === "function") {
|
|
326
|
+
preconnect(url);
|
|
316
327
|
}
|
|
317
328
|
}
|
|
318
329
|
|
|
@@ -43,12 +43,14 @@ describe("anthropic driver", () => {
|
|
|
43
43
|
model: "claude-test",
|
|
44
44
|
fetch: fetchFn,
|
|
45
45
|
});
|
|
46
|
+
const ac = new AbortController();
|
|
46
47
|
const result = await client.complete({
|
|
47
48
|
messages: [
|
|
48
49
|
{ role: "system", content: "be brief" },
|
|
49
50
|
{ role: "user", content: "hi" },
|
|
50
51
|
],
|
|
51
52
|
maxTokens: 64,
|
|
53
|
+
signal: ac.signal,
|
|
52
54
|
});
|
|
53
55
|
|
|
54
56
|
expect(result.driverId).toBe("anthropic");
|
|
@@ -62,6 +64,7 @@ describe("anthropic driver", () => {
|
|
|
62
64
|
};
|
|
63
65
|
expect(body.system).toBe("be brief");
|
|
64
66
|
expect(body.messages).toEqual([{ role: "user", content: "hi" } as { role: string }]);
|
|
67
|
+
expect(calls[0]!.init?.signal).toBe(ac.signal);
|
|
65
68
|
});
|
|
66
69
|
|
|
67
70
|
test("HTTP error surfaces provider message", async () => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bun native-client completeness — gaps closed vs platform limitations.
|
|
3
3
|
*
|
|
4
|
-
* Checked against Bun 1.
|
|
5
|
-
*
|
|
4
|
+
* Checked against Bun 1.4 (runtime). Do not invent custom protocol clients
|
|
5
|
+
* where Bun still lacks an API.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { describe, expect, test } from "bun:test";
|
|
@@ -46,7 +46,7 @@ export function bunNativeCompletenessReport(): readonly BunNativeGapRow[] {
|
|
|
46
46
|
status: hasTypedEval ? "closed" : "bun_limitation",
|
|
47
47
|
note: hasTypedEval
|
|
48
48
|
? "typed eval bound"
|
|
49
|
-
: "Bun
|
|
49
|
+
: "Bun.RedisClient.eval unavailable — would keep send(EVAL)",
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
surface: "redis signal",
|
|
@@ -63,7 +63,7 @@ export function bunNativeCompletenessReport(): readonly BunNativeGapRow[] {
|
|
|
63
63
|
status: hasTypedXadd ? "closed" : "bun_limitation",
|
|
64
64
|
note: hasTypedXadd
|
|
65
65
|
? "typed stream helpers bound"
|
|
66
|
-
: "Bun
|
|
66
|
+
: "Bun.RedisClient.xadd unavailable — Streams would keep send",
|
|
67
67
|
},
|
|
68
68
|
{
|
|
69
69
|
surface: "redis signal",
|
|
@@ -95,23 +95,21 @@ export function bunNativeCompletenessReport(): readonly BunNativeGapRow[] {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
describe("Bun native client completeness", () => {
|
|
98
|
-
test("report table is exact and matches runtime Bun 1.
|
|
98
|
+
test("report table is exact and matches runtime Bun 1.4", () => {
|
|
99
99
|
const rows = bunNativeCompletenessReport();
|
|
100
100
|
expect(rows.length).toBe(8);
|
|
101
101
|
|
|
102
102
|
const closed = rows.filter((r) => r.status === "closed").map((r) => r.capability);
|
|
103
103
|
const limited = rows.filter((r) => r.status === "bun_limitation").map((r) => r.capability);
|
|
104
104
|
|
|
105
|
-
// Closed on Bun 1.3.14
|
|
106
105
|
expect(closed).toContain("SCAN (typed)");
|
|
106
|
+
expect(closed).toContain("EVAL (typed)");
|
|
107
107
|
expect(closed).toContain("native Bun bind (publish/subscribe)");
|
|
108
|
+
expect(closed).toContain("XADD / XREADGROUP (typed)");
|
|
108
109
|
expect(closed).toContain("send escape hatch");
|
|
109
110
|
expect(closed).toContain("Bun.SQL.unsafe query/exec");
|
|
110
111
|
expect(closed).toContain("Bun.S3Client file/list");
|
|
111
112
|
|
|
112
|
-
// Genuine Bun platform limitations (do not invent workarounds)
|
|
113
|
-
expect(limited).toContain("EVAL (typed)");
|
|
114
|
-
expect(limited).toContain("XADD / XREADGROUP (typed)");
|
|
115
113
|
expect(limited).toContain("LISTEN / NOTIFY on Bun.SQL");
|
|
116
114
|
});
|
|
117
115
|
|
|
@@ -7,7 +7,13 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { CronRow, CronStatus, CronStore } from "../elements/clock/reconcile.ts";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
resolvePostgresUrl,
|
|
12
|
+
sharedPostgresClient,
|
|
13
|
+
toPostgresParams,
|
|
14
|
+
withPinnedPostgres,
|
|
15
|
+
type PostgresClientLike,
|
|
16
|
+
} from "./postgres.ts";
|
|
11
17
|
|
|
12
18
|
/** Row shape in `oke_crons` (lease columns mirror `oke_signal_messages`). */
|
|
13
19
|
interface CronDbRow {
|
|
@@ -69,16 +75,16 @@ export interface BunCronClient {
|
|
|
69
75
|
close?(options?: { timeout?: number }): Promise<void>;
|
|
70
76
|
}
|
|
71
77
|
|
|
72
|
-
function wrapBunClient(client:
|
|
78
|
+
function wrapBunClient(client: PostgresClientLike): PostgresCronSql {
|
|
73
79
|
const api: PostgresCronSql = {
|
|
74
80
|
async query(sql, params = []) {
|
|
75
|
-
const pg = toPostgresParams(sql);
|
|
81
|
+
const pg = toPostgresParams(sql, params);
|
|
76
82
|
const result = await client.unsafe(pg, [...params]);
|
|
77
83
|
if (Array.isArray(result)) return result as Record<string, unknown>[];
|
|
78
84
|
return Array.from(result as ArrayLike<Record<string, unknown>>);
|
|
79
85
|
},
|
|
80
86
|
async exec(sql, params = []) {
|
|
81
|
-
const pg = toPostgresParams(sql);
|
|
87
|
+
const pg = toPostgresParams(sql, params);
|
|
82
88
|
const result = await client.unsafe(pg, [...params]);
|
|
83
89
|
if (
|
|
84
90
|
result &&
|
|
@@ -92,7 +98,7 @@ function wrapBunClient(client: BunCronClient): PostgresCronSql {
|
|
|
92
98
|
return { changes: 0 };
|
|
93
99
|
},
|
|
94
100
|
async begin(fn) {
|
|
95
|
-
return client
|
|
101
|
+
return withPinnedPostgres(client, (tx) => fn(wrapBunClient(tx)));
|
|
96
102
|
},
|
|
97
103
|
async close() {
|
|
98
104
|
await client.close?.();
|
|
@@ -365,10 +371,8 @@ export async function createPostgresCronStore(
|
|
|
365
371
|
const sql =
|
|
366
372
|
options.sql ??
|
|
367
373
|
wrapBunClient(
|
|
368
|
-
options.client ??
|
|
369
|
-
(
|
|
370
|
-
options.url ?? process.env.DATABASE_URL ?? "postgres://localhost:5432/oke",
|
|
371
|
-
) as unknown as BunCronClient),
|
|
374
|
+
(options.client ??
|
|
375
|
+
sharedPostgresClient(resolvePostgresUrl(options.url))) as PostgresClientLike,
|
|
372
376
|
);
|
|
373
377
|
|
|
374
378
|
await ensureSchema(sql);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ConfigEnv } from "../config/index.ts";
|
|
9
9
|
import type { InstanceRow, InstanceStore } from "../kernel/instances.ts";
|
|
10
|
-
import { toPostgresParams } from "./postgres.ts";
|
|
10
|
+
import { resolvePostgresUrl, sharedPostgresClient, toPostgresParams } from "./postgres.ts";
|
|
11
11
|
|
|
12
12
|
/** Minimal SQL surface for the postgres instance store. */
|
|
13
13
|
export interface PostgresInstanceSql {
|
|
@@ -40,13 +40,13 @@ const UPSERT_SQL = `INSERT INTO oke_instances (id, started_at, heartbeat_at, lea
|
|
|
40
40
|
function wrapBunClient(client: BunInstanceClient): PostgresInstanceSql {
|
|
41
41
|
return {
|
|
42
42
|
async query(sql, params = []) {
|
|
43
|
-
const pg = toPostgresParams(sql);
|
|
43
|
+
const pg = toPostgresParams(sql, params);
|
|
44
44
|
const result = await client.unsafe(pg, [...params]);
|
|
45
45
|
if (Array.isArray(result)) return result as Record<string, unknown>[];
|
|
46
46
|
return Array.from(result as ArrayLike<Record<string, unknown>>);
|
|
47
47
|
},
|
|
48
48
|
async exec(sql, params = []) {
|
|
49
|
-
const pg = toPostgresParams(sql);
|
|
49
|
+
const pg = toPostgresParams(sql, params);
|
|
50
50
|
const result = await client.unsafe(pg, [...params]);
|
|
51
51
|
if (
|
|
52
52
|
result &&
|
|
@@ -109,9 +109,7 @@ export async function createPostgresInstanceStore(
|
|
|
109
109
|
options.sql ??
|
|
110
110
|
wrapBunClient(
|
|
111
111
|
options.client ??
|
|
112
|
-
(
|
|
113
|
-
options.url ?? process.env.DATABASE_URL ?? "postgres://localhost:5432/oke",
|
|
114
|
-
) as unknown as BunInstanceClient),
|
|
112
|
+
(sharedPostgresClient(resolvePostgresUrl(options.url)) as unknown as BunInstanceClient),
|
|
115
113
|
);
|
|
116
114
|
|
|
117
115
|
await ensureSchema(sql);
|
|
@@ -14,7 +14,13 @@ import {
|
|
|
14
14
|
type JournalRun,
|
|
15
15
|
type JournalStore,
|
|
16
16
|
} from "../kernel/journal.ts";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
resolvePostgresUrl,
|
|
19
|
+
sharedPostgresClient,
|
|
20
|
+
toPostgresParams,
|
|
21
|
+
withPinnedPostgres,
|
|
22
|
+
type PostgresClientLike,
|
|
23
|
+
} from "./postgres.ts";
|
|
18
24
|
|
|
19
25
|
/** Row shape in `oke_journal_runs` (lease columns mirror `oke_crons`). */
|
|
20
26
|
interface JournalDbRow {
|
|
@@ -87,16 +93,16 @@ export interface BunJournalClient {
|
|
|
87
93
|
close?(options?: { timeout?: number }): Promise<void>;
|
|
88
94
|
}
|
|
89
95
|
|
|
90
|
-
function wrapBunClient(client:
|
|
96
|
+
function wrapBunClient(client: PostgresClientLike): PostgresJournalSql {
|
|
91
97
|
const api: PostgresJournalSql = {
|
|
92
98
|
async query(sql, params = []) {
|
|
93
|
-
const pg = toPostgresParams(sql);
|
|
99
|
+
const pg = toPostgresParams(sql, params);
|
|
94
100
|
const result = await client.unsafe(pg, [...params]);
|
|
95
101
|
if (Array.isArray(result)) return result as Record<string, unknown>[];
|
|
96
102
|
return Array.from(result as ArrayLike<Record<string, unknown>>);
|
|
97
103
|
},
|
|
98
104
|
async exec(sql, params = []) {
|
|
99
|
-
const pg = toPostgresParams(sql);
|
|
105
|
+
const pg = toPostgresParams(sql, params);
|
|
100
106
|
const result = await client.unsafe(pg, [...params]);
|
|
101
107
|
if (
|
|
102
108
|
result &&
|
|
@@ -110,7 +116,7 @@ function wrapBunClient(client: BunJournalClient): PostgresJournalSql {
|
|
|
110
116
|
return { changes: 0 };
|
|
111
117
|
},
|
|
112
118
|
async begin(fn) {
|
|
113
|
-
return client
|
|
119
|
+
return withPinnedPostgres(client, (tx) => fn(wrapBunClient(tx)));
|
|
114
120
|
},
|
|
115
121
|
async close() {
|
|
116
122
|
await client.close?.();
|
|
@@ -437,10 +443,8 @@ export async function createPostgresJournalStore(
|
|
|
437
443
|
const sql =
|
|
438
444
|
options.sql ??
|
|
439
445
|
wrapBunClient(
|
|
440
|
-
options.client ??
|
|
441
|
-
(
|
|
442
|
-
options.url ?? process.env.DATABASE_URL ?? "postgres://localhost:5432/oke",
|
|
443
|
-
) as unknown as BunJournalClient),
|
|
446
|
+
(options.client ??
|
|
447
|
+
sharedPostgresClient(resolvePostgresUrl(options.url))) as PostgresClientLike,
|
|
444
448
|
);
|
|
445
449
|
|
|
446
450
|
await ensureSchema(sql);
|
|
@@ -2,7 +2,13 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
import {
|
|
3
3
|
buildAlterPolicySql,
|
|
4
4
|
buildCreatePolicySql,
|
|
5
|
+
buildRlsIdentityPreludeSql,
|
|
6
|
+
emitPgPolicySource,
|
|
7
|
+
formatPolicyRole,
|
|
8
|
+
OKE_RLS_HELPER_STATEMENTS,
|
|
9
|
+
OKE_RLS_ROLE,
|
|
5
10
|
parseSqlPolicySpec,
|
|
11
|
+
rlsScopesJson,
|
|
6
12
|
sqlPolicyRowId,
|
|
7
13
|
} from "./pg-rls.ts";
|
|
8
14
|
|
|
@@ -36,6 +42,22 @@ describe("parseSqlPolicySpec", () => {
|
|
|
36
42
|
expect(spec.withCheck).toBe("true");
|
|
37
43
|
});
|
|
38
44
|
|
|
45
|
+
test("accepts Drizzle as/to/for aliases", () => {
|
|
46
|
+
const spec = parseSqlPolicySpec({
|
|
47
|
+
name: "gate_read",
|
|
48
|
+
table: "bookings",
|
|
49
|
+
for: "select",
|
|
50
|
+
as: "permissive",
|
|
51
|
+
to: "current_user",
|
|
52
|
+
using: "oke.gate() = 'member'",
|
|
53
|
+
});
|
|
54
|
+
expect(spec.command).toBe("SELECT");
|
|
55
|
+
expect(spec.behavior).toBe("PERMISSIVE");
|
|
56
|
+
expect(spec.roles).toEqual(["current_user"]);
|
|
57
|
+
expect(buildCreatePolicySql(spec)).toContain("TO current_user");
|
|
58
|
+
expect(buildCreatePolicySql(spec)).not.toContain('"current_user"');
|
|
59
|
+
});
|
|
60
|
+
|
|
39
61
|
test("rejects stacked statements in USING", () => {
|
|
40
62
|
expect(() =>
|
|
41
63
|
parseSqlPolicySpec({
|
|
@@ -69,3 +91,68 @@ describe("buildAlterPolicySql", () => {
|
|
|
69
91
|
).toThrow("invalid USING");
|
|
70
92
|
});
|
|
71
93
|
});
|
|
94
|
+
|
|
95
|
+
describe("formatPolicyRole", () => {
|
|
96
|
+
test("leaves Drizzle special roles unquoted", () => {
|
|
97
|
+
expect(formatPolicyRole("public")).toBe("public");
|
|
98
|
+
expect(formatPolicyRole("current_user")).toBe("current_user");
|
|
99
|
+
expect(formatPolicyRole("member")).toBe('"member"');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("emitPgPolicySource", () => {
|
|
104
|
+
test("emits drizzle pgPolicy with oke helpers", () => {
|
|
105
|
+
const spec = parseSqlPolicySpec({
|
|
106
|
+
name: "gate_member_select",
|
|
107
|
+
table: "bookings",
|
|
108
|
+
command: "SELECT",
|
|
109
|
+
using: "oke.gate() = 'member'",
|
|
110
|
+
});
|
|
111
|
+
expect(emitPgPolicySource(spec)).toContain('pgPolicy("gate_member_select"');
|
|
112
|
+
expect(emitPgPolicySource(spec)).toContain('for: "select"');
|
|
113
|
+
expect(emitPgPolicySource(spec)).toContain("oke.gate() = 'member'");
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("rlsScopesJson", () => {
|
|
118
|
+
test("sorts unique scopes", () => {
|
|
119
|
+
expect(rlsScopesJson(["booking:create", "member", "member"])).toBe(
|
|
120
|
+
'["booking:create","member"]',
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("buildRlsIdentityPreludeSql", () => {
|
|
126
|
+
test("emits one statement per exec — never a multi-command batch", () => {
|
|
127
|
+
const stmts = buildRlsIdentityPreludeSql({
|
|
128
|
+
gate: "member",
|
|
129
|
+
userId: "alice",
|
|
130
|
+
scopes: ["member"],
|
|
131
|
+
});
|
|
132
|
+
expect(stmts.map((s) => s.sql)).toEqual([
|
|
133
|
+
`SET LOCAL ROLE ${OKE_RLS_ROLE}`,
|
|
134
|
+
"SET LOCAL row_security = on",
|
|
135
|
+
"SELECT set_config('oke.gate', ?, true), set_config('oke.user', ?, true), set_config('oke.scopes', ?, true)",
|
|
136
|
+
]);
|
|
137
|
+
for (const stmt of stmts) {
|
|
138
|
+
expect(stmt.sql.includes(";")).toBe(false);
|
|
139
|
+
}
|
|
140
|
+
expect(stmts[2]?.params).toEqual(["member", "alice", '["member"]']);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe("OKE_RLS_HELPER_STATEMENTS", () => {
|
|
145
|
+
test("has_scope uses jsonb_exists so ? is not a placeholder", () => {
|
|
146
|
+
const hasScope = OKE_RLS_HELPER_STATEMENTS.find((s) => s.includes("oke.has_scope"));
|
|
147
|
+
expect(hasScope).toBeDefined();
|
|
148
|
+
expect(hasScope).toContain("jsonb_exists(");
|
|
149
|
+
expect(hasScope).not.toMatch(/jsonb\s+\?/);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("pins search_path to public so schema oke is not the table home", () => {
|
|
153
|
+
expect(OKE_RLS_HELPER_STATEMENTS).toContain("SET search_path TO public, oke");
|
|
154
|
+
expect(
|
|
155
|
+
OKE_RLS_HELPER_STATEMENTS.some((s) => s.includes("ALTER TABLE oke.%I SET SCHEMA public")),
|
|
156
|
+
).toBe(true);
|
|
157
|
+
});
|
|
158
|
+
});
|