uql-orm 0.22.0 → 0.24.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 (107) hide show
  1. package/README.md +4 -4
  2. package/dist/browser/uql-browser.min.js.map +3 -3
  3. package/dist/dialect/abstractSqlDialect.d.ts +14 -1
  4. package/dist/dialect/abstractSqlDialect.js +21 -14
  5. package/dist/dialect/mysqlLikeSqlDialect.d.ts +11 -1
  6. package/dist/dialect/mysqlLikeSqlDialect.js +25 -0
  7. package/dist/dialect/pgLikeSqlDialect.d.ts +4 -7
  8. package/dist/dialect/pgLikeSqlDialect.js +7 -12
  9. package/dist/entity/decorator/bag.d.ts +35 -0
  10. package/dist/entity/decorator/bag.js +54 -0
  11. package/dist/entity/decorator/entity.d.ts +28 -2
  12. package/dist/entity/decorator/entity.js +40 -2
  13. package/dist/entity/decorator/members.d.ts +51 -0
  14. package/dist/entity/decorator/members.js +51 -0
  15. package/dist/entity/index.d.ts +3 -1
  16. package/dist/entity/index.js +3 -1
  17. package/dist/entity/metadata/definition.d.ts +23 -9
  18. package/dist/entity/metadata/definition.js +55 -100
  19. package/dist/http/contract.js +1 -2
  20. package/dist/http/handler.js +5 -26
  21. package/dist/http/query.js +1 -1
  22. package/dist/migrate/acquireQuerierForMigrations.d.ts +13 -1
  23. package/dist/migrate/acquireQuerierForMigrations.js +28 -0
  24. package/dist/migrate/cli-config.js +24 -8
  25. package/dist/migrate/cli.js +5 -1
  26. package/dist/migrate/codegen/entityCodeGenerator.js +3 -4
  27. package/dist/migrate/codegen/index.d.ts +0 -1
  28. package/dist/migrate/codegen/index.js +0 -2
  29. package/dist/migrate/drift/driftDetector.d.ts +10 -1
  30. package/dist/migrate/drift/driftDetector.js +22 -5
  31. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +5 -1
  32. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +17 -27
  33. package/dist/migrate/introspection/mongoIntrospector.js +7 -13
  34. package/dist/migrate/migrator.js +44 -81
  35. package/dist/migrate/storage/databaseStorage.d.ts +2 -0
  36. package/dist/migrate/storage/databaseStorage.js +8 -23
  37. package/dist/mongo/mongodbQuerier.js +171 -229
  38. package/dist/nestjs/uqlContextInterceptor.js +58 -22
  39. package/dist/nestjs/uqlModule.d.ts +2 -11
  40. package/dist/nestjs/uqlModule.js +93 -44
  41. package/dist/postgres/postgresDialect.js +2 -2
  42. package/dist/querier/abstractQuerier.d.ts +28 -0
  43. package/dist/querier/abstractQuerier.js +47 -22
  44. package/dist/querier/abstractQuerierPool.d.ts +21 -1
  45. package/dist/querier/abstractQuerierPool.js +113 -3
  46. package/dist/querier/abstractSqlQuerier.d.ts +1 -3
  47. package/dist/querier/abstractSqlQuerier.js +51 -96
  48. package/dist/querier/index.d.ts +3 -1
  49. package/dist/querier/index.js +3 -1
  50. package/dist/querier/querierContext.browser.d.ts +12 -0
  51. package/dist/querier/querierContext.browser.js +18 -0
  52. package/dist/querier/querierContext.d.ts +22 -0
  53. package/dist/querier/querierContext.js +42 -0
  54. package/dist/querier/queryError.d.ts +22 -0
  55. package/dist/querier/queryError.js +20 -0
  56. package/dist/querier/transactional.d.ts +26 -0
  57. package/dist/querier/transactional.js +43 -0
  58. package/dist/schema/index.d.ts +1 -1
  59. package/dist/schema/schemaASTBuilder.d.ts +1 -1
  60. package/dist/schema/schemaASTBuilder.js +2 -2
  61. package/dist/schema/types.d.ts +0 -21
  62. package/dist/sqlite/abstractSqliteQuerier.d.ts +9 -2
  63. package/dist/sqlite/hranaQuerier.js +20 -42
  64. package/dist/sqlite/index.d.ts +1 -0
  65. package/dist/sqlite/index.js +1 -0
  66. package/dist/sqlite/localSqliteQuerierPool.d.ts +31 -0
  67. package/dist/sqlite/localSqliteQuerierPool.js +34 -0
  68. package/dist/sqlite/nodeSqliteAdapter.d.ts +35 -0
  69. package/dist/sqlite/nodeSqliteAdapter.js +28 -0
  70. package/dist/sqlite/nodeSqliteQuerierPool.d.ts +28 -0
  71. package/dist/sqlite/nodeSqliteQuerierPool.js +29 -0
  72. package/dist/sqlite/sqliteDialect.d.ts +1 -2
  73. package/dist/sqlite/sqliteDialect.js +0 -13
  74. package/dist/sqlite/sqliteQuerierPool.d.ts +10 -23
  75. package/dist/sqlite/sqliteQuerierPool.js +9 -28
  76. package/dist/type/entity.d.ts +95 -28
  77. package/dist/type/querier.d.ts +8 -35
  78. package/dist/type/querierPool.d.ts +13 -12
  79. package/dist/type/universalQuerier.d.ts +23 -7
  80. package/dist/util/hook.util.js +1 -1
  81. package/package.json +7 -16
  82. package/dist/entity/decorator/field.d.ts +0 -2
  83. package/dist/entity/decorator/field.js +0 -7
  84. package/dist/entity/decorator/filter.d.ts +0 -7
  85. package/dist/entity/decorator/filter.js +0 -11
  86. package/dist/entity/decorator/hook.d.ts +0 -7
  87. package/dist/entity/decorator/hook.js +0 -14
  88. package/dist/entity/decorator/id.d.ts +0 -2
  89. package/dist/entity/decorator/id.js +0 -7
  90. package/dist/entity/decorator/index-decorator.d.ts +0 -22
  91. package/dist/entity/decorator/index-decorator.js +0 -26
  92. package/dist/entity/decorator/index.d.ts +0 -8
  93. package/dist/entity/decorator/index.js +0 -8
  94. package/dist/entity/decorator/relation.d.ts +0 -8
  95. package/dist/entity/decorator/relation.js +0 -19
  96. package/dist/migrate/codegen/smartRelationDetector.d.ts +0 -47
  97. package/dist/migrate/codegen/smartRelationDetector.js +0 -134
  98. package/dist/querier/decorator/index.d.ts +0 -4
  99. package/dist/querier/decorator/index.js +0 -4
  100. package/dist/querier/decorator/injectQuerier.d.ts +0 -3
  101. package/dist/querier/decorator/injectQuerier.js +0 -33
  102. package/dist/querier/decorator/log.d.ts +0 -32
  103. package/dist/querier/decorator/log.js +0 -52
  104. package/dist/querier/decorator/serialized.d.ts +0 -6
  105. package/dist/querier/decorator/serialized.js +0 -13
  106. package/dist/querier/decorator/transactional.d.ts +0 -6
  107. package/dist/querier/decorator/transactional.js +0 -48
@@ -1,16 +1,7 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- var __metadata = (this && this.__metadata) || function (k, v) {
8
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
- };
10
1
  import { getMeta } from '../entity/index.js';
11
2
  import { buildUpdateResult, clone, getInsertFieldKeys, getRelationRequestSummary, isAutoIncrement, obtainAttrsPaths, throwNoPendingTransaction, throwPendingTransaction, unflatObject, unflatObjects, withoutSoftDeleteFilter, } from '../util/index.js';
12
3
  import { AbstractQuerier } from './abstractQuerier.js';
13
- import { enrichError, Log, Serialized } from './decorator/index.js';
4
+ import { enrichError } from './queryError.js';
14
5
  export class AbstractSqlQuerier extends AbstractQuerier {
15
6
  dialect;
16
7
  extra;
@@ -44,22 +35,20 @@ export class AbstractSqlQuerier extends AbstractQuerier {
44
35
  }
45
36
  /**
46
37
  * Hook for subclasses (e.g. pool queriers) to establish a connection.
47
- * Called before every query but outside the `@Log()` timer.
38
+ * Called before every query but outside the timing window.
48
39
  */
49
40
  async lazyConnect() { }
50
41
  async all(query, values) {
51
- await this.lazyConnect();
52
- return this.timedAll(query, values);
53
- }
54
- async timedAll(query, values) {
55
- return this.internalAll(query, this.dialect.normalizeValues(values));
42
+ return this.serialize(async () => {
43
+ await this.lazyConnect();
44
+ return this.timed(query, values, () => this.internalAll(query, this.dialect.normalizeValues(values)));
45
+ });
56
46
  }
57
47
  async run(query, values) {
58
- await this.lazyConnect();
59
- return this.timedRun(query, values);
60
- }
61
- async timedRun(query, values) {
62
- return this.internalRun(query, this.dialect.normalizeValues(values));
48
+ return this.serialize(async () => {
49
+ await this.lazyConnect();
50
+ return this.timed(query, values, () => this.internalRun(query, this.dialect.normalizeValues(values)));
51
+ });
63
52
  }
64
53
  async internalFindMany(entity, q, opts) {
65
54
  const ctx = this.dialect.createContext();
@@ -75,6 +64,9 @@ export class AbstractSqlQuerier extends AbstractQuerier {
75
64
  if (toManyKeys.length) {
76
65
  throw new TypeError(`findManyStream does not load to-many relations (${toManyKeys.join(', ')}). Use findMany so fillToManyRelations can run, or omit those keys from the stream query.`);
77
66
  }
67
+ // The one path that does not go through `all`/`run`, so it connects on its own: streaming first on
68
+ // a freshly acquired querier used to reach `getConn()` with nothing acquired.
69
+ await this.lazyConnect();
78
70
  const ctx = this.dialect.createContext();
79
71
  this.dialect.find(ctx, entity, q, opts);
80
72
  const normalizedParams = this.dialect.normalizeValues(ctx.values);
@@ -86,7 +78,7 @@ export class AbstractSqlQuerier extends AbstractQuerier {
86
78
  }
87
79
  }
88
80
  catch (err) {
89
- enrichError(err, this.logger, ctx.sql, normalizedParams);
81
+ throw enrichError(err, this.logger, ctx.sql, normalizedParams);
90
82
  }
91
83
  }
92
84
  /**
@@ -243,85 +235,48 @@ export class AbstractSqlQuerier extends AbstractQuerier {
243
235
  return !!this.hasPendingTransaction;
244
236
  }
245
237
  async beginTransaction(opts) {
246
- if (this.hasPendingTransaction) {
247
- throwPendingTransaction();
248
- }
249
- await this.lazyConnect();
250
- const statements = this.dialect.getBeginTransactionStatements(opts?.isolationLevel);
251
- for (const sql of statements) {
252
- try {
253
- await this.internalRun(sql);
238
+ return this.serialize(async () => {
239
+ if (this.hasPendingTransaction) {
240
+ throwPendingTransaction();
254
241
  }
255
- catch (err) {
256
- enrichError(err, this.logger, sql);
242
+ await this.lazyConnect();
243
+ for (const sql of this.dialect.getBeginTransactionStatements(opts?.isolationLevel)) {
244
+ try {
245
+ await this.internalRun(sql);
246
+ }
247
+ catch (err) {
248
+ throw enrichError(err, this.logger, sql);
249
+ }
257
250
  }
258
- }
259
- this.hasPendingTransaction = true;
251
+ this.hasPendingTransaction = true;
252
+ });
260
253
  }
261
254
  async commitTransaction() {
262
- if (!this.hasPendingTransaction) {
263
- throwNoPendingTransaction();
264
- }
265
- try {
266
- await this.internalRun(this.dialect.commitTransactionCommand);
267
- }
268
- catch (err) {
269
- enrichError(err, this.logger, this.dialect.commitTransactionCommand);
270
- }
271
- this.hasPendingTransaction = false;
255
+ return this.serialize(async () => {
256
+ if (!this.hasPendingTransaction) {
257
+ throwNoPendingTransaction();
258
+ }
259
+ try {
260
+ await this.internalRun(this.dialect.commitTransactionCommand);
261
+ }
262
+ catch (err) {
263
+ throw enrichError(err, this.logger, this.dialect.commitTransactionCommand);
264
+ }
265
+ this.hasPendingTransaction = false;
266
+ });
272
267
  }
273
268
  async rollbackTransaction() {
274
- if (!this.hasPendingTransaction) {
275
- throwNoPendingTransaction();
276
- }
277
- try {
278
- await this.internalRun(this.dialect.rollbackTransactionCommand);
279
- }
280
- catch (err) {
281
- enrichError(err, this.logger, this.dialect.rollbackTransactionCommand);
282
- }
283
- this.hasPendingTransaction = false;
269
+ return this.serialize(async () => {
270
+ if (!this.hasPendingTransaction) {
271
+ throwNoPendingTransaction();
272
+ }
273
+ try {
274
+ await this.internalRun(this.dialect.rollbackTransactionCommand);
275
+ }
276
+ catch (err) {
277
+ throw enrichError(err, this.logger, this.dialect.rollbackTransactionCommand);
278
+ }
279
+ this.hasPendingTransaction = false;
280
+ });
284
281
  }
285
282
  }
286
- __decorate([
287
- Serialized(),
288
- __metadata("design:type", Function),
289
- __metadata("design:paramtypes", [String, Array]),
290
- __metadata("design:returntype", Promise)
291
- ], AbstractSqlQuerier.prototype, "all", null);
292
- __decorate([
293
- Log(),
294
- __metadata("design:type", Function),
295
- __metadata("design:paramtypes", [String, Array]),
296
- __metadata("design:returntype", Promise)
297
- ], AbstractSqlQuerier.prototype, "timedAll", null);
298
- __decorate([
299
- Serialized(),
300
- __metadata("design:type", Function),
301
- __metadata("design:paramtypes", [String, Array]),
302
- __metadata("design:returntype", Promise)
303
- ], AbstractSqlQuerier.prototype, "run", null);
304
- __decorate([
305
- Log(),
306
- __metadata("design:type", Function),
307
- __metadata("design:paramtypes", [String, Array]),
308
- __metadata("design:returntype", Promise)
309
- ], AbstractSqlQuerier.prototype, "timedRun", null);
310
- __decorate([
311
- Serialized(),
312
- __metadata("design:type", Function),
313
- __metadata("design:paramtypes", [Object]),
314
- __metadata("design:returntype", Promise)
315
- ], AbstractSqlQuerier.prototype, "beginTransaction", null);
316
- __decorate([
317
- Serialized(),
318
- __metadata("design:type", Function),
319
- __metadata("design:paramtypes", []),
320
- __metadata("design:returntype", Promise)
321
- ], AbstractSqlQuerier.prototype, "commitTransaction", null);
322
- __decorate([
323
- Serialized(),
324
- __metadata("design:type", Function),
325
- __metadata("design:paramtypes", []),
326
- __metadata("design:returntype", Promise)
327
- ], AbstractSqlQuerier.prototype, "rollbackTransaction", null);
@@ -2,4 +2,6 @@ export * from './abstractQuerier.js';
2
2
  export * from './abstractQuerierPool.js';
3
3
  export * from './abstractSqlQuerier.js';
4
4
  export * from './abstractSqlQuerierPool.js';
5
- export * from './decorator/index.js';
5
+ export * from './querierContext.js';
6
+ export * from './queryError.js';
7
+ export * from './transactional.js';
@@ -2,4 +2,6 @@ export * from './abstractQuerier.js';
2
2
  export * from './abstractQuerierPool.js';
3
3
  export * from './abstractSqlQuerier.js';
4
4
  export * from './abstractSqlQuerierPool.js';
5
- export * from './decorator/index.js';
5
+ export * from './querierContext.js';
6
+ export * from './queryError.js';
7
+ export * from './transactional.js';
@@ -0,0 +1,12 @@
1
+ import type { Querier } from '../type/index.js';
2
+ /**
3
+ * Browser build of the transactional querier context: bundlers targeting the browser resolve
4
+ * `querierContext.ts` to this file (see the `browser` map in package.json), keeping the root entrypoint
5
+ * free of `node:async_hooks`.
6
+ *
7
+ * Nothing in a browser bundle opens a transaction. The browser querier serializes queries over HTTP and
8
+ * the server owns the transaction, so there is no ambient querier to hand out and no flow to track.
9
+ */
10
+ export declare function withQuerierContext<T>(_querier: Querier, callback: () => T): T;
11
+ export declare function currentQuerier(): Querier;
12
+ export declare function currentQuerierIfAny(): Querier | undefined;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Browser build of the transactional querier context: bundlers targeting the browser resolve
3
+ * `querierContext.ts` to this file (see the `browser` map in package.json), keeping the root entrypoint
4
+ * free of `node:async_hooks`.
5
+ *
6
+ * Nothing in a browser bundle opens a transaction. The browser querier serializes queries over HTTP and
7
+ * the server owns the transaction, so there is no ambient querier to hand out and no flow to track.
8
+ */
9
+ export function withQuerierContext(_querier, callback) {
10
+ return callback();
11
+ }
12
+ export function currentQuerier() {
13
+ throw new TypeError('currentQuerier() is server-only: transactions run on the server, and the browser querier sends each ' +
14
+ 'request over HTTP. Call it from server code, or use the querier you already have.');
15
+ }
16
+ export function currentQuerierIfAny() {
17
+ return undefined;
18
+ }
@@ -0,0 +1,22 @@
1
+ import type { Querier } from '../type/index.js';
2
+ /** Runs `callback` with `querier` as the ambient one, for the whole async flow beneath it. */
3
+ export declare function withQuerierContext<T>(querier: Querier, callback: () => T): T;
4
+ /**
5
+ * The querier of the enclosing `@Transactional()` method.
6
+ *
7
+ * This is what replaced `@InjectQuerier()`: the standard decorator spec has no parameter decorators, so
8
+ * the querier can no longer be injected into an argument and is read from the ambient flow instead.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * class UserService {
13
+ * @Transactional()
14
+ * async register(data: Partial<User>) {
15
+ * await currentQuerier().insertOne(User, data);
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
+ export declare function currentQuerier(): Querier;
21
+ /** The ambient querier, or `undefined` outside a transaction. For callers that can work without one. */
22
+ export declare function currentQuerierIfAny(): Querier | undefined;
@@ -0,0 +1,42 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ /**
3
+ * The querier `@Transactional()` opened for the current async flow.
4
+ *
5
+ * Separate from the `UqlContext` storage in `context/context.ts` on purpose: that one is remapped to a
6
+ * synchronous browser shim which cannot propagate across `await`, and a querier handle must. Nothing in
7
+ * a browser bundle opens a transaction, so this stays server-only and `verify-dist` keeps it out of the
8
+ * browser-facing graph.
9
+ */
10
+ const querierStorage = new AsyncLocalStorage();
11
+ /** Runs `callback` with `querier` as the ambient one, for the whole async flow beneath it. */
12
+ export function withQuerierContext(querier, callback) {
13
+ return querierStorage.run(querier, callback);
14
+ }
15
+ /**
16
+ * The querier of the enclosing `@Transactional()` method.
17
+ *
18
+ * This is what replaced `@InjectQuerier()`: the standard decorator spec has no parameter decorators, so
19
+ * the querier can no longer be injected into an argument and is read from the ambient flow instead.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * class UserService {
24
+ * @Transactional()
25
+ * async register(data: Partial<User>) {
26
+ * await currentQuerier().insertOne(User, data);
27
+ * }
28
+ * }
29
+ * ```
30
+ */
31
+ export function currentQuerier() {
32
+ const querier = querierStorage.getStore();
33
+ if (!querier) {
34
+ throw new TypeError('currentQuerier() found no active querier. Call it inside a @Transactional() method, or take a querier ' +
35
+ 'from the pool yourself with `await using querier = await pool.getQuerier()`.');
36
+ }
37
+ return querier;
38
+ }
39
+ /** The ambient querier, or `undefined` outside a transaction. For callers that can work without one. */
40
+ export function currentQuerierIfAny() {
41
+ return querierStorage.getStore();
42
+ }
@@ -0,0 +1,22 @@
1
+ import type { LoggerWrapper } from '../util/logger.js';
2
+ /**
3
+ * A driver error enriched with context by {@link enrichError}. `query` is always attached; `values`
4
+ * only when the querier's logger is configured to surface them (see {@link enrichError}) - they can
5
+ * carry sensitive data (PII, tokens, etc.) and would otherwise leak into whatever error-tracking
6
+ * pipeline (Sentry, console.error, ...) serializes the error, without the developer opting in.
7
+ */
8
+ export interface QueryError extends Error {
9
+ query?: string;
10
+ values?: unknown[];
11
+ }
12
+ /**
13
+ * Tags `err` with the query it failed on (as {@link QueryError}) and hands it back for the caller to
14
+ * throw. `values` is only attached when `logger?.willLogValues()` is true, i.e. the app already has
15
+ * query values surfacing somewhere (query-level or slow-query logging), so attaching them here does not
16
+ * introduce a new leak surface. Shared by every query call site (timed queries, streams, transaction
17
+ * statements) so the logic lives in one place.
18
+ *
19
+ * Returns rather than throws so `throw enrichError(...)` reads as the control flow it is, which also
20
+ * means callers need no `never` annotation and no unreachable-code suppression.
21
+ */
22
+ export declare function enrichError(err: unknown, logger: LoggerWrapper | undefined, query: string, values?: unknown[]): unknown;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Tags `err` with the query it failed on (as {@link QueryError}) and hands it back for the caller to
3
+ * throw. `values` is only attached when `logger?.willLogValues()` is true, i.e. the app already has
4
+ * query values surfacing somewhere (query-level or slow-query logging), so attaching them here does not
5
+ * introduce a new leak surface. Shared by every query call site (timed queries, streams, transaction
6
+ * statements) so the logic lives in one place.
7
+ *
8
+ * Returns rather than throws so `throw enrichError(...)` reads as the control flow it is, which also
9
+ * means callers need no `never` annotation and no unreachable-code suppression.
10
+ */
11
+ export function enrichError(err, logger, query, values) {
12
+ if (err instanceof Error) {
13
+ const queryError = err;
14
+ queryError.query ??= query;
15
+ if (values !== undefined && logger?.willLogValues()) {
16
+ queryError.values ??= values;
17
+ }
18
+ }
19
+ return err;
20
+ }
@@ -0,0 +1,26 @@
1
+ import type { IsolationLevel, QuerierPool } from '../type/index.js';
2
+ export type TransactionalOptions = {
3
+ /** `required` opens a transaction when none is active; `supported` joins one but never starts one. */
4
+ readonly propagation?: 'supported' | 'required';
5
+ readonly pool?: QuerierPool;
6
+ readonly isolationLevel?: IsolationLevel;
7
+ };
8
+ /**
9
+ * Wraps the method in a transaction and publishes its querier for {@link currentQuerier} to pick up.
10
+ *
11
+ * @remarks Replaces the `@InjectQuerier()` parameter that used to receive the querier. The standard
12
+ * decorator spec has no parameter decorators, and the separate TC39 proposal for them is still Stage 1,
13
+ * so the querier travels through async-local storage instead. A nested call joins the transaction
14
+ * already in flight rather than opening a second one.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * class UserService {
19
+ * @Transactional()
20
+ * async register(data: Partial<User>) {
21
+ * await currentQuerier().insertOne(User, data);
22
+ * }
23
+ * }
24
+ * ```
25
+ */
26
+ export declare function Transactional({ propagation, pool, isolationLevel }?: TransactionalOptions): <This, Args extends unknown[], R>(original: (this: This, ...args: Args) => Promise<R>, context: ClassMethodDecoratorContext<This>) => (this: This, ...args: Args) => Promise<R>;
@@ -0,0 +1,43 @@
1
+ import { getQuerierPool } from '../options.js';
2
+ import { currentQuerierIfAny, withQuerierContext } from './querierContext.js';
3
+ /**
4
+ * Wraps the method in a transaction and publishes its querier for {@link currentQuerier} to pick up.
5
+ *
6
+ * @remarks Replaces the `@InjectQuerier()` parameter that used to receive the querier. The standard
7
+ * decorator spec has no parameter decorators, and the separate TC39 proposal for them is still Stage 1,
8
+ * so the querier travels through async-local storage instead. A nested call joins the transaction
9
+ * already in flight rather than opening a second one.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * class UserService {
14
+ * @Transactional()
15
+ * async register(data: Partial<User>) {
16
+ * await currentQuerier().insertOne(User, data);
17
+ * }
18
+ * }
19
+ * ```
20
+ */
21
+ export function Transactional({ propagation = 'required', pool, isolationLevel } = {}) {
22
+ return (original, context) => {
23
+ // Checked at decoration time rather than on the first call: a synchronous method cannot be wrapped in
24
+ // a transaction, and finding that out at startup beats finding out mid-request.
25
+ if (original.constructor.name !== 'AsyncFunction') {
26
+ throw new TypeError(`@Transactional() needs an async method, but '${String(context.name)}' is not one.`);
27
+ }
28
+ return async function (...args) {
29
+ // Already inside a transactional flow: join it and let the outermost call own commit and release.
30
+ if (currentQuerierIfAny()) {
31
+ return original.apply(this, args);
32
+ }
33
+ // `withQuerier` releases; `transaction` commits or rolls back. `supported` joins a transaction but
34
+ // never starts one, so it takes only the first half.
35
+ return (pool ?? getQuerierPool()).withQuerier((querier) => {
36
+ const run = () => original.apply(this, args);
37
+ return withQuerierContext(querier, () => propagation === 'supported'
38
+ ? run()
39
+ : querier.transaction(run, isolationLevel ? { isolationLevel } : undefined));
40
+ });
41
+ };
42
+ };
43
+ }
@@ -22,4 +22,4 @@ export type { BuildFromEntitiesOptions } from './schemaASTBuilder.js';
22
22
  export { SchemaASTBuilder } from './schemaASTBuilder.js';
23
23
  export type { DiffOptions } from './schemaASTDiffer.js';
24
24
  export { diffSchemas, SchemaASTDiffer } from './schemaASTDiffer.js';
25
- export type { CanonicalType, ColumnDiff, ColumnNode, DetectedRelation, Drift, DriftReport, DriftSeverity, DriftStatus, DriftType, ForeignKeyAction, IndexDiff, IndexNode, IndexSource, IndexSyncStatus, IndexType, RelationshipDiff, RelationshipNode, RelationshipSource, RelationshipType, SchemaAST as ISchemaAST, SchemaDiffResult, SizeVariant, TableDiff, TableNode, TypeCategory, ValidationError, ValidationErrorType, } from './types.js';
25
+ export type { CanonicalType, ColumnDiff, ColumnNode, Drift, DriftReport, DriftSeverity, DriftStatus, DriftType, ForeignKeyAction, IndexDiff, IndexNode, IndexSource, IndexSyncStatus, IndexType, RelationshipDiff, RelationshipNode, RelationshipSource, RelationshipType, SchemaAST as ISchemaAST, SchemaDiffResult, SizeVariant, TableDiff, TableNode, TypeCategory, ValidationError, ValidationErrorType, } from './types.js';
@@ -54,7 +54,7 @@ export declare class SchemaASTBuilder {
54
54
  * foreign key column whose type doesn't match the UUID primary key it
55
55
  * references, which Postgres (and most databases) reject outright.
56
56
  *
57
- * `field.typeInferred` (set by `defineField`, see entity/metadata/definition.ts)
57
+ * `field.typeFromReference` (set by `defineField`, see entity/metadata/definition.ts)
58
58
  * is what distinguishes "no type was given" from "the decorator explicitly set
59
59
  * a type" - including explicit constructor overrides like `type: BigInt`, which
60
60
  * a value-based check (e.g. `typeof field.type === 'string'`) would miss since
@@ -74,7 +74,7 @@ export class SchemaASTBuilder {
74
74
  * foreign key column whose type doesn't match the UUID primary key it
75
75
  * references, which Postgres (and most databases) reject outright.
76
76
  *
77
- * `field.typeInferred` (set by `defineField`, see entity/metadata/definition.ts)
77
+ * `field.typeFromReference` (set by `defineField`, see entity/metadata/definition.ts)
78
78
  * is what distinguishes "no type was given" from "the decorator explicitly set
79
79
  * a type" - including explicit constructor overrides like `type: BigInt`, which
80
80
  * a value-based check (e.g. `typeof field.type === 'string'`) would miss since
@@ -82,7 +82,7 @@ export class SchemaASTBuilder {
82
82
  * `columnType` remains the unambiguous, always-respected explicit override.
83
83
  */
84
84
  resolveColumnCanonicalType(field, seen = new Set()) {
85
- const hasExplicitType = !!field.columnType || !field.typeInferred;
85
+ const hasExplicitType = !!field.columnType || !field.typeFromReference;
86
86
  if (!hasExplicitType && field.references && !seen.has(field.references)) {
87
87
  seen.add(field.references);
88
88
  const referencedMeta = getMeta(field.references());
@@ -256,27 +256,6 @@ export interface ValidationError {
256
256
  readonly relationship?: RelationshipNode;
257
257
  readonly tables?: TableNode[];
258
258
  }
259
- /**
260
- * A detected/inferred relationship with confidence score.
261
- */
262
- export interface DetectedRelation {
263
- readonly type: RelationshipType;
264
- readonly from: {
265
- readonly table: TableNode;
266
- readonly columns: ColumnNode[];
267
- };
268
- readonly to: {
269
- readonly table: TableNode;
270
- readonly columns: ColumnNode[];
271
- };
272
- readonly through?: TableNode;
273
- /** Confidence level (0-1) */
274
- readonly confidence: number;
275
- /** How this relation was detected */
276
- readonly source: RelationshipSource;
277
- /** Suggested constraint name */
278
- readonly suggestedName?: string;
279
- }
280
259
  /**
281
260
  * Severity level for schema drift issues.
282
261
  */
@@ -1,6 +1,13 @@
1
1
  import { AbstractSqlQuerier } from '../querier/index.js';
2
- /** Values every SQLite driver accepts as a bound parameter. */
3
- export type SqliteBindValue = null | string | number | bigint | boolean | Uint8Array;
2
+ /**
3
+ * Values every SQLite driver accepts as a bound parameter.
4
+ *
5
+ * @remarks No `boolean`: SQLite has no boolean storage class, and both `better-sqlite3` ("SQLite3 can
6
+ * only bind numbers, strings, bigints, buffers, and null") and `node:sqlite` reject one outright. The
7
+ * dialect already binds booleans as integers (`booleanLiteral: 'integer'`), so nothing reaches a
8
+ * driver as one; leaving `boolean` here only invited a runtime throw that is now a compile error.
9
+ */
10
+ export type SqliteBindValue = null | string | number | bigint | Uint8Array;
4
11
  /** Header a SQLite driver returns for a statement without a `RETURNING` clause. */
5
12
  export type SqliteRunResult = {
6
13
  changes: number;
@@ -1,13 +1,3 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- var __metadata = (this && this.__metadata) || function (k, v) {
8
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
- };
10
- import { Serialized } from '../querier/decorator/index.js';
11
1
  import { throwNoPendingTransaction, throwPendingTransaction } from '../util/index.js';
12
2
  import { AbstractSqliteQuerier } from './abstractSqliteQuerier.js';
13
3
  /**
@@ -44,24 +34,30 @@ export class HranaQuerier extends AbstractSqliteQuerier {
44
34
  return !!this.tx;
45
35
  }
46
36
  async beginTransaction(_opts) {
47
- if (this.tx) {
48
- throwPendingTransaction();
49
- }
50
- this.tx = await this.client.transaction('write');
37
+ return this.serialize(async () => {
38
+ if (this.tx) {
39
+ throwPendingTransaction();
40
+ }
41
+ this.tx = await this.client.transaction('write');
42
+ });
51
43
  }
52
44
  async commitTransaction() {
53
- if (!this.tx) {
54
- throwNoPendingTransaction();
55
- }
56
- await this.tx.commit();
57
- this.tx = undefined;
45
+ return this.serialize(async () => {
46
+ if (!this.tx) {
47
+ throwNoPendingTransaction();
48
+ }
49
+ await this.tx.commit();
50
+ this.tx = undefined;
51
+ });
58
52
  }
59
53
  async rollbackTransaction() {
60
- if (!this.tx) {
61
- throwNoPendingTransaction();
62
- }
63
- await this.tx.rollback();
64
- this.tx = undefined;
54
+ return this.serialize(async () => {
55
+ if (!this.tx) {
56
+ throwNoPendingTransaction();
57
+ }
58
+ await this.tx.rollback();
59
+ this.tx = undefined;
60
+ });
65
61
  }
66
62
  async internalRelease() {
67
63
  await super.internalRelease();
@@ -70,21 +66,3 @@ export class HranaQuerier extends AbstractSqliteQuerier {
70
66
  }
71
67
  }
72
68
  }
73
- __decorate([
74
- Serialized(),
75
- __metadata("design:type", Function),
76
- __metadata("design:paramtypes", [Object]),
77
- __metadata("design:returntype", Promise)
78
- ], HranaQuerier.prototype, "beginTransaction", null);
79
- __decorate([
80
- Serialized(),
81
- __metadata("design:type", Function),
82
- __metadata("design:paramtypes", []),
83
- __metadata("design:returntype", Promise)
84
- ], HranaQuerier.prototype, "commitTransaction", null);
85
- __decorate([
86
- Serialized(),
87
- __metadata("design:type", Function),
88
- __metadata("design:paramtypes", []),
89
- __metadata("design:returntype", Promise)
90
- ], HranaQuerier.prototype, "rollbackTransaction", null);
@@ -1,6 +1,7 @@
1
1
  export * from './abstractSqliteQuerier.js';
2
2
  export * from './hranaQuerier.js';
3
3
  export * from './hranaQuerierPool.js';
4
+ export * from './nodeSqliteQuerierPool.js';
4
5
  export * from './sqliteDialect.js';
5
6
  export * from './sqliteQuerier.js';
6
7
  export * from './sqliteQuerierPool.js';
@@ -1,6 +1,7 @@
1
1
  export * from './abstractSqliteQuerier.js';
2
2
  export * from './hranaQuerier.js';
3
3
  export * from './hranaQuerierPool.js';
4
+ export * from './nodeSqliteQuerierPool.js';
4
5
  export * from './sqliteDialect.js';
5
6
  export * from './sqliteQuerier.js';
6
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
+ }