@fgv/ts-agent-memory-sqlite-vec 5.1.0-49 → 5.1.0-51

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 (31) hide show
  1. package/README.md +26 -1
  2. package/dist/packlets/sqlite-vec-index/connection.js +54 -0
  3. package/dist/packlets/sqlite-vec-index/connection.js.map +1 -0
  4. package/dist/packlets/sqlite-vec-index/model.js.map +1 -1
  5. package/dist/packlets/sqlite-vec-index/rebuildHelpers.js +48 -0
  6. package/dist/packlets/sqlite-vec-index/rebuildHelpers.js.map +1 -0
  7. package/dist/packlets/sqlite-vec-index/sqliteVecFragmentIndex.js +189 -8
  8. package/dist/packlets/sqlite-vec-index/sqliteVecFragmentIndex.js.map +1 -1
  9. package/dist/packlets/sqlite-vec-index/sqliteVecVectorIndex.js +107 -50
  10. package/dist/packlets/sqlite-vec-index/sqliteVecVectorIndex.js.map +1 -1
  11. package/dist/ts-agent-memory-sqlite-vec.d.ts +188 -13
  12. package/lib/packlets/sqlite-vec-index/connection.d.ts +42 -0
  13. package/lib/packlets/sqlite-vec-index/connection.d.ts.map +1 -0
  14. package/lib/packlets/sqlite-vec-index/connection.js +91 -0
  15. package/lib/packlets/sqlite-vec-index/connection.js.map +1 -0
  16. package/lib/packlets/sqlite-vec-index/model.d.ts +92 -0
  17. package/lib/packlets/sqlite-vec-index/model.d.ts.map +1 -1
  18. package/lib/packlets/sqlite-vec-index/model.js.map +1 -1
  19. package/lib/packlets/sqlite-vec-index/rebuildHelpers.d.ts +38 -0
  20. package/lib/packlets/sqlite-vec-index/rebuildHelpers.d.ts.map +1 -0
  21. package/lib/packlets/sqlite-vec-index/rebuildHelpers.js +53 -0
  22. package/lib/packlets/sqlite-vec-index/rebuildHelpers.js.map +1 -0
  23. package/lib/packlets/sqlite-vec-index/sqliteVecFragmentIndex.d.ts +52 -7
  24. package/lib/packlets/sqlite-vec-index/sqliteVecFragmentIndex.d.ts.map +1 -1
  25. package/lib/packlets/sqlite-vec-index/sqliteVecFragmentIndex.js +188 -7
  26. package/lib/packlets/sqlite-vec-index/sqliteVecFragmentIndex.js.map +1 -1
  27. package/lib/packlets/sqlite-vec-index/sqliteVecVectorIndex.d.ts +44 -11
  28. package/lib/packlets/sqlite-vec-index/sqliteVecVectorIndex.d.ts.map +1 -1
  29. package/lib/packlets/sqlite-vec-index/sqliteVecVectorIndex.js +108 -51
  30. package/lib/packlets/sqlite-vec-index/sqliteVecVectorIndex.js.map +1 -1
  31. package/package.json +7 -7
@@ -1,7 +1,11 @@
1
1
  import type BetterSqlite3 from 'better-sqlite3';
2
+ import { DetailedResult } from '@fgv/ts-utils';
3
+ import { FragmentEmbedder } from '@fgv/ts-agent-memory';
2
4
  import { IEdgeTarget } from '@fgv/ts-agent-memory';
3
5
  import { IEmbeddedFragment } from '@fgv/ts-agent-memory';
6
+ import { IFragmentQueryOptions } from '@fgv/ts-agent-memory';
4
7
  import { IFragmentVectorIndex } from '@fgv/ts-agent-memory';
8
+ import { IFragmentVectorRebuildReport } from '@fgv/ts-agent-memory';
5
9
  import { IMemoryRecordSource } from '@fgv/ts-agent-memory';
6
10
  import { IVectorIndex } from '@fgv/ts-agent-memory';
7
11
  import { IVectorQueryHit } from '@fgv/ts-agent-memory';
@@ -34,6 +38,47 @@ export declare interface ISqliteVecFragmentIndexCreateParams {
34
38
  readonly tableName?: string;
35
39
  }
36
40
 
41
+ /**
42
+ * An index plus the connection {@link SqliteVecFragmentIndex.open} opened for it.
43
+ *
44
+ * @public
45
+ */
46
+ export declare interface ISqliteVecFragmentIndexHandle {
47
+ /** The index, ready to use. */
48
+ readonly index: SqliteVecFragmentIndex;
49
+ /**
50
+ * Closes the connection **this `open` call created**. Idempotent — a second
51
+ * `close` succeeds rather than failing. See
52
+ * {@link ISqliteVecVectorIndexHandle.close} for why the disposer lives here
53
+ * rather than on the index class.
54
+ */
55
+ close(): Result<true>;
56
+ }
57
+
58
+ /**
59
+ * Parameters for {@link SqliteVecFragmentIndex.open}.
60
+ * @public
61
+ */
62
+ export declare interface ISqliteVecFragmentIndexOpenParams {
63
+ /**
64
+ * Filesystem path to the database file, opened by this package rather than by
65
+ * the consumer. Created if it does not exist, exactly as `better-sqlite3` would.
66
+ * `':memory:'` is accepted and yields an owned ephemeral connection.
67
+ *
68
+ * **Two `open` calls on one path produce two independent connections, not a
69
+ * shared one** — see {@link ISqliteVecVectorIndexOpenParams.path}. To put a
70
+ * fragment index and a record index on one connection, open it yourself and pass
71
+ * it to both `create` methods.
72
+ */
73
+ readonly path: string;
74
+ /**
75
+ * Name of the `vec0` virtual table that holds the fragment embeddings. Must be a
76
+ * simple SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to
77
+ * `'memory_fragments'`.
78
+ */
79
+ readonly tableName?: string;
80
+ }
81
+
37
82
  /**
38
83
  * Parameters for {@link SqliteVecVectorIndex.create}.
39
84
  * @public
@@ -58,6 +103,58 @@ export declare interface ISqliteVecVectorIndexCreateParams {
58
103
  readonly tableName?: string;
59
104
  }
60
105
 
106
+ /**
107
+ * An index plus the connection {@link SqliteVecVectorIndex.open} opened for it.
108
+ *
109
+ * @public
110
+ */
111
+ export declare interface ISqliteVecVectorIndexHandle {
112
+ /** The index, ready to use. */
113
+ readonly index: SqliteVecVectorIndex;
114
+ /**
115
+ * Closes the connection **this `open` call created**. Idempotent — a second
116
+ * `close` succeeds rather than failing.
117
+ *
118
+ * @remarks
119
+ * The disposer travels on this handle rather than on the index class because an
120
+ * index built by `create` holds a connection the **consumer** owns, and must stay
121
+ * incapable of closing it. A `close()` method meaningful on some instances and
122
+ * forbidden on others would be a lie in the type; here, only the caller that
123
+ * caused the connection to exist is handed the means to end it.
124
+ *
125
+ * The index is unusable afterwards — every operation on it will fail against a
126
+ * closed connection.
127
+ */
128
+ close(): Result<true>;
129
+ }
130
+
131
+ /**
132
+ * Parameters for {@link SqliteVecVectorIndex.open}.
133
+ * @public
134
+ */
135
+ export declare interface ISqliteVecVectorIndexOpenParams {
136
+ /**
137
+ * Filesystem path to the database file, opened by this package rather than by
138
+ * the consumer. Created if it does not exist, exactly as `better-sqlite3` would.
139
+ * `':memory:'` is accepted and yields an owned ephemeral connection.
140
+ *
141
+ * **Two `open` calls on one path produce two independent connections, not a
142
+ * shared one.** That is legal in SQLite and has a different locking story than
143
+ * the single-connection case — writes contend, and a reader can see a
144
+ * `SQLITE_BUSY`. To put a record index and a fragment index on one connection
145
+ * (the intended shared-handle case), open the connection yourself and pass it to
146
+ * both `create` methods.
147
+ */
148
+ readonly path: string;
149
+ /**
150
+ * Name of the `vec0` virtual table that holds the embeddings. Must be a simple
151
+ * SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to `'memory_vectors'`.
152
+ * Supply a distinct name to hold more than one independent index in a single
153
+ * database file.
154
+ */
155
+ readonly tableName?: string;
156
+ }
157
+
61
158
  /**
62
159
  * A persistent, `sqlite-vec`-backed `IFragmentVectorIndex` (from
63
160
  * `@fgv/ts-agent-memory`) — the fragment-granular sibling of
@@ -97,9 +194,13 @@ export declare interface ISqliteVecVectorIndexCreateParams {
97
194
  * cosine (`score = 1 - cosineDistance`), byte-identical to the in-memory index.
98
195
  * Large-N ANN indexing is explicitly out of scope, same regime as the record index.
99
196
  *
100
- * The `better-sqlite3` `Database` is consumer-owned (bring-your-own): this index
101
- * loads the `sqlite-vec` extension onto it and reads/writes the table, but never
102
- * opens or closes the connection.
197
+ * **Connection ownership depends on which factory you use.** With
198
+ * {@link SqliteVecFragmentIndex.create} the `Database` is consumer-owned
199
+ * (bring-your-own): this index loads the `sqlite-vec` extension onto it and
200
+ * reads/writes the table, but never opens or closes the connection — and that is
201
+ * the seam for backing this index and a record index with one connection. With
202
+ * {@link SqliteVecFragmentIndex.open} this package opens the file itself and hands
203
+ * back a handle carrying the disposer for the connection it created.
103
204
  * @public
104
205
  */
105
206
  export declare class SqliteVecFragmentIndex implements IFragmentVectorIndex {
@@ -128,12 +229,53 @@ export declare class SqliteVecFragmentIndex implements IFragmentVectorIndex {
128
229
  * drop-and-re-index — `vec0` cannot be altered in place).
129
230
  */
130
231
  static create(params: ISqliteVecFragmentIndexCreateParams): Promise<Result<SqliteVecFragmentIndex>>;
232
+ /**
233
+ * Path-based factory. Opens the database file itself and returns the index
234
+ * together with a disposer for the connection it created.
235
+ *
236
+ * @remarks
237
+ * The fragment-granular sibling of {@link SqliteVecVectorIndex.open}, and present
238
+ * for the same reason: a consumer doing sub-document retrieval only would
239
+ * otherwise still value-import `better-sqlite3` and hand-roll a `captureResult`
240
+ * around a constructor that throws.
241
+ *
242
+ * **Use `create` instead when one connection must back both a fragment index and
243
+ * a record index** — the intended shared-handle case. Two `open` calls on one path
244
+ * give two independent connections, not a shared one.
245
+ *
246
+ * If initialization fails after the file is opened, the connection is closed
247
+ * before returning, so a failed `open` does not leak the descriptor it created.
248
+ * Should that close *itself* fail — the connection is then genuinely leaked — the
249
+ * returned message says so rather than hiding it. That includes the
250
+ * auxiliary-column mismatch failure, which is reported by `create` only after the
251
+ * file is open.
252
+ *
253
+ * @param params - See {@link ISqliteVecFragmentIndexOpenParams}.
254
+ * @returns `Success` with a {@link ISqliteVecFragmentIndexHandle}, or `Failure` if
255
+ * the driver could not be loaded, the file could not be opened, the table name is
256
+ * not a simple identifier, the extension fails to load, or the existing table was
257
+ * written by a version with a different auxiliary-column set.
258
+ */
259
+ static open(params: ISqliteVecFragmentIndexOpenParams): Promise<Result<ISqliteVecFragmentIndexHandle>>;
131
260
  /** {@inheritDoc IFragmentVectorIndex.addFragments} */
132
261
  addFragments(target: IEdgeTarget, fragments: ReadonlyArray<IEmbeddedFragment>): Promise<Result<number>>;
133
262
  /** {@inheritDoc IFragmentVectorIndex.remove} */
134
263
  remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;
264
+ /** {@inheritDoc IFragmentVectorIndex.has} */
265
+ has(target: IEdgeTarget): Promise<Result<boolean>>;
266
+ /** {@inheritDoc IFragmentVectorIndex.rebuild} */
267
+ rebuild(source: IMemoryRecordSource, embed: FragmentEmbedder, options?: IVectorRebuildOptions): Promise<DetailedResult<IFragmentVectorRebuildReport, IFragmentVectorRebuildReport>>;
268
+ /**
269
+ * **Empties the rows; does NOT release the table's declared dimension.** That
270
+ * is a `vec0` constraint rather than a choice — the dimension is schema, and
271
+ * there is no `ALTER TABLE` for it — so a rebuild at a new dimension fails
272
+ * here where it would succeed on the in-memory sibling, which forgets its
273
+ * dimension on reset. Changing dimension needs a drop-and-re-index; see the
274
+ * note on `IVectorIndex.rebuild`. Tolerates a table that does not exist yet.
275
+ */
276
+ private _clear;
135
277
  /** {@inheritDoc IFragmentVectorIndex.query} */
136
- query(vector: Float32Array, topK: number, maxPerRecord?: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;
278
+ query(vector: Float32Array, topK: number, options?: IFragmentQueryOptions): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;
137
279
  /**
138
280
  * Create the fragment `vec0` virtual table with the established dimension. The
139
281
  * auxiliary columns must stay in sync with `AUXILIARY_COLUMNS`, which
@@ -232,9 +374,13 @@ export declare class SqliteVecFragmentIndex implements IFragmentVectorIndex {
232
374
  * durable, appropriate for the same "thousands of records" regime the in-memory
233
375
  * index targets. Large-N ANN indexing is explicitly out of scope — see the README.
234
376
  *
235
- * The `better-sqlite3` `Database` is consumer-owned (bring-your-own): this index
236
- * loads the `sqlite-vec` extension onto it and reads/writes the table, but never
237
- * opens or closes the connection.
377
+ * **Connection ownership depends on which factory you use.** With
378
+ * {@link SqliteVecVectorIndex.create} the `Database` is consumer-owned
379
+ * (bring-your-own): this index loads the `sqlite-vec` extension onto it and
380
+ * reads/writes the table, but never opens or closes the connection — and that is
381
+ * the seam for backing a record index and a fragment index with one connection.
382
+ * With {@link SqliteVecVectorIndex.open} this package opens the file itself and
383
+ * hands back a handle carrying the disposer for the connection it created.
238
384
  * @public
239
385
  */
240
386
  export declare class SqliteVecVectorIndex implements IVectorIndex {
@@ -258,8 +404,36 @@ export declare class SqliteVecVectorIndex implements IVectorIndex {
258
404
  * simple identifier or the extension fails to load.
259
405
  */
260
406
  static create(params: ISqliteVecVectorIndexCreateParams): Promise<Result<SqliteVecVectorIndex>>;
407
+ /**
408
+ * Path-based factory. Opens the database file itself and returns the index
409
+ * together with a disposer for the connection it created.
410
+ *
411
+ * @remarks
412
+ * The convenience over {@link SqliteVecVectorIndex.create} is that the consumer
413
+ * neither value-imports `better-sqlite3` nor re-establishes `Result` discipline
414
+ * around a constructor that throws — this is the one place the package leaked its
415
+ * own dependency into consumer source.
416
+ *
417
+ * **Use `create` instead when one connection must back more than one index** (a
418
+ * record index and a fragment index in the same file, the intended shared-handle
419
+ * case). Two `open` calls on one path give two independent connections, not a
420
+ * shared one.
421
+ *
422
+ * If initialization fails after the file is opened, the connection is closed
423
+ * before returning, so a failed `open` does not leak the descriptor it created.
424
+ * Should that close *itself* fail — the connection is then genuinely leaked — the
425
+ * returned message says so rather than hiding it.
426
+ *
427
+ * @param params - See {@link ISqliteVecVectorIndexOpenParams}.
428
+ * @returns `Success` with a {@link ISqliteVecVectorIndexHandle}, or `Failure` if
429
+ * the driver could not be loaded, the file could not be opened, the table name is
430
+ * not a simple identifier, or the extension fails to load.
431
+ */
432
+ static open(params: ISqliteVecVectorIndexOpenParams): Promise<Result<ISqliteVecVectorIndexHandle>>;
261
433
  /** {@inheritDoc IVectorIndex.add} */
262
434
  add(target: IEdgeTarget, vector: Float32Array): Promise<Result<string>>;
435
+ /** {@inheritDoc IVectorIndex.has} */
436
+ has(target: IEdgeTarget): Promise<Result<boolean>>;
263
437
  /** {@inheritDoc IVectorIndex.remove} */
264
438
  remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;
265
439
  /**
@@ -275,13 +449,14 @@ export declare class SqliteVecVectorIndex implements IVectorIndex {
275
449
  * a process kill mid-rebuild leaves the table holding neither the old index nor
276
450
  * the complete new one, and the remedy is to run `rebuild` again.
277
451
  */
278
- rebuild(source: IMemoryRecordSource, embed: MemoryEmbedder, options?: IVectorRebuildOptions): Promise<Result<IVectorRebuildReport>>;
452
+ rebuild(source: IMemoryRecordSource, embed: MemoryEmbedder, options?: IVectorRebuildOptions): Promise<DetailedResult<IVectorRebuildReport, IVectorRebuildReport>>;
279
453
  /**
280
- * Empty the table. Deliberately does NOT drop it or forget the established
281
- * dimension: the `vec0` table's dimension is fixed at creation and a re-embed at
282
- * a different dimension needs a drop-and-re-index, which is a consumer decision
283
- * (see the package README on `vec0` schema changes), not something a rebuild
284
- * should do silently.
454
+ * **Empties the rows; does NOT release the table's declared dimension.** That
455
+ * is a `vec0` constraint rather than a choice the dimension is schema, and
456
+ * there is no `ALTER TABLE` for it so a rebuild at a new dimension fails
457
+ * here where it would succeed on the in-memory sibling, which forgets its
458
+ * dimension on reset. Changing dimension needs a drop-and-re-index; see the
459
+ * note on `IVectorIndex.rebuild`.
285
460
  */
286
461
  private _clear;
287
462
  /** {@inheritDoc IVectorIndex.query} */
@@ -0,0 +1,42 @@
1
+ import type BetterSqlite3 from 'better-sqlite3';
2
+ import { Result } from '@fgv/ts-utils';
3
+ /**
4
+ * Opens a `better-sqlite3` connection this package owns.
5
+ *
6
+ * @remarks
7
+ * **The value import of `better-sqlite3` lives here, and it is deliberately
8
+ * lazy.** Every other module in this package imports the driver as `import type`
9
+ * only, so merely importing `@fgv/ts-agent-memory-sqlite-vec` has never loaded the
10
+ * native binding — a property the path-based factories must not cost consumers who
11
+ * only ever call `create()`. A static import would move the native load (and its
12
+ * failure mode) to package-load time for everyone. The dynamic `import()` keeps it
13
+ * at the one call that actually needs a connection.
14
+ *
15
+ * Taking this import is the entire point of the path-based factories: it is the
16
+ * only place this wrapper leaked its own dependency into consumer source.
17
+ *
18
+ * @param path - Filesystem path to the database file. `':memory:'` is accepted and
19
+ * yields an owned ephemeral connection.
20
+ * @param label - Package-facing prefix for the failure message.
21
+ * @returns `Success` with a connection this package owns, or `Failure` if the
22
+ * driver could not be loaded or the file could not be opened.
23
+ * @internal
24
+ */
25
+ export declare function openOwnedConnection(path: string, label: string): Promise<Result<BetterSqlite3.Database>>;
26
+ /**
27
+ * Closes a connection this package opened.
28
+ *
29
+ * @remarks
30
+ * Only ever called on a connection produced by {@link openOwnedConnection}. A
31
+ * connection supplied through a `create()` param is the consumer's and is never
32
+ * closed here — that asymmetry is the reason `close` travels on the handle
33
+ * returned by `open` rather than sitting on the index class.
34
+ *
35
+ * `better-sqlite3`'s own `close()` is safe to call on an already-closed
36
+ * connection, so a second `close()` on the same handle succeeds rather than
37
+ * failing.
38
+ *
39
+ * @internal
40
+ */
41
+ export declare function closeOwnedConnection(db: BetterSqlite3.Database, label: string): Result<true>;
42
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../src/packlets/sqlite-vec-index/connection.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,aAAa,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAqC,MAAM,eAAe,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAMzC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAK5F"}
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2026 Erik Fortune
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.openOwnedConnection = openOwnedConnection;
41
+ exports.closeOwnedConnection = closeOwnedConnection;
42
+ const ts_utils_1 = require("@fgv/ts-utils");
43
+ /**
44
+ * Opens a `better-sqlite3` connection this package owns.
45
+ *
46
+ * @remarks
47
+ * **The value import of `better-sqlite3` lives here, and it is deliberately
48
+ * lazy.** Every other module in this package imports the driver as `import type`
49
+ * only, so merely importing `@fgv/ts-agent-memory-sqlite-vec` has never loaded the
50
+ * native binding — a property the path-based factories must not cost consumers who
51
+ * only ever call `create()`. A static import would move the native load (and its
52
+ * failure mode) to package-load time for everyone. The dynamic `import()` keeps it
53
+ * at the one call that actually needs a connection.
54
+ *
55
+ * Taking this import is the entire point of the path-based factories: it is the
56
+ * only place this wrapper leaked its own dependency into consumer source.
57
+ *
58
+ * @param path - Filesystem path to the database file. `':memory:'` is accepted and
59
+ * yields an owned ephemeral connection.
60
+ * @param label - Package-facing prefix for the failure message.
61
+ * @returns `Success` with a connection this package owns, or `Failure` if the
62
+ * driver could not be loaded or the file could not be opened.
63
+ * @internal
64
+ */
65
+ async function openOwnedConnection(path, label) {
66
+ return (await (0, ts_utils_1.captureAsyncResult)(async () => (await Promise.resolve().then(() => __importStar(require('better-sqlite3')))).default))
67
+ .withErrorFormat((m) => `${label}: failed to load the 'better-sqlite3' driver: ${m}`)
68
+ .onSuccess((driver) => (0, ts_utils_1.captureResult)(() => new driver(path)).withErrorFormat((m) => `${label}: failed to open '${path}': ${m}`));
69
+ }
70
+ /**
71
+ * Closes a connection this package opened.
72
+ *
73
+ * @remarks
74
+ * Only ever called on a connection produced by {@link openOwnedConnection}. A
75
+ * connection supplied through a `create()` param is the consumer's and is never
76
+ * closed here — that asymmetry is the reason `close` travels on the handle
77
+ * returned by `open` rather than sitting on the index class.
78
+ *
79
+ * `better-sqlite3`'s own `close()` is safe to call on an already-closed
80
+ * connection, so a second `close()` on the same handle succeeds rather than
81
+ * failing.
82
+ *
83
+ * @internal
84
+ */
85
+ function closeOwnedConnection(db, label) {
86
+ return (0, ts_utils_1.captureResult)(() => {
87
+ db.close();
88
+ return true;
89
+ }).withErrorFormat((m) => `${label}: failed to close the connection: ${m}`);
90
+ }
91
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../src/packlets/sqlite-vec-index/connection.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BH,kDASC;AAiBD,oDAKC;AAvDD,4CAA0E;AAE1E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACI,KAAK,UAAU,mBAAmB,CACvC,IAAY,EACZ,KAAa;IAEb,OAAO,CAAC,MAAM,IAAA,6BAAkB,EAAC,KAAK,IAAI,EAAE,CAAC,CAAC,wDAAa,gBAAgB,GAAC,CAAC,CAAC,OAAO,CAAC,CAAC;SACpF,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,iDAAiD,CAAC,EAAE,CAAC;SACpF,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CACpB,IAAA,wBAAa,EAAC,GAAG,EAAE,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,qBAAqB,IAAI,MAAM,CAAC,EAAE,CAAC,CACzG,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,oBAAoB,CAAC,EAA0B,EAAE,KAAa;IAC5E,OAAO,IAAA,wBAAa,EAAO,GAAG,EAAE;QAC9B,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,qCAAqC,CAAC,EAAE,CAAC,CAAC;AAC9E,CAAC","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport type BetterSqlite3 from 'better-sqlite3';\nimport { Result, captureAsyncResult, captureResult } from '@fgv/ts-utils';\n\n/**\n * Opens a `better-sqlite3` connection this package owns.\n *\n * @remarks\n * **The value import of `better-sqlite3` lives here, and it is deliberately\n * lazy.** Every other module in this package imports the driver as `import type`\n * only, so merely importing `@fgv/ts-agent-memory-sqlite-vec` has never loaded the\n * native binding — a property the path-based factories must not cost consumers who\n * only ever call `create()`. A static import would move the native load (and its\n * failure mode) to package-load time for everyone. The dynamic `import()` keeps it\n * at the one call that actually needs a connection.\n *\n * Taking this import is the entire point of the path-based factories: it is the\n * only place this wrapper leaked its own dependency into consumer source.\n *\n * @param path - Filesystem path to the database file. `':memory:'` is accepted and\n * yields an owned ephemeral connection.\n * @param label - Package-facing prefix for the failure message.\n * @returns `Success` with a connection this package owns, or `Failure` if the\n * driver could not be loaded or the file could not be opened.\n * @internal\n */\nexport async function openOwnedConnection(\n path: string,\n label: string\n): Promise<Result<BetterSqlite3.Database>> {\n return (await captureAsyncResult(async () => (await import('better-sqlite3')).default))\n .withErrorFormat((m) => `${label}: failed to load the 'better-sqlite3' driver: ${m}`)\n .onSuccess((driver) =>\n captureResult(() => new driver(path)).withErrorFormat((m) => `${label}: failed to open '${path}': ${m}`)\n );\n}\n\n/**\n * Closes a connection this package opened.\n *\n * @remarks\n * Only ever called on a connection produced by {@link openOwnedConnection}. A\n * connection supplied through a `create()` param is the consumer's and is never\n * closed here — that asymmetry is the reason `close` travels on the handle\n * returned by `open` rather than sitting on the index class.\n *\n * `better-sqlite3`'s own `close()` is safe to call on an already-closed\n * connection, so a second `close()` on the same handle succeeds rather than\n * failing.\n *\n * @internal\n */\nexport function closeOwnedConnection(db: BetterSqlite3.Database, label: string): Result<true> {\n return captureResult<true>(() => {\n db.close();\n return true;\n }).withErrorFormat((m) => `${label}: failed to close the connection: ${m}`);\n}\n"]}
@@ -1,4 +1,7 @@
1
1
  import type BetterSqlite3 from 'better-sqlite3';
2
+ import type { Result } from '@fgv/ts-utils';
3
+ import type { SqliteVecFragmentIndex } from './sqliteVecFragmentIndex';
4
+ import type { SqliteVecVectorIndex } from './sqliteVecVectorIndex';
2
5
  /**
3
6
  * Parameters for {@link SqliteVecVectorIndex.create}.
4
7
  * @public
@@ -22,6 +25,95 @@ export interface ISqliteVecVectorIndexCreateParams {
22
25
  */
23
26
  readonly tableName?: string;
24
27
  }
28
+ /**
29
+ * Parameters for {@link SqliteVecVectorIndex.open}.
30
+ * @public
31
+ */
32
+ export interface ISqliteVecVectorIndexOpenParams {
33
+ /**
34
+ * Filesystem path to the database file, opened by this package rather than by
35
+ * the consumer. Created if it does not exist, exactly as `better-sqlite3` would.
36
+ * `':memory:'` is accepted and yields an owned ephemeral connection.
37
+ *
38
+ * **Two `open` calls on one path produce two independent connections, not a
39
+ * shared one.** That is legal in SQLite and has a different locking story than
40
+ * the single-connection case — writes contend, and a reader can see a
41
+ * `SQLITE_BUSY`. To put a record index and a fragment index on one connection
42
+ * (the intended shared-handle case), open the connection yourself and pass it to
43
+ * both `create` methods.
44
+ */
45
+ readonly path: string;
46
+ /**
47
+ * Name of the `vec0` virtual table that holds the embeddings. Must be a simple
48
+ * SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to `'memory_vectors'`.
49
+ * Supply a distinct name to hold more than one independent index in a single
50
+ * database file.
51
+ */
52
+ readonly tableName?: string;
53
+ }
54
+ /**
55
+ * An index plus the connection {@link SqliteVecVectorIndex.open} opened for it.
56
+ *
57
+ * @public
58
+ */
59
+ export interface ISqliteVecVectorIndexHandle {
60
+ /** The index, ready to use. */
61
+ readonly index: SqliteVecVectorIndex;
62
+ /**
63
+ * Closes the connection **this `open` call created**. Idempotent — a second
64
+ * `close` succeeds rather than failing.
65
+ *
66
+ * @remarks
67
+ * The disposer travels on this handle rather than on the index class because an
68
+ * index built by `create` holds a connection the **consumer** owns, and must stay
69
+ * incapable of closing it. A `close()` method meaningful on some instances and
70
+ * forbidden on others would be a lie in the type; here, only the caller that
71
+ * caused the connection to exist is handed the means to end it.
72
+ *
73
+ * The index is unusable afterwards — every operation on it will fail against a
74
+ * closed connection.
75
+ */
76
+ close(): Result<true>;
77
+ }
78
+ /**
79
+ * Parameters for {@link SqliteVecFragmentIndex.open}.
80
+ * @public
81
+ */
82
+ export interface ISqliteVecFragmentIndexOpenParams {
83
+ /**
84
+ * Filesystem path to the database file, opened by this package rather than by
85
+ * the consumer. Created if it does not exist, exactly as `better-sqlite3` would.
86
+ * `':memory:'` is accepted and yields an owned ephemeral connection.
87
+ *
88
+ * **Two `open` calls on one path produce two independent connections, not a
89
+ * shared one** — see {@link ISqliteVecVectorIndexOpenParams.path}. To put a
90
+ * fragment index and a record index on one connection, open it yourself and pass
91
+ * it to both `create` methods.
92
+ */
93
+ readonly path: string;
94
+ /**
95
+ * Name of the `vec0` virtual table that holds the fragment embeddings. Must be a
96
+ * simple SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to
97
+ * `'memory_fragments'`.
98
+ */
99
+ readonly tableName?: string;
100
+ }
101
+ /**
102
+ * An index plus the connection {@link SqliteVecFragmentIndex.open} opened for it.
103
+ *
104
+ * @public
105
+ */
106
+ export interface ISqliteVecFragmentIndexHandle {
107
+ /** The index, ready to use. */
108
+ readonly index: SqliteVecFragmentIndex;
109
+ /**
110
+ * Closes the connection **this `open` call created**. Idempotent — a second
111
+ * `close` succeeds rather than failing. See
112
+ * {@link ISqliteVecVectorIndexHandle.close} for why the disposer lives here
113
+ * rather than on the index class.
114
+ */
115
+ close(): Result<true>;
116
+ }
25
117
  /**
26
118
  * Parameters for {@link SqliteVecFragmentIndex.create}.
27
119
  * @public
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/packlets/sqlite-vec-index/model.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,aAAa,MAAM,gBAAgB,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,iCAAiC;IAChD;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;IAE1C;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,mCAAmC;IAClD;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;IAE1C;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/packlets/sqlite-vec-index/model.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,aAAa,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEnE;;;GAGG;AACH,MAAM,WAAW,iCAAiC;IAChD;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;IAE1C;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,+BAA+B;IAC9C;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,+BAA+B;IAC/B,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IAErC;;;;;;;;;;;;;OAaG;IACH,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,iCAAiC;IAChD;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC5C,+BAA+B;IAC/B,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAC;IAEvC;;;;;OAKG;IACH,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,mCAAmC;IAClD;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;IAE1C;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/packlets/sqlite-vec-index/model.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport type BetterSqlite3 from 'better-sqlite3';\n\n/**\n * Parameters for {@link SqliteVecVectorIndex.create}.\n * @public\n */\nexport interface ISqliteVecVectorIndexCreateParams {\n /**\n * A `better-sqlite3` `Database` the consumer owns (bring-your-own, mirroring\n * the boundary-package convention). The consumer opens it (`new Database(path)`\n * for a persistent file, or `new Database(':memory:')` for an ephemeral index)\n * and owns its lifecycle — this index never closes it. `create` loads the\n * `sqlite-vec` extension onto the connection and, if the vector table already\n * exists (a reopened persistent file), recovers its established dimension so no\n * re-embedding is required on open.\n */\n readonly database: BetterSqlite3.Database;\n\n /**\n * Name of the `vec0` virtual table that holds the embeddings. Must be a simple\n * SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to `'memory_vectors'`.\n * Supply a distinct name to hold more than one independent index in a single\n * database file.\n */\n readonly tableName?: string;\n}\n\n/**\n * Parameters for {@link SqliteVecFragmentIndex.create}.\n * @public\n */\nexport interface ISqliteVecFragmentIndexCreateParams {\n /**\n * A `better-sqlite3` `Database` the consumer owns (bring-your-own, mirroring\n * {@link ISqliteVecVectorIndexCreateParams.database}). The consumer opens it and\n * owns its lifecycle — this index never closes it. `create` loads the\n * `sqlite-vec` extension onto the connection and, if the fragment table already\n * exists (a reopened persistent file), recovers its established dimension so no\n * re-embedding is required on open.\n */\n readonly database: BetterSqlite3.Database;\n\n /**\n * Name of the `vec0` virtual table that holds the fragment embeddings. Must be a\n * simple SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to\n * `'memory_fragments'`. Supply a distinct name (distinct from any record-level\n * {@link SqliteVecVectorIndex} table) to hold more than one independent index in\n * a single database file.\n */\n readonly tableName?: string;\n}\n"]}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/packlets/sqlite-vec-index/model.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport type BetterSqlite3 from 'better-sqlite3';\nimport type { Result } from '@fgv/ts-utils';\nimport type { SqliteVecFragmentIndex } from './sqliteVecFragmentIndex';\nimport type { SqliteVecVectorIndex } from './sqliteVecVectorIndex';\n\n/**\n * Parameters for {@link SqliteVecVectorIndex.create}.\n * @public\n */\nexport interface ISqliteVecVectorIndexCreateParams {\n /**\n * A `better-sqlite3` `Database` the consumer owns (bring-your-own, mirroring\n * the boundary-package convention). The consumer opens it (`new Database(path)`\n * for a persistent file, or `new Database(':memory:')` for an ephemeral index)\n * and owns its lifecycle — this index never closes it. `create` loads the\n * `sqlite-vec` extension onto the connection and, if the vector table already\n * exists (a reopened persistent file), recovers its established dimension so no\n * re-embedding is required on open.\n */\n readonly database: BetterSqlite3.Database;\n\n /**\n * Name of the `vec0` virtual table that holds the embeddings. Must be a simple\n * SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to `'memory_vectors'`.\n * Supply a distinct name to hold more than one independent index in a single\n * database file.\n */\n readonly tableName?: string;\n}\n\n/**\n * Parameters for {@link SqliteVecVectorIndex.open}.\n * @public\n */\nexport interface ISqliteVecVectorIndexOpenParams {\n /**\n * Filesystem path to the database file, opened by this package rather than by\n * the consumer. Created if it does not exist, exactly as `better-sqlite3` would.\n * `':memory:'` is accepted and yields an owned ephemeral connection.\n *\n * **Two `open` calls on one path produce two independent connections, not a\n * shared one.** That is legal in SQLite and has a different locking story than\n * the single-connection case — writes contend, and a reader can see a\n * `SQLITE_BUSY`. To put a record index and a fragment index on one connection\n * (the intended shared-handle case), open the connection yourself and pass it to\n * both `create` methods.\n */\n readonly path: string;\n\n /**\n * Name of the `vec0` virtual table that holds the embeddings. Must be a simple\n * SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to `'memory_vectors'`.\n * Supply a distinct name to hold more than one independent index in a single\n * database file.\n */\n readonly tableName?: string;\n}\n\n/**\n * An index plus the connection {@link SqliteVecVectorIndex.open} opened for it.\n *\n * @public\n */\nexport interface ISqliteVecVectorIndexHandle {\n /** The index, ready to use. */\n readonly index: SqliteVecVectorIndex;\n\n /**\n * Closes the connection **this `open` call created**. Idempotent — a second\n * `close` succeeds rather than failing.\n *\n * @remarks\n * The disposer travels on this handle rather than on the index class because an\n * index built by `create` holds a connection the **consumer** owns, and must stay\n * incapable of closing it. A `close()` method meaningful on some instances and\n * forbidden on others would be a lie in the type; here, only the caller that\n * caused the connection to exist is handed the means to end it.\n *\n * The index is unusable afterwards — every operation on it will fail against a\n * closed connection.\n */\n close(): Result<true>;\n}\n\n/**\n * Parameters for {@link SqliteVecFragmentIndex.open}.\n * @public\n */\nexport interface ISqliteVecFragmentIndexOpenParams {\n /**\n * Filesystem path to the database file, opened by this package rather than by\n * the consumer. Created if it does not exist, exactly as `better-sqlite3` would.\n * `':memory:'` is accepted and yields an owned ephemeral connection.\n *\n * **Two `open` calls on one path produce two independent connections, not a\n * shared one** — see {@link ISqliteVecVectorIndexOpenParams.path}. To put a\n * fragment index and a record index on one connection, open it yourself and pass\n * it to both `create` methods.\n */\n readonly path: string;\n\n /**\n * Name of the `vec0` virtual table that holds the fragment embeddings. Must be a\n * simple SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to\n * `'memory_fragments'`.\n */\n readonly tableName?: string;\n}\n\n/**\n * An index plus the connection {@link SqliteVecFragmentIndex.open} opened for it.\n *\n * @public\n */\nexport interface ISqliteVecFragmentIndexHandle {\n /** The index, ready to use. */\n readonly index: SqliteVecFragmentIndex;\n\n /**\n * Closes the connection **this `open` call created**. Idempotent — a second\n * `close` succeeds rather than failing. See\n * {@link ISqliteVecVectorIndexHandle.close} for why the disposer lives here\n * rather than on the index class.\n */\n close(): Result<true>;\n}\n\n/**\n * Parameters for {@link SqliteVecFragmentIndex.create}.\n * @public\n */\nexport interface ISqliteVecFragmentIndexCreateParams {\n /**\n * A `better-sqlite3` `Database` the consumer owns (bring-your-own, mirroring\n * {@link ISqliteVecVectorIndexCreateParams.database}). The consumer opens it and\n * owns its lifecycle — this index never closes it. `create` loads the\n * `sqlite-vec` extension onto the connection and, if the fragment table already\n * exists (a reopened persistent file), recovers its established dimension so no\n * re-embedding is required on open.\n */\n readonly database: BetterSqlite3.Database;\n\n /**\n * Name of the `vec0` virtual table that holds the fragment embeddings. Must be a\n * simple SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to\n * `'memory_fragments'`. Supply a distinct name (distinct from any record-level\n * {@link SqliteVecVectorIndex} table) to hold more than one independent index in\n * a single database file.\n */\n readonly tableName?: string;\n}\n"]}
@@ -0,0 +1,38 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ import { Kind } from '@fgv/ts-agent-memory';
3
+ /**
4
+ * Invoke a consumer-supplied hook that already returns a `Result`, converting a
5
+ * synchronous throw or a promise rejection into a `Failure` rather than letting
6
+ * it escape.
7
+ *
8
+ * @remarks
9
+ * Package-internal. `@fgv/ts-agent-memory` carries an identical private copy for
10
+ * its in-memory indexes. Exporting a single `AsyncDeferredResult`-invoking
11
+ * primitive from `ts-utils` is the right home and is recorded in
12
+ * `docs/TECH_DEBT.md`; this module exists because *both* index classes in *this*
13
+ * package now need it, which is the point at which a second in-package copy stops
14
+ * being the cheaper thing.
15
+ */
16
+ export declare function invokeHook<T>(hook: () => Promise<Result<T>>): Promise<Result<T>>;
17
+ /**
18
+ * Compose the failure that aborted a rebuild with the outcome of the rollback
19
+ * that followed it.
20
+ *
21
+ * @remarks
22
+ * A rollback that ALSO fails is worth saying out loud: the `'fail'` path promises
23
+ * an empty index, and a caller that retries against a table which is neither the
24
+ * old index nor empty is working from a state the contract never described. This
25
+ * matters more here than in the in-memory package — these tables are **durable**,
26
+ * so a botched rollback survives the process.
27
+ */
28
+ export declare function withRollbackNote(error: string, rollback: Result<true>): string;
29
+ /**
30
+ * Increment `kind`'s tally by `by` (default one).
31
+ *
32
+ * @remarks
33
+ * The `by` parameter exists for the fragment lane, whose `fragments` count
34
+ * accumulates a fan-out rather than a record count — the one place a rebuild adds
35
+ * more than one per record.
36
+ */
37
+ export declare function tally(counts: Map<Kind, number>, kind: Kind, by?: number): void;
38
+ //# sourceMappingURL=rebuildHelpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebuildHelpers.d.ts","sourceRoot":"","sources":["../../../src/packlets/sqlite-vec-index/rebuildHelpers.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAsB,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE5C;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAEtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAE9E;AAED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,GAAE,MAAU,GAAG,IAAI,CAEjF"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2026 Erik Fortune
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.invokeHook = invokeHook;
8
+ exports.withRollbackNote = withRollbackNote;
9
+ exports.tally = tally;
10
+ const ts_utils_1 = require("@fgv/ts-utils");
11
+ /**
12
+ * Invoke a consumer-supplied hook that already returns a `Result`, converting a
13
+ * synchronous throw or a promise rejection into a `Failure` rather than letting
14
+ * it escape.
15
+ *
16
+ * @remarks
17
+ * Package-internal. `@fgv/ts-agent-memory` carries an identical private copy for
18
+ * its in-memory indexes. Exporting a single `AsyncDeferredResult`-invoking
19
+ * primitive from `ts-utils` is the right home and is recorded in
20
+ * `docs/TECH_DEBT.md`; this module exists because *both* index classes in *this*
21
+ * package now need it, which is the point at which a second in-package copy stops
22
+ * being the cheaper thing.
23
+ */
24
+ async function invokeHook(hook) {
25
+ return (await (0, ts_utils_1.captureAsyncResult)(hook)).onSuccess((inner) => inner);
26
+ }
27
+ /**
28
+ * Compose the failure that aborted a rebuild with the outcome of the rollback
29
+ * that followed it.
30
+ *
31
+ * @remarks
32
+ * A rollback that ALSO fails is worth saying out loud: the `'fail'` path promises
33
+ * an empty index, and a caller that retries against a table which is neither the
34
+ * old index nor empty is working from a state the contract never described. This
35
+ * matters more here than in the in-memory package — these tables are **durable**,
36
+ * so a botched rollback survives the process.
37
+ */
38
+ function withRollbackNote(error, rollback) {
39
+ return rollback.isFailure() ? `${error} (rollback also failed: ${rollback.message})` : error;
40
+ }
41
+ /**
42
+ * Increment `kind`'s tally by `by` (default one).
43
+ *
44
+ * @remarks
45
+ * The `by` parameter exists for the fragment lane, whose `fragments` count
46
+ * accumulates a fan-out rather than a record count — the one place a rebuild adds
47
+ * more than one per record.
48
+ */
49
+ function tally(counts, kind, by = 1) {
50
+ var _a;
51
+ counts.set(kind, ((_a = counts.get(kind)) !== null && _a !== void 0 ? _a : 0) + by);
52
+ }
53
+ //# sourceMappingURL=rebuildHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebuildHelpers.js","sourceRoot":"","sources":["../../../src/packlets/sqlite-vec-index/rebuildHelpers.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAkBH,gCAEC;AAaD,4CAEC;AAUD,sBAEC;AA7CD,4CAA2D;AAG3D;;;;;;;;;;;;GAYG;AACI,KAAK,UAAU,UAAU,CAAI,IAA8B;IAChE,OAAO,CAAC,MAAM,IAAA,6BAAkB,EAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,gBAAgB,CAAC,KAAa,EAAE,QAAsB;IACpE,OAAO,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,2BAA2B,QAAQ,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/F,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,KAAK,CAAC,MAAyB,EAAE,IAAU,EAAE,KAAa,CAAC;;IACzE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAA,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,mCAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;AACjD,CAAC","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, captureAsyncResult } from '@fgv/ts-utils';\nimport { Kind } from '@fgv/ts-agent-memory';\n\n/**\n * Invoke a consumer-supplied hook that already returns a `Result`, converting a\n * synchronous throw or a promise rejection into a `Failure` rather than letting\n * it escape.\n *\n * @remarks\n * Package-internal. `@fgv/ts-agent-memory` carries an identical private copy for\n * its in-memory indexes. Exporting a single `AsyncDeferredResult`-invoking\n * primitive from `ts-utils` is the right home and is recorded in\n * `docs/TECH_DEBT.md`; this module exists because *both* index classes in *this*\n * package now need it, which is the point at which a second in-package copy stops\n * being the cheaper thing.\n */\nexport async function invokeHook<T>(hook: () => Promise<Result<T>>): Promise<Result<T>> {\n return (await captureAsyncResult(hook)).onSuccess((inner) => inner);\n}\n\n/**\n * Compose the failure that aborted a rebuild with the outcome of the rollback\n * that followed it.\n *\n * @remarks\n * A rollback that ALSO fails is worth saying out loud: the `'fail'` path promises\n * an empty index, and a caller that retries against a table which is neither the\n * old index nor empty is working from a state the contract never described. This\n * matters more here than in the in-memory package — these tables are **durable**,\n * so a botched rollback survives the process.\n */\nexport function withRollbackNote(error: string, rollback: Result<true>): string {\n return rollback.isFailure() ? `${error} (rollback also failed: ${rollback.message})` : error;\n}\n\n/**\n * Increment `kind`'s tally by `by` (default one).\n *\n * @remarks\n * The `by` parameter exists for the fragment lane, whose `fragments` count\n * accumulates a fan-out rather than a record count — the one place a rebuild adds\n * more than one per record.\n */\nexport function tally(counts: Map<Kind, number>, kind: Kind, by: number = 1): void {\n counts.set(kind, (counts.get(kind) ?? 0) + by);\n}\n"]}