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.
- package/README.md +4 -4
- package/dist/browser/uql-browser.min.js.map +3 -3
- package/dist/dialect/abstractSqlDialect.d.ts +14 -1
- package/dist/dialect/abstractSqlDialect.js +21 -14
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +11 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +25 -0
- package/dist/dialect/pgLikeSqlDialect.d.ts +4 -7
- package/dist/dialect/pgLikeSqlDialect.js +7 -12
- package/dist/entity/decorator/bag.d.ts +35 -0
- package/dist/entity/decorator/bag.js +54 -0
- package/dist/entity/decorator/entity.d.ts +28 -2
- package/dist/entity/decorator/entity.js +40 -2
- package/dist/entity/decorator/members.d.ts +51 -0
- package/dist/entity/decorator/members.js +51 -0
- package/dist/entity/index.d.ts +3 -1
- package/dist/entity/index.js +3 -1
- package/dist/entity/metadata/definition.d.ts +23 -9
- package/dist/entity/metadata/definition.js +55 -100
- package/dist/http/contract.js +1 -2
- package/dist/http/handler.js +5 -26
- package/dist/http/query.js +1 -1
- package/dist/migrate/acquireQuerierForMigrations.d.ts +13 -1
- package/dist/migrate/acquireQuerierForMigrations.js +28 -0
- package/dist/migrate/cli-config.js +24 -8
- package/dist/migrate/cli.js +5 -1
- package/dist/migrate/codegen/entityCodeGenerator.js +3 -4
- package/dist/migrate/codegen/index.d.ts +0 -1
- package/dist/migrate/codegen/index.js +0 -2
- package/dist/migrate/drift/driftDetector.d.ts +10 -1
- package/dist/migrate/drift/driftDetector.js +22 -5
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +5 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +17 -27
- package/dist/migrate/introspection/mongoIntrospector.js +7 -13
- package/dist/migrate/migrator.js +44 -81
- package/dist/migrate/storage/databaseStorage.d.ts +2 -0
- package/dist/migrate/storage/databaseStorage.js +8 -23
- package/dist/mongo/mongodbQuerier.js +171 -229
- package/dist/nestjs/uqlContextInterceptor.js +58 -22
- package/dist/nestjs/uqlModule.d.ts +2 -11
- package/dist/nestjs/uqlModule.js +93 -44
- package/dist/postgres/postgresDialect.js +2 -2
- package/dist/querier/abstractQuerier.d.ts +28 -0
- package/dist/querier/abstractQuerier.js +47 -22
- package/dist/querier/abstractQuerierPool.d.ts +21 -1
- package/dist/querier/abstractQuerierPool.js +113 -3
- package/dist/querier/abstractSqlQuerier.d.ts +1 -3
- package/dist/querier/abstractSqlQuerier.js +51 -96
- package/dist/querier/index.d.ts +3 -1
- package/dist/querier/index.js +3 -1
- package/dist/querier/querierContext.browser.d.ts +12 -0
- package/dist/querier/querierContext.browser.js +18 -0
- package/dist/querier/querierContext.d.ts +22 -0
- package/dist/querier/querierContext.js +42 -0
- package/dist/querier/queryError.d.ts +22 -0
- package/dist/querier/queryError.js +20 -0
- package/dist/querier/transactional.d.ts +26 -0
- package/dist/querier/transactional.js +43 -0
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/schemaASTBuilder.d.ts +1 -1
- package/dist/schema/schemaASTBuilder.js +2 -2
- package/dist/schema/types.d.ts +0 -21
- package/dist/sqlite/abstractSqliteQuerier.d.ts +9 -2
- package/dist/sqlite/hranaQuerier.js +20 -42
- package/dist/sqlite/index.d.ts +1 -0
- package/dist/sqlite/index.js +1 -0
- package/dist/sqlite/localSqliteQuerierPool.d.ts +31 -0
- package/dist/sqlite/localSqliteQuerierPool.js +34 -0
- package/dist/sqlite/nodeSqliteAdapter.d.ts +35 -0
- package/dist/sqlite/nodeSqliteAdapter.js +28 -0
- package/dist/sqlite/nodeSqliteQuerierPool.d.ts +28 -0
- package/dist/sqlite/nodeSqliteQuerierPool.js +29 -0
- package/dist/sqlite/sqliteDialect.d.ts +1 -2
- package/dist/sqlite/sqliteDialect.js +0 -13
- package/dist/sqlite/sqliteQuerierPool.d.ts +10 -23
- package/dist/sqlite/sqliteQuerierPool.js +9 -28
- package/dist/type/entity.d.ts +95 -28
- package/dist/type/querier.d.ts +8 -35
- package/dist/type/querierPool.d.ts +13 -12
- package/dist/type/universalQuerier.d.ts +23 -7
- package/dist/util/hook.util.js +1 -1
- package/package.json +7 -16
- package/dist/entity/decorator/field.d.ts +0 -2
- package/dist/entity/decorator/field.js +0 -7
- package/dist/entity/decorator/filter.d.ts +0 -7
- package/dist/entity/decorator/filter.js +0 -11
- package/dist/entity/decorator/hook.d.ts +0 -7
- package/dist/entity/decorator/hook.js +0 -14
- package/dist/entity/decorator/id.d.ts +0 -2
- package/dist/entity/decorator/id.js +0 -7
- package/dist/entity/decorator/index-decorator.d.ts +0 -22
- package/dist/entity/decorator/index-decorator.js +0 -26
- package/dist/entity/decorator/index.d.ts +0 -8
- package/dist/entity/decorator/index.js +0 -8
- package/dist/entity/decorator/relation.d.ts +0 -8
- package/dist/entity/decorator/relation.js +0 -19
- package/dist/migrate/codegen/smartRelationDetector.d.ts +0 -47
- package/dist/migrate/codegen/smartRelationDetector.js +0 -134
- package/dist/querier/decorator/index.d.ts +0 -4
- package/dist/querier/decorator/index.js +0 -4
- package/dist/querier/decorator/injectQuerier.d.ts +0 -3
- package/dist/querier/decorator/injectQuerier.js +0 -33
- package/dist/querier/decorator/log.d.ts +0 -32
- package/dist/querier/decorator/log.js +0 -52
- package/dist/querier/decorator/serialized.d.ts +0 -6
- package/dist/querier/decorator/serialized.js +0 -13
- package/dist/querier/decorator/transactional.d.ts +0 -6
- 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
|
|
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
|
|
38
|
+
* Called before every query but outside the timing window.
|
|
48
39
|
*/
|
|
49
40
|
async lazyConnect() { }
|
|
50
41
|
async all(query, values) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
247
|
-
|
|
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
|
-
|
|
256
|
-
|
|
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
|
-
|
|
251
|
+
this.hasPendingTransaction = true;
|
|
252
|
+
});
|
|
260
253
|
}
|
|
261
254
|
async commitTransaction() {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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);
|
package/dist/querier/index.d.ts
CHANGED
|
@@ -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 './
|
|
5
|
+
export * from './querierContext.js';
|
|
6
|
+
export * from './queryError.js';
|
|
7
|
+
export * from './transactional.js';
|
package/dist/querier/index.js
CHANGED
|
@@ -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 './
|
|
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
|
+
}
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -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,
|
|
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.
|
|
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.
|
|
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.
|
|
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());
|
package/dist/schema/types.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
3
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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);
|
package/dist/sqlite/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/sqlite/index.js
CHANGED
|
@@ -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
|
+
}
|