okengine 0.20.0 → 0.21.1
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 -7
- package/site/content/docs/client/calling.mdx +117 -29
- package/site/content/docs/client/index.mdx +3 -3
- package/site/content/docs/elements/flow/http.mdx +18 -15
- package/site/content/docs/elements/flow/index.mdx +26 -18
- package/site/content/docs/elements/gate/tenancy.mdx +1 -1
- package/site/content/docs/elements/store/files.mdx +11 -5
- package/site/content/docs/elements/store/index.mdx +2 -3
- package/site/content/docs/elements/store/kv.mdx +8 -2
- package/site/content/docs/elements/store/sql.mdx +9 -6
- package/site/content/docs/elements/vault/index.mdx +11 -8
- package/site/content/docs/elements/vault/secrets.mdx +6 -3
- package/site/content/docs/index.mdx +1 -1
- package/site/content/docs/recipes/rustfs.mdx +1 -1
- package/site/content/docs/reference/configuration.mdx +1 -1
- package/site/content/docs/reference/errors.mdx +199 -25
- package/site/content/docs/reference/fx.mdx +6 -1
- package/site/content/docs/understand/the-architecture.mdx +2 -2
- package/site/content/docs/understand/try-it.mdx +758 -25
- package/src/cli/dev-app-runner.ts +2 -1
- package/src/cli/dev.test.ts +105 -2
- package/src/cli/dev.ts +37 -2
- package/src/cli/start.ts +2 -1
- package/src/client/create.ts +14 -27
- package/src/client/explain.test.ts +252 -0
- package/src/client/explain.ts +272 -0
- package/src/client/live.test.ts +44 -0
- package/src/client/live.ts +44 -101
- package/src/client/notes-contract.test.ts +10 -0
- package/src/client/sse.ts +26 -68
- package/src/client/stream.ts +25 -67
- package/src/client/transport.test.ts +67 -0
- package/src/client/transport.ts +51 -112
- package/src/client/types.ts +17 -6
- package/src/client/wire.ts +119 -0
- package/src/client-react/live-resource.ts +6 -2
- package/src/compiler/aot.ts +3 -32
- package/src/compiler/dynamic.ts +13 -11
- package/src/compiler/interpret.ts +45 -0
- package/src/compiler/response.ts +17 -27
- package/src/console/server/invoke-user-flow.test.ts +8 -2
- package/src/console/server/invoke-user-flow.ts +12 -18
- package/src/console/server/security.gate.test.ts +1 -1
- package/src/console/ui-next/dist/assets/{access-page-DFLu0wTA.js → access-page-Bgt9bq2r.js} +1 -1
- package/src/console/ui-next/dist/assets/{agent-disclosure-DGscxaF5.js → agent-disclosure-B43CXZZR.js} +1 -1
- package/src/console/ui-next/dist/assets/{cache-glyph-BGmRZk7d.js → cache-glyph-92uM5MO7.js} +1 -1
- package/src/console/ui-next/dist/assets/{call-pii-button--feUYxvG.js → call-pii-button-DtGVPtcs.js} +1 -1
- package/src/console/ui-next/dist/assets/{collapsible-JWvpaiGY.js → collapsible-DN7l6zmC.js} +1 -1
- package/src/console/ui-next/dist/assets/{duration-tone-D9yCJG4n.js → duration-tone-BmIR9FV8.js} +1 -1
- package/src/console/ui-next/dist/assets/{flows-page-Bs6MD9GB.js → flows-page-BMHs-IzK.js} +1 -1
- package/src/console/ui-next/dist/assets/{highlighted-json-xH8MrEnv.js → highlighted-json-BlAEVgNW.js} +1 -1
- package/src/console/ui-next/dist/assets/{http-method-C4vB6ZIw.js → http-method-BDf7OAHv.js} +1 -1
- package/src/console/ui-next/dist/assets/{index-yTCY4AcS.js → index-BKpaes3n.js} +3 -3
- package/src/console/ui-next/dist/assets/{observability-page-BxJ3R6dU.js → observability-page-WnVLI-0j.js} +1 -1
- package/src/console/ui-next/dist/assets/{replica-lag-QRKB_IE8.js → replica-lag-B3GLNVfF.js} +1 -1
- package/src/console/ui-next/dist/assets/{request-meta-DqZ-fMu5.js → request-meta-DMbnAe3f.js} +1 -1
- package/src/console/ui-next/dist/assets/{store-page-Dixb6L7a.js → store-page-KvFDingJ.js} +1 -1
- package/src/console/ui-next/dist/assets/{trace-detail-sheet-CazhjtiU.js → trace-detail-sheet-Htk8Cm9t.js} +1 -1
- package/src/console/ui-next/dist/assets/{tree-expand-toggle-DlnqYKfr.js → tree-expand-toggle-CFMPWX4f.js} +1 -1
- package/src/console/ui-next/dist/assets/{units-page-BXTLjU2-.js → units-page-C4NdNuxP.js} +1 -1
- package/src/console/ui-next/dist/assets/{vault-page-39KR__bc.js → vault-page-CBYl0LW_.js} +1 -1
- package/src/console/ui-next/dist/index.html +1 -1
- package/src/docker/docker.test.ts +3 -3
- package/src/docker/images-config.test.ts +4 -4
- package/src/docker/stack-id.test.ts +1 -1
- package/src/elements/store/files-errors.test.ts +149 -0
- package/src/elements/store/files-errors.ts +189 -0
- package/src/elements/store/kv-errors.test.ts +98 -0
- package/src/elements/store/kv-errors.ts +139 -0
- package/src/elements/store/resource.ts +11 -7
- package/src/elements/store/runtime.ts +18 -13
- package/src/elements/store/sql-errors.test.ts +197 -0
- package/src/elements/store/sql-errors.ts +294 -0
- package/src/elements/store/sql-session.test.ts +52 -0
- package/src/elements/store/sql-session.ts +26 -4
- package/src/elements/store/store-errors.ts +47 -0
- package/src/http.ts +9 -1
- package/src/i18n/catalogs/ar.ts +18 -0
- package/src/i18n/catalogs/en.ts +18 -0
- package/src/index.ts +9 -1
- package/src/kernel/app.ts +23 -4
- package/src/kernel/builtin-errors.test.ts +117 -0
- package/src/kernel/builtin-errors.ts +129 -0
- package/src/kernel/call.test.ts +182 -0
- package/src/kernel/client-descriptor.test.ts +78 -0
- package/src/kernel/client-descriptor.ts +23 -0
- package/src/kernel/errors-text.ts +99 -0
- package/src/kernel/errors-vault.ts +16 -0
- package/src/kernel/errors.registry.test.ts +7 -0
- package/src/kernel/errors.ts +184 -159
- package/src/kernel/fail-helpers.ts +34 -0
- package/src/kernel/fx-sql-handle.ts +305 -0
- package/src/kernel/fx.test.ts +8 -0
- package/src/kernel/fx.ts +49 -335
- package/src/kernel/index.ts +12 -1
- package/src/kernel/json-result.ts +59 -0
- package/src/kernel/project-out.ts +6 -1
- package/src/okid-extended.ts +175 -0
- package/src/okid-shared.ts +103 -0
- package/src/okid.ts +30 -213
- package/src/release/build-lib.ts +9 -0
- package/src/runtime/dev-request-log.ts +29 -11
- package/src/term.test.ts +76 -0
- package/src/term.ts +166 -3
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";
|
|
@@ -58,16 +61,9 @@ import {
|
|
|
58
61
|
recordWouldHaveFired,
|
|
59
62
|
touchDryRunStore,
|
|
60
63
|
} from "./dry-run.ts";
|
|
61
|
-
import
|
|
64
|
+
import type { FailFn } from "./errors.ts";
|
|
62
65
|
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";
|
|
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
|
|
|
@@ -82,9 +78,31 @@ import { okid } from "../okid.ts";
|
|
|
82
78
|
import { lazyRequire } from "./lazy-require.ts";
|
|
83
79
|
import type { AppMessageKey, MessageValues } from "../i18n/types.ts";
|
|
84
80
|
|
|
81
|
+
function loadFail(): FailFn {
|
|
82
|
+
return lazyRequire<typeof import("./fail-helpers.ts")>(
|
|
83
|
+
import.meta.dir,
|
|
84
|
+
["fail", "helpers"].join("-"),
|
|
85
|
+
).fail;
|
|
86
|
+
}
|
|
87
|
+
|
|
85
88
|
/** Lazy runs/window helpers — kept off the cold `oke` static graph. */
|
|
86
|
-
|
|
87
|
-
|
|
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(""));
|
|
88
106
|
}
|
|
89
107
|
|
|
90
108
|
/**
|
|
@@ -383,29 +401,8 @@ export interface FxSearchOptions {
|
|
|
383
401
|
readonly topK?: number;
|
|
384
402
|
}
|
|
385
403
|
|
|
386
|
-
|
|
387
|
-
export
|
|
388
|
-
|
|
389
|
-
/** Carrier from {@link FxJson} — status + body read by the response encoder. */
|
|
390
|
-
export interface JsonResult<T = unknown> {
|
|
391
|
-
readonly [jsonResultBrand]: true;
|
|
392
|
-
readonly status: number;
|
|
393
|
-
readonly value?: T;
|
|
394
|
-
readonly meta?: Record<string, unknown>;
|
|
395
|
-
readonly kind?: undefined;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/** SSE carrier from {@link FxJson.stream} / {@link Fx.live}. */
|
|
399
|
-
export interface JsonStreamResult {
|
|
400
|
-
readonly [jsonResultBrand]: true;
|
|
401
|
-
readonly kind: "stream";
|
|
402
|
-
readonly status: 200;
|
|
403
|
-
readonly chunks: AsyncIterable<unknown>;
|
|
404
|
-
/** Awaited before the 200 SSE body; throws OKE1210 on a missing resume cursor. */
|
|
405
|
-
ready?: () => Promise<void>;
|
|
406
|
-
/** Set by the kernel to commit journal / Runs after the stream settles. */
|
|
407
|
-
finalize?: () => Promise<void>;
|
|
408
|
-
}
|
|
404
|
+
export { isJsonResult, isJsonStreamResult, jsonResultBrand };
|
|
405
|
+
export type { JsonResult, JsonStreamResult };
|
|
409
406
|
|
|
410
407
|
const sseFrameBrand: unique symbol = Symbol.for("oke.sse.frame");
|
|
411
408
|
|
|
@@ -435,26 +432,6 @@ export function isSseFrame(value: unknown): value is SseFrame {
|
|
|
435
432
|
return typeof value === "object" && value !== null && (value as SseFrame)[sseFrameBrand] === true;
|
|
436
433
|
}
|
|
437
434
|
|
|
438
|
-
/** True when `value` is an {@link FxJson} JSON-envelope carrier. */
|
|
439
|
-
export function isJsonResult(value: unknown): value is JsonResult {
|
|
440
|
-
return (
|
|
441
|
-
typeof value === "object" &&
|
|
442
|
-
value !== null &&
|
|
443
|
-
(value as JsonResult)[jsonResultBrand] === true &&
|
|
444
|
-
(value as JsonStreamResult).kind !== "stream"
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/** True when `value` is an SSE stream carrier from {@link FxJson.stream}. */
|
|
449
|
-
export function isJsonStreamResult(value: unknown): value is JsonStreamResult {
|
|
450
|
-
return (
|
|
451
|
-
typeof value === "object" &&
|
|
452
|
-
value !== null &&
|
|
453
|
-
(value as JsonStreamResult)[jsonResultBrand] === true &&
|
|
454
|
-
(value as JsonStreamResult).kind === "stream"
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
435
|
/**
|
|
459
436
|
* Paginated JSON page — `data` is Flow `out`; `meta` is the HTTP pager.
|
|
460
437
|
*
|
|
@@ -884,11 +861,10 @@ export interface Fx {
|
|
|
884
861
|
/**
|
|
885
862
|
* Flow-boundary failure value (does not throw).
|
|
886
863
|
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
889
|
-
* @param opts - Optional message
|
|
864
|
+
* Built-in helpers (`fx.fail.notFound`, `fx.fail.forbidden`, …) need no
|
|
865
|
+
* `errors:` declaration. Domain codes still use `fx.fail("OutOfStock", data)`.
|
|
890
866
|
*/
|
|
891
|
-
fail
|
|
867
|
+
readonly fail: FailFn;
|
|
892
868
|
/** JSON response helpers (status + Stripe-style envelope). */
|
|
893
869
|
readonly json: FxJson;
|
|
894
870
|
/**
|
|
@@ -1287,271 +1263,6 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1287
1263
|
};
|
|
1288
1264
|
}
|
|
1289
1265
|
|
|
1290
|
-
/**
|
|
1291
|
-
* Lazy, capability-gated proxy over a driver-backed {@link SqlStoreHandle}.
|
|
1292
|
-
*
|
|
1293
|
-
* @param decl - Store declaration
|
|
1294
|
-
* @param open - Opens (and caches) the runtime handle
|
|
1295
|
-
*/
|
|
1296
|
-
function gatedSqlHandle(decl: StoreDecl, open: () => Promise<SqlStoreHandle>): SqlStoreHandle {
|
|
1297
|
-
const ref = decl.ref as `sql:${string}`;
|
|
1298
|
-
let cached: SqlStoreHandle | undefined;
|
|
1299
|
-
const ensure = async (): Promise<SqlStoreHandle> => {
|
|
1300
|
-
if (!cached) cached = await open();
|
|
1301
|
-
return cached;
|
|
1302
|
-
};
|
|
1303
|
-
/** Driver-backed SQL has no dry-run transaction — refuse writes. */
|
|
1304
|
-
const refuseDryRunWrite = (): void => {
|
|
1305
|
-
if (isDryRun()) {
|
|
1306
|
-
throw new DryRunWriteIsolationError(
|
|
1307
|
-
`Driver-backed store "${ref}" cannot isolate writes during dry-run; dry-run refused rather than risk a double-write.`,
|
|
1308
|
-
);
|
|
1309
|
-
}
|
|
1310
|
-
};
|
|
1311
|
-
/**
|
|
1312
|
-
* Gate a table-scoped SQL operation. Prefers the precise `sql:<table>`
|
|
1313
|
-
* ref (matches what the compiler's AST inference derives from the same
|
|
1314
|
-
* call site — {@link "../manifest/sql-resource.ts"}); falls back to the
|
|
1315
|
-
* store-level ref when the table ref isn't declared — every flow that
|
|
1316
|
-
* hand-declared the older `effects: { writes: ["sql:<store>"] }`
|
|
1317
|
-
* convention (every existing template, `upsert-app.test.ts`, …) must
|
|
1318
|
-
* keep working unchanged. Ledger / journal record whichever ref the
|
|
1319
|
-
* capability check actually matched, not always the coarser one.
|
|
1320
|
-
*
|
|
1321
|
-
* @param kind - Effect kind
|
|
1322
|
-
* @param table - Table argument passed to a `SqlStoreHandle` method
|
|
1323
|
-
* @param body - Work to run under the gate
|
|
1324
|
-
*/
|
|
1325
|
-
const gatedTable = <T>(
|
|
1326
|
-
kind: Parameters<CapabilityToken["assert"]>[0],
|
|
1327
|
-
table: unknown,
|
|
1328
|
-
body: () => T | Promise<T>,
|
|
1329
|
-
): Promise<T> => {
|
|
1330
|
-
const externalOf = (): EffectExternal | undefined => cached?.external;
|
|
1331
|
-
const name = schemaTableName(table);
|
|
1332
|
-
if (name !== undefined) {
|
|
1333
|
-
const perTable = sqlTableRef(name);
|
|
1334
|
-
if (perTable !== ref && capability.allows(kind, perTable)) {
|
|
1335
|
-
return gated(kind, perTable, body, externalOf);
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
return gated(kind, ref, body, externalOf);
|
|
1339
|
-
};
|
|
1340
|
-
|
|
1341
|
-
return {
|
|
1342
|
-
ref,
|
|
1343
|
-
get routedRole() {
|
|
1344
|
-
return cached?.routedRole ?? "primary";
|
|
1345
|
-
},
|
|
1346
|
-
get driverId() {
|
|
1347
|
-
return cached?.driverId ?? "memory";
|
|
1348
|
-
},
|
|
1349
|
-
select: ((columns?: unknown) => {
|
|
1350
|
-
return {
|
|
1351
|
-
from(table: unknown) {
|
|
1352
|
-
const run = (plan: {
|
|
1353
|
-
where?: unknown;
|
|
1354
|
-
orders?: readonly unknown[];
|
|
1355
|
-
limit?: number;
|
|
1356
|
-
offset?: number;
|
|
1357
|
-
}): Promise<SqlRow[]> =>
|
|
1358
|
-
gatedTable("read", table, async () => {
|
|
1359
|
-
const h = await ensure();
|
|
1360
|
-
const from = h.select(columns).from(table) as SelectFromBuilder;
|
|
1361
|
-
const filtered = plan.where === undefined ? from : from.where(plan.where);
|
|
1362
|
-
const ordered =
|
|
1363
|
-
plan.orders === undefined ? filtered : filtered.orderBy(...plan.orders);
|
|
1364
|
-
if (plan.offset !== undefined) return ordered.offset(plan.offset);
|
|
1365
|
-
return plan.limit === undefined ? ordered : ordered.limit(plan.limit);
|
|
1366
|
-
});
|
|
1367
|
-
|
|
1368
|
-
const tail = (plan: {
|
|
1369
|
-
where?: unknown;
|
|
1370
|
-
orders?: readonly unknown[];
|
|
1371
|
-
}): SelectOrderBuilder => ({
|
|
1372
|
-
limit(n) {
|
|
1373
|
-
return run({ ...plan, limit: n });
|
|
1374
|
-
},
|
|
1375
|
-
offset(n) {
|
|
1376
|
-
return run({ ...plan, offset: n });
|
|
1377
|
-
},
|
|
1378
|
-
then(onfulfilled, onrejected) {
|
|
1379
|
-
return run(plan).then(onfulfilled, onrejected);
|
|
1380
|
-
},
|
|
1381
|
-
});
|
|
1382
|
-
|
|
1383
|
-
return {
|
|
1384
|
-
where(where: unknown) {
|
|
1385
|
-
return {
|
|
1386
|
-
...tail({ where }),
|
|
1387
|
-
orderBy: (...orders: readonly unknown[]) => tail({ where, orders }),
|
|
1388
|
-
};
|
|
1389
|
-
},
|
|
1390
|
-
orderBy: (...orders: readonly unknown[]) => tail({ orders }),
|
|
1391
|
-
limit(n: number) {
|
|
1392
|
-
return run({ limit: n });
|
|
1393
|
-
},
|
|
1394
|
-
offset(n: number) {
|
|
1395
|
-
return run({ offset: n });
|
|
1396
|
-
},
|
|
1397
|
-
then(
|
|
1398
|
-
onfulfilled: (value: SqlRow[]) => unknown,
|
|
1399
|
-
onrejected?: (reason: unknown) => unknown,
|
|
1400
|
-
) {
|
|
1401
|
-
return run({}).then(onfulfilled, onrejected);
|
|
1402
|
-
},
|
|
1403
|
-
};
|
|
1404
|
-
},
|
|
1405
|
-
};
|
|
1406
|
-
}) as SqlStoreHandle["select"],
|
|
1407
|
-
insert(table) {
|
|
1408
|
-
return {
|
|
1409
|
-
values(row) {
|
|
1410
|
-
const runExecute = () =>
|
|
1411
|
-
gatedTable("write", table, async () => {
|
|
1412
|
-
refuseDryRunWrite();
|
|
1413
|
-
const h = await ensure();
|
|
1414
|
-
await h.insert(table).values(row).execute();
|
|
1415
|
-
});
|
|
1416
|
-
return {
|
|
1417
|
-
returning() {
|
|
1418
|
-
return gatedTable("write", table, async () => {
|
|
1419
|
-
refuseDryRunWrite();
|
|
1420
|
-
const h = await ensure();
|
|
1421
|
-
return h.insert(table).values(row).returning();
|
|
1422
|
-
});
|
|
1423
|
-
},
|
|
1424
|
-
execute: runExecute,
|
|
1425
|
-
then(onfulfilled, onrejected) {
|
|
1426
|
-
return runExecute().then(onfulfilled, onrejected);
|
|
1427
|
-
},
|
|
1428
|
-
};
|
|
1429
|
-
},
|
|
1430
|
-
};
|
|
1431
|
-
},
|
|
1432
|
-
update(table) {
|
|
1433
|
-
return {
|
|
1434
|
-
set(row) {
|
|
1435
|
-
return {
|
|
1436
|
-
where(where) {
|
|
1437
|
-
return gatedTable("write", table, async () => {
|
|
1438
|
-
refuseDryRunWrite();
|
|
1439
|
-
const h = await ensure();
|
|
1440
|
-
return h.update(table).set(row).where(where);
|
|
1441
|
-
});
|
|
1442
|
-
},
|
|
1443
|
-
};
|
|
1444
|
-
},
|
|
1445
|
-
};
|
|
1446
|
-
},
|
|
1447
|
-
findById(table, id) {
|
|
1448
|
-
return gatedTable("read", table, async () => {
|
|
1449
|
-
const h = await ensure();
|
|
1450
|
-
return h.findById(table, id);
|
|
1451
|
-
});
|
|
1452
|
-
},
|
|
1453
|
-
delete(table: Parameters<SqlStoreHandle["delete"]>[0], id?: string) {
|
|
1454
|
-
if (id !== undefined) {
|
|
1455
|
-
return gatedTable("write", table, async () => {
|
|
1456
|
-
refuseDryRunWrite();
|
|
1457
|
-
const h = await ensure();
|
|
1458
|
-
return h.delete(table, id);
|
|
1459
|
-
});
|
|
1460
|
-
}
|
|
1461
|
-
return {
|
|
1462
|
-
where(where: unknown) {
|
|
1463
|
-
return gatedTable("write", table, async () => {
|
|
1464
|
-
refuseDryRunWrite();
|
|
1465
|
-
const h = await ensure();
|
|
1466
|
-
return h.delete(table).where(where);
|
|
1467
|
-
});
|
|
1468
|
-
},
|
|
1469
|
-
};
|
|
1470
|
-
},
|
|
1471
|
-
exists(table, idOrWhere) {
|
|
1472
|
-
return gatedTable("read", table, async () => {
|
|
1473
|
-
const h = await ensure();
|
|
1474
|
-
return h.exists(table, idOrWhere);
|
|
1475
|
-
});
|
|
1476
|
-
},
|
|
1477
|
-
upsert(table, matchOn, values, upsertOptions) {
|
|
1478
|
-
return gatedTable("write", table, async () => {
|
|
1479
|
-
refuseDryRunWrite();
|
|
1480
|
-
const h = await ensure();
|
|
1481
|
-
return h.upsert(table, matchOn, values, upsertOptions);
|
|
1482
|
-
});
|
|
1483
|
-
},
|
|
1484
|
-
increment(table, id, column, by) {
|
|
1485
|
-
return gatedTable("write", table, async () => {
|
|
1486
|
-
refuseDryRunWrite();
|
|
1487
|
-
const h = await ensure();
|
|
1488
|
-
return h.increment(table, id, column, by);
|
|
1489
|
-
});
|
|
1490
|
-
},
|
|
1491
|
-
raw(sql, params) {
|
|
1492
|
-
return gated("read", ref, async () => {
|
|
1493
|
-
const h = await ensure();
|
|
1494
|
-
return h.raw(sql, params);
|
|
1495
|
-
});
|
|
1496
|
-
},
|
|
1497
|
-
count(table, where) {
|
|
1498
|
-
return gatedTable("read", table, async () => {
|
|
1499
|
-
const h = await ensure();
|
|
1500
|
-
return h.count(table, where);
|
|
1501
|
-
});
|
|
1502
|
-
},
|
|
1503
|
-
page(table, pageOptions) {
|
|
1504
|
-
return gatedTable("read", table, async () => {
|
|
1505
|
-
const h = await ensure();
|
|
1506
|
-
return h.page(table, pageOptions);
|
|
1507
|
-
});
|
|
1508
|
-
},
|
|
1509
|
-
search(table, searchOptions) {
|
|
1510
|
-
return gatedTable("read", table, async () => {
|
|
1511
|
-
const h = await ensure();
|
|
1512
|
-
const result = await h.search(table, searchOptions);
|
|
1513
|
-
// Optional rerank via fx.ask — only when explicitly requested.
|
|
1514
|
-
if (
|
|
1515
|
-
searchOptions.rerank &&
|
|
1516
|
-
typeof searchOptions.rerank === "object" &&
|
|
1517
|
-
searchOptions.rerank.model
|
|
1518
|
-
) {
|
|
1519
|
-
const model = searchOptions.rerank.model;
|
|
1520
|
-
const pk = "id";
|
|
1521
|
-
const docs = result.data.map((row) => ({
|
|
1522
|
-
id: String(row[pk] ?? ""),
|
|
1523
|
-
text: Object.values(row)
|
|
1524
|
-
.filter((v) => typeof v === "string")
|
|
1525
|
-
.join("\n"),
|
|
1526
|
-
score: 0,
|
|
1527
|
-
}));
|
|
1528
|
-
const out = (await fx.ask(model, {
|
|
1529
|
-
query: searchOptions.query,
|
|
1530
|
-
docs,
|
|
1531
|
-
})) as { rankedIds?: string[] };
|
|
1532
|
-
if (out.rankedIds && out.rankedIds.length > 0) {
|
|
1533
|
-
const byId = new Map(result.data.map((r) => [String(r[pk] ?? ""), r]));
|
|
1534
|
-
return {
|
|
1535
|
-
...result,
|
|
1536
|
-
data: out.rankedIds
|
|
1537
|
-
.map((id) => byId.get(id))
|
|
1538
|
-
.filter((r): r is NonNullable<typeof r> => r !== undefined),
|
|
1539
|
-
};
|
|
1540
|
-
}
|
|
1541
|
-
}
|
|
1542
|
-
return result;
|
|
1543
|
-
});
|
|
1544
|
-
},
|
|
1545
|
-
ensureTable(table) {
|
|
1546
|
-
return gatedTable("write", table, async () => {
|
|
1547
|
-
refuseDryRunWrite();
|
|
1548
|
-
const h = await ensure();
|
|
1549
|
-
return h.ensureTable(table);
|
|
1550
|
-
});
|
|
1551
|
-
},
|
|
1552
|
-
} as SqlStoreHandle;
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
1266
|
function loadFxTenantStore(): {
|
|
1556
1267
|
kv: (
|
|
1557
1268
|
mode: "in" | "out",
|
|
@@ -1611,9 +1322,12 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1611
1322
|
};
|
|
1612
1323
|
|
|
1613
1324
|
if (decl.facet === "sql") {
|
|
1614
|
-
return
|
|
1615
|
-
|
|
1616
|
-
|
|
1325
|
+
return loadFxSql().createGatedSqlHandle({
|
|
1326
|
+
decl,
|
|
1327
|
+
open: async () => (await open()) as SqlStoreHandle,
|
|
1328
|
+
gated,
|
|
1329
|
+
capability,
|
|
1330
|
+
ask: (model, input) => fx.ask(model, input),
|
|
1617
1331
|
});
|
|
1618
1332
|
}
|
|
1619
1333
|
|
|
@@ -2246,7 +1960,7 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
2246
1960
|
operator,
|
|
2247
1961
|
principal,
|
|
2248
1962
|
tenant,
|
|
2249
|
-
fail,
|
|
1963
|
+
fail: loadFail(),
|
|
2250
1964
|
json: {
|
|
2251
1965
|
ok<T>(value: T, opts?: { readonly meta?: Record<string, unknown> }): JsonResult<T> {
|
|
2252
1966
|
return {
|
|
@@ -2286,16 +2000,16 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
2286
2000
|
return rlsInvokeContext().rls;
|
|
2287
2001
|
},
|
|
2288
2002
|
all(thunks) {
|
|
2289
|
-
return fxAll(thunks);
|
|
2003
|
+
return loadConcurrency().fxAll(thunks);
|
|
2290
2004
|
},
|
|
2291
2005
|
race(thunks) {
|
|
2292
|
-
return fxRace(thunks);
|
|
2006
|
+
return loadConcurrency().fxRace(thunks);
|
|
2293
2007
|
},
|
|
2294
2008
|
retry(fn, opts) {
|
|
2295
|
-
return fxRetry(fn, opts);
|
|
2009
|
+
return loadConcurrency().fxRetry(fn, opts);
|
|
2296
2010
|
},
|
|
2297
2011
|
using(acquire, release, use) {
|
|
2298
|
-
return fxUsing(acquire, release, use);
|
|
2012
|
+
return loadConcurrency().fxUsing(acquire, release, use);
|
|
2299
2013
|
},
|
|
2300
2014
|
};
|
|
2301
2015
|
|
package/src/kernel/index.ts
CHANGED
|
@@ -106,14 +106,15 @@ export {
|
|
|
106
106
|
type RunTelemetry,
|
|
107
107
|
} from "./run-telemetry.ts";
|
|
108
108
|
|
|
109
|
+
export { fail } from "./fail-helpers.ts";
|
|
109
110
|
export {
|
|
110
|
-
fail,
|
|
111
111
|
formatOkeMessage,
|
|
112
112
|
lookupOkeError,
|
|
113
113
|
OKE_ERROR_RANGES,
|
|
114
114
|
OKE_ERRORS,
|
|
115
115
|
OkeError,
|
|
116
116
|
throwOke,
|
|
117
|
+
type FailFn,
|
|
117
118
|
type FailOptions,
|
|
118
119
|
type FlowErrorValue,
|
|
119
120
|
type FlowFailure,
|
|
@@ -122,6 +123,16 @@ export {
|
|
|
122
123
|
type OkeErrorDomain,
|
|
123
124
|
type OkeErrorParams,
|
|
124
125
|
} from "./errors.ts";
|
|
126
|
+
export {
|
|
127
|
+
BUILTIN_ERROR_STATUS,
|
|
128
|
+
httpStatusForFailure,
|
|
129
|
+
statusForBuiltinError,
|
|
130
|
+
type BuiltinErrorBag,
|
|
131
|
+
type BuiltinErrorCode,
|
|
132
|
+
type BuiltinErrorMap,
|
|
133
|
+
type BuiltinValidationIssue,
|
|
134
|
+
type DatabaseErrorReason,
|
|
135
|
+
} from "./builtin-errors.ts";
|
|
125
136
|
|
|
126
137
|
export {
|
|
127
138
|
flow,
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fx.json` carriers — brand and guards, with no `fx` / Store graph.
|
|
3
|
+
*
|
|
4
|
+
* `project-out` and the HTTP encoder need these on the cold-start path.
|
|
5
|
+
* Importing them from `fx.ts` would evaluate the Store barrel (Zod, hybrid
|
|
6
|
+
* search) before the server listens.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Brand for {@link JsonResult} (kept internal — flows never construct it). */
|
|
10
|
+
export const jsonResultBrand: unique symbol = Symbol.for("oke.json");
|
|
11
|
+
|
|
12
|
+
/** Carrier from `fx.json` — status + body read by the response encoder. */
|
|
13
|
+
export interface JsonResult<T = unknown> {
|
|
14
|
+
readonly [jsonResultBrand]: true;
|
|
15
|
+
readonly status: number;
|
|
16
|
+
readonly value?: T;
|
|
17
|
+
readonly meta?: Record<string, unknown>;
|
|
18
|
+
readonly kind?: undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** SSE carrier from `fx.json.stream` / `fx.live`. */
|
|
22
|
+
export interface JsonStreamResult {
|
|
23
|
+
readonly [jsonResultBrand]: true;
|
|
24
|
+
readonly kind: "stream";
|
|
25
|
+
readonly status: 200;
|
|
26
|
+
readonly chunks: AsyncIterable<unknown>;
|
|
27
|
+
/** Awaited before the 200 SSE body; throws OKE1210 on a missing resume cursor. */
|
|
28
|
+
ready?: () => Promise<void>;
|
|
29
|
+
/** Set by the kernel to commit journal / Runs after the stream settles. */
|
|
30
|
+
finalize?: () => Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* True when `value` is an `fx.json` envelope carrier.
|
|
35
|
+
*
|
|
36
|
+
* @param value - Unknown handler output
|
|
37
|
+
*/
|
|
38
|
+
export function isJsonResult(value: unknown): value is JsonResult {
|
|
39
|
+
return (
|
|
40
|
+
typeof value === "object" &&
|
|
41
|
+
value !== null &&
|
|
42
|
+
(value as JsonResult)[jsonResultBrand] === true &&
|
|
43
|
+
(value as JsonStreamResult).kind !== "stream"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* True when `value` is an SSE stream carrier from `fx.json.stream`.
|
|
49
|
+
*
|
|
50
|
+
* @param value - Unknown handler output
|
|
51
|
+
*/
|
|
52
|
+
export function isJsonStreamResult(value: unknown): value is JsonStreamResult {
|
|
53
|
+
return (
|
|
54
|
+
typeof value === "object" &&
|
|
55
|
+
value !== null &&
|
|
56
|
+
(value as JsonStreamResult)[jsonResultBrand] === true &&
|
|
57
|
+
(value as JsonStreamResult).kind === "stream"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -12,7 +12,12 @@
|
|
|
12
12
|
|
|
13
13
|
import type { SchemaInput } from "../validation/standard-schema.ts";
|
|
14
14
|
import { validate } from "../validation/standard-schema.ts";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
isJsonResult,
|
|
17
|
+
isJsonStreamResult,
|
|
18
|
+
jsonResultBrand,
|
|
19
|
+
type JsonResult,
|
|
20
|
+
} from "./json-result.ts";
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
23
|
* Project `output` through `schema` when the exposure declared `out`.
|