uql-orm 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/README.md +5 -4
  2. package/dist/browser/uql-browser.min.js.map +3 -3
  3. package/dist/bunSql/bunSqlCockroachDialect.d.ts +2 -2
  4. package/dist/bunSql/bunSqlCockroachDialect.js +4 -10
  5. package/dist/bunSql/bunSqlPostgresDialect.d.ts +2 -2
  6. package/dist/bunSql/bunSqlPostgresDialect.js +4 -10
  7. package/dist/cockroachdb/cockroachDialect.d.ts +20 -4
  8. package/dist/cockroachdb/cockroachDialect.js +33 -18
  9. package/dist/d1/d1Querier.d.ts +4 -3
  10. package/dist/d1/d1Querier.js +0 -3
  11. package/dist/d1/d1SqliteDialect.d.ts +7 -0
  12. package/dist/d1/d1SqliteDialect.js +9 -0
  13. package/dist/dialect/abstractDialect.d.ts +8 -3
  14. package/dist/dialect/abstractDialect.js +9 -4
  15. package/dist/dialect/abstractSqlDialect.d.ts +49 -8
  16. package/dist/dialect/abstractSqlDialect.js +95 -47
  17. package/dist/dialect/index.d.ts +1 -0
  18. package/dist/dialect/index.js +1 -0
  19. package/dist/dialect/indexSqlDialect.d.ts +57 -0
  20. package/dist/dialect/indexSqlDialect.js +101 -0
  21. package/dist/dialect/mysqlLikeSqlDialect.d.ts +26 -1
  22. package/dist/dialect/mysqlLikeSqlDialect.js +57 -1
  23. package/dist/dialect/pgLikeSqlDialect.d.ts +37 -22
  24. package/dist/dialect/pgLikeSqlDialect.js +97 -68
  25. package/dist/dialect/vectorCast.d.ts +21 -0
  26. package/dist/dialect/vectorCast.js +28 -0
  27. package/dist/dialect/vectorSqlDialect.d.ts +10 -3
  28. package/dist/dialect/vectorSqlDialect.js +19 -8
  29. package/dist/entity/decorator/bag.d.ts +35 -0
  30. package/dist/entity/decorator/bag.js +54 -0
  31. package/dist/entity/decorator/entity.d.ts +28 -2
  32. package/dist/entity/decorator/entity.js +40 -2
  33. package/dist/entity/decorator/members.d.ts +51 -0
  34. package/dist/entity/decorator/members.js +51 -0
  35. package/dist/entity/index.d.ts +3 -1
  36. package/dist/entity/index.js +3 -1
  37. package/dist/entity/metadata/definition.d.ts +23 -8
  38. package/dist/entity/metadata/definition.js +56 -100
  39. package/dist/http/contract.js +1 -2
  40. package/dist/http/handler.js +5 -26
  41. package/dist/http/query.js +1 -1
  42. package/dist/libsql/libsqlDialect.d.ts +12 -2
  43. package/dist/libsql/libsqlDialect.js +14 -2
  44. package/dist/libsql/libsqlQuerier.d.ts +9 -22
  45. package/dist/libsql/libsqlQuerier.js +9 -85
  46. package/dist/libsql/libsqlQuerierPool.d.ts +10 -8
  47. package/dist/libsql/libsqlQuerierPool.js +17 -15
  48. package/dist/maria/mariaDialect.d.ts +26 -4
  49. package/dist/maria/mariaDialect.js +54 -15
  50. package/dist/migrate/acquireQuerierForMigrations.d.ts +13 -1
  51. package/dist/migrate/acquireQuerierForMigrations.js +28 -0
  52. package/dist/migrate/builder/migrationBuilder.d.ts +9 -29
  53. package/dist/migrate/builder/migrationBuilder.js +58 -76
  54. package/dist/migrate/builder/splitSqlStatements.d.ts +0 -2
  55. package/dist/migrate/builder/splitSqlStatements.js +0 -2
  56. package/dist/migrate/builder/tableBuilder.d.ts +12 -2
  57. package/dist/migrate/builder/tableBuilder.js +71 -119
  58. package/dist/migrate/builder/types.d.ts +30 -41
  59. package/dist/migrate/cli-config.js +24 -8
  60. package/dist/migrate/cli.d.ts +3 -2
  61. package/dist/migrate/cli.js +26 -49
  62. package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -4
  63. package/dist/migrate/codegen/entityCodeGenerator.js +8 -67
  64. package/dist/migrate/codegen/fieldOptionsSource.d.ts +10 -0
  65. package/dist/migrate/codegen/fieldOptionsSource.js +55 -0
  66. package/dist/migrate/codegen/index.d.ts +0 -3
  67. package/dist/migrate/codegen/index.js +0 -6
  68. package/dist/migrate/drift/driftDetector.d.ts +10 -1
  69. package/dist/migrate/drift/driftDetector.js +22 -5
  70. package/dist/migrate/generator/indexNodeToSchema.d.ts +9 -0
  71. package/dist/migrate/generator/indexNodeToSchema.js +14 -0
  72. package/dist/migrate/generator/mongoCommand.d.ts +57 -0
  73. package/dist/migrate/generator/mongoCommand.js +26 -0
  74. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +9 -20
  75. package/dist/migrate/generator/mongoSchemaGenerator.js +40 -82
  76. package/dist/migrate/index.d.ts +0 -1
  77. package/dist/migrate/index.js +0 -1
  78. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +30 -11
  79. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +61 -57
  80. package/dist/migrate/introspection/baseSqlIntrospector.d.ts +0 -1
  81. package/dist/migrate/introspection/baseSqlIntrospector.js +3 -12
  82. package/dist/migrate/introspection/mongoIntrospector.js +9 -15
  83. package/dist/migrate/introspection/mysqlIntrospector.d.ts +5 -8
  84. package/dist/migrate/introspection/mysqlIntrospector.js +4 -10
  85. package/dist/migrate/introspection/postgresIntrospector.d.ts +5 -8
  86. package/dist/migrate/introspection/postgresIntrospector.js +4 -10
  87. package/dist/migrate/introspection/sqliteIntrospector.d.ts +6 -5
  88. package/dist/migrate/introspection/sqliteIntrospector.js +26 -18
  89. package/dist/migrate/migrator.d.ts +23 -3
  90. package/dist/migrate/migrator.js +101 -150
  91. package/dist/migrate/schemaGenerator.d.ts +26 -37
  92. package/dist/migrate/schemaGenerator.js +69 -174
  93. package/dist/migrate/schemaGeneratorAsync.d.ts +2 -2
  94. package/dist/migrate/schemaGeneratorAsync.js +3 -3
  95. package/dist/migrate/storage/databaseStorage.d.ts +2 -0
  96. package/dist/migrate/storage/databaseStorage.js +12 -27
  97. package/dist/mongo/mongoDialect.d.ts +2 -0
  98. package/dist/mongo/mongoDialect.js +14 -3
  99. package/dist/mongo/mongodbQuerier.js +171 -229
  100. package/dist/mysql/mysqlDialect.d.ts +8 -0
  101. package/dist/mysql/mysqlDialect.js +13 -0
  102. package/dist/nestjs/uqlContextInterceptor.js +58 -22
  103. package/dist/nestjs/uqlModule.d.ts +2 -11
  104. package/dist/nestjs/uqlModule.js +93 -44
  105. package/dist/postgres/postgresDialect.js +2 -2
  106. package/dist/querier/abstractQuerier.d.ts +28 -0
  107. package/dist/querier/abstractQuerier.js +47 -22
  108. package/dist/querier/abstractQuerierPool.d.ts +3 -0
  109. package/dist/querier/abstractQuerierPool.js +5 -3
  110. package/dist/querier/abstractSqlQuerier.d.ts +1 -3
  111. package/dist/querier/abstractSqlQuerier.js +48 -96
  112. package/dist/querier/index.d.ts +3 -1
  113. package/dist/querier/index.js +3 -1
  114. package/dist/querier/querierContext.browser.d.ts +12 -0
  115. package/dist/querier/querierContext.browser.js +18 -0
  116. package/dist/querier/querierContext.d.ts +22 -0
  117. package/dist/querier/querierContext.js +42 -0
  118. package/dist/querier/queryError.d.ts +22 -0
  119. package/dist/querier/queryError.js +20 -0
  120. package/dist/querier/transactional.d.ts +26 -0
  121. package/dist/querier/transactional.js +43 -0
  122. package/dist/schema/canonicalType.d.ts +14 -2
  123. package/dist/schema/canonicalType.js +41 -31
  124. package/dist/schema/index.d.ts +1 -1
  125. package/dist/schema/schemaASTBuilder.d.ts +9 -2
  126. package/dist/schema/schemaASTBuilder.js +58 -49
  127. package/dist/schema/types.d.ts +10 -58
  128. package/dist/sqlite/abstractSqliteQuerier.d.ts +46 -0
  129. package/dist/sqlite/abstractSqliteQuerier.js +44 -0
  130. package/dist/sqlite/bunSqliteAdapter.bun.d.ts +26 -0
  131. package/dist/sqlite/bunSqliteAdapter.bun.js +25 -0
  132. package/dist/sqlite/hranaQuerier.d.ts +54 -0
  133. package/dist/sqlite/hranaQuerier.js +68 -0
  134. package/dist/sqlite/hranaQuerierPool.d.ts +21 -0
  135. package/dist/sqlite/hranaQuerierPool.js +25 -0
  136. package/dist/sqlite/index.d.ts +3 -1
  137. package/dist/sqlite/index.js +3 -1
  138. package/dist/sqlite/localSqliteQuerierPool.d.ts +31 -0
  139. package/dist/sqlite/localSqliteQuerierPool.js +34 -0
  140. package/dist/sqlite/nodeSqliteAdapter.d.ts +35 -0
  141. package/dist/sqlite/nodeSqliteAdapter.js +28 -0
  142. package/dist/sqlite/nodeSqliteQuerierPool.d.ts +28 -0
  143. package/dist/sqlite/nodeSqliteQuerierPool.js +29 -0
  144. package/dist/sqlite/sqliteDialect.d.ts +18 -5
  145. package/dist/sqlite/sqliteDialect.js +23 -36
  146. package/dist/sqlite/sqliteQuerier.d.ts +16 -9
  147. package/dist/sqlite/sqliteQuerier.js +4 -27
  148. package/dist/sqlite/sqliteQuerierPool.d.ts +11 -14
  149. package/dist/sqlite/sqliteQuerierPool.js +16 -24
  150. package/dist/turso/index.d.ts +3 -0
  151. package/dist/turso/index.js +3 -0
  152. package/dist/turso/local.d.ts +3 -0
  153. package/dist/turso/local.js +3 -0
  154. package/dist/turso/tursoDialect.d.ts +15 -0
  155. package/dist/turso/tursoDialect.js +18 -0
  156. package/dist/turso/tursoLocalQuerier.d.ts +25 -0
  157. package/dist/turso/tursoLocalQuerier.js +20 -0
  158. package/dist/turso/tursoLocalQuerierPool.d.ts +32 -0
  159. package/dist/turso/tursoLocalQuerierPool.js +39 -0
  160. package/dist/turso/tursoQuerier.d.ts +10 -0
  161. package/dist/turso/tursoQuerier.js +10 -0
  162. package/dist/turso/tursoQuerierPool.d.ts +39 -0
  163. package/dist/turso/tursoQuerierPool.js +38 -0
  164. package/dist/type/config.d.ts +0 -6
  165. package/dist/type/dialect.d.ts +12 -4
  166. package/dist/type/dialect.js +7 -1
  167. package/dist/type/entity.d.ts +154 -26
  168. package/dist/type/migration.d.ts +32 -23
  169. package/dist/type/querier.d.ts +6 -0
  170. package/dist/type/vector.d.ts +5 -2
  171. package/dist/util/hook.util.js +1 -1
  172. package/dist/util/index.d.ts +1 -0
  173. package/dist/util/index.js +1 -0
  174. package/dist/util/indexColumn.util.d.ts +6 -0
  175. package/dist/util/indexColumn.util.js +26 -0
  176. package/package.json +21 -16
  177. package/dist/entity/decorator/field.d.ts +0 -2
  178. package/dist/entity/decorator/field.js +0 -7
  179. package/dist/entity/decorator/filter.d.ts +0 -7
  180. package/dist/entity/decorator/filter.js +0 -11
  181. package/dist/entity/decorator/hook.d.ts +0 -7
  182. package/dist/entity/decorator/hook.js +0 -14
  183. package/dist/entity/decorator/id.d.ts +0 -2
  184. package/dist/entity/decorator/id.js +0 -7
  185. package/dist/entity/decorator/index-decorator.d.ts +0 -29
  186. package/dist/entity/decorator/index-decorator.js +0 -26
  187. package/dist/entity/decorator/index.d.ts +0 -8
  188. package/dist/entity/decorator/index.js +0 -8
  189. package/dist/entity/decorator/relation.d.ts +0 -8
  190. package/dist/entity/decorator/relation.js +0 -19
  191. package/dist/migrate/codegen/entityMerger.d.ts +0 -114
  192. package/dist/migrate/codegen/entityMerger.js +0 -293
  193. package/dist/migrate/codegen/migrationCodeGenerator.d.ts +0 -61
  194. package/dist/migrate/codegen/migrationCodeGenerator.js +0 -355
  195. package/dist/migrate/codegen/smartRelationDetector.d.ts +0 -47
  196. package/dist/migrate/codegen/smartRelationDetector.js +0 -134
  197. package/dist/migrate/generator/index.d.ts +0 -2
  198. package/dist/migrate/generator/index.js +0 -2
  199. package/dist/migrate/sync/index.d.ts +0 -6
  200. package/dist/migrate/sync/index.js +0 -6
  201. package/dist/migrate/sync/schemaSync.d.ts +0 -131
  202. package/dist/migrate/sync/schemaSync.js +0 -259
  203. package/dist/querier/decorator/index.d.ts +0 -4
  204. package/dist/querier/decorator/index.js +0 -4
  205. package/dist/querier/decorator/injectQuerier.d.ts +0 -3
  206. package/dist/querier/decorator/injectQuerier.js +0 -33
  207. package/dist/querier/decorator/log.d.ts +0 -32
  208. package/dist/querier/decorator/log.js +0 -52
  209. package/dist/querier/decorator/serialized.d.ts +0 -6
  210. package/dist/querier/decorator/serialized.js +0 -13
  211. package/dist/querier/decorator/transactional.d.ts +0 -6
  212. package/dist/querier/decorator/transactional.js +0 -48
  213. package/dist/sqlite/betterSqlite3Dialect.d.ts +0 -9
  214. package/dist/sqlite/betterSqlite3Dialect.js +0 -9
@@ -1,3 +1,47 @@
1
1
  import { AbstractSqlQuerier } from '../querier/index.js';
2
+ import { throwPendingTransaction } from '../util/index.js';
3
+ /** Bound parameters reach a driver as `unknown[]` from the compiler; every driver types them narrowly. */
4
+ export function toSqliteBindValues(values) {
5
+ return (values || []);
6
+ }
2
7
  export class AbstractSqliteQuerier extends AbstractSqlQuerier {
8
+ /**
9
+ * SQLite drivers hold a single shared handle rather than a connection from a pool, so releasing
10
+ * a querier returns nothing; it only asserts the unit of work was finished. Drivers owning a
11
+ * closable per-querier connection override this.
12
+ */
13
+ async internalRelease() {
14
+ if (this.hasOpenTransaction) {
15
+ throwPendingTransaction();
16
+ }
17
+ }
18
+ }
19
+ /**
20
+ * Querier for the SQLite drivers that expose prepared statements: `better-sqlite3`, `bun:sqlite`
21
+ * (through `adaptBunSqlite`) and the embedded Turso engine. They differ only in whether preparing and
22
+ * stepping are synchronous, which `await` and `for await` absorb, so the read/write/stream logic -
23
+ * including the `reader` rule below, whose loss silently drops inserted ids - is written once.
24
+ */
25
+ export class PreparedSqliteQuerier extends AbstractSqliteQuerier {
26
+ async internalAll(query, values) {
27
+ const stmt = await this.prepare(query);
28
+ return (await stmt.all(...toSqliteBindValues(values)));
29
+ }
30
+ async *internalStream(query, values) {
31
+ const stmt = await this.prepare(query);
32
+ for await (const row of stmt.iterate(...toSqliteBindValues(values))) {
33
+ yield row;
34
+ }
35
+ }
36
+ async internalRun(query, values) {
37
+ const stmt = await this.prepare(query);
38
+ // `reader` is true for any statement with a RETURNING clause; `.run()` silently discards
39
+ // returned rows, so those statements must go through `.all()` instead.
40
+ if (stmt.reader) {
41
+ const rows = (await stmt.all(...toSqliteBindValues(values)));
42
+ return this.buildUpdateResult({ rows });
43
+ }
44
+ const { changes, lastInsertRowid } = await stmt.run(...toSqliteBindValues(values));
45
+ return this.buildUpdateResult({ changes, id: lastInsertRowid });
46
+ }
3
47
  }
@@ -0,0 +1,26 @@
1
+ import type { SqliteBindValue, SqliteRunResult } from './abstractSqliteQuerier.js';
2
+ import type { SqliteDatabase } from './sqliteQuerier.js';
3
+ /** A `bun:sqlite` statement: better-sqlite3-shaped, except it reports columns instead of `reader`. */
4
+ type BunStatement = {
5
+ columnNames: string[];
6
+ all(...values: SqliteBindValue[]): unknown[];
7
+ run(...values: SqliteBindValue[]): SqliteRunResult;
8
+ iterate(...values: SqliteBindValue[]): Iterable<unknown>;
9
+ };
10
+ type BunDatabase = {
11
+ prepare(sql: string): BunStatement;
12
+ loadExtension(path: string): void;
13
+ close(): unknown;
14
+ };
15
+ /**
16
+ * Presents a `bun:sqlite` handle as a {@link SqliteDatabase}.
17
+ *
18
+ * @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
19
+ * would take the `run()` path, which discards returned rows, and inserts would report no ids.
20
+ * `columnNames` is non-empty for exactly the statements better-sqlite3 marks as readers.
21
+ *
22
+ * Lives in a `.bun.ts` file because it only ever executes under Bun: the Node coverage run cannot
23
+ * reach it, and `sqliteQuerier.bun.test.ts` covers it under `test:bun` instead.
24
+ */
25
+ export declare function adaptBunSqlite(db: BunDatabase): SqliteDatabase;
26
+ export {};
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Presents a `bun:sqlite` handle as a {@link SqliteDatabase}.
3
+ *
4
+ * @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
5
+ * would take the `run()` path, which discards returned rows, and inserts would report no ids.
6
+ * `columnNames` is non-empty for exactly the statements better-sqlite3 marks as readers.
7
+ *
8
+ * Lives in a `.bun.ts` file because it only ever executes under Bun: the Node coverage run cannot
9
+ * reach it, and `sqliteQuerier.bun.test.ts` covers it under `test:bun` instead.
10
+ */
11
+ export function adaptBunSqlite(db) {
12
+ return {
13
+ prepare: (sql) => {
14
+ const stmt = db.prepare(sql);
15
+ return {
16
+ reader: stmt.columnNames.length > 0,
17
+ all: (...values) => stmt.all(...values),
18
+ run: (...values) => stmt.run(...values),
19
+ iterate: (...values) => stmt.iterate(...values),
20
+ };
21
+ },
22
+ loadExtension: (path) => db.loadExtension(path),
23
+ close: () => db.close(),
24
+ };
25
+ }
@@ -0,0 +1,54 @@
1
+ import type { ExtraOptions, TransactionOptions } from '../type/index.js';
2
+ import { AbstractSqliteQuerier, type SqliteBindValue } from './abstractSqliteQuerier.js';
3
+ import type { SqliteDialect } from './sqliteDialect.js';
4
+ /**
5
+ * Structural subset of the Hrana client API, the wire protocol shared by `@libsql/client` and
6
+ * `@tursodatabase/serverless/compat`. Declared here rather than imported so a querier works with
7
+ * any client of this shape (including `@libsql/client/web` and `@libsql/client-wasm`) without
8
+ * depending on vendor types.
9
+ */
10
+ export type HranaInValue = SqliteBindValue | ArrayBuffer | Date;
11
+ export type HranaResultSet = {
12
+ rows: unknown[];
13
+ rowsAffected: number;
14
+ lastInsertRowid?: bigint;
15
+ };
16
+ export type HranaExecutor = {
17
+ execute(stmt: {
18
+ sql: string;
19
+ args?: HranaInValue[];
20
+ }): Promise<HranaResultSet>;
21
+ };
22
+ export type HranaTransaction = HranaExecutor & {
23
+ commit(): Promise<void>;
24
+ rollback(): Promise<void>;
25
+ };
26
+ export type HranaClient = HranaExecutor & {
27
+ transaction(mode?: 'write' | 'read' | 'deferred'): Promise<HranaTransaction>;
28
+ close(): void;
29
+ };
30
+ /** Connection lifecycle for a {@link HranaQuerier} (separate from {@link ExtraOptions}). */
31
+ export type HranaQuerierConnectionOptions = {
32
+ /** When set, {@link release} closes {@link HranaQuerier.client} (one-shot migration connections). */
33
+ closeClientOnRelease?: boolean;
34
+ };
35
+ /**
36
+ * Querier for SQLite databases reached through a Hrana client.
37
+ *
38
+ * @remarks Transactions use the client's own session handle rather than `BEGIN`/`COMMIT` statements,
39
+ * because over plain HTTP consecutive requests need not share a connection.
40
+ */
41
+ export declare class HranaQuerier extends AbstractSqliteQuerier {
42
+ readonly client: HranaClient;
43
+ readonly extra?: ExtraOptions | undefined;
44
+ private tx?;
45
+ private readonly closeClientOnRelease;
46
+ constructor(client: HranaClient, dialect: SqliteDialect, extra?: ExtraOptions | undefined, connection?: HranaQuerierConnectionOptions);
47
+ internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
48
+ internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
49
+ get hasOpenTransaction(): boolean;
50
+ beginTransaction(_opts?: TransactionOptions): Promise<void>;
51
+ commitTransaction(): Promise<void>;
52
+ rollbackTransaction(): Promise<void>;
53
+ internalRelease(): Promise<void>;
54
+ }
@@ -0,0 +1,68 @@
1
+ import { throwNoPendingTransaction, throwPendingTransaction } from '../util/index.js';
2
+ import { AbstractSqliteQuerier } from './abstractSqliteQuerier.js';
3
+ /**
4
+ * Querier for SQLite databases reached through a Hrana client.
5
+ *
6
+ * @remarks Transactions use the client's own session handle rather than `BEGIN`/`COMMIT` statements,
7
+ * because over plain HTTP consecutive requests need not share a connection.
8
+ */
9
+ export class HranaQuerier extends AbstractSqliteQuerier {
10
+ client;
11
+ extra;
12
+ tx;
13
+ closeClientOnRelease;
14
+ constructor(client, dialect, extra, connection) {
15
+ super(dialect, extra);
16
+ this.client = client;
17
+ this.extra = extra;
18
+ this.closeClientOnRelease = connection?.closeClientOnRelease ?? false;
19
+ }
20
+ async internalAll(query, values) {
21
+ const target = this.tx || this.client;
22
+ const res = await target.execute({ sql: query, args: values });
23
+ return res.rows;
24
+ }
25
+ async internalRun(query, values) {
26
+ const target = this.tx || this.client;
27
+ const res = await target.execute({ sql: query, args: values });
28
+ const rows = res.rows;
29
+ // `rowsAffected` is unreliably 0 whenever the statement has a RETURNING clause, so prefer
30
+ // the actual row count when rows were returned.
31
+ return this.buildUpdateResult({ rows, changes: rows.length || res.rowsAffected, id: res.lastInsertRowid });
32
+ }
33
+ get hasOpenTransaction() {
34
+ return !!this.tx;
35
+ }
36
+ async beginTransaction(_opts) {
37
+ return this.serialize(async () => {
38
+ if (this.tx) {
39
+ throwPendingTransaction();
40
+ }
41
+ this.tx = await this.client.transaction('write');
42
+ });
43
+ }
44
+ async commitTransaction() {
45
+ return this.serialize(async () => {
46
+ if (!this.tx) {
47
+ throwNoPendingTransaction();
48
+ }
49
+ await this.tx.commit();
50
+ this.tx = undefined;
51
+ });
52
+ }
53
+ async rollbackTransaction() {
54
+ return this.serialize(async () => {
55
+ if (!this.tx) {
56
+ throwNoPendingTransaction();
57
+ }
58
+ await this.tx.rollback();
59
+ this.tx = undefined;
60
+ });
61
+ }
62
+ async internalRelease() {
63
+ await super.internalRelease();
64
+ if (this.closeClientOnRelease) {
65
+ this.client.close();
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,21 @@
1
+ import { AbstractSqlQuerierPool } from '../querier/index.js';
2
+ import type { HranaClient, HranaQuerier, HranaQuerierConnectionOptions } from './hranaQuerier.js';
3
+ import type { SqliteDialect } from './sqliteDialect.js';
4
+ /**
5
+ * Pool for SQLite databases reached over the Hrana wire protocol (`@libsql/client`,
6
+ * `@tursodatabase/serverless/compat`).
7
+ *
8
+ * @remarks The client is shared by every querier, since Hrana keeps no per-connection state: a
9
+ * transaction takes its own session handle. It is resolved on first use rather than in the
10
+ * constructor, so building a pool never throws when the optional driver peer is absent, which is what
11
+ * lets a Workers bundle construct one at module scope.
12
+ */
13
+ export declare abstract class AbstractHranaQuerierPool<Q extends HranaQuerier, D extends SqliteDialect> extends AbstractSqlQuerierPool<Q, D> {
14
+ private client?;
15
+ /** False when the caller injected their own client, in which case they own its lifecycle. */
16
+ protected readonly ownsClient: boolean;
17
+ protected abstract openClient(): Promise<HranaClient>;
18
+ protected abstract buildQuerier(client: HranaClient, connection?: HranaQuerierConnectionOptions): Q;
19
+ getQuerier(): Promise<Q>;
20
+ end(): Promise<void>;
21
+ }
@@ -0,0 +1,25 @@
1
+ import { AbstractSqlQuerierPool } from '../querier/index.js';
2
+ /**
3
+ * Pool for SQLite databases reached over the Hrana wire protocol (`@libsql/client`,
4
+ * `@tursodatabase/serverless/compat`).
5
+ *
6
+ * @remarks The client is shared by every querier, since Hrana keeps no per-connection state: a
7
+ * transaction takes its own session handle. It is resolved on first use rather than in the
8
+ * constructor, so building a pool never throws when the optional driver peer is absent, which is what
9
+ * lets a Workers bundle construct one at module scope.
10
+ */
11
+ export class AbstractHranaQuerierPool extends AbstractSqlQuerierPool {
12
+ client;
13
+ /** False when the caller injected their own client, in which case they own its lifecycle. */
14
+ ownsClient = true;
15
+ async getQuerier() {
16
+ this.client ??= await this.openClient();
17
+ return this.buildQuerier(this.client);
18
+ }
19
+ async end() {
20
+ if (this.ownsClient) {
21
+ this.client?.close();
22
+ }
23
+ this.client = undefined;
24
+ }
25
+ }
@@ -1,5 +1,7 @@
1
1
  export * from './abstractSqliteQuerier.js';
2
- export * from './betterSqlite3Dialect.js';
2
+ export * from './hranaQuerier.js';
3
+ export * from './hranaQuerierPool.js';
4
+ export * from './nodeSqliteQuerierPool.js';
3
5
  export * from './sqliteDialect.js';
4
6
  export * from './sqliteQuerier.js';
5
7
  export * from './sqliteQuerierPool.js';
@@ -1,5 +1,7 @@
1
1
  export * from './abstractSqliteQuerier.js';
2
- export * from './betterSqlite3Dialect.js';
2
+ export * from './hranaQuerier.js';
3
+ export * from './hranaQuerierPool.js';
4
+ export * from './nodeSqliteQuerierPool.js';
3
5
  export * from './sqliteDialect.js';
4
6
  export * from './sqliteQuerier.js';
5
7
  export * from './sqliteQuerierPool.js';
@@ -0,0 +1,31 @@
1
+ import { AbstractSqlQuerierPool } from '../querier/index.js';
2
+ import type { ExtraOptions } from '../type/index.js';
3
+ import { SqliteDialect } from './sqliteDialect.js';
4
+ import { type SqliteDatabase, SqliteQuerier } from './sqliteQuerier.js';
5
+ /** What every local SQLite pool accepts on top of its driver's own options. */
6
+ export type LocalSqlitePoolOptions = {
7
+ /**
8
+ * Paths of loadable extensions to install when the connection opens - e.g. what `sqlite-vec`'s
9
+ * `getLoadablePath()` returns, which vector search needs because SQLite itself has no vector
10
+ * functions.
11
+ */
12
+ extensions?: readonly string[];
13
+ };
14
+ /**
15
+ * Pool for a SQLite database opened in this process, whichever driver provides it.
16
+ *
17
+ * The handle is shared - SQLite gives one connection per file - but each acquisition gets its own
18
+ * querier, so transaction state stays per unit of work. Subclasses supply only {@link createDb}: the
19
+ * lifecycle, and loading the extensions on the way up, are the same for `better-sqlite3`, `bun:sqlite`
20
+ * and `node:sqlite`, and were written out once per pool before.
21
+ */
22
+ export declare abstract class AbstractLocalSqliteQuerierPool<O extends LocalSqlitePoolOptions> extends AbstractSqlQuerierPool<SqliteQuerier, SqliteDialect> {
23
+ readonly opts?: O | undefined;
24
+ private db?;
25
+ constructor(opts?: O | undefined, extra?: ExtraOptions);
26
+ /** Opens the driver's database. Extensions are loaded by the caller, not here. */
27
+ protected abstract createDb(): Promise<SqliteDatabase>;
28
+ getQuerier(): Promise<SqliteQuerier>;
29
+ private openDb;
30
+ end(): Promise<void>;
31
+ }
@@ -0,0 +1,34 @@
1
+ import { AbstractSqlQuerierPool } from '../querier/index.js';
2
+ import { SqliteDialect } from './sqliteDialect.js';
3
+ import { SqliteQuerier } from './sqliteQuerier.js';
4
+ /**
5
+ * Pool for a SQLite database opened in this process, whichever driver provides it.
6
+ *
7
+ * The handle is shared - SQLite gives one connection per file - but each acquisition gets its own
8
+ * querier, so transaction state stays per unit of work. Subclasses supply only {@link createDb}: the
9
+ * lifecycle, and loading the extensions on the way up, are the same for `better-sqlite3`, `bun:sqlite`
10
+ * and `node:sqlite`, and were written out once per pool before.
11
+ */
12
+ export class AbstractLocalSqliteQuerierPool extends AbstractSqlQuerierPool {
13
+ opts;
14
+ db;
15
+ constructor(opts, extra) {
16
+ super(new SqliteDialect({ namingStrategy: extra?.namingStrategy }), extra);
17
+ this.opts = opts;
18
+ }
19
+ async getQuerier() {
20
+ this.db ??= await this.openDb();
21
+ return new SqliteQuerier(this.db, this.dialect, this.extra);
22
+ }
23
+ async openDb() {
24
+ const db = await this.createDb();
25
+ for (const extension of this.opts?.extensions ?? []) {
26
+ db.loadExtension(extension);
27
+ }
28
+ return db;
29
+ }
30
+ async end() {
31
+ await this.db?.close();
32
+ this.db = undefined;
33
+ }
34
+ }
@@ -0,0 +1,35 @@
1
+ import type { SqliteBindValue } from './abstractSqliteQuerier.js';
2
+ import type { SqliteDatabase } from './sqliteQuerier.js';
3
+ /**
4
+ * A `node:sqlite` statement: better-sqlite3-shaped, except it describes columns instead of reporting
5
+ * `reader`, and types `changes` as possibly `bigint` where better-sqlite3 always answers a `number`.
6
+ */
7
+ type NodeSqliteStatement = {
8
+ columns(): readonly unknown[];
9
+ all(...values: SqliteBindValue[]): unknown[];
10
+ run(...values: SqliteBindValue[]): {
11
+ changes: number | bigint;
12
+ lastInsertRowid: number | bigint;
13
+ };
14
+ iterate(...values: SqliteBindValue[]): Iterable<unknown>;
15
+ };
16
+ /**
17
+ * Structural subset of `node:sqlite`'s `DatabaseSync`, declared locally for the same reason the rest
18
+ * of this folder declares its driver shapes: nothing here depends on `@types/node` being in scope.
19
+ */
20
+ export type NodeSqliteDatabase = {
21
+ prepare(sql: string): NodeSqliteStatement;
22
+ exec(sql: string): void;
23
+ loadExtension(path: string): void;
24
+ close(): void;
25
+ };
26
+ /**
27
+ * Presents a `node:sqlite` handle as a {@link SqliteDatabase}.
28
+ *
29
+ * @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
30
+ * would take the `run()` path, which discards returned rows, and inserts would report no ids.
31
+ * `columns()` is non-empty for exactly the statements better-sqlite3 marks as readers, including
32
+ * `INSERT ... RETURNING` and `DELETE ... RETURNING`.
33
+ */
34
+ export declare function adaptNodeSqlite(db: NodeSqliteDatabase): SqliteDatabase;
35
+ export {};
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Presents a `node:sqlite` handle as a {@link SqliteDatabase}.
3
+ *
4
+ * @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
5
+ * would take the `run()` path, which discards returned rows, and inserts would report no ids.
6
+ * `columns()` is non-empty for exactly the statements better-sqlite3 marks as readers, including
7
+ * `INSERT ... RETURNING` and `DELETE ... RETURNING`.
8
+ */
9
+ export function adaptNodeSqlite(db) {
10
+ return {
11
+ prepare: (sql) => {
12
+ const stmt = db.prepare(sql);
13
+ return {
14
+ reader: stmt.columns().length > 0,
15
+ all: (...values) => stmt.all(...values),
16
+ // `changes` is narrowed to `number` to match every other driver; a row count cannot exceed
17
+ // the safe-integer range, so nothing is lost. `lastInsertRowid` keeps its `bigint` arm.
18
+ run: (...values) => {
19
+ const { changes, lastInsertRowid } = stmt.run(...values);
20
+ return { changes: Number(changes), lastInsertRowid };
21
+ },
22
+ iterate: (...values) => stmt.iterate(...values),
23
+ };
24
+ },
25
+ loadExtension: (path) => db.loadExtension(path),
26
+ close: () => db.close(),
27
+ };
28
+ }
@@ -0,0 +1,28 @@
1
+ import type { ExtraOptions } from '../type/index.js';
2
+ import { AbstractLocalSqliteQuerierPool, type LocalSqlitePoolOptions } from './localSqliteQuerierPool.js';
3
+ import type { SqliteDatabase } from './sqliteQuerier.js';
4
+ /**
5
+ * The `DatabaseSync` options worth surfacing, plus the loadable extensions to install. Declared here
6
+ * rather than imported from `node:sqlite` so this module needs no ambient Node types; unknown keys
7
+ * are ignored by the driver, so the list only has to cover what callers actually set.
8
+ */
9
+ export type NodeSqlitePoolOptions = LocalSqlitePoolOptions & {
10
+ readonly readOnly?: boolean;
11
+ readonly enableForeignKeyConstraints?: boolean;
12
+ /** Milliseconds a locked database is retried before `SQLITE_BUSY`. */
13
+ readonly timeout?: number;
14
+ };
15
+ /**
16
+ * Pool backed by Node's built-in `node:sqlite`, so SQLite works with **no dependency at all** rather
17
+ * than requiring the `better-sqlite3` native build. Use {@link Sqlite3QuerierPool} instead when you
18
+ * want `better-sqlite3`, or are on Bun.
19
+ *
20
+ * @remarks `node:sqlite` needs no CLI flag from Node 22.13, and is still a release candidate
21
+ * (stability 1.2) as of Node 26, so `better-sqlite3` via {@link Sqlite3QuerierPool} remains the
22
+ * faster option for read-heavy work.
23
+ */
24
+ export declare class NodeSqliteQuerierPool extends AbstractLocalSqliteQuerierPool<NodeSqlitePoolOptions> {
25
+ readonly filename: string;
26
+ constructor(filename?: string, opts?: NodeSqlitePoolOptions, extra?: ExtraOptions);
27
+ protected createDb(): Promise<SqliteDatabase>;
28
+ }
@@ -0,0 +1,29 @@
1
+ import { AbstractLocalSqliteQuerierPool } from './localSqliteQuerierPool.js';
2
+ import { adaptNodeSqlite } from './nodeSqliteAdapter.js';
3
+ /**
4
+ * Pool backed by Node's built-in `node:sqlite`, so SQLite works with **no dependency at all** rather
5
+ * than requiring the `better-sqlite3` native build. Use {@link Sqlite3QuerierPool} instead when you
6
+ * want `better-sqlite3`, or are on Bun.
7
+ *
8
+ * @remarks `node:sqlite` needs no CLI flag from Node 22.13, and is still a release candidate
9
+ * (stability 1.2) as of Node 26, so `better-sqlite3` via {@link Sqlite3QuerierPool} remains the
10
+ * faster option for read-heavy work.
11
+ */
12
+ export class NodeSqliteQuerierPool extends AbstractLocalSqliteQuerierPool {
13
+ filename;
14
+ constructor(filename = ':memory:', opts, extra) {
15
+ super(opts, extra);
16
+ this.filename = filename;
17
+ }
18
+ async createDb() {
19
+ const { DatabaseSync } = await import('node:sqlite');
20
+ const { extensions, ...driverOpts } = this.opts ?? {};
21
+ const nodeDb = new DatabaseSync(this.filename, {
22
+ ...driverOpts,
23
+ // `node:sqlite` refuses `loadExtension` unless the database was opened with this on.
24
+ ...(extensions?.length ? { allowExtension: true } : undefined),
25
+ });
26
+ nodeDb.exec('PRAGMA journal_mode = WAL');
27
+ return adaptNodeSqlite(nodeDb);
28
+ }
29
+ }
@@ -1,5 +1,5 @@
1
1
  import { AbstractSqlDialect } from '../dialect/abstractSqlDialect.js';
2
- import type { DialectFeatures, FieldOptions, QueryComparisonOptions, QueryConflictPaths, QueryContext, QueryOptions, QuerySizeComparisonOps, Type, VectorDistance } from '../type/index.js';
2
+ import type { DialectFeatures, EntityMeta, FieldOptions, QueryContext, QuerySizeComparisonOps, QueryTextSearchOptions, Type, VectorDistance } from '../type/index.js';
3
3
  export declare class SqliteDialect extends AbstractSqlDialect {
4
4
  /** Default {@link DialectFeatures} for SQLite and SQLite-derived dialects. */
5
5
  protected readonly featureDefaults: DialectFeatures;
@@ -14,6 +14,11 @@ export declare class SqliteDialect extends AbstractSqlDialect {
14
14
  readonly alterColumnSyntax = "none";
15
15
  readonly booleanLiteral = "integer";
16
16
  readonly insertIdSource = "returning";
17
+ /**
18
+ * The [sqlite-vec](https://github.com/asg017/sqlite-vec) functions, which need that extension
19
+ * loaded on the connection (see `Sqlite3QuerierPool`'s `extensions` option). libSQL and Turso ship
20
+ * their own vector functions instead, so `LibsqlDialect` overrides this.
21
+ */
17
22
  protected readonly vectorDistanceFns: ReadonlyMap<VectorDistance, string>;
18
23
  /**
19
24
  * SQLite does not support the `DEFAULT` keyword inside `VALUES`. Inline the metadata default
@@ -23,7 +28,11 @@ export declare class SqliteDialect extends AbstractSqlDialect {
23
28
  protected ilikeExpr(f: string, ph: string): string;
24
29
  protected get neOp(): string;
25
30
  normalizeValue(value: unknown): unknown;
26
- compare<E>(ctx: QueryContext, entity: Type<E>, key: string, val: unknown, opts?: QueryComparisonOptions): void;
31
+ /**
32
+ * FTS5 matches the table itself rather than its columns, so this only works when the table *is* an
33
+ * FTS5 virtual table (UQL does not create those; declare it outside your entities).
34
+ */
35
+ protected appendTextSearch<E>(ctx: QueryContext, entity: Type<E>, meta: EntityMeta<E>, search: QueryTextSearchOptions<E>): void;
27
36
  /**
28
37
  * SQLite compares an exploded element as whole JSON text, so containment cannot express "this
29
38
  * element includes these keys" - `$elemMatch` always expands to per-field conditions.
@@ -43,8 +52,6 @@ export declare class SqliteDialect extends AbstractSqlDialect {
43
52
  protected jsonElemRef(alias: string, field?: string, asJson?: boolean): string;
44
53
  protected getJsonPathScalarExpr(escapedColumn: string, jsonPathStr: string): string;
45
54
  protected numericCast(expr: string): string;
46
- insert<E>(ctx: QueryContext, entity: Type<E>, payload: E | E[], opts?: QueryOptions): void;
47
- upsert<E>(ctx: QueryContext, entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: E | E[]): void;
48
55
  protected jsonCast(operand: string): string;
49
56
  /**
50
57
  * `json_replace` leaves an absent key (and a NULL column) untouched. Elements are read back
@@ -53,7 +60,13 @@ export declare class SqliteDialect extends AbstractSqlDialect {
53
60
  */
54
61
  protected jsonPullKey(ctx: QueryContext, expr: string, escapedCol: string, key: string, value: unknown): string;
55
62
  protected jsonSet(ctx: QueryContext, expr: string, set: Record<string, unknown>, field?: FieldOptions): string;
56
- /** `[#]` appends, creating the array when the key is absent. */
63
+ /**
64
+ * `[#]` appends, creating the array when the key is absent.
65
+ *
66
+ * @remarks `json_set` rather than `json_insert`: the two are equivalent here because `[#]` always
67
+ * resolves past the end of the array, and Turso's engine implements `json_insert` as create-only,
68
+ * so it silently drops the element when the array already exists.
69
+ */
57
70
  protected jsonPush(ctx: QueryContext, expr: string, push: Record<string, unknown>): string;
58
71
  protected jsonUnset(_ctx: QueryContext, expr: string, unset: readonly string[]): string;
59
72
  }
@@ -1,6 +1,5 @@
1
1
  import { AbstractSqlDialect } from '../dialect/abstractSqlDialect.js';
2
2
  import { JSON_ELEM_ALIAS_PREFIX, JSON_PULL_ALIAS, jsonAssignCall, jsonElemExists, jsonPath, jsonRemoveCall, jsonSetTarget, } from '../dialect/jsonSql.js';
3
- import { getMeta } from '../entity/index.js';
4
3
  export class SqliteDialect extends AbstractSqlDialect {
5
4
  /** Default {@link DialectFeatures} for SQLite and SQLite-derived dialects. */
6
5
  featureDefaults = {
@@ -13,7 +12,7 @@ export class SqliteDialect extends AbstractSqlDialect {
13
12
  renameColumn: true,
14
13
  foreignKeyAlter: false, // SQLite does not support adding FKs to existing tables
15
14
  columnComment: false, // SQLite does not support column comments
16
- vectorIndexStyle: 'create',
15
+ inlineVectorIndex: false,
17
16
  vectorSupportsLength: false,
18
17
  supportsTimestamptz: false,
19
18
  defaultStringAsText: true,
@@ -30,10 +29,15 @@ export class SqliteDialect extends AbstractSqlDialect {
30
29
  booleanLiteral = 'integer';
31
30
  // SQLite supports `RETURNING` (including on `INSERT ... ON CONFLICT`), so IDs are exact per row.
32
31
  insertIdSource = 'returning';
32
+ /**
33
+ * The [sqlite-vec](https://github.com/asg017/sqlite-vec) functions, which need that extension
34
+ * loaded on the connection (see `Sqlite3QuerierPool`'s `extensions` option). libSQL and Turso ship
35
+ * their own vector functions instead, so `LibsqlDialect` overrides this.
36
+ */
33
37
  vectorDistanceFns = new Map([
34
38
  ['cosine', 'vec_distance_cosine'],
35
39
  ['l2', 'vec_distance_L2'],
36
- ['hamming', 'vec_distance_hamming'],
40
+ ['l1', 'vec_distance_L1'],
37
41
  ]);
38
42
  /**
39
43
  * SQLite does not support the `DEFAULT` keyword inside `VALUES`. Inline the metadata default
@@ -58,21 +62,14 @@ export class SqliteDialect extends AbstractSqlDialect {
58
62
  return value.getTime();
59
63
  return super.normalizeValue(value);
60
64
  }
61
- compare(ctx, entity, key, val, opts) {
62
- if (key === '$text') {
63
- const meta = getMeta(entity);
64
- const search = val;
65
- const fields = search.$fields.map((fKey) => {
66
- const field = meta.fields[fKey];
67
- const columnName = this.resolveColumnName(fKey, field);
68
- return this.escapeId(columnName);
69
- });
70
- const tableName = this.resolveTableName(entity, meta);
71
- ctx.append(`${this.escapeId(tableName)} MATCH {${fields.join(' ')}} : `);
72
- ctx.addValue(search.$value);
73
- return;
74
- }
75
- super.compare(ctx, entity, key, val, opts);
65
+ /**
66
+ * FTS5 matches the table itself rather than its columns, so this only works when the table *is* an
67
+ * FTS5 virtual table (UQL does not create those; declare it outside your entities).
68
+ */
69
+ appendTextSearch(ctx, entity, meta, search) {
70
+ const columns = search.$fields.map((key) => this.escapeId(this.resolveColumnName(key, meta.fields[key])));
71
+ ctx.append(`${this.escapeId(this.resolveTableName(entity, meta))} MATCH {${columns.join(' ')}} : `);
72
+ ctx.addValue(search.$value);
76
73
  }
77
74
  /**
78
75
  * SQLite compares an exploded element as whole JSON text, so containment cannot express "this
@@ -111,22 +108,6 @@ export class SqliteDialect extends AbstractSqlDialect {
111
108
  numericCast(expr) {
112
109
  return `CAST(${expr} AS REAL)`;
113
110
  }
114
- insert(ctx, entity, payload, opts) {
115
- super.insert(ctx, entity, payload, opts);
116
- ctx.append(' ' + this.returningId(entity));
117
- }
118
- upsert(ctx, entity, conflictPaths, payload) {
119
- const meta = getMeta(entity);
120
- const updateCtx = this.createContext();
121
- const update = this.getUpsertUpdateAssignments(updateCtx, meta, conflictPaths, payload, (name) => `EXCLUDED.${name}`);
122
- const keysStr = this.getUpsertConflictPathsStr(meta, conflictPaths);
123
- const onConflict = update ? `DO UPDATE SET ${update}` : 'DO NOTHING';
124
- // Use the base (non-RETURNING) insert here: the appended RETURNING below would otherwise
125
- // be doubled by `this.insert`'s own.
126
- super.insert(ctx, entity, payload);
127
- ctx.append(` ON CONFLICT (${keysStr}) ${onConflict} ${this.returningId(entity)}`);
128
- ctx.pushValue(...updateCtx.values);
129
- }
130
111
  jsonCast(operand) {
131
112
  return `json(${operand})`;
132
113
  }
@@ -144,9 +125,15 @@ export class SqliteDialect extends AbstractSqlDialect {
144
125
  jsonSet(ctx, expr, set, field) {
145
126
  return jsonAssignCall((value) => this.jsonScalarParam(ctx, value), 'json_set', jsonSetTarget(expr, field, `'{}'`), set);
146
127
  }
147
- /** `[#]` appends, creating the array when the key is absent. */
128
+ /**
129
+ * `[#]` appends, creating the array when the key is absent.
130
+ *
131
+ * @remarks `json_set` rather than `json_insert`: the two are equivalent here because `[#]` always
132
+ * resolves past the end of the array, and Turso's engine implements `json_insert` as create-only,
133
+ * so it silently drops the element when the array already exists.
134
+ */
148
135
  jsonPush(ctx, expr, push) {
149
- return jsonAssignCall((value) => this.jsonScalarParam(ctx, value), 'json_insert', expr, push, '[#]');
136
+ return jsonAssignCall((value) => this.jsonScalarParam(ctx, value), 'json_set', expr, push, '[#]');
150
137
  }
151
138
  jsonUnset(_ctx, expr, unset) {
152
139
  return jsonRemoveCall('json_remove', expr, unset);