@atscript/db-sqlite 0.1.127 → 0.1.129
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/dist/index.cjs +548 -173
- package/dist/index.d.cts +231 -60
- package/dist/index.d.mts +231 -60
- package/dist/index.mjs +548 -176
- package/package.json +10 -10
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbFieldMeta, TDbInsertManyResult, TDbInsertResult, TDbUpdateResult, TExistingColumn, TFieldOps, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair } from "@atscript/db";
|
|
1
|
+
import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbFieldMeta, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TFieldOps, TGenericLogger, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair } from "@atscript/db";
|
|
2
2
|
import { TMetadataMap } from "@atscript/typescript/utils";
|
|
3
3
|
import { FilterExpr as FilterExpr$1 } from "@uniqu/core";
|
|
4
4
|
import { TSqlFragment, TSqlFragment as TSqlFragment$1 } from "@atscript/db-sql-tools";
|
|
@@ -54,6 +54,176 @@ interface TSqliteDriver {
|
|
|
54
54
|
readonly hasVectorExt?: boolean;
|
|
55
55
|
}
|
|
56
56
|
//#endregion
|
|
57
|
+
//#region src/better-sqlite3-driver.d.ts
|
|
58
|
+
interface TBetterSqlite3DriverOptions extends Record<string, unknown> {
|
|
59
|
+
/** Load the optional `sqlite-vec` extension. */
|
|
60
|
+
vector?: boolean;
|
|
61
|
+
/** Absolute paths to SQLite loadable extensions, passed to `Database.loadExtension`. */
|
|
62
|
+
loadExtensions?: string[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* {@link TSqliteDriver} implementation backed by `better-sqlite3`.
|
|
66
|
+
*
|
|
67
|
+
* Accepts either a file path (opens a new database) or a pre-created
|
|
68
|
+
* `Database` instance from `better-sqlite3`.
|
|
69
|
+
*
|
|
70
|
+
* ```typescript
|
|
71
|
+
* import { BetterSqlite3Driver } from '@atscript/db-sqlite'
|
|
72
|
+
*
|
|
73
|
+
* // In-memory database
|
|
74
|
+
* const driver = new BetterSqlite3Driver(':memory:')
|
|
75
|
+
*
|
|
76
|
+
* // File-based database
|
|
77
|
+
* const driver = new BetterSqlite3Driver('./my-data.db')
|
|
78
|
+
*
|
|
79
|
+
* // With sqlite-vec extension loaded
|
|
80
|
+
* const driver = new BetterSqlite3Driver('./my-data.db', { vector: true })
|
|
81
|
+
*
|
|
82
|
+
* // Pre-created instance
|
|
83
|
+
* import Database from 'better-sqlite3'
|
|
84
|
+
* const db = new Database(':memory:', { verbose: console.log })
|
|
85
|
+
* const driver = new BetterSqlite3Driver(db)
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* Requires `better-sqlite3` to be installed:
|
|
89
|
+
* ```bash
|
|
90
|
+
* pnpm add better-sqlite3
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* Vector search support requires the optional `sqlite-vec` package:
|
|
94
|
+
* ```bash
|
|
95
|
+
* pnpm add sqlite-vec
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare class BetterSqlite3Driver implements TSqliteDriver {
|
|
99
|
+
private db;
|
|
100
|
+
readonly hasVectorExt: boolean;
|
|
101
|
+
constructor(pathOrDb: string | import("better-sqlite3").Database, options?: TBetterSqlite3DriverOptions);
|
|
102
|
+
run(sql: string, params?: unknown[]): TSqliteRunResult;
|
|
103
|
+
all<T = Record<string, unknown>>(sql: string, params?: unknown[]): T[];
|
|
104
|
+
get<T = Record<string, unknown>>(sql: string, params?: unknown[]): T | null;
|
|
105
|
+
exec(sql: string): void;
|
|
106
|
+
close(): void;
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/tx-gate.d.ts
|
|
110
|
+
/**
|
|
111
|
+
* Per-waiter options for the transaction gate (taken from the adapter's
|
|
112
|
+
* {@link SqliteAdapterOptions} at wait time — the gate itself is per driver).
|
|
113
|
+
*/
|
|
114
|
+
interface SqliteTxWaitOptions {
|
|
115
|
+
/**
|
|
116
|
+
* Max milliseconds a waiter blocks for the connection before rejecting with
|
|
117
|
+
* `DbError("TX_WAIT_TIMEOUT")`. Default: unbounded (matches the mysql2 / pg
|
|
118
|
+
* pool defaults).
|
|
119
|
+
*/
|
|
120
|
+
transactionWaitTimeoutMs?: number;
|
|
121
|
+
/**
|
|
122
|
+
* Log a warning through `logger` once a waiter has waited this long.
|
|
123
|
+
* Default: 5000 ms. `0` disables the warning.
|
|
124
|
+
*/
|
|
125
|
+
transactionWaitWarnMs?: number;
|
|
126
|
+
/** Logger for the wait warning (the adapter passes its own). */
|
|
127
|
+
logger?: TGenericLogger;
|
|
128
|
+
}
|
|
129
|
+
/** Options accepted by `SqliteAdapter` / `createAdapter` for the transaction gate. */
|
|
130
|
+
type SqliteAdapterOptions = Pick<SqliteTxWaitOptions, "transactionWaitTimeoutMs" | "transactionWaitWarnMs">;
|
|
131
|
+
/**
|
|
132
|
+
* One waiter for the gate, created once per `_stmt` / `acquire` call and kept
|
|
133
|
+
* across wake-ups: the timeout and warn timers are armed ONCE with an absolute
|
|
134
|
+
* deadline, so `transactionWaitTimeoutMs` bounds the waiter's TOTAL wait
|
|
135
|
+
* however many transactions run ahead of it, and a re-queued waiter allocates
|
|
136
|
+
* nothing new. `next()` resolves on the next release (or rejects once the
|
|
137
|
+
* deadline passed); `dispose()` clears the timers.
|
|
138
|
+
*/
|
|
139
|
+
declare class GateWaiter {
|
|
140
|
+
private readonly _gate;
|
|
141
|
+
private _resolve?;
|
|
142
|
+
private _reject?;
|
|
143
|
+
private _timeoutTimer?;
|
|
144
|
+
private _warnTimer?;
|
|
145
|
+
private _expired?;
|
|
146
|
+
private readonly _startedAt;
|
|
147
|
+
constructor(_gate: SqliteTxGate, opts: SqliteTxWaitOptions | undefined);
|
|
148
|
+
/** Resolves on the NEXT release; rejects with `TX_WAIT_TIMEOUT` once the deadline passed. */
|
|
149
|
+
next(): Promise<void>;
|
|
150
|
+
/** Called by the gate on release: wakes the pending `next()` (a no-op when none is pending). */
|
|
151
|
+
wake(): void;
|
|
152
|
+
dispose(): void;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Logical FIFO mutex around the single synchronous SQLite connection.
|
|
156
|
+
*
|
|
157
|
+
* SQLite has one connection per driver, so two async contexts cannot each
|
|
158
|
+
* own a transaction: a second `BEGIN` fails with "cannot start a transaction
|
|
159
|
+
* within a transaction", and — worse — any statement from another context
|
|
160
|
+
* that runs while a transaction is open executes INSIDE that transaction
|
|
161
|
+
* (rolled back with it, reads its uncommitted rows). The gate serialises:
|
|
162
|
+
*
|
|
163
|
+
* - {@link acquire} / {@link release} — taken by `BEGIN`, released by
|
|
164
|
+
* `COMMIT` / `ROLLBACK`;
|
|
165
|
+
* - {@link runWhenFree} — plain (non-transactional) statements check
|
|
166
|
+
* {@link held} synchronously right before executing and wait otherwise;
|
|
167
|
+
* - {@link runExclusive} — hold the connection without a transaction (schema sync).
|
|
168
|
+
*
|
|
169
|
+
* Fairness: one FIFO waiter list for acquirers and statement waiters alike;
|
|
170
|
+
* a release wakes every current waiter in registration order, each re-checks
|
|
171
|
+
* synchronously and re-queues (ahead of any new arrival) if someone ahead took
|
|
172
|
+
* the gate. A waiter is one object with timers armed once, whatever the
|
|
173
|
+
* number of wake-ups. The fast path (nothing held) allocates nothing.
|
|
174
|
+
*/
|
|
175
|
+
declare class SqliteTxGate {
|
|
176
|
+
/** True while a transaction (or an exclusive hold) owns the connection. */
|
|
177
|
+
held: boolean;
|
|
178
|
+
/** `Date.now()` when the current holder took the gate (0 when free). */
|
|
179
|
+
heldSince: number;
|
|
180
|
+
private _waiters;
|
|
181
|
+
/** True when the current async context holds this gate via {@link runExclusive}. */
|
|
182
|
+
get heldByCurrentContext(): boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Waits until the gate is free, then takes it synchronously (no `await`
|
|
185
|
+
* between the check and `held = true`). Pair with {@link release}.
|
|
186
|
+
*/
|
|
187
|
+
acquire(opts?: SqliteTxWaitOptions): Promise<void>;
|
|
188
|
+
/** Frees the gate and wakes every waiter (in registration order). */
|
|
189
|
+
release(): void;
|
|
190
|
+
/**
|
|
191
|
+
* Runs one synchronous statement once no transaction holds the connection
|
|
192
|
+
* (immediately when the gate is free). The `held` check and `fn` run in
|
|
193
|
+
* the same synchronous segment, so they cannot interleave with a `BEGIN`
|
|
194
|
+
* from another context.
|
|
195
|
+
*/
|
|
196
|
+
runWhenFree<R>(fn: () => R, opts?: SqliteTxWaitOptions): Promise<R>;
|
|
197
|
+
/**
|
|
198
|
+
* Holds the gate for the duration of `fn` WITHOUT opening a transaction
|
|
199
|
+
* (nested transactions inside `fn` still `BEGIN`/`COMMIT` on the held
|
|
200
|
+
* connection without re-acquiring). Re-entrant for the same async context.
|
|
201
|
+
*/
|
|
202
|
+
runExclusive<T>(fn: () => Promise<T>, opts?: SqliteTxWaitOptions): Promise<T>;
|
|
203
|
+
/** @internal */
|
|
204
|
+
_enqueue(waiter: GateWaiter): void;
|
|
205
|
+
/** @internal */
|
|
206
|
+
_dequeue(waiter: GateWaiter): void;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Transaction state returned by `SqliteAdapter._beginTransaction`: releases
|
|
210
|
+
* the gate the transaction holds exactly once (a transaction opened inside an
|
|
211
|
+
* exclusive hold owns nothing to release). Ownership across adapters is
|
|
212
|
+
* decided by the core (`_transactionOwner()` = the driver), not here.
|
|
213
|
+
*/
|
|
214
|
+
declare class SqliteTxState {
|
|
215
|
+
private readonly _gate;
|
|
216
|
+
private _holdsGate;
|
|
217
|
+
constructor(_gate: SqliteTxGate, _holdsGate: boolean);
|
|
218
|
+
release(): void;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Returns the gate for a driver instance (one gate per driver — every adapter
|
|
222
|
+
* of a space shares its driver, so they share the gate; two driver wrappers
|
|
223
|
+
* around one `Database` would get two gates: construct one driver per database).
|
|
224
|
+
*/
|
|
225
|
+
declare function getSqliteTxGate(driver: TSqliteDriver): SqliteTxGate;
|
|
226
|
+
//#endregion
|
|
57
227
|
//#region src/sqlite-adapter.d.ts
|
|
58
228
|
/**
|
|
59
229
|
* SQLite adapter for {@link AtscriptDbTable}.
|
|
@@ -83,14 +253,42 @@ declare class SqliteAdapter extends BaseDbAdapter {
|
|
|
83
253
|
private _vectorThresholds;
|
|
84
254
|
/** Partition filter fields per vector index (from @db.search.filter). Field paths. */
|
|
85
255
|
private _vectorPartitionFields;
|
|
86
|
-
|
|
256
|
+
/** Per-driver transaction gate (shared by every adapter over this driver). */
|
|
257
|
+
private readonly _gate;
|
|
258
|
+
private readonly _txOptions;
|
|
259
|
+
constructor(driver: TSqliteDriver, options?: SqliteAdapterOptions);
|
|
87
260
|
onFieldScanned(field: string, _type: unknown, metadata: TMetadataMap<AtscriptMetadata>): void;
|
|
88
261
|
formatValue(field: TDbFieldMeta): TValueFormatterPair | undefined;
|
|
89
262
|
isVectorSearchable(): boolean;
|
|
90
263
|
private _detectVectorSupport;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
protected
|
|
264
|
+
private _waitOpts;
|
|
265
|
+
/** Every adapter over this driver shares one transaction — and one gate (since 0.1.128). */
|
|
266
|
+
protected _transactionOwner(): unknown;
|
|
267
|
+
/** Inside our own transaction, or holding the connection exclusively. */
|
|
268
|
+
private _ownsConnection;
|
|
269
|
+
/**
|
|
270
|
+
* Runs one synchronous driver statement. Inside our own transaction (or an
|
|
271
|
+
* exclusive hold) it runs immediately; otherwise it waits until no
|
|
272
|
+
* transaction is open on the connection. The `held` check and the statement
|
|
273
|
+
* run in the same synchronous segment, so they cannot interleave with a
|
|
274
|
+
* `BEGIN` from another context.
|
|
275
|
+
*/
|
|
276
|
+
private _stmt;
|
|
277
|
+
/**
|
|
278
|
+
* Holds the connection for the duration of `fn` WITHOUT opening a
|
|
279
|
+
* transaction: no other context's statement or transaction can interleave.
|
|
280
|
+
* Meant for the schema entry points that `await` mid-body or toggle
|
|
281
|
+
* connection-level PRAGMAs (`ensureTable`, `recreateTable`, `dropColumns`,
|
|
282
|
+
* `dropTablesByName`, `syncIndexes`) — their DDL must not land inside a
|
|
283
|
+
* request's transaction (PRAGMA changes are no-ops inside one). Inside our
|
|
284
|
+
* own transaction `fn` runs directly; re-entrant for the same async
|
|
285
|
+
* context; nested `withTransaction` calls inside `fn` still BEGIN/COMMIT
|
|
286
|
+
* on the held connection.
|
|
287
|
+
*/
|
|
288
|
+
protected _withExclusiveConnection<T>(fn: () => Promise<T>): Promise<T>;
|
|
289
|
+
protected _beginTransaction(): Promise<SqliteTxState>;
|
|
290
|
+
protected _commitTransaction(state: unknown): Promise<void>;
|
|
291
|
+
protected _rollbackTransaction(state: unknown): Promise<void>;
|
|
94
292
|
/** SQLite does not use schemas — override to always exclude schema. */
|
|
95
293
|
resolveTableName(): string;
|
|
96
294
|
/** SQLite enforces FK constraints natively via PRAGMA foreign_keys. */
|
|
@@ -113,7 +311,11 @@ declare class SqliteAdapter extends BaseDbAdapter {
|
|
|
113
311
|
replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
|
|
114
312
|
deleteOne(filter: FilterExpr): Promise<TDbDeleteResult>;
|
|
115
313
|
deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
|
|
116
|
-
|
|
314
|
+
/**
|
|
315
|
+
* SQLite accepts forward FK references, so a foreign-key cycle is created
|
|
316
|
+
* inline in any order — `opts.deferForeignKeysTo` is accepted and ignored.
|
|
317
|
+
*/
|
|
318
|
+
ensureTable(_opts?: TEnsureTableOptions): Promise<void>;
|
|
117
319
|
private _incrementSeeded;
|
|
118
320
|
/**
|
|
119
321
|
* Seeds the sqlite_sequence table for auto-increment fields that have a start value.
|
|
@@ -129,6 +331,25 @@ declare class SqliteAdapter extends BaseDbAdapter {
|
|
|
129
331
|
dropIndexesForColumns(columns: string[]): Promise<void>;
|
|
130
332
|
dropTableByName(tableName: string): Promise<void>;
|
|
131
333
|
dropViewByName(viewName: string): Promise<void>;
|
|
334
|
+
/**
|
|
335
|
+
* Drops a group of mutually referencing tables with FK enforcement off for
|
|
336
|
+
* the duration (`PRAGMA foreign_keys` is a no-op inside a transaction, so
|
|
337
|
+
* the exclusive connection hold — not a `BEGIN` — keeps another context's
|
|
338
|
+
* transaction from overlapping it). Safe because schema sync only passes
|
|
339
|
+
* groups whose referrers are all inside the group.
|
|
340
|
+
*/
|
|
341
|
+
dropTablesByName(tableNames: string[]): Promise<void>;
|
|
342
|
+
hasRows(tableName?: string): Promise<boolean>;
|
|
343
|
+
/**
|
|
344
|
+
* Live foreign keys referencing `tableName`: `PRAGMA foreign_key_list` is
|
|
345
|
+
* outbound-only, so the tables in `sqlite_master` are scanned (a loop
|
|
346
|
+
* rather than the `pragma_foreign_key_list` table-valued function, which
|
|
347
|
+
* not every bundled SQLite exposes). Only tables whose CREATE text contains
|
|
348
|
+
* `REFERENCES` can declare a foreign key — the prefilter is a superset, a
|
|
349
|
+
* false positive just costs one PRAGMA.
|
|
350
|
+
*/
|
|
351
|
+
getReferencingForeignKeys(tableName: string): Promise<TReferencingForeignKey[]>;
|
|
352
|
+
getObjectKind(name: string): Promise<TDbObjectKind | undefined>;
|
|
132
353
|
renameTable(oldName: string): Promise<void>;
|
|
133
354
|
typeMapper(field: TDbFieldMeta): string;
|
|
134
355
|
getExistingColumnsForTable(tableName: string): Promise<TExistingColumn[]>;
|
|
@@ -214,58 +435,6 @@ declare class SqliteAdapter extends BaseDbAdapter {
|
|
|
214
435
|
private _dropAllVecTables;
|
|
215
436
|
}
|
|
216
437
|
//#endregion
|
|
217
|
-
//#region src/better-sqlite3-driver.d.ts
|
|
218
|
-
interface TBetterSqlite3DriverOptions extends Record<string, unknown> {
|
|
219
|
-
/** Load the optional `sqlite-vec` extension. */
|
|
220
|
-
vector?: boolean;
|
|
221
|
-
/** Absolute paths to SQLite loadable extensions, passed to `Database.loadExtension`. */
|
|
222
|
-
loadExtensions?: string[];
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* {@link TSqliteDriver} implementation backed by `better-sqlite3`.
|
|
226
|
-
*
|
|
227
|
-
* Accepts either a file path (opens a new database) or a pre-created
|
|
228
|
-
* `Database` instance from `better-sqlite3`.
|
|
229
|
-
*
|
|
230
|
-
* ```typescript
|
|
231
|
-
* import { BetterSqlite3Driver } from '@atscript/db-sqlite'
|
|
232
|
-
*
|
|
233
|
-
* // In-memory database
|
|
234
|
-
* const driver = new BetterSqlite3Driver(':memory:')
|
|
235
|
-
*
|
|
236
|
-
* // File-based database
|
|
237
|
-
* const driver = new BetterSqlite3Driver('./my-data.db')
|
|
238
|
-
*
|
|
239
|
-
* // With sqlite-vec extension loaded
|
|
240
|
-
* const driver = new BetterSqlite3Driver('./my-data.db', { vector: true })
|
|
241
|
-
*
|
|
242
|
-
* // Pre-created instance
|
|
243
|
-
* import Database from 'better-sqlite3'
|
|
244
|
-
* const db = new Database(':memory:', { verbose: console.log })
|
|
245
|
-
* const driver = new BetterSqlite3Driver(db)
|
|
246
|
-
* ```
|
|
247
|
-
*
|
|
248
|
-
* Requires `better-sqlite3` to be installed:
|
|
249
|
-
* ```bash
|
|
250
|
-
* pnpm add better-sqlite3
|
|
251
|
-
* ```
|
|
252
|
-
*
|
|
253
|
-
* Vector search support requires the optional `sqlite-vec` package:
|
|
254
|
-
* ```bash
|
|
255
|
-
* pnpm add sqlite-vec
|
|
256
|
-
* ```
|
|
257
|
-
*/
|
|
258
|
-
declare class BetterSqlite3Driver implements TSqliteDriver {
|
|
259
|
-
private db;
|
|
260
|
-
readonly hasVectorExt: boolean;
|
|
261
|
-
constructor(pathOrDb: string | import("better-sqlite3").Database, options?: TBetterSqlite3DriverOptions);
|
|
262
|
-
run(sql: string, params?: unknown[]): TSqliteRunResult;
|
|
263
|
-
all<T = Record<string, unknown>>(sql: string, params?: unknown[]): T[];
|
|
264
|
-
get<T = Record<string, unknown>>(sql: string, params?: unknown[]): T | null;
|
|
265
|
-
exec(sql: string): void;
|
|
266
|
-
close(): void;
|
|
267
|
-
}
|
|
268
|
-
//#endregion
|
|
269
438
|
//#region src/filter-builder.d.ts
|
|
270
439
|
/**
|
|
271
440
|
* Translates a uniqu filter expression into a parameterized SQL WHERE clause.
|
|
@@ -276,6 +445,8 @@ declare class BetterSqlite3Driver implements TSqliteDriver {
|
|
|
276
445
|
declare function buildWhere(filter: FilterExpr$1): TSqlFragment$1;
|
|
277
446
|
//#endregion
|
|
278
447
|
//#region src/index.d.ts
|
|
279
|
-
|
|
448
|
+
/** `createAdapter` options: driver options plus the transaction-gate options (since 0.1.128). */
|
|
449
|
+
type TCreateSqliteAdapterOptions = TBetterSqlite3DriverOptions & SqliteAdapterOptions;
|
|
450
|
+
declare function createAdapter(connection: string, options?: TCreateSqliteAdapterOptions): DbSpace;
|
|
280
451
|
//#endregion
|
|
281
|
-
export { BetterSqlite3Driver, SqliteAdapter, type TSqlFragment, type TSqliteDriver, type TSqliteRunResult, buildWhere, createAdapter };
|
|
452
|
+
export { BetterSqlite3Driver, SqliteAdapter, type SqliteAdapterOptions, SqliteTxGate, SqliteTxState, type SqliteTxWaitOptions, type TBetterSqlite3DriverOptions, TCreateSqliteAdapterOptions, type TSqlFragment, type TSqliteDriver, type TSqliteRunResult, buildWhere, createAdapter, getSqliteTxGate };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbFieldMeta, TDbInsertManyResult, TDbInsertResult, TDbUpdateResult, TExistingColumn, TFieldOps, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair } from "@atscript/db";
|
|
1
|
+
import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbFieldMeta, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TFieldOps, TGenericLogger, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair } from "@atscript/db";
|
|
2
2
|
import { TSqlFragment, TSqlFragment as TSqlFragment$1 } from "@atscript/db-sql-tools";
|
|
3
3
|
import { TMetadataMap } from "@atscript/typescript/utils";
|
|
4
4
|
import { FilterExpr as FilterExpr$1 } from "@uniqu/core";
|
|
@@ -54,6 +54,176 @@ interface TSqliteDriver {
|
|
|
54
54
|
readonly hasVectorExt?: boolean;
|
|
55
55
|
}
|
|
56
56
|
//#endregion
|
|
57
|
+
//#region src/better-sqlite3-driver.d.ts
|
|
58
|
+
interface TBetterSqlite3DriverOptions extends Record<string, unknown> {
|
|
59
|
+
/** Load the optional `sqlite-vec` extension. */
|
|
60
|
+
vector?: boolean;
|
|
61
|
+
/** Absolute paths to SQLite loadable extensions, passed to `Database.loadExtension`. */
|
|
62
|
+
loadExtensions?: string[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* {@link TSqliteDriver} implementation backed by `better-sqlite3`.
|
|
66
|
+
*
|
|
67
|
+
* Accepts either a file path (opens a new database) or a pre-created
|
|
68
|
+
* `Database` instance from `better-sqlite3`.
|
|
69
|
+
*
|
|
70
|
+
* ```typescript
|
|
71
|
+
* import { BetterSqlite3Driver } from '@atscript/db-sqlite'
|
|
72
|
+
*
|
|
73
|
+
* // In-memory database
|
|
74
|
+
* const driver = new BetterSqlite3Driver(':memory:')
|
|
75
|
+
*
|
|
76
|
+
* // File-based database
|
|
77
|
+
* const driver = new BetterSqlite3Driver('./my-data.db')
|
|
78
|
+
*
|
|
79
|
+
* // With sqlite-vec extension loaded
|
|
80
|
+
* const driver = new BetterSqlite3Driver('./my-data.db', { vector: true })
|
|
81
|
+
*
|
|
82
|
+
* // Pre-created instance
|
|
83
|
+
* import Database from 'better-sqlite3'
|
|
84
|
+
* const db = new Database(':memory:', { verbose: console.log })
|
|
85
|
+
* const driver = new BetterSqlite3Driver(db)
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* Requires `better-sqlite3` to be installed:
|
|
89
|
+
* ```bash
|
|
90
|
+
* pnpm add better-sqlite3
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* Vector search support requires the optional `sqlite-vec` package:
|
|
94
|
+
* ```bash
|
|
95
|
+
* pnpm add sqlite-vec
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare class BetterSqlite3Driver implements TSqliteDriver {
|
|
99
|
+
private db;
|
|
100
|
+
readonly hasVectorExt: boolean;
|
|
101
|
+
constructor(pathOrDb: string | import("better-sqlite3").Database, options?: TBetterSqlite3DriverOptions);
|
|
102
|
+
run(sql: string, params?: unknown[]): TSqliteRunResult;
|
|
103
|
+
all<T = Record<string, unknown>>(sql: string, params?: unknown[]): T[];
|
|
104
|
+
get<T = Record<string, unknown>>(sql: string, params?: unknown[]): T | null;
|
|
105
|
+
exec(sql: string): void;
|
|
106
|
+
close(): void;
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/tx-gate.d.ts
|
|
110
|
+
/**
|
|
111
|
+
* Per-waiter options for the transaction gate (taken from the adapter's
|
|
112
|
+
* {@link SqliteAdapterOptions} at wait time — the gate itself is per driver).
|
|
113
|
+
*/
|
|
114
|
+
interface SqliteTxWaitOptions {
|
|
115
|
+
/**
|
|
116
|
+
* Max milliseconds a waiter blocks for the connection before rejecting with
|
|
117
|
+
* `DbError("TX_WAIT_TIMEOUT")`. Default: unbounded (matches the mysql2 / pg
|
|
118
|
+
* pool defaults).
|
|
119
|
+
*/
|
|
120
|
+
transactionWaitTimeoutMs?: number;
|
|
121
|
+
/**
|
|
122
|
+
* Log a warning through `logger` once a waiter has waited this long.
|
|
123
|
+
* Default: 5000 ms. `0` disables the warning.
|
|
124
|
+
*/
|
|
125
|
+
transactionWaitWarnMs?: number;
|
|
126
|
+
/** Logger for the wait warning (the adapter passes its own). */
|
|
127
|
+
logger?: TGenericLogger;
|
|
128
|
+
}
|
|
129
|
+
/** Options accepted by `SqliteAdapter` / `createAdapter` for the transaction gate. */
|
|
130
|
+
type SqliteAdapterOptions = Pick<SqliteTxWaitOptions, "transactionWaitTimeoutMs" | "transactionWaitWarnMs">;
|
|
131
|
+
/**
|
|
132
|
+
* One waiter for the gate, created once per `_stmt` / `acquire` call and kept
|
|
133
|
+
* across wake-ups: the timeout and warn timers are armed ONCE with an absolute
|
|
134
|
+
* deadline, so `transactionWaitTimeoutMs` bounds the waiter's TOTAL wait
|
|
135
|
+
* however many transactions run ahead of it, and a re-queued waiter allocates
|
|
136
|
+
* nothing new. `next()` resolves on the next release (or rejects once the
|
|
137
|
+
* deadline passed); `dispose()` clears the timers.
|
|
138
|
+
*/
|
|
139
|
+
declare class GateWaiter {
|
|
140
|
+
private readonly _gate;
|
|
141
|
+
private _resolve?;
|
|
142
|
+
private _reject?;
|
|
143
|
+
private _timeoutTimer?;
|
|
144
|
+
private _warnTimer?;
|
|
145
|
+
private _expired?;
|
|
146
|
+
private readonly _startedAt;
|
|
147
|
+
constructor(_gate: SqliteTxGate, opts: SqliteTxWaitOptions | undefined);
|
|
148
|
+
/** Resolves on the NEXT release; rejects with `TX_WAIT_TIMEOUT` once the deadline passed. */
|
|
149
|
+
next(): Promise<void>;
|
|
150
|
+
/** Called by the gate on release: wakes the pending `next()` (a no-op when none is pending). */
|
|
151
|
+
wake(): void;
|
|
152
|
+
dispose(): void;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Logical FIFO mutex around the single synchronous SQLite connection.
|
|
156
|
+
*
|
|
157
|
+
* SQLite has one connection per driver, so two async contexts cannot each
|
|
158
|
+
* own a transaction: a second `BEGIN` fails with "cannot start a transaction
|
|
159
|
+
* within a transaction", and — worse — any statement from another context
|
|
160
|
+
* that runs while a transaction is open executes INSIDE that transaction
|
|
161
|
+
* (rolled back with it, reads its uncommitted rows). The gate serialises:
|
|
162
|
+
*
|
|
163
|
+
* - {@link acquire} / {@link release} — taken by `BEGIN`, released by
|
|
164
|
+
* `COMMIT` / `ROLLBACK`;
|
|
165
|
+
* - {@link runWhenFree} — plain (non-transactional) statements check
|
|
166
|
+
* {@link held} synchronously right before executing and wait otherwise;
|
|
167
|
+
* - {@link runExclusive} — hold the connection without a transaction (schema sync).
|
|
168
|
+
*
|
|
169
|
+
* Fairness: one FIFO waiter list for acquirers and statement waiters alike;
|
|
170
|
+
* a release wakes every current waiter in registration order, each re-checks
|
|
171
|
+
* synchronously and re-queues (ahead of any new arrival) if someone ahead took
|
|
172
|
+
* the gate. A waiter is one object with timers armed once, whatever the
|
|
173
|
+
* number of wake-ups. The fast path (nothing held) allocates nothing.
|
|
174
|
+
*/
|
|
175
|
+
declare class SqliteTxGate {
|
|
176
|
+
/** True while a transaction (or an exclusive hold) owns the connection. */
|
|
177
|
+
held: boolean;
|
|
178
|
+
/** `Date.now()` when the current holder took the gate (0 when free). */
|
|
179
|
+
heldSince: number;
|
|
180
|
+
private _waiters;
|
|
181
|
+
/** True when the current async context holds this gate via {@link runExclusive}. */
|
|
182
|
+
get heldByCurrentContext(): boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Waits until the gate is free, then takes it synchronously (no `await`
|
|
185
|
+
* between the check and `held = true`). Pair with {@link release}.
|
|
186
|
+
*/
|
|
187
|
+
acquire(opts?: SqliteTxWaitOptions): Promise<void>;
|
|
188
|
+
/** Frees the gate and wakes every waiter (in registration order). */
|
|
189
|
+
release(): void;
|
|
190
|
+
/**
|
|
191
|
+
* Runs one synchronous statement once no transaction holds the connection
|
|
192
|
+
* (immediately when the gate is free). The `held` check and `fn` run in
|
|
193
|
+
* the same synchronous segment, so they cannot interleave with a `BEGIN`
|
|
194
|
+
* from another context.
|
|
195
|
+
*/
|
|
196
|
+
runWhenFree<R>(fn: () => R, opts?: SqliteTxWaitOptions): Promise<R>;
|
|
197
|
+
/**
|
|
198
|
+
* Holds the gate for the duration of `fn` WITHOUT opening a transaction
|
|
199
|
+
* (nested transactions inside `fn` still `BEGIN`/`COMMIT` on the held
|
|
200
|
+
* connection without re-acquiring). Re-entrant for the same async context.
|
|
201
|
+
*/
|
|
202
|
+
runExclusive<T>(fn: () => Promise<T>, opts?: SqliteTxWaitOptions): Promise<T>;
|
|
203
|
+
/** @internal */
|
|
204
|
+
_enqueue(waiter: GateWaiter): void;
|
|
205
|
+
/** @internal */
|
|
206
|
+
_dequeue(waiter: GateWaiter): void;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Transaction state returned by `SqliteAdapter._beginTransaction`: releases
|
|
210
|
+
* the gate the transaction holds exactly once (a transaction opened inside an
|
|
211
|
+
* exclusive hold owns nothing to release). Ownership across adapters is
|
|
212
|
+
* decided by the core (`_transactionOwner()` = the driver), not here.
|
|
213
|
+
*/
|
|
214
|
+
declare class SqliteTxState {
|
|
215
|
+
private readonly _gate;
|
|
216
|
+
private _holdsGate;
|
|
217
|
+
constructor(_gate: SqliteTxGate, _holdsGate: boolean);
|
|
218
|
+
release(): void;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Returns the gate for a driver instance (one gate per driver — every adapter
|
|
222
|
+
* of a space shares its driver, so they share the gate; two driver wrappers
|
|
223
|
+
* around one `Database` would get two gates: construct one driver per database).
|
|
224
|
+
*/
|
|
225
|
+
declare function getSqliteTxGate(driver: TSqliteDriver): SqliteTxGate;
|
|
226
|
+
//#endregion
|
|
57
227
|
//#region src/sqlite-adapter.d.ts
|
|
58
228
|
/**
|
|
59
229
|
* SQLite adapter for {@link AtscriptDbTable}.
|
|
@@ -83,14 +253,42 @@ declare class SqliteAdapter extends BaseDbAdapter {
|
|
|
83
253
|
private _vectorThresholds;
|
|
84
254
|
/** Partition filter fields per vector index (from @db.search.filter). Field paths. */
|
|
85
255
|
private _vectorPartitionFields;
|
|
86
|
-
|
|
256
|
+
/** Per-driver transaction gate (shared by every adapter over this driver). */
|
|
257
|
+
private readonly _gate;
|
|
258
|
+
private readonly _txOptions;
|
|
259
|
+
constructor(driver: TSqliteDriver, options?: SqliteAdapterOptions);
|
|
87
260
|
onFieldScanned(field: string, _type: unknown, metadata: TMetadataMap<AtscriptMetadata>): void;
|
|
88
261
|
formatValue(field: TDbFieldMeta): TValueFormatterPair | undefined;
|
|
89
262
|
isVectorSearchable(): boolean;
|
|
90
263
|
private _detectVectorSupport;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
protected
|
|
264
|
+
private _waitOpts;
|
|
265
|
+
/** Every adapter over this driver shares one transaction — and one gate (since 0.1.128). */
|
|
266
|
+
protected _transactionOwner(): unknown;
|
|
267
|
+
/** Inside our own transaction, or holding the connection exclusively. */
|
|
268
|
+
private _ownsConnection;
|
|
269
|
+
/**
|
|
270
|
+
* Runs one synchronous driver statement. Inside our own transaction (or an
|
|
271
|
+
* exclusive hold) it runs immediately; otherwise it waits until no
|
|
272
|
+
* transaction is open on the connection. The `held` check and the statement
|
|
273
|
+
* run in the same synchronous segment, so they cannot interleave with a
|
|
274
|
+
* `BEGIN` from another context.
|
|
275
|
+
*/
|
|
276
|
+
private _stmt;
|
|
277
|
+
/**
|
|
278
|
+
* Holds the connection for the duration of `fn` WITHOUT opening a
|
|
279
|
+
* transaction: no other context's statement or transaction can interleave.
|
|
280
|
+
* Meant for the schema entry points that `await` mid-body or toggle
|
|
281
|
+
* connection-level PRAGMAs (`ensureTable`, `recreateTable`, `dropColumns`,
|
|
282
|
+
* `dropTablesByName`, `syncIndexes`) — their DDL must not land inside a
|
|
283
|
+
* request's transaction (PRAGMA changes are no-ops inside one). Inside our
|
|
284
|
+
* own transaction `fn` runs directly; re-entrant for the same async
|
|
285
|
+
* context; nested `withTransaction` calls inside `fn` still BEGIN/COMMIT
|
|
286
|
+
* on the held connection.
|
|
287
|
+
*/
|
|
288
|
+
protected _withExclusiveConnection<T>(fn: () => Promise<T>): Promise<T>;
|
|
289
|
+
protected _beginTransaction(): Promise<SqliteTxState>;
|
|
290
|
+
protected _commitTransaction(state: unknown): Promise<void>;
|
|
291
|
+
protected _rollbackTransaction(state: unknown): Promise<void>;
|
|
94
292
|
/** SQLite does not use schemas — override to always exclude schema. */
|
|
95
293
|
resolveTableName(): string;
|
|
96
294
|
/** SQLite enforces FK constraints natively via PRAGMA foreign_keys. */
|
|
@@ -113,7 +311,11 @@ declare class SqliteAdapter extends BaseDbAdapter {
|
|
|
113
311
|
replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
|
|
114
312
|
deleteOne(filter: FilterExpr): Promise<TDbDeleteResult>;
|
|
115
313
|
deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
|
|
116
|
-
|
|
314
|
+
/**
|
|
315
|
+
* SQLite accepts forward FK references, so a foreign-key cycle is created
|
|
316
|
+
* inline in any order — `opts.deferForeignKeysTo` is accepted and ignored.
|
|
317
|
+
*/
|
|
318
|
+
ensureTable(_opts?: TEnsureTableOptions): Promise<void>;
|
|
117
319
|
private _incrementSeeded;
|
|
118
320
|
/**
|
|
119
321
|
* Seeds the sqlite_sequence table for auto-increment fields that have a start value.
|
|
@@ -129,6 +331,25 @@ declare class SqliteAdapter extends BaseDbAdapter {
|
|
|
129
331
|
dropIndexesForColumns(columns: string[]): Promise<void>;
|
|
130
332
|
dropTableByName(tableName: string): Promise<void>;
|
|
131
333
|
dropViewByName(viewName: string): Promise<void>;
|
|
334
|
+
/**
|
|
335
|
+
* Drops a group of mutually referencing tables with FK enforcement off for
|
|
336
|
+
* the duration (`PRAGMA foreign_keys` is a no-op inside a transaction, so
|
|
337
|
+
* the exclusive connection hold — not a `BEGIN` — keeps another context's
|
|
338
|
+
* transaction from overlapping it). Safe because schema sync only passes
|
|
339
|
+
* groups whose referrers are all inside the group.
|
|
340
|
+
*/
|
|
341
|
+
dropTablesByName(tableNames: string[]): Promise<void>;
|
|
342
|
+
hasRows(tableName?: string): Promise<boolean>;
|
|
343
|
+
/**
|
|
344
|
+
* Live foreign keys referencing `tableName`: `PRAGMA foreign_key_list` is
|
|
345
|
+
* outbound-only, so the tables in `sqlite_master` are scanned (a loop
|
|
346
|
+
* rather than the `pragma_foreign_key_list` table-valued function, which
|
|
347
|
+
* not every bundled SQLite exposes). Only tables whose CREATE text contains
|
|
348
|
+
* `REFERENCES` can declare a foreign key — the prefilter is a superset, a
|
|
349
|
+
* false positive just costs one PRAGMA.
|
|
350
|
+
*/
|
|
351
|
+
getReferencingForeignKeys(tableName: string): Promise<TReferencingForeignKey[]>;
|
|
352
|
+
getObjectKind(name: string): Promise<TDbObjectKind | undefined>;
|
|
132
353
|
renameTable(oldName: string): Promise<void>;
|
|
133
354
|
typeMapper(field: TDbFieldMeta): string;
|
|
134
355
|
getExistingColumnsForTable(tableName: string): Promise<TExistingColumn[]>;
|
|
@@ -214,58 +435,6 @@ declare class SqliteAdapter extends BaseDbAdapter {
|
|
|
214
435
|
private _dropAllVecTables;
|
|
215
436
|
}
|
|
216
437
|
//#endregion
|
|
217
|
-
//#region src/better-sqlite3-driver.d.ts
|
|
218
|
-
interface TBetterSqlite3DriverOptions extends Record<string, unknown> {
|
|
219
|
-
/** Load the optional `sqlite-vec` extension. */
|
|
220
|
-
vector?: boolean;
|
|
221
|
-
/** Absolute paths to SQLite loadable extensions, passed to `Database.loadExtension`. */
|
|
222
|
-
loadExtensions?: string[];
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* {@link TSqliteDriver} implementation backed by `better-sqlite3`.
|
|
226
|
-
*
|
|
227
|
-
* Accepts either a file path (opens a new database) or a pre-created
|
|
228
|
-
* `Database` instance from `better-sqlite3`.
|
|
229
|
-
*
|
|
230
|
-
* ```typescript
|
|
231
|
-
* import { BetterSqlite3Driver } from '@atscript/db-sqlite'
|
|
232
|
-
*
|
|
233
|
-
* // In-memory database
|
|
234
|
-
* const driver = new BetterSqlite3Driver(':memory:')
|
|
235
|
-
*
|
|
236
|
-
* // File-based database
|
|
237
|
-
* const driver = new BetterSqlite3Driver('./my-data.db')
|
|
238
|
-
*
|
|
239
|
-
* // With sqlite-vec extension loaded
|
|
240
|
-
* const driver = new BetterSqlite3Driver('./my-data.db', { vector: true })
|
|
241
|
-
*
|
|
242
|
-
* // Pre-created instance
|
|
243
|
-
* import Database from 'better-sqlite3'
|
|
244
|
-
* const db = new Database(':memory:', { verbose: console.log })
|
|
245
|
-
* const driver = new BetterSqlite3Driver(db)
|
|
246
|
-
* ```
|
|
247
|
-
*
|
|
248
|
-
* Requires `better-sqlite3` to be installed:
|
|
249
|
-
* ```bash
|
|
250
|
-
* pnpm add better-sqlite3
|
|
251
|
-
* ```
|
|
252
|
-
*
|
|
253
|
-
* Vector search support requires the optional `sqlite-vec` package:
|
|
254
|
-
* ```bash
|
|
255
|
-
* pnpm add sqlite-vec
|
|
256
|
-
* ```
|
|
257
|
-
*/
|
|
258
|
-
declare class BetterSqlite3Driver implements TSqliteDriver {
|
|
259
|
-
private db;
|
|
260
|
-
readonly hasVectorExt: boolean;
|
|
261
|
-
constructor(pathOrDb: string | import("better-sqlite3").Database, options?: TBetterSqlite3DriverOptions);
|
|
262
|
-
run(sql: string, params?: unknown[]): TSqliteRunResult;
|
|
263
|
-
all<T = Record<string, unknown>>(sql: string, params?: unknown[]): T[];
|
|
264
|
-
get<T = Record<string, unknown>>(sql: string, params?: unknown[]): T | null;
|
|
265
|
-
exec(sql: string): void;
|
|
266
|
-
close(): void;
|
|
267
|
-
}
|
|
268
|
-
//#endregion
|
|
269
438
|
//#region src/filter-builder.d.ts
|
|
270
439
|
/**
|
|
271
440
|
* Translates a uniqu filter expression into a parameterized SQL WHERE clause.
|
|
@@ -276,6 +445,8 @@ declare class BetterSqlite3Driver implements TSqliteDriver {
|
|
|
276
445
|
declare function buildWhere(filter: FilterExpr$1): TSqlFragment$1;
|
|
277
446
|
//#endregion
|
|
278
447
|
//#region src/index.d.ts
|
|
279
|
-
|
|
448
|
+
/** `createAdapter` options: driver options plus the transaction-gate options (since 0.1.128). */
|
|
449
|
+
type TCreateSqliteAdapterOptions = TBetterSqlite3DriverOptions & SqliteAdapterOptions;
|
|
450
|
+
declare function createAdapter(connection: string, options?: TCreateSqliteAdapterOptions): DbSpace;
|
|
280
451
|
//#endregion
|
|
281
|
-
export { BetterSqlite3Driver, SqliteAdapter, type TSqlFragment, type TSqliteDriver, type TSqliteRunResult, buildWhere, createAdapter };
|
|
452
|
+
export { BetterSqlite3Driver, SqliteAdapter, type SqliteAdapterOptions, SqliteTxGate, SqliteTxState, type SqliteTxWaitOptions, type TBetterSqlite3DriverOptions, TCreateSqliteAdapterOptions, type TSqlFragment, type TSqliteDriver, type TSqliteRunResult, buildWhere, createAdapter, getSqliteTxGate };
|