okengine 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -4
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +15 -9
- package/manifest.v1.schema.json +10 -0
- package/package.json +22 -25
- package/site/content/docs/ai/skills.mdx +1 -1
- package/site/content/docs/client/calling.mdx +27 -19
- package/site/content/docs/client/index.mdx +1 -1
- package/site/content/docs/elements/channel/email.mdx +1 -1
- package/site/content/docs/elements/channel/index.mdx +1 -1
- package/site/content/docs/elements/flow/http.mdx +4 -4
- package/site/content/docs/elements/flow/index.mdx +3 -3
- package/site/content/docs/elements/flow/meta.json +1 -1
- package/site/content/docs/elements/gate/tenancy.mdx +1 -1
- package/site/content/docs/elements/vault/rotation.mdx +15 -0
- package/site/content/docs/plugins/otp.mdx +31 -31
- package/site/content/docs/recipes/mailpit.mdx +1 -1
- package/site/content/docs/recipes/meilisearch.mdx +1 -1
- package/site/content/docs/recipes/pgdog.mdx +1 -1
- package/site/content/docs/reference/configuration.mdx +2 -2
- package/site/content/docs/reference/errors.mdx +12 -2
- package/site/content/docs/reference/fx.mdx +28 -15
- package/site/content/docs/reference/idempotency.mdx +192 -0
- package/site/content/docs/reference/index.mdx +6 -1
- package/site/content/docs/reference/meta.json +2 -1
- package/site/content/docs/understand/meta.json +1 -1
- package/site/content/docs/{elements/flow → understand}/routing.mdx +11 -10
- package/site/content/docs/understand/the-architecture.mdx +14 -12
- package/site/content/docs/understand/try-it.mdx +3 -3
- package/src/cli/competitor-mention-removal.test.ts +3 -3
- package/src/cli/dev.test.ts +105 -2
- package/src/cli/dev.ts +37 -2
- package/src/cli/docker-cli.test.ts +1 -1
- package/src/cli/index.ts +9 -1
- package/src/cli/load-config.images.test.ts +10 -10
- package/src/cli/load-config.ts +1 -1
- package/src/cli/vault-cmd.test.ts +23 -0
- package/src/cli/vault-cmd.ts +7 -0
- package/src/client/create.ts +31 -27
- package/src/client/live.ts +44 -101
- package/src/client/sse.ts +20 -66
- package/src/client/stream.ts +25 -67
- package/src/client/transport.test.ts +225 -0
- package/src/client/transport.ts +158 -90
- package/src/client/types.ts +29 -3
- package/src/client/wire.ts +119 -0
- package/src/compiler/aot.ts +3 -32
- package/src/compiler/dynamic.ts +13 -11
- package/src/compiler/effects-infer.ts +726 -16
- package/src/compiler/extract.test.ts +37 -0
- package/src/compiler/extract.ts +113 -12
- package/src/compiler/fixtures/skyport.expected.json +24 -0
- package/src/compiler/fx-follow.test.ts +164 -0
- package/src/compiler/fx-index.ts +399 -0
- package/src/compiler/interpret.ts +45 -0
- package/src/console/server/flows-invoke.test.ts +2 -2
- package/src/console/ui-next/dist/assets/{access-page-tIbsiphz.js → access-page-Ze6ckjdd.js} +2 -2
- package/src/console/ui-next/dist/assets/agent-disclosure-Bkohc_8X.js +1 -0
- package/src/console/ui-next/dist/assets/{cache-glyph-BCC-DxKT.js → cache-glyph-rL0lHxat.js} +1 -1
- package/src/console/ui-next/dist/assets/{call-pii-button-jOmYISdc.js → call-pii-button-Deo4PP18.js} +1 -1
- package/src/console/ui-next/dist/assets/{collapsible-DC2xNaAb.js → collapsible-CoJ6amHf.js} +1 -1
- package/src/console/ui-next/dist/assets/{copy-inline-button-CAYD18cr.js → copy-inline-button-CWG5_ZOh.js} +1 -1
- package/src/console/ui-next/dist/assets/{detail-header-DVWNjWjg.js → detail-header-b_ZzIsmU.js} +1 -1
- package/src/console/ui-next/dist/assets/{dropdown-menu-4h2LOVXM.js → dropdown-menu-nwXEO1Ac.js} +1 -1
- package/src/console/ui-next/dist/assets/duration-tone-BbQ_8z50.js +9 -0
- package/src/console/ui-next/dist/assets/element-icons-CwyLpVXz.js +1 -0
- package/src/console/ui-next/dist/assets/{explorer-empty-CJs5A-wm.js → explorer-empty-C8sSCqYT.js} +1 -1
- package/src/console/ui-next/dist/assets/{flows-page-gT1lsWQK.js → flows-page-D2E7BtKK.js} +1 -1
- package/src/console/ui-next/dist/assets/{highlighted-json-C2GZEJNI.js → highlighted-json-CPYBzDh0.js} +1 -1
- package/src/console/ui-next/dist/assets/{http-method-BdYjcIrD.js → http-method-DTPevKmM.js} +1 -1
- package/src/console/ui-next/dist/assets/index-vTuwmeQz.js +58 -0
- package/src/console/ui-next/dist/assets/observability-page-SJTSzHFJ.js +4 -0
- package/src/console/ui-next/dist/assets/{replica-lag-C4QdAF7J.js → replica-lag-BM27lXld.js} +3 -3
- package/src/console/ui-next/dist/assets/{request-meta-C43DHyld.js → request-meta-BjFT7DNl.js} +1 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-CUNegEV4.js +1 -0
- package/src/console/ui-next/dist/assets/store-page-Qj9ThRE-.js +41 -0
- package/src/console/ui-next/dist/assets/{trace-detail-sheet-Dcpo4Us_.js → trace-detail-sheet-Cmitq3da.js} +2 -2
- package/src/console/ui-next/dist/assets/{tree-expand-toggle-CKJTuv43.js → tree-expand-toggle-CbDIB-7x.js} +2 -2
- package/src/console/ui-next/dist/assets/units-page-_PuYqFty.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-CT4-gajj.js → use-vault-list-Cs45Czkt.js} +1 -1
- package/src/console/ui-next/dist/assets/{vault-page-BwZ9YjTW.js → vault-page-BOMr0go9.js} +2 -2
- package/src/console/ui-next/dist/index.html +2 -2
- package/src/console/ui-next/src/features/units/detail/flow-contract-panel.tsx +9 -0
- package/src/docker/docker.test.ts +13 -13
- package/src/docker/images-config.test.ts +12 -12
- package/src/docker/stack-id.test.ts +1 -1
- package/src/drivers/journal-postgres.ts +168 -3
- package/src/drivers/meilisearch.ts +2 -0
- package/src/drivers/vault-builtin.test.ts +10 -0
- package/src/drivers/vault-builtin.ts +13 -1
- package/src/drivers/vault-remote-bag.ts +5 -1
- package/src/drivers/vault-types.ts +6 -1
- package/src/elements/store/emit-drizzle.ts +2 -2
- package/src/elements/store/live-default.test.ts +2 -0
- package/src/elements/store/live-http.test.ts +1 -0
- package/src/elements/store/resource-list-docs.test.ts +2 -2
- package/src/elements/store/resource.test.ts +3 -3
- package/src/elements/store/schema-decl.test.ts +1 -0
- package/src/elements/store/sql-condition.ts +24 -7
- package/src/elements/store/sql-session.ts +20 -4
- package/src/elements/store/upsert-app.test.ts +1 -1
- package/src/elements/vault/audit.test.ts +137 -0
- package/src/elements/vault/audit.ts +106 -13
- package/src/elements/vault/boot-chain.ts +9 -1
- package/src/elements/vault/builtin-adapter.ts +33 -3
- package/src/i18n/catalogs/ar.ts +4 -0
- package/src/i18n/catalogs/en.ts +4 -0
- package/src/kernel/abort-scope.ts +33 -2
- package/src/kernel/app.ts +200 -20
- package/src/kernel/auto-cache.test.ts +6 -6
- package/src/kernel/boot-bind/vault.ts +1 -0
- package/src/kernel/boot.ts +19 -1
- package/src/kernel/builtin-errors.ts +8 -0
- package/src/kernel/client-descriptor.test.ts +78 -0
- package/src/kernel/client-descriptor.ts +24 -0
- package/src/kernel/concurrency.test.ts +33 -0
- package/src/kernel/effects-stamping.test.ts +2 -2
- package/src/kernel/errors-compiler.ts +20 -0
- package/src/kernel/errors-text.ts +99 -0
- package/src/kernel/errors.ts +90 -138
- package/src/kernel/external-effects.test.ts +36 -0
- package/src/kernel/flow.ts +24 -0
- package/src/kernel/fx-fetch.ts +5 -1
- package/src/kernel/fx-sql-handle.ts +305 -0
- package/src/kernel/fx.ts +42 -330
- package/src/kernel/idempotency-store.ts +578 -0
- package/src/kernel/idempotency.test.ts +556 -0
- package/src/kernel/idempotency.ts +304 -0
- package/src/kernel/index.ts +1 -0
- package/src/kernel/journal.ts +56 -0
- package/src/kernel/json-result.ts +59 -0
- package/src/kernel/project-out.ts +6 -1
- package/src/manifest/types.ts +8 -0
- package/src/okid-extended.ts +175 -0
- package/src/okid-shared.ts +103 -0
- package/src/okid.ts +30 -213
- package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
- package/src/plugins/otp.test.ts +65 -1
- package/src/plugins/otp.ts +59 -7
- package/src/release/absolute-regression.test.ts +63 -0
- package/src/release/build-lib.ts +9 -0
- package/src/release/http-graph.test.ts +25 -0
- package/src/release/http-graph.ts +90 -0
- package/src/release/index.ts +3 -0
- package/src/release/limits.ts +17 -2
- package/src/release/measure.ts +105 -14
- package/src/test/create-test-app.test.ts +3 -1
- package/src/test/create-test-app.ts +40 -0
- package/src/test/live-signals.test.ts +1 -0
- package/src/test/provisions.integration.test.ts +1 -0
- package/src/test/tenant-isolation.test.ts +1 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-CSKumwS2.js +0 -1
- package/src/console/ui-next/dist/assets/duration-tone-CwoV56jn.js +0 -9
- package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +0 -1
- package/src/console/ui-next/dist/assets/index-Cul17AcV.js +0 -63
- package/src/console/ui-next/dist/assets/observability-page-oY9vdYBk.js +0 -4
- package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-CL0D9dOq.js +0 -41
- package/src/console/ui-next/dist/assets/units-page-1PlT19ft.js +0 -1
package/src/kernel/fx.ts
CHANGED
|
@@ -9,10 +9,16 @@
|
|
|
9
9
|
* for deterministic tests (§7.6).
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import {
|
|
13
|
+
isJsonResult,
|
|
14
|
+
isJsonStreamResult,
|
|
15
|
+
jsonResultBrand,
|
|
16
|
+
type JsonResult,
|
|
17
|
+
type JsonStreamResult,
|
|
18
|
+
} from "./json-result.ts";
|
|
12
19
|
import type { Effects, ResourceRef, SignalResourceRef } from "../manifest/types.ts";
|
|
13
20
|
import { isMcpToolRef } from "../manifest/mcp-ref.ts";
|
|
14
21
|
import type { QueryPageSpec } from "./list-page.ts";
|
|
15
|
-
import { schemaTableName, sqlTableRef } from "../manifest/sql-resource.ts";
|
|
16
22
|
import type {
|
|
17
23
|
FilesStoreDecl,
|
|
18
24
|
FilesStoreFxHandle,
|
|
@@ -20,8 +26,6 @@ import type {
|
|
|
20
26
|
IndexStoreFxHandle,
|
|
21
27
|
KvStoreDecl,
|
|
22
28
|
KvStoreFxHandle,
|
|
23
|
-
SelectFromBuilder,
|
|
24
|
-
SelectOrderBuilder,
|
|
25
29
|
SqlStoreDecl,
|
|
26
30
|
StoreDecl,
|
|
27
31
|
StoreHandle,
|
|
@@ -30,7 +34,6 @@ import type {
|
|
|
30
34
|
} from "../elements/store.ts";
|
|
31
35
|
import { rlsIdentityFromAuth } from "../elements/store.ts";
|
|
32
36
|
import type { RlsIdentity } from "../drivers/pg-rls.ts";
|
|
33
|
-
import type { SqlRow } from "../drivers/types.ts";
|
|
34
37
|
import type { SignalDecl, SignalRuntime } from "../elements/signal.ts";
|
|
35
38
|
import type { DeadLetter, SignalEmitOptions } from "../drivers/signal-types.ts";
|
|
36
39
|
import type { VaultActor, VaultAdapter, VaultRuntime } from "../elements/vault.ts";
|
|
@@ -59,15 +62,8 @@ import {
|
|
|
59
62
|
touchDryRunStore,
|
|
60
63
|
} from "./dry-run.ts";
|
|
61
64
|
import type { FailFn } from "./errors.ts";
|
|
62
|
-
import { currentAbortSignal, linkAbort } from "./abort-scope.ts";
|
|
63
|
-
import {
|
|
64
|
-
fxAll,
|
|
65
|
-
fxRace,
|
|
66
|
-
fxRetry,
|
|
67
|
-
fxUsing,
|
|
68
|
-
type FxRetryOptions,
|
|
69
|
-
type FxThunk,
|
|
70
|
-
} from "./concurrency.ts";
|
|
65
|
+
import { abortError, currentAbortSignal, linkAbort } from "./abort-scope.ts";
|
|
66
|
+
import type { FxRetryOptions, FxThunk } from "./concurrency.ts";
|
|
71
67
|
import { maskRedactedDeep, Redacted } from "./redacted.ts";
|
|
72
68
|
import type { JournalSession, JournalStepOptions } from "./journal.ts";
|
|
73
69
|
|
|
@@ -90,8 +86,23 @@ function loadFail(): FailFn {
|
|
|
90
86
|
}
|
|
91
87
|
|
|
92
88
|
/** Lazy runs/window helpers — kept off the cold `oke` static graph. */
|
|
93
|
-
|
|
94
|
-
|
|
89
|
+
function loadRunsWindow(): typeof import("../runs/window.ts") {
|
|
90
|
+
const stem = ["win", "dow"].join("");
|
|
91
|
+
try {
|
|
92
|
+
return lazyRequire(`${import.meta.dir}/../runs`, stem);
|
|
93
|
+
} catch {
|
|
94
|
+
return lazyRequire(import.meta.dir, stem);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Driver-backed SQL query builder — off the edge profile until `fx.store` opens SQL. */
|
|
99
|
+
function loadFxSql(): typeof import("./fx-sql-handle.ts") {
|
|
100
|
+
return lazyRequire(import.meta.dir, ["fx", "sql", "handle"].join("-"));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** `fx.all` / `race` / `retry` / `using` — off the edge profile until called. */
|
|
104
|
+
function loadConcurrency(): typeof import("./concurrency.ts") {
|
|
105
|
+
return lazyRequire(import.meta.dir, ["concur", "rency"].join(""));
|
|
95
106
|
}
|
|
96
107
|
|
|
97
108
|
/**
|
|
@@ -390,29 +401,8 @@ export interface FxSearchOptions {
|
|
|
390
401
|
readonly topK?: number;
|
|
391
402
|
}
|
|
392
403
|
|
|
393
|
-
|
|
394
|
-
export
|
|
395
|
-
|
|
396
|
-
/** Carrier from {@link FxJson} — status + body read by the response encoder. */
|
|
397
|
-
export interface JsonResult<T = unknown> {
|
|
398
|
-
readonly [jsonResultBrand]: true;
|
|
399
|
-
readonly status: number;
|
|
400
|
-
readonly value?: T;
|
|
401
|
-
readonly meta?: Record<string, unknown>;
|
|
402
|
-
readonly kind?: undefined;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/** SSE carrier from {@link FxJson.stream} / {@link Fx.live}. */
|
|
406
|
-
export interface JsonStreamResult {
|
|
407
|
-
readonly [jsonResultBrand]: true;
|
|
408
|
-
readonly kind: "stream";
|
|
409
|
-
readonly status: 200;
|
|
410
|
-
readonly chunks: AsyncIterable<unknown>;
|
|
411
|
-
/** Awaited before the 200 SSE body; throws OKE1210 on a missing resume cursor. */
|
|
412
|
-
ready?: () => Promise<void>;
|
|
413
|
-
/** Set by the kernel to commit journal / Runs after the stream settles. */
|
|
414
|
-
finalize?: () => Promise<void>;
|
|
415
|
-
}
|
|
404
|
+
export { isJsonResult, isJsonStreamResult, jsonResultBrand };
|
|
405
|
+
export type { JsonResult, JsonStreamResult };
|
|
416
406
|
|
|
417
407
|
const sseFrameBrand: unique symbol = Symbol.for("oke.sse.frame");
|
|
418
408
|
|
|
@@ -442,26 +432,6 @@ export function isSseFrame(value: unknown): value is SseFrame {
|
|
|
442
432
|
return typeof value === "object" && value !== null && (value as SseFrame)[sseFrameBrand] === true;
|
|
443
433
|
}
|
|
444
434
|
|
|
445
|
-
/** True when `value` is an {@link FxJson} JSON-envelope carrier. */
|
|
446
|
-
export function isJsonResult(value: unknown): value is JsonResult {
|
|
447
|
-
return (
|
|
448
|
-
typeof value === "object" &&
|
|
449
|
-
value !== null &&
|
|
450
|
-
(value as JsonResult)[jsonResultBrand] === true &&
|
|
451
|
-
(value as JsonStreamResult).kind !== "stream"
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/** True when `value` is an SSE stream carrier from {@link FxJson.stream}. */
|
|
456
|
-
export function isJsonStreamResult(value: unknown): value is JsonStreamResult {
|
|
457
|
-
return (
|
|
458
|
-
typeof value === "object" &&
|
|
459
|
-
value !== null &&
|
|
460
|
-
(value as JsonStreamResult)[jsonResultBrand] === true &&
|
|
461
|
-
(value as JsonStreamResult).kind === "stream"
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
435
|
/**
|
|
466
436
|
* Paginated JSON page — `data` is Flow `out`; `meta` is the HTTP pager.
|
|
467
437
|
*
|
|
@@ -1231,6 +1201,8 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1231
1201
|
| EffectExternal
|
|
1232
1202
|
| ((result: T | undefined, error: unknown) => EffectExternal | undefined),
|
|
1233
1203
|
): Promise<T> {
|
|
1204
|
+
const signal = currentAbortSignal();
|
|
1205
|
+
if (signal.aborted) throw abortError(signal.reason);
|
|
1234
1206
|
capability.assert(kind, resource);
|
|
1235
1207
|
const execute = () => recordEffect(ledger, kind, resource, now, body, externalOf);
|
|
1236
1208
|
if (journal) {
|
|
@@ -1293,271 +1265,6 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1293
1265
|
};
|
|
1294
1266
|
}
|
|
1295
1267
|
|
|
1296
|
-
/**
|
|
1297
|
-
* Lazy, capability-gated proxy over a driver-backed {@link SqlStoreHandle}.
|
|
1298
|
-
*
|
|
1299
|
-
* @param decl - Store declaration
|
|
1300
|
-
* @param open - Opens (and caches) the runtime handle
|
|
1301
|
-
*/
|
|
1302
|
-
function gatedSqlHandle(decl: StoreDecl, open: () => Promise<SqlStoreHandle>): SqlStoreHandle {
|
|
1303
|
-
const ref = decl.ref as `sql:${string}`;
|
|
1304
|
-
let cached: SqlStoreHandle | undefined;
|
|
1305
|
-
const ensure = async (): Promise<SqlStoreHandle> => {
|
|
1306
|
-
if (!cached) cached = await open();
|
|
1307
|
-
return cached;
|
|
1308
|
-
};
|
|
1309
|
-
/** Driver-backed SQL has no dry-run transaction — refuse writes. */
|
|
1310
|
-
const refuseDryRunWrite = (): void => {
|
|
1311
|
-
if (isDryRun()) {
|
|
1312
|
-
throw new DryRunWriteIsolationError(
|
|
1313
|
-
`Driver-backed store "${ref}" cannot isolate writes during dry-run; dry-run refused rather than risk a double-write.`,
|
|
1314
|
-
);
|
|
1315
|
-
}
|
|
1316
|
-
};
|
|
1317
|
-
/**
|
|
1318
|
-
* Gate a table-scoped SQL operation. Prefers the precise `sql:<table>`
|
|
1319
|
-
* ref (matches what the compiler's AST inference derives from the same
|
|
1320
|
-
* call site — {@link "../manifest/sql-resource.ts"}); falls back to the
|
|
1321
|
-
* store-level ref when the table ref isn't declared — every flow that
|
|
1322
|
-
* hand-declared the older `effects: { writes: ["sql:<store>"] }`
|
|
1323
|
-
* convention (every existing template, `upsert-app.test.ts`, …) must
|
|
1324
|
-
* keep working unchanged. Ledger / journal record whichever ref the
|
|
1325
|
-
* capability check actually matched, not always the coarser one.
|
|
1326
|
-
*
|
|
1327
|
-
* @param kind - Effect kind
|
|
1328
|
-
* @param table - Table argument passed to a `SqlStoreHandle` method
|
|
1329
|
-
* @param body - Work to run under the gate
|
|
1330
|
-
*/
|
|
1331
|
-
const gatedTable = <T>(
|
|
1332
|
-
kind: Parameters<CapabilityToken["assert"]>[0],
|
|
1333
|
-
table: unknown,
|
|
1334
|
-
body: () => T | Promise<T>,
|
|
1335
|
-
): Promise<T> => {
|
|
1336
|
-
const externalOf = (): EffectExternal | undefined => cached?.external;
|
|
1337
|
-
const name = schemaTableName(table);
|
|
1338
|
-
if (name !== undefined) {
|
|
1339
|
-
const perTable = sqlTableRef(name);
|
|
1340
|
-
if (perTable !== ref && capability.allows(kind, perTable)) {
|
|
1341
|
-
return gated(kind, perTable, body, externalOf);
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
return gated(kind, ref, body, externalOf);
|
|
1345
|
-
};
|
|
1346
|
-
|
|
1347
|
-
return {
|
|
1348
|
-
ref,
|
|
1349
|
-
get routedRole() {
|
|
1350
|
-
return cached?.routedRole ?? "primary";
|
|
1351
|
-
},
|
|
1352
|
-
get driverId() {
|
|
1353
|
-
return cached?.driverId ?? "memory";
|
|
1354
|
-
},
|
|
1355
|
-
select: ((columns?: unknown) => {
|
|
1356
|
-
return {
|
|
1357
|
-
from(table: unknown) {
|
|
1358
|
-
const run = (plan: {
|
|
1359
|
-
where?: unknown;
|
|
1360
|
-
orders?: readonly unknown[];
|
|
1361
|
-
limit?: number;
|
|
1362
|
-
offset?: number;
|
|
1363
|
-
}): Promise<SqlRow[]> =>
|
|
1364
|
-
gatedTable("read", table, async () => {
|
|
1365
|
-
const h = await ensure();
|
|
1366
|
-
const from = h.select(columns).from(table) as SelectFromBuilder;
|
|
1367
|
-
const filtered = plan.where === undefined ? from : from.where(plan.where);
|
|
1368
|
-
const ordered =
|
|
1369
|
-
plan.orders === undefined ? filtered : filtered.orderBy(...plan.orders);
|
|
1370
|
-
if (plan.offset !== undefined) return ordered.offset(plan.offset);
|
|
1371
|
-
return plan.limit === undefined ? ordered : ordered.limit(plan.limit);
|
|
1372
|
-
});
|
|
1373
|
-
|
|
1374
|
-
const tail = (plan: {
|
|
1375
|
-
where?: unknown;
|
|
1376
|
-
orders?: readonly unknown[];
|
|
1377
|
-
}): SelectOrderBuilder => ({
|
|
1378
|
-
limit(n) {
|
|
1379
|
-
return run({ ...plan, limit: n });
|
|
1380
|
-
},
|
|
1381
|
-
offset(n) {
|
|
1382
|
-
return run({ ...plan, offset: n });
|
|
1383
|
-
},
|
|
1384
|
-
then(onfulfilled, onrejected) {
|
|
1385
|
-
return run(plan).then(onfulfilled, onrejected);
|
|
1386
|
-
},
|
|
1387
|
-
});
|
|
1388
|
-
|
|
1389
|
-
return {
|
|
1390
|
-
where(where: unknown) {
|
|
1391
|
-
return {
|
|
1392
|
-
...tail({ where }),
|
|
1393
|
-
orderBy: (...orders: readonly unknown[]) => tail({ where, orders }),
|
|
1394
|
-
};
|
|
1395
|
-
},
|
|
1396
|
-
orderBy: (...orders: readonly unknown[]) => tail({ orders }),
|
|
1397
|
-
limit(n: number) {
|
|
1398
|
-
return run({ limit: n });
|
|
1399
|
-
},
|
|
1400
|
-
offset(n: number) {
|
|
1401
|
-
return run({ offset: n });
|
|
1402
|
-
},
|
|
1403
|
-
then(
|
|
1404
|
-
onfulfilled: (value: SqlRow[]) => unknown,
|
|
1405
|
-
onrejected?: (reason: unknown) => unknown,
|
|
1406
|
-
) {
|
|
1407
|
-
return run({}).then(onfulfilled, onrejected);
|
|
1408
|
-
},
|
|
1409
|
-
};
|
|
1410
|
-
},
|
|
1411
|
-
};
|
|
1412
|
-
}) as SqlStoreHandle["select"],
|
|
1413
|
-
insert(table) {
|
|
1414
|
-
return {
|
|
1415
|
-
values(row) {
|
|
1416
|
-
const runExecute = () =>
|
|
1417
|
-
gatedTable("write", table, async () => {
|
|
1418
|
-
refuseDryRunWrite();
|
|
1419
|
-
const h = await ensure();
|
|
1420
|
-
await h.insert(table).values(row).execute();
|
|
1421
|
-
});
|
|
1422
|
-
return {
|
|
1423
|
-
returning() {
|
|
1424
|
-
return gatedTable("write", table, async () => {
|
|
1425
|
-
refuseDryRunWrite();
|
|
1426
|
-
const h = await ensure();
|
|
1427
|
-
return h.insert(table).values(row).returning();
|
|
1428
|
-
});
|
|
1429
|
-
},
|
|
1430
|
-
execute: runExecute,
|
|
1431
|
-
then(onfulfilled, onrejected) {
|
|
1432
|
-
return runExecute().then(onfulfilled, onrejected);
|
|
1433
|
-
},
|
|
1434
|
-
};
|
|
1435
|
-
},
|
|
1436
|
-
};
|
|
1437
|
-
},
|
|
1438
|
-
update(table) {
|
|
1439
|
-
return {
|
|
1440
|
-
set(row) {
|
|
1441
|
-
return {
|
|
1442
|
-
where(where) {
|
|
1443
|
-
return gatedTable("write", table, async () => {
|
|
1444
|
-
refuseDryRunWrite();
|
|
1445
|
-
const h = await ensure();
|
|
1446
|
-
return h.update(table).set(row).where(where);
|
|
1447
|
-
});
|
|
1448
|
-
},
|
|
1449
|
-
};
|
|
1450
|
-
},
|
|
1451
|
-
};
|
|
1452
|
-
},
|
|
1453
|
-
findById(table, id) {
|
|
1454
|
-
return gatedTable("read", table, async () => {
|
|
1455
|
-
const h = await ensure();
|
|
1456
|
-
return h.findById(table, id);
|
|
1457
|
-
});
|
|
1458
|
-
},
|
|
1459
|
-
delete(table: Parameters<SqlStoreHandle["delete"]>[0], id?: string) {
|
|
1460
|
-
if (id !== undefined) {
|
|
1461
|
-
return gatedTable("write", table, async () => {
|
|
1462
|
-
refuseDryRunWrite();
|
|
1463
|
-
const h = await ensure();
|
|
1464
|
-
return h.delete(table, id);
|
|
1465
|
-
});
|
|
1466
|
-
}
|
|
1467
|
-
return {
|
|
1468
|
-
where(where: unknown) {
|
|
1469
|
-
return gatedTable("write", table, async () => {
|
|
1470
|
-
refuseDryRunWrite();
|
|
1471
|
-
const h = await ensure();
|
|
1472
|
-
return h.delete(table).where(where);
|
|
1473
|
-
});
|
|
1474
|
-
},
|
|
1475
|
-
};
|
|
1476
|
-
},
|
|
1477
|
-
exists(table, idOrWhere) {
|
|
1478
|
-
return gatedTable("read", table, async () => {
|
|
1479
|
-
const h = await ensure();
|
|
1480
|
-
return h.exists(table, idOrWhere);
|
|
1481
|
-
});
|
|
1482
|
-
},
|
|
1483
|
-
upsert(table, matchOn, values, upsertOptions) {
|
|
1484
|
-
return gatedTable("write", table, async () => {
|
|
1485
|
-
refuseDryRunWrite();
|
|
1486
|
-
const h = await ensure();
|
|
1487
|
-
return h.upsert(table, matchOn, values, upsertOptions);
|
|
1488
|
-
});
|
|
1489
|
-
},
|
|
1490
|
-
increment(table, id, column, by) {
|
|
1491
|
-
return gatedTable("write", table, async () => {
|
|
1492
|
-
refuseDryRunWrite();
|
|
1493
|
-
const h = await ensure();
|
|
1494
|
-
return h.increment(table, id, column, by);
|
|
1495
|
-
});
|
|
1496
|
-
},
|
|
1497
|
-
raw(sql, params) {
|
|
1498
|
-
return gated("read", ref, async () => {
|
|
1499
|
-
const h = await ensure();
|
|
1500
|
-
return h.raw(sql, params);
|
|
1501
|
-
});
|
|
1502
|
-
},
|
|
1503
|
-
count(table, where) {
|
|
1504
|
-
return gatedTable("read", table, async () => {
|
|
1505
|
-
const h = await ensure();
|
|
1506
|
-
return h.count(table, where);
|
|
1507
|
-
});
|
|
1508
|
-
},
|
|
1509
|
-
page(table, pageOptions) {
|
|
1510
|
-
return gatedTable("read", table, async () => {
|
|
1511
|
-
const h = await ensure();
|
|
1512
|
-
return h.page(table, pageOptions);
|
|
1513
|
-
});
|
|
1514
|
-
},
|
|
1515
|
-
search(table, searchOptions) {
|
|
1516
|
-
return gatedTable("read", table, async () => {
|
|
1517
|
-
const h = await ensure();
|
|
1518
|
-
const result = await h.search(table, searchOptions);
|
|
1519
|
-
// Optional rerank via fx.ask — only when explicitly requested.
|
|
1520
|
-
if (
|
|
1521
|
-
searchOptions.rerank &&
|
|
1522
|
-
typeof searchOptions.rerank === "object" &&
|
|
1523
|
-
searchOptions.rerank.model
|
|
1524
|
-
) {
|
|
1525
|
-
const model = searchOptions.rerank.model;
|
|
1526
|
-
const pk = "id";
|
|
1527
|
-
const docs = result.data.map((row) => ({
|
|
1528
|
-
id: String(row[pk] ?? ""),
|
|
1529
|
-
text: Object.values(row)
|
|
1530
|
-
.filter((v) => typeof v === "string")
|
|
1531
|
-
.join("\n"),
|
|
1532
|
-
score: 0,
|
|
1533
|
-
}));
|
|
1534
|
-
const out = (await fx.ask(model, {
|
|
1535
|
-
query: searchOptions.query,
|
|
1536
|
-
docs,
|
|
1537
|
-
})) as { rankedIds?: string[] };
|
|
1538
|
-
if (out.rankedIds && out.rankedIds.length > 0) {
|
|
1539
|
-
const byId = new Map(result.data.map((r) => [String(r[pk] ?? ""), r]));
|
|
1540
|
-
return {
|
|
1541
|
-
...result,
|
|
1542
|
-
data: out.rankedIds
|
|
1543
|
-
.map((id) => byId.get(id))
|
|
1544
|
-
.filter((r): r is NonNullable<typeof r> => r !== undefined),
|
|
1545
|
-
};
|
|
1546
|
-
}
|
|
1547
|
-
}
|
|
1548
|
-
return result;
|
|
1549
|
-
});
|
|
1550
|
-
},
|
|
1551
|
-
ensureTable(table) {
|
|
1552
|
-
return gatedTable("write", table, async () => {
|
|
1553
|
-
refuseDryRunWrite();
|
|
1554
|
-
const h = await ensure();
|
|
1555
|
-
return h.ensureTable(table);
|
|
1556
|
-
});
|
|
1557
|
-
},
|
|
1558
|
-
} as SqlStoreHandle;
|
|
1559
|
-
}
|
|
1560
|
-
|
|
1561
1268
|
function loadFxTenantStore(): {
|
|
1562
1269
|
kv: (
|
|
1563
1270
|
mode: "in" | "out",
|
|
@@ -1617,9 +1324,12 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1617
1324
|
};
|
|
1618
1325
|
|
|
1619
1326
|
if (decl.facet === "sql") {
|
|
1620
|
-
return
|
|
1621
|
-
|
|
1622
|
-
|
|
1327
|
+
return loadFxSql().createGatedSqlHandle({
|
|
1328
|
+
decl,
|
|
1329
|
+
open: async () => (await open()) as SqlStoreHandle,
|
|
1330
|
+
gated,
|
|
1331
|
+
capability,
|
|
1332
|
+
ask: (model, input) => fx.ask(model, input),
|
|
1623
1333
|
});
|
|
1624
1334
|
}
|
|
1625
1335
|
|
|
@@ -1803,6 +1513,8 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1803
1513
|
* @param body - Work to run under the gate
|
|
1804
1514
|
*/
|
|
1805
1515
|
async function gatedSecret<T>(name: string, body: () => T | Promise<T>): Promise<T> {
|
|
1516
|
+
const signal = currentAbortSignal();
|
|
1517
|
+
if (signal.aborted) throw abortError(signal.reason);
|
|
1806
1518
|
capability.assert("secret", name);
|
|
1807
1519
|
const timestamp = now();
|
|
1808
1520
|
const t0 = performance.now();
|
|
@@ -2292,16 +2004,16 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
2292
2004
|
return rlsInvokeContext().rls;
|
|
2293
2005
|
},
|
|
2294
2006
|
all(thunks) {
|
|
2295
|
-
return fxAll(thunks);
|
|
2007
|
+
return loadConcurrency().fxAll(thunks);
|
|
2296
2008
|
},
|
|
2297
2009
|
race(thunks) {
|
|
2298
|
-
return fxRace(thunks);
|
|
2010
|
+
return loadConcurrency().fxRace(thunks);
|
|
2299
2011
|
},
|
|
2300
2012
|
retry(fn, opts) {
|
|
2301
|
-
return fxRetry(fn, opts);
|
|
2013
|
+
return loadConcurrency().fxRetry(fn, opts);
|
|
2302
2014
|
},
|
|
2303
2015
|
using(acquire, release, use) {
|
|
2304
|
-
return fxUsing(acquire, release, use);
|
|
2016
|
+
return loadConcurrency().fxUsing(acquire, release, use);
|
|
2305
2017
|
},
|
|
2306
2018
|
};
|
|
2307
2019
|
|