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
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Driver-backed `fx.store` SQL handle — lazy chunk.
|
|
3
|
+
*
|
|
4
|
+
* The in-memory stub stays on the edge profile. This query builder loads
|
|
5
|
+
* the first time a SQL facet is opened.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { SqlRow } from "../drivers/types.ts";
|
|
9
|
+
import type {
|
|
10
|
+
SelectFromBuilder,
|
|
11
|
+
SelectOrderBuilder,
|
|
12
|
+
SqlStoreHandle,
|
|
13
|
+
StoreDecl,
|
|
14
|
+
} from "../elements/store.ts";
|
|
15
|
+
import { schemaTableName, sqlTableRef } from "../manifest/sql-resource.ts";
|
|
16
|
+
import type { CapabilityToken } from "./capability.ts";
|
|
17
|
+
import { DryRunWriteIsolationError, isDryRun } from "./dry-run.ts";
|
|
18
|
+
import type { EffectExternal } from "./effects.ts";
|
|
19
|
+
|
|
20
|
+
/** Capability-gated effect runner from {@link createFx}. */
|
|
21
|
+
type Gated = <T>(
|
|
22
|
+
kind: Parameters<CapabilityToken["assert"]>[0],
|
|
23
|
+
resource: string,
|
|
24
|
+
body: () => T | Promise<T>,
|
|
25
|
+
externalOf?:
|
|
26
|
+
| EffectExternal
|
|
27
|
+
| ((result: T | undefined, error: unknown) => EffectExternal | undefined),
|
|
28
|
+
) => Promise<T>;
|
|
29
|
+
|
|
30
|
+
/** Inputs for {@link createGatedSqlHandle}. */
|
|
31
|
+
export interface GatedSqlHandleOptions {
|
|
32
|
+
/** Store declaration (`facet: "sql"`). */
|
|
33
|
+
readonly decl: StoreDecl;
|
|
34
|
+
/** Opens (and caches) the runtime handle. */
|
|
35
|
+
readonly open: () => Promise<SqlStoreHandle>;
|
|
36
|
+
/** Flow `gated` helper. */
|
|
37
|
+
readonly gated: Gated;
|
|
38
|
+
/** Capability token for per-table ref checks. */
|
|
39
|
+
readonly capability: CapabilityToken;
|
|
40
|
+
/** `fx.ask` — optional search rerank. */
|
|
41
|
+
readonly ask: (model: string, input: unknown) => Promise<unknown>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Lazy, capability-gated proxy over a driver-backed {@link SqlStoreHandle}.
|
|
46
|
+
*
|
|
47
|
+
* @param options - Declaration, opener, gate, and rerank hook
|
|
48
|
+
*/
|
|
49
|
+
export function createGatedSqlHandle(options: GatedSqlHandleOptions): SqlStoreHandle {
|
|
50
|
+
const { decl, open, gated, capability, ask } = options;
|
|
51
|
+
const ref = decl.ref as `sql:${string}`;
|
|
52
|
+
let cached: SqlStoreHandle | undefined;
|
|
53
|
+
const ensure = async (): Promise<SqlStoreHandle> => {
|
|
54
|
+
if (!cached) cached = await open();
|
|
55
|
+
return cached;
|
|
56
|
+
};
|
|
57
|
+
/** Driver-backed SQL has no dry-run transaction — refuse writes. */
|
|
58
|
+
const refuseDryRunWrite = (): void => {
|
|
59
|
+
if (isDryRun()) {
|
|
60
|
+
throw new DryRunWriteIsolationError(
|
|
61
|
+
`Driver-backed store "${ref}" cannot isolate writes during dry-run; dry-run refused rather than risk a double-write.`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Gate a table-scoped SQL operation. Prefers the precise `sql:<table>`
|
|
67
|
+
* ref (matches what the compiler's AST inference derives from the same
|
|
68
|
+
* call site); falls back to the store-level ref when the table ref isn't
|
|
69
|
+
* declared — flows that hand-declared `effects: { writes: ["sql:<store>"] }`
|
|
70
|
+
* keep working. Ledger / journal record whichever ref the capability check
|
|
71
|
+
* actually matched.
|
|
72
|
+
*
|
|
73
|
+
* @param kind - Effect kind
|
|
74
|
+
* @param table - Table argument passed to a `SqlStoreHandle` method
|
|
75
|
+
* @param body - Work to run under the gate
|
|
76
|
+
*/
|
|
77
|
+
const gatedTable = <T>(
|
|
78
|
+
kind: Parameters<CapabilityToken["assert"]>[0],
|
|
79
|
+
table: unknown,
|
|
80
|
+
body: () => T | Promise<T>,
|
|
81
|
+
): Promise<T> => {
|
|
82
|
+
const externalOf = (): EffectExternal | undefined => cached?.external;
|
|
83
|
+
const name = schemaTableName(table);
|
|
84
|
+
if (name !== undefined) {
|
|
85
|
+
const perTable = sqlTableRef(name);
|
|
86
|
+
if (perTable !== ref && capability.allows(kind, perTable)) {
|
|
87
|
+
return gated(kind, perTable, body, externalOf);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return gated(kind, ref, body, externalOf);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
ref,
|
|
95
|
+
get routedRole() {
|
|
96
|
+
return cached?.routedRole ?? "primary";
|
|
97
|
+
},
|
|
98
|
+
get driverId() {
|
|
99
|
+
return cached?.driverId ?? "memory";
|
|
100
|
+
},
|
|
101
|
+
select: ((columns?: unknown) => {
|
|
102
|
+
return {
|
|
103
|
+
from(table: unknown) {
|
|
104
|
+
const run = (plan: {
|
|
105
|
+
where?: unknown;
|
|
106
|
+
orders?: readonly unknown[];
|
|
107
|
+
limit?: number;
|
|
108
|
+
offset?: number;
|
|
109
|
+
}): Promise<SqlRow[]> =>
|
|
110
|
+
gatedTable("read", table, async () => {
|
|
111
|
+
const h = await ensure();
|
|
112
|
+
const from = h.select(columns).from(table) as SelectFromBuilder;
|
|
113
|
+
const filtered = plan.where === undefined ? from : from.where(plan.where);
|
|
114
|
+
const ordered =
|
|
115
|
+
plan.orders === undefined ? filtered : filtered.orderBy(...plan.orders);
|
|
116
|
+
if (plan.offset !== undefined) return ordered.offset(plan.offset);
|
|
117
|
+
return plan.limit === undefined ? ordered : ordered.limit(plan.limit);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const tail = (plan: {
|
|
121
|
+
where?: unknown;
|
|
122
|
+
orders?: readonly unknown[];
|
|
123
|
+
}): SelectOrderBuilder => ({
|
|
124
|
+
limit(n) {
|
|
125
|
+
return run({ ...plan, limit: n });
|
|
126
|
+
},
|
|
127
|
+
offset(n) {
|
|
128
|
+
return run({ ...plan, offset: n });
|
|
129
|
+
},
|
|
130
|
+
then(onfulfilled, onrejected) {
|
|
131
|
+
return run(plan).then(onfulfilled, onrejected);
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
where(where: unknown) {
|
|
137
|
+
return {
|
|
138
|
+
...tail({ where }),
|
|
139
|
+
orderBy: (...orders: readonly unknown[]) => tail({ where, orders }),
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
orderBy: (...orders: readonly unknown[]) => tail({ orders }),
|
|
143
|
+
limit(n: number) {
|
|
144
|
+
return run({ limit: n });
|
|
145
|
+
},
|
|
146
|
+
offset(n: number) {
|
|
147
|
+
return run({ offset: n });
|
|
148
|
+
},
|
|
149
|
+
then(
|
|
150
|
+
onfulfilled: (value: SqlRow[]) => unknown,
|
|
151
|
+
onrejected?: (reason: unknown) => unknown,
|
|
152
|
+
) {
|
|
153
|
+
return run({}).then(onfulfilled, onrejected);
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}) as SqlStoreHandle["select"],
|
|
159
|
+
insert(table) {
|
|
160
|
+
return {
|
|
161
|
+
values(row) {
|
|
162
|
+
const runExecute = () =>
|
|
163
|
+
gatedTable("write", table, async () => {
|
|
164
|
+
refuseDryRunWrite();
|
|
165
|
+
const h = await ensure();
|
|
166
|
+
await h.insert(table).values(row).execute();
|
|
167
|
+
});
|
|
168
|
+
return {
|
|
169
|
+
returning() {
|
|
170
|
+
return gatedTable("write", table, async () => {
|
|
171
|
+
refuseDryRunWrite();
|
|
172
|
+
const h = await ensure();
|
|
173
|
+
return h.insert(table).values(row).returning();
|
|
174
|
+
});
|
|
175
|
+
},
|
|
176
|
+
execute: runExecute,
|
|
177
|
+
then(onfulfilled, onrejected) {
|
|
178
|
+
return runExecute().then(onfulfilled, onrejected);
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
update(table) {
|
|
185
|
+
return {
|
|
186
|
+
set(row) {
|
|
187
|
+
return {
|
|
188
|
+
where(where) {
|
|
189
|
+
return gatedTable("write", table, async () => {
|
|
190
|
+
refuseDryRunWrite();
|
|
191
|
+
const h = await ensure();
|
|
192
|
+
return h.update(table).set(row).where(where);
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
findById(table, id) {
|
|
200
|
+
return gatedTable("read", table, async () => {
|
|
201
|
+
const h = await ensure();
|
|
202
|
+
return h.findById(table, id);
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
delete(table: Parameters<SqlStoreHandle["delete"]>[0], id?: string) {
|
|
206
|
+
if (id !== undefined) {
|
|
207
|
+
return gatedTable("write", table, async () => {
|
|
208
|
+
refuseDryRunWrite();
|
|
209
|
+
const h = await ensure();
|
|
210
|
+
return h.delete(table, id);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
where(where: unknown) {
|
|
215
|
+
return gatedTable("write", table, async () => {
|
|
216
|
+
refuseDryRunWrite();
|
|
217
|
+
const h = await ensure();
|
|
218
|
+
return h.delete(table).where(where);
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
exists(table, idOrWhere) {
|
|
224
|
+
return gatedTable("read", table, async () => {
|
|
225
|
+
const h = await ensure();
|
|
226
|
+
return h.exists(table, idOrWhere);
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
upsert(table, matchOn, values, upsertOptions) {
|
|
230
|
+
return gatedTable("write", table, async () => {
|
|
231
|
+
refuseDryRunWrite();
|
|
232
|
+
const h = await ensure();
|
|
233
|
+
return h.upsert(table, matchOn, values, upsertOptions);
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
increment(table, id, column, by) {
|
|
237
|
+
return gatedTable("write", table, async () => {
|
|
238
|
+
refuseDryRunWrite();
|
|
239
|
+
const h = await ensure();
|
|
240
|
+
return h.increment(table, id, column, by);
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
raw(sql, params) {
|
|
244
|
+
return gated("read", ref, async () => {
|
|
245
|
+
const h = await ensure();
|
|
246
|
+
return h.raw(sql, params);
|
|
247
|
+
});
|
|
248
|
+
},
|
|
249
|
+
count(table, where) {
|
|
250
|
+
return gatedTable("read", table, async () => {
|
|
251
|
+
const h = await ensure();
|
|
252
|
+
return h.count(table, where);
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
page(table, pageOptions) {
|
|
256
|
+
return gatedTable("read", table, async () => {
|
|
257
|
+
const h = await ensure();
|
|
258
|
+
return h.page(table, pageOptions);
|
|
259
|
+
});
|
|
260
|
+
},
|
|
261
|
+
search(table, searchOptions) {
|
|
262
|
+
return gatedTable("read", table, async () => {
|
|
263
|
+
const h = await ensure();
|
|
264
|
+
const result = await h.search(table, searchOptions);
|
|
265
|
+
// Optional rerank via fx.ask — only when explicitly requested.
|
|
266
|
+
if (
|
|
267
|
+
searchOptions.rerank &&
|
|
268
|
+
typeof searchOptions.rerank === "object" &&
|
|
269
|
+
searchOptions.rerank.model
|
|
270
|
+
) {
|
|
271
|
+
const model = searchOptions.rerank.model;
|
|
272
|
+
const pk = "id";
|
|
273
|
+
const docs = result.data.map((row) => ({
|
|
274
|
+
id: String(row[pk] ?? ""),
|
|
275
|
+
text: Object.values(row)
|
|
276
|
+
.filter((v) => typeof v === "string")
|
|
277
|
+
.join("\n"),
|
|
278
|
+
score: 0,
|
|
279
|
+
}));
|
|
280
|
+
const out = (await ask(model, {
|
|
281
|
+
query: searchOptions.query,
|
|
282
|
+
docs,
|
|
283
|
+
})) as { rankedIds?: string[] };
|
|
284
|
+
if (out.rankedIds && out.rankedIds.length > 0) {
|
|
285
|
+
const byId = new Map(result.data.map((r) => [String(r[pk] ?? ""), r]));
|
|
286
|
+
return {
|
|
287
|
+
...result,
|
|
288
|
+
data: out.rankedIds
|
|
289
|
+
.map((id) => byId.get(id))
|
|
290
|
+
.filter((r): r is NonNullable<typeof r> => r !== undefined),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return result;
|
|
295
|
+
});
|
|
296
|
+
},
|
|
297
|
+
ensureTable(table) {
|
|
298
|
+
return gatedTable("write", table, async () => {
|
|
299
|
+
refuseDryRunWrite();
|
|
300
|
+
const h = await ensure();
|
|
301
|
+
return h.ensureTable(table);
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
} as SqlStoreHandle;
|
|
305
|
+
}
|