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.
Files changed (160) hide show
  1. package/AGENTS.md +4 -4
  2. package/LICENSE +202 -0
  3. package/NOTICE +2 -0
  4. package/README.md +15 -9
  5. package/manifest.v1.schema.json +10 -0
  6. package/package.json +22 -25
  7. package/site/content/docs/ai/skills.mdx +1 -1
  8. package/site/content/docs/client/calling.mdx +27 -19
  9. package/site/content/docs/client/index.mdx +1 -1
  10. package/site/content/docs/elements/channel/email.mdx +1 -1
  11. package/site/content/docs/elements/channel/index.mdx +1 -1
  12. package/site/content/docs/elements/flow/http.mdx +4 -4
  13. package/site/content/docs/elements/flow/index.mdx +3 -3
  14. package/site/content/docs/elements/flow/meta.json +1 -1
  15. package/site/content/docs/elements/gate/tenancy.mdx +1 -1
  16. package/site/content/docs/elements/vault/rotation.mdx +15 -0
  17. package/site/content/docs/plugins/otp.mdx +31 -31
  18. package/site/content/docs/recipes/mailpit.mdx +1 -1
  19. package/site/content/docs/recipes/meilisearch.mdx +1 -1
  20. package/site/content/docs/recipes/pgdog.mdx +1 -1
  21. package/site/content/docs/reference/configuration.mdx +2 -2
  22. package/site/content/docs/reference/errors.mdx +12 -2
  23. package/site/content/docs/reference/fx.mdx +28 -15
  24. package/site/content/docs/reference/idempotency.mdx +192 -0
  25. package/site/content/docs/reference/index.mdx +6 -1
  26. package/site/content/docs/reference/meta.json +2 -1
  27. package/site/content/docs/understand/meta.json +1 -1
  28. package/site/content/docs/{elements/flow → understand}/routing.mdx +11 -10
  29. package/site/content/docs/understand/the-architecture.mdx +14 -12
  30. package/site/content/docs/understand/try-it.mdx +3 -3
  31. package/src/cli/competitor-mention-removal.test.ts +3 -3
  32. package/src/cli/dev.test.ts +105 -2
  33. package/src/cli/dev.ts +37 -2
  34. package/src/cli/docker-cli.test.ts +1 -1
  35. package/src/cli/index.ts +9 -1
  36. package/src/cli/load-config.images.test.ts +10 -10
  37. package/src/cli/load-config.ts +1 -1
  38. package/src/cli/vault-cmd.test.ts +23 -0
  39. package/src/cli/vault-cmd.ts +7 -0
  40. package/src/client/create.ts +31 -27
  41. package/src/client/live.ts +44 -101
  42. package/src/client/sse.ts +20 -66
  43. package/src/client/stream.ts +25 -67
  44. package/src/client/transport.test.ts +225 -0
  45. package/src/client/transport.ts +158 -90
  46. package/src/client/types.ts +29 -3
  47. package/src/client/wire.ts +119 -0
  48. package/src/compiler/aot.ts +3 -32
  49. package/src/compiler/dynamic.ts +13 -11
  50. package/src/compiler/effects-infer.ts +726 -16
  51. package/src/compiler/extract.test.ts +37 -0
  52. package/src/compiler/extract.ts +113 -12
  53. package/src/compiler/fixtures/skyport.expected.json +24 -0
  54. package/src/compiler/fx-follow.test.ts +164 -0
  55. package/src/compiler/fx-index.ts +399 -0
  56. package/src/compiler/interpret.ts +45 -0
  57. package/src/console/server/flows-invoke.test.ts +2 -2
  58. package/src/console/ui-next/dist/assets/{access-page-tIbsiphz.js → access-page-Ze6ckjdd.js} +2 -2
  59. package/src/console/ui-next/dist/assets/agent-disclosure-Bkohc_8X.js +1 -0
  60. package/src/console/ui-next/dist/assets/{cache-glyph-BCC-DxKT.js → cache-glyph-rL0lHxat.js} +1 -1
  61. package/src/console/ui-next/dist/assets/{call-pii-button-jOmYISdc.js → call-pii-button-Deo4PP18.js} +1 -1
  62. package/src/console/ui-next/dist/assets/{collapsible-DC2xNaAb.js → collapsible-CoJ6amHf.js} +1 -1
  63. package/src/console/ui-next/dist/assets/{copy-inline-button-CAYD18cr.js → copy-inline-button-CWG5_ZOh.js} +1 -1
  64. package/src/console/ui-next/dist/assets/{detail-header-DVWNjWjg.js → detail-header-b_ZzIsmU.js} +1 -1
  65. package/src/console/ui-next/dist/assets/{dropdown-menu-4h2LOVXM.js → dropdown-menu-nwXEO1Ac.js} +1 -1
  66. package/src/console/ui-next/dist/assets/duration-tone-BbQ_8z50.js +9 -0
  67. package/src/console/ui-next/dist/assets/element-icons-CwyLpVXz.js +1 -0
  68. package/src/console/ui-next/dist/assets/{explorer-empty-CJs5A-wm.js → explorer-empty-C8sSCqYT.js} +1 -1
  69. package/src/console/ui-next/dist/assets/{flows-page-gT1lsWQK.js → flows-page-D2E7BtKK.js} +1 -1
  70. package/src/console/ui-next/dist/assets/{highlighted-json-C2GZEJNI.js → highlighted-json-CPYBzDh0.js} +1 -1
  71. package/src/console/ui-next/dist/assets/{http-method-BdYjcIrD.js → http-method-DTPevKmM.js} +1 -1
  72. package/src/console/ui-next/dist/assets/index-vTuwmeQz.js +58 -0
  73. package/src/console/ui-next/dist/assets/observability-page-SJTSzHFJ.js +4 -0
  74. package/src/console/ui-next/dist/assets/{replica-lag-C4QdAF7J.js → replica-lag-BM27lXld.js} +3 -3
  75. package/src/console/ui-next/dist/assets/{request-meta-C43DHyld.js → request-meta-BjFT7DNl.js} +1 -1
  76. package/src/console/ui-next/dist/assets/shortcut-keys-CUNegEV4.js +1 -0
  77. package/src/console/ui-next/dist/assets/store-page-Qj9ThRE-.js +41 -0
  78. package/src/console/ui-next/dist/assets/{trace-detail-sheet-Dcpo4Us_.js → trace-detail-sheet-Cmitq3da.js} +2 -2
  79. package/src/console/ui-next/dist/assets/{tree-expand-toggle-CKJTuv43.js → tree-expand-toggle-CbDIB-7x.js} +2 -2
  80. package/src/console/ui-next/dist/assets/units-page-_PuYqFty.js +1 -0
  81. package/src/console/ui-next/dist/assets/{use-vault-list-CT4-gajj.js → use-vault-list-Cs45Czkt.js} +1 -1
  82. package/src/console/ui-next/dist/assets/{vault-page-BwZ9YjTW.js → vault-page-BOMr0go9.js} +2 -2
  83. package/src/console/ui-next/dist/index.html +2 -2
  84. package/src/console/ui-next/src/features/units/detail/flow-contract-panel.tsx +9 -0
  85. package/src/docker/docker.test.ts +13 -13
  86. package/src/docker/images-config.test.ts +12 -12
  87. package/src/docker/stack-id.test.ts +1 -1
  88. package/src/drivers/journal-postgres.ts +168 -3
  89. package/src/drivers/meilisearch.ts +2 -0
  90. package/src/drivers/vault-builtin.test.ts +10 -0
  91. package/src/drivers/vault-builtin.ts +13 -1
  92. package/src/drivers/vault-remote-bag.ts +5 -1
  93. package/src/drivers/vault-types.ts +6 -1
  94. package/src/elements/store/emit-drizzle.ts +2 -2
  95. package/src/elements/store/live-default.test.ts +2 -0
  96. package/src/elements/store/live-http.test.ts +1 -0
  97. package/src/elements/store/resource-list-docs.test.ts +2 -2
  98. package/src/elements/store/resource.test.ts +3 -3
  99. package/src/elements/store/schema-decl.test.ts +1 -0
  100. package/src/elements/store/sql-condition.ts +24 -7
  101. package/src/elements/store/sql-session.ts +20 -4
  102. package/src/elements/store/upsert-app.test.ts +1 -1
  103. package/src/elements/vault/audit.test.ts +137 -0
  104. package/src/elements/vault/audit.ts +106 -13
  105. package/src/elements/vault/boot-chain.ts +9 -1
  106. package/src/elements/vault/builtin-adapter.ts +33 -3
  107. package/src/i18n/catalogs/ar.ts +4 -0
  108. package/src/i18n/catalogs/en.ts +4 -0
  109. package/src/kernel/abort-scope.ts +33 -2
  110. package/src/kernel/app.ts +200 -20
  111. package/src/kernel/auto-cache.test.ts +6 -6
  112. package/src/kernel/boot-bind/vault.ts +1 -0
  113. package/src/kernel/boot.ts +19 -1
  114. package/src/kernel/builtin-errors.ts +8 -0
  115. package/src/kernel/client-descriptor.test.ts +78 -0
  116. package/src/kernel/client-descriptor.ts +24 -0
  117. package/src/kernel/concurrency.test.ts +33 -0
  118. package/src/kernel/effects-stamping.test.ts +2 -2
  119. package/src/kernel/errors-compiler.ts +20 -0
  120. package/src/kernel/errors-text.ts +99 -0
  121. package/src/kernel/errors.ts +90 -138
  122. package/src/kernel/external-effects.test.ts +36 -0
  123. package/src/kernel/flow.ts +24 -0
  124. package/src/kernel/fx-fetch.ts +5 -1
  125. package/src/kernel/fx-sql-handle.ts +305 -0
  126. package/src/kernel/fx.ts +42 -330
  127. package/src/kernel/idempotency-store.ts +578 -0
  128. package/src/kernel/idempotency.test.ts +556 -0
  129. package/src/kernel/idempotency.ts +304 -0
  130. package/src/kernel/index.ts +1 -0
  131. package/src/kernel/journal.ts +56 -0
  132. package/src/kernel/json-result.ts +59 -0
  133. package/src/kernel/project-out.ts +6 -1
  134. package/src/manifest/types.ts +8 -0
  135. package/src/okid-extended.ts +175 -0
  136. package/src/okid-shared.ts +103 -0
  137. package/src/okid.ts +30 -213
  138. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  139. package/src/plugins/otp.test.ts +65 -1
  140. package/src/plugins/otp.ts +59 -7
  141. package/src/release/absolute-regression.test.ts +63 -0
  142. package/src/release/build-lib.ts +9 -0
  143. package/src/release/http-graph.test.ts +25 -0
  144. package/src/release/http-graph.ts +90 -0
  145. package/src/release/index.ts +3 -0
  146. package/src/release/limits.ts +17 -2
  147. package/src/release/measure.ts +105 -14
  148. package/src/test/create-test-app.test.ts +3 -1
  149. package/src/test/create-test-app.ts +40 -0
  150. package/src/test/live-signals.test.ts +1 -0
  151. package/src/test/provisions.integration.test.ts +1 -0
  152. package/src/test/tenant-isolation.test.ts +1 -0
  153. package/src/console/ui-next/dist/assets/agent-disclosure-CSKumwS2.js +0 -1
  154. package/src/console/ui-next/dist/assets/duration-tone-CwoV56jn.js +0 -9
  155. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +0 -1
  156. package/src/console/ui-next/dist/assets/index-Cul17AcV.js +0 -63
  157. package/src/console/ui-next/dist/assets/observability-page-oY9vdYBk.js +0 -4
  158. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +0 -1
  159. package/src/console/ui-next/dist/assets/store-page-CL0D9dOq.js +0 -41
  160. 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
- async function loadRunsWindow(): Promise<typeof import("../runs/window.ts")> {
94
- return import("../runs/window.ts");
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
- /** Brand for {@link JsonResult} (kept internal — flows never construct it). */
394
- export const jsonResultBrand: unique symbol = Symbol.for("oke.json");
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 gatedSqlHandle(decl, async () => {
1621
- const h = await open();
1622
- return h as SqlStoreHandle;
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