uql-orm 0.22.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 (104) hide show
  1. package/dist/browser/uql-browser.min.js.map +3 -3
  2. package/dist/dialect/abstractSqlDialect.d.ts +14 -1
  3. package/dist/dialect/abstractSqlDialect.js +21 -14
  4. package/dist/dialect/mysqlLikeSqlDialect.d.ts +11 -1
  5. package/dist/dialect/mysqlLikeSqlDialect.js +25 -0
  6. package/dist/dialect/pgLikeSqlDialect.d.ts +4 -7
  7. package/dist/dialect/pgLikeSqlDialect.js +7 -12
  8. package/dist/entity/decorator/bag.d.ts +35 -0
  9. package/dist/entity/decorator/bag.js +54 -0
  10. package/dist/entity/decorator/entity.d.ts +28 -2
  11. package/dist/entity/decorator/entity.js +40 -2
  12. package/dist/entity/decorator/members.d.ts +51 -0
  13. package/dist/entity/decorator/members.js +51 -0
  14. package/dist/entity/index.d.ts +3 -1
  15. package/dist/entity/index.js +3 -1
  16. package/dist/entity/metadata/definition.d.ts +23 -9
  17. package/dist/entity/metadata/definition.js +55 -100
  18. package/dist/http/contract.js +1 -2
  19. package/dist/http/handler.js +5 -26
  20. package/dist/http/query.js +1 -1
  21. package/dist/migrate/acquireQuerierForMigrations.d.ts +13 -1
  22. package/dist/migrate/acquireQuerierForMigrations.js +28 -0
  23. package/dist/migrate/cli-config.js +24 -8
  24. package/dist/migrate/cli.js +5 -1
  25. package/dist/migrate/codegen/entityCodeGenerator.js +3 -4
  26. package/dist/migrate/codegen/index.d.ts +0 -1
  27. package/dist/migrate/codegen/index.js +0 -2
  28. package/dist/migrate/drift/driftDetector.d.ts +10 -1
  29. package/dist/migrate/drift/driftDetector.js +22 -5
  30. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +5 -1
  31. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +17 -27
  32. package/dist/migrate/introspection/mongoIntrospector.js +7 -13
  33. package/dist/migrate/migrator.js +44 -81
  34. package/dist/migrate/storage/databaseStorage.d.ts +2 -0
  35. package/dist/migrate/storage/databaseStorage.js +8 -23
  36. package/dist/mongo/mongodbQuerier.js +171 -229
  37. package/dist/nestjs/uqlContextInterceptor.js +58 -22
  38. package/dist/nestjs/uqlModule.d.ts +2 -11
  39. package/dist/nestjs/uqlModule.js +93 -44
  40. package/dist/postgres/postgresDialect.js +2 -2
  41. package/dist/querier/abstractQuerier.d.ts +28 -0
  42. package/dist/querier/abstractQuerier.js +47 -22
  43. package/dist/querier/abstractQuerierPool.d.ts +3 -0
  44. package/dist/querier/abstractQuerierPool.js +5 -3
  45. package/dist/querier/abstractSqlQuerier.d.ts +1 -3
  46. package/dist/querier/abstractSqlQuerier.js +48 -96
  47. package/dist/querier/index.d.ts +3 -1
  48. package/dist/querier/index.js +3 -1
  49. package/dist/querier/querierContext.browser.d.ts +12 -0
  50. package/dist/querier/querierContext.browser.js +18 -0
  51. package/dist/querier/querierContext.d.ts +22 -0
  52. package/dist/querier/querierContext.js +42 -0
  53. package/dist/querier/queryError.d.ts +22 -0
  54. package/dist/querier/queryError.js +20 -0
  55. package/dist/querier/transactional.d.ts +26 -0
  56. package/dist/querier/transactional.js +43 -0
  57. package/dist/schema/index.d.ts +1 -1
  58. package/dist/schema/schemaASTBuilder.d.ts +1 -1
  59. package/dist/schema/schemaASTBuilder.js +2 -2
  60. package/dist/schema/types.d.ts +0 -21
  61. package/dist/sqlite/abstractSqliteQuerier.d.ts +9 -2
  62. package/dist/sqlite/hranaQuerier.js +20 -42
  63. package/dist/sqlite/index.d.ts +1 -0
  64. package/dist/sqlite/index.js +1 -0
  65. package/dist/sqlite/localSqliteQuerierPool.d.ts +31 -0
  66. package/dist/sqlite/localSqliteQuerierPool.js +34 -0
  67. package/dist/sqlite/nodeSqliteAdapter.d.ts +35 -0
  68. package/dist/sqlite/nodeSqliteAdapter.js +28 -0
  69. package/dist/sqlite/nodeSqliteQuerierPool.d.ts +28 -0
  70. package/dist/sqlite/nodeSqliteQuerierPool.js +29 -0
  71. package/dist/sqlite/sqliteDialect.d.ts +1 -2
  72. package/dist/sqlite/sqliteDialect.js +0 -13
  73. package/dist/sqlite/sqliteQuerierPool.d.ts +10 -23
  74. package/dist/sqlite/sqliteQuerierPool.js +9 -28
  75. package/dist/type/entity.d.ts +95 -28
  76. package/dist/type/querier.d.ts +6 -0
  77. package/dist/util/hook.util.js +1 -1
  78. package/package.json +8 -16
  79. package/dist/entity/decorator/field.d.ts +0 -2
  80. package/dist/entity/decorator/field.js +0 -7
  81. package/dist/entity/decorator/filter.d.ts +0 -7
  82. package/dist/entity/decorator/filter.js +0 -11
  83. package/dist/entity/decorator/hook.d.ts +0 -7
  84. package/dist/entity/decorator/hook.js +0 -14
  85. package/dist/entity/decorator/id.d.ts +0 -2
  86. package/dist/entity/decorator/id.js +0 -7
  87. package/dist/entity/decorator/index-decorator.d.ts +0 -22
  88. package/dist/entity/decorator/index-decorator.js +0 -26
  89. package/dist/entity/decorator/index.d.ts +0 -8
  90. package/dist/entity/decorator/index.js +0 -8
  91. package/dist/entity/decorator/relation.d.ts +0 -8
  92. package/dist/entity/decorator/relation.js +0 -19
  93. package/dist/migrate/codegen/smartRelationDetector.d.ts +0 -47
  94. package/dist/migrate/codegen/smartRelationDetector.js +0 -134
  95. package/dist/querier/decorator/index.d.ts +0 -4
  96. package/dist/querier/decorator/index.js +0 -4
  97. package/dist/querier/decorator/injectQuerier.d.ts +0 -3
  98. package/dist/querier/decorator/injectQuerier.js +0 -33
  99. package/dist/querier/decorator/log.d.ts +0 -32
  100. package/dist/querier/decorator/log.js +0 -52
  101. package/dist/querier/decorator/serialized.d.ts +0 -6
  102. package/dist/querier/decorator/serialized.js +0 -13
  103. package/dist/querier/decorator/transactional.d.ts +0 -6
  104. package/dist/querier/decorator/transactional.js +0 -48
@@ -1,15 +1,5 @@
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
- var _a;
11
1
  import { getMeta } from '../entity/index.js';
12
- import { AbstractQuerier, enrichError, Log, Serialized } from '../querier/index.js';
2
+ import { AbstractQuerier, enrichError } from '../querier/index.js';
13
3
  import { clone, getKeys, getRelationRequestSummary, getSoftDeleteValue, hasKeys, throwNoPendingTransaction, throwPendingTransaction, withoutSoftDeleteFilter, } from '../util/index.js';
14
4
  export class MongodbQuerier extends AbstractQuerier {
15
5
  dialect;
@@ -23,34 +13,38 @@ export class MongodbQuerier extends AbstractQuerier {
23
13
  this.extra = extra;
24
14
  }
25
15
  async execute(task) {
26
- return task(this.session);
16
+ return this.serialize(async () => {
17
+ return task(this.session);
18
+ });
27
19
  }
28
20
  async internalFindMany(entity, q, opts) {
29
- const meta = getMeta(entity);
30
- const vectorSort = this.dialect.extractVectorSort(q.$sort);
31
- let documents;
32
- if (vectorSort) {
33
- const pipeline = this.buildVectorPipeline(entity, q, vectorSort, opts);
34
- documents = await this.runPipeline(entity, meta, pipeline);
35
- // to-many relations need their own query, exactly as in the non-vector path
36
- await this.fillToManyRelations(entity, documents, q.$populate);
37
- }
38
- else {
39
- const relationSummary = getRelationRequestSummary(meta, q.$populate);
40
- // A relation condition needs `$lookup`, so it forces the aggregation path just like populating
41
- // one does; a plain `find` cursor cannot express it.
42
- if (relationSummary.requestedKeys.length || this.dialect.constrainsRelations(entity, q.$where)) {
43
- const pipeline = this.dialect.aggregationPipeline(entity, q, relationSummary, opts);
21
+ return this.timed('internalFindMany', undefined, async () => {
22
+ const meta = getMeta(entity);
23
+ const vectorSort = this.dialect.extractVectorSort(q.$sort);
24
+ let documents;
25
+ if (vectorSort) {
26
+ const pipeline = this.buildVectorPipeline(entity, q, vectorSort, opts);
44
27
  documents = await this.runPipeline(entity, meta, pipeline);
28
+ // to-many relations need their own query, exactly as in the non-vector path
45
29
  await this.fillToManyRelations(entity, documents, q.$populate);
46
30
  }
47
31
  else {
48
- const cursor = this.buildFindCursor(entity, q, opts);
49
- documents = await this.execute(() => cursor.toArray());
50
- documents = this.dialect.normalizeIds(meta, documents) || [];
32
+ const relationSummary = getRelationRequestSummary(meta, q.$populate);
33
+ // A relation condition needs `$lookup`, so it forces the aggregation path just like populating
34
+ // one does; a plain `find` cursor cannot express it.
35
+ if (relationSummary.requestedKeys.length || this.dialect.constrainsRelations(entity, q.$where)) {
36
+ const pipeline = this.dialect.aggregationPipeline(entity, q, relationSummary, opts);
37
+ documents = await this.runPipeline(entity, meta, pipeline);
38
+ await this.fillToManyRelations(entity, documents, q.$populate);
39
+ }
40
+ else {
41
+ const cursor = this.buildFindCursor(entity, q, opts);
42
+ documents = await this.execute(() => cursor.toArray());
43
+ documents = this.dialect.normalizeIds(meta, documents) || [];
44
+ }
51
45
  }
52
- }
53
- return documents;
46
+ return documents;
47
+ });
54
48
  }
55
49
  async *internalFindManyStream(entity, q, opts) {
56
50
  const meta = getMeta(entity);
@@ -71,7 +65,7 @@ export class MongodbQuerier extends AbstractQuerier {
71
65
  }
72
66
  }
73
67
  catch (err) {
74
- enrichError(err, this.logger, 'internalFindManyStream');
68
+ throw enrichError(err, this.logger, 'internalFindManyStream');
75
69
  }
76
70
  }
77
71
  buildScalarProjection(entity, q) {
@@ -144,22 +138,26 @@ export class MongodbQuerier extends AbstractQuerier {
144
138
  return pipeline;
145
139
  }
146
140
  async internalAggregate(entity, q, opts) {
147
- const pipeline = this.dialect.buildAggregateStages(entity, q, opts);
148
- // biome-ignore lint/suspicious/noExplicitAny: aggregate result type matches QueryAggregateResult at runtime but TS can't verify
149
- return this.execute((session) => this.collection(entity).aggregate(pipeline, { session }).toArray());
141
+ return this.timed('internalAggregate', undefined, async () => {
142
+ const pipeline = this.dialect.buildAggregateStages(entity, q, opts);
143
+ // biome-ignore lint/suspicious/noExplicitAny: aggregate result type matches QueryAggregateResult at runtime but TS can't verify
144
+ return this.execute((session) => this.collection(entity).aggregate(pipeline, { session }).toArray());
145
+ });
150
146
  }
151
147
  async internalCount(entity, qm = {}, opts) {
152
- if (this.dialect.constrainsRelations(entity, qm.$where)) {
153
- const { stages, filter } = this.dialect.whereWithRelations(entity, qm.$where, opts);
154
- const [counted] = await this.execute((session) => this.collection(entity)
155
- .aggregate([...stages, { $match: filter }, { $count: 'n' }], { session })
156
- .toArray());
157
- return counted?.n ?? 0;
158
- }
159
- const filter = this.dialect.where(entity, qm.$where, opts);
160
- return this.execute((session) => this.collection(entity).countDocuments(filter, {
161
- session,
162
- }));
148
+ return this.timed('internalCount', undefined, async () => {
149
+ if (this.dialect.constrainsRelations(entity, qm.$where)) {
150
+ const { stages, filter } = this.dialect.whereWithRelations(entity, qm.$where, opts);
151
+ const [counted] = await this.execute((session) => this.collection(entity)
152
+ .aggregate([...stages, { $match: filter }, { $count: 'n' }], { session })
153
+ .toArray());
154
+ return counted?.n ?? 0;
155
+ }
156
+ const filter = this.dialect.where(entity, qm.$where, opts);
157
+ return this.execute((session) => this.collection(entity).countDocuments(filter, {
158
+ session,
159
+ }));
160
+ });
163
161
  }
164
162
  /**
165
163
  * The `_id`s a search matches. Writes need this whenever the `$where` constrains a relation, since a
@@ -174,35 +172,39 @@ export class MongodbQuerier extends AbstractQuerier {
174
172
  return (this.dialect.normalizeIds(meta, founds) || []).map((found) => found[meta.id]);
175
173
  }
176
174
  async internalInsertMany(entity, payloads) {
177
- if (!payloads?.length) {
178
- return [];
179
- }
180
- payloads = clone(payloads);
181
- const meta = getMeta(entity);
182
- const persistables = this.dialect.getPersistables(meta, payloads, 'onInsert');
183
- const { insertedIds } = await this.execute((session) => this.collection(entity).insertMany(persistables, { session }));
184
- const ids = Object.values(insertedIds);
185
- for (const [index, it] of payloads.entries()) {
186
- it[meta.id] = ids[index];
187
- }
188
- await this.insertRelations(entity, payloads);
189
- return ids;
175
+ return this.timed('internalInsertMany', undefined, async () => {
176
+ if (!payloads?.length) {
177
+ return [];
178
+ }
179
+ payloads = clone(payloads);
180
+ const meta = getMeta(entity);
181
+ const persistables = this.dialect.getPersistables(meta, payloads, 'onInsert');
182
+ const { insertedIds } = await this.execute((session) => this.collection(entity).insertMany(persistables, { session }));
183
+ const ids = Object.values(insertedIds);
184
+ for (const [index, it] of payloads.entries()) {
185
+ it[meta.id] = ids[index];
186
+ }
187
+ await this.insertRelations(entity, payloads);
188
+ return ids;
189
+ });
190
190
  }
191
191
  async internalUpdateMany(entity, qm, payload, opts) {
192
- payload = clone(payload);
193
- const meta = getMeta(entity);
194
- const persistable = this.dialect.getPersistable(meta, payload, 'onUpdate');
195
- // An `updateMany` filter cannot host a `$lookup`, so a relation condition is resolved to ids first.
196
- const where = this.dialect.constrainsRelations(entity, qm.$where)
197
- ? { _id: { $in: await this.matchedIds(entity, qm.$where, opts) } }
198
- : this.dialect.where(entity, qm.$where, opts);
199
- // Maps JSON operators ($set/$unset/$push/$pull) onto their native MongoDB equivalents.
200
- const update = this.dialect.getUpdateFilter(persistable);
201
- const { matchedCount } = await this.execute((session) => this.collection(entity).updateMany(where, update, {
202
- session,
203
- }));
204
- await this.updateRelations(entity, qm, payload, opts);
205
- return matchedCount;
192
+ return this.timed('internalUpdateMany', undefined, async () => {
193
+ payload = clone(payload);
194
+ const meta = getMeta(entity);
195
+ const persistable = this.dialect.getPersistable(meta, payload, 'onUpdate');
196
+ // An `updateMany` filter cannot host a `$lookup`, so a relation condition is resolved to ids first.
197
+ const where = this.dialect.constrainsRelations(entity, qm.$where)
198
+ ? { _id: { $in: await this.matchedIds(entity, qm.$where, opts) } }
199
+ : this.dialect.where(entity, qm.$where, opts);
200
+ // Maps JSON operators ($set/$unset/$push/$pull) onto their native MongoDB equivalents.
201
+ const update = this.dialect.getUpdateFilter(persistable);
202
+ const { matchedCount } = await this.execute((session) => this.collection(entity).updateMany(where, update, {
203
+ session,
204
+ }));
205
+ await this.updateRelations(entity, qm, payload, opts);
206
+ return matchedCount;
207
+ });
206
208
  }
207
209
  buildConflictFilter(entity, conflictPaths, item) {
208
210
  const where = getKeys(conflictPaths).reduce((acc, key) => {
@@ -212,77 +214,83 @@ export class MongodbQuerier extends AbstractQuerier {
212
214
  return this.dialect.where(entity, where);
213
215
  }
214
216
  async upsertOne(entity, conflictPaths, payload) {
215
- payload = clone(payload);
216
- const meta = getMeta(entity);
217
- const persistable = this.dialect.getPersistable(meta, payload, 'onInsert');
218
- const filter = this.buildConflictFilter(entity, conflictPaths, payload);
219
- const update = { $set: persistable };
220
- const res = await this.execute((session) => this.collection(entity).findOneAndUpdate(filter, update, {
221
- upsert: true,
222
- returnDocument: 'after',
223
- includeResultMetadata: true,
224
- session,
225
- }));
226
- const firstId = res?.value?._id;
227
- // `updatedExisting` is false when a new document was inserted (upserted).
228
- const created = res?.lastErrorObject?.['updatedExisting'] === false;
229
- return { firstId, changes: firstId ? 1 : 0, created };
217
+ return this.timed('upsertOne', undefined, async () => {
218
+ payload = clone(payload);
219
+ const meta = getMeta(entity);
220
+ const persistable = this.dialect.getPersistable(meta, payload, 'onInsert');
221
+ const filter = this.buildConflictFilter(entity, conflictPaths, payload);
222
+ const update = { $set: persistable };
223
+ const res = await this.execute((session) => this.collection(entity).findOneAndUpdate(filter, update, {
224
+ upsert: true,
225
+ returnDocument: 'after',
226
+ includeResultMetadata: true,
227
+ session,
228
+ }));
229
+ const firstId = res?.value?._id;
230
+ // `updatedExisting` is false when a new document was inserted (upserted).
231
+ const created = res?.lastErrorObject?.['updatedExisting'] === false;
232
+ return { firstId, changes: firstId ? 1 : 0, created };
233
+ });
230
234
  }
231
235
  async upsertMany(entity, conflictPaths, payload) {
232
- if (!payload?.length) {
233
- return { changes: 0 };
234
- }
235
- payload = clone(payload);
236
- const meta = getMeta(entity);
237
- const operations = payload.map((item) => {
238
- const persistable = this.dialect.getPersistable(meta, item, 'onInsert');
239
- const filter = this.buildConflictFilter(entity, conflictPaths, item);
240
- const update = { $set: persistable };
241
- return {
242
- updateOne: {
243
- filter,
244
- update,
245
- upsert: true,
246
- },
247
- };
236
+ return this.timed('upsertMany', undefined, async () => {
237
+ if (!payload?.length) {
238
+ return { changes: 0 };
239
+ }
240
+ payload = clone(payload);
241
+ const meta = getMeta(entity);
242
+ const operations = payload.map((item) => {
243
+ const persistable = this.dialect.getPersistable(meta, item, 'onInsert');
244
+ const filter = this.buildConflictFilter(entity, conflictPaths, item);
245
+ const update = { $set: persistable };
246
+ return {
247
+ updateOne: {
248
+ filter,
249
+ update,
250
+ upsert: true,
251
+ },
252
+ };
253
+ });
254
+ const res = await this.execute((session) => this.collection(entity).bulkWrite(operations, { session }));
255
+ const changes = (res.upsertedCount ?? 0) + (res.modifiedCount ?? 0);
256
+ // `upsertedIds` only covers newly-inserted documents (keyed by operation index); a matched and
257
+ // updated document's `_id` isn't in the response, so it's simply not represented here - same
258
+ // "exact where knowable, absent otherwise" convention `RETURNING`-based SQL dialects use for
259
+ // rows that hit `DO NOTHING`.
260
+ const ids = Object.values(res.upsertedIds);
261
+ return { changes, ids, firstId: ids[0] };
248
262
  });
249
- const res = await this.execute((session) => this.collection(entity).bulkWrite(operations, { session }));
250
- const changes = (res.upsertedCount ?? 0) + (res.modifiedCount ?? 0);
251
- // `upsertedIds` only covers newly-inserted documents (keyed by operation index); a matched and
252
- // updated document's `_id` isn't in the response, so it's simply not represented here - same
253
- // "exact where knowable, absent otherwise" convention `RETURNING`-based SQL dialects use for
254
- // rows that hit `DO NOTHING`.
255
- const ids = Object.values(res.upsertedIds);
256
- return { changes, ids, firstId: ids[0] };
257
263
  }
258
264
  async internalDeleteMany(entity, qm, opts = {}) {
259
- const meta = getMeta(entity);
260
- // Soft-delete (stamp) unless `hardDelete` is requested or the entity has no soft-delete field.
261
- const field = !opts.hardDelete && meta.softDelete ? meta.fields[meta.softDelete] : undefined;
262
- // Hard delete targets matching rows regardless of soft-delete state (keeps other filters).
263
- const findOpts = field ? opts : { ...opts, filters: withoutSoftDeleteFilter(opts.filters) };
264
- // Delete has always resolved its ids first (it stamps or removes them by `_id`), so a relation
265
- // condition needs nothing extra here.
266
- const ids = await this.matchedIds(entity, qm.$where, findOpts);
267
- if (!ids.length) {
268
- return 0;
269
- }
270
- let changes;
271
- if (field) {
272
- // Stamp the mapped column: reads filter on it, so a `@Field({ name })` mismatch here would
273
- // report a successful delete and leave the row visible.
274
- const softDeleteColumn = this.dialect.resolveColumnName(meta.softDelete, field);
275
- const updateResult = await this.execute((session) => this.collection(entity).updateMany({ _id: { $in: ids } }, { $set: { [softDeleteColumn]: getSoftDeleteValue(field) } }, {
276
- session,
277
- }));
278
- changes = updateResult.matchedCount;
279
- }
280
- else {
281
- const deleteResult = await this.execute((session) => this.collection(entity).deleteMany({ _id: { $in: ids } }, { session }));
282
- changes = deleteResult.deletedCount;
283
- }
284
- await this.deleteRelations(entity, ids, opts);
285
- return changes;
265
+ return this.timed('internalDeleteMany', undefined, async () => {
266
+ const meta = getMeta(entity);
267
+ // Soft-delete (stamp) unless `hardDelete` is requested or the entity has no soft-delete field.
268
+ const field = !opts.hardDelete && meta.softDelete ? meta.fields[meta.softDelete] : undefined;
269
+ // Hard delete targets matching rows regardless of soft-delete state (keeps other filters).
270
+ const findOpts = field ? opts : { ...opts, filters: withoutSoftDeleteFilter(opts.filters) };
271
+ // Delete has always resolved its ids first (it stamps or removes them by `_id`), so a relation
272
+ // condition needs nothing extra here.
273
+ const ids = await this.matchedIds(entity, qm.$where, findOpts);
274
+ if (!ids.length) {
275
+ return 0;
276
+ }
277
+ let changes;
278
+ if (field) {
279
+ // Stamp the mapped column: reads filter on it, so a `@Field({ name })` mismatch here would
280
+ // report a successful delete and leave the row visible.
281
+ const softDeleteColumn = this.dialect.resolveColumnName(meta.softDelete, field);
282
+ const updateResult = await this.execute((session) => this.collection(entity).updateMany({ _id: { $in: ids } }, { $set: { [softDeleteColumn]: getSoftDeleteValue(field) } }, {
283
+ session,
284
+ }));
285
+ changes = updateResult.matchedCount;
286
+ }
287
+ else {
288
+ const deleteResult = await this.execute((session) => this.collection(entity).deleteMany({ _id: { $in: ids } }, { session }));
289
+ changes = deleteResult.deletedCount;
290
+ }
291
+ await this.deleteRelations(entity, ids, opts);
292
+ return changes;
293
+ });
286
294
  }
287
295
  get hasOpenTransaction() {
288
296
  return !!this.session?.inTransaction();
@@ -295,27 +303,33 @@ export class MongodbQuerier extends AbstractQuerier {
295
303
  return this.conn.db();
296
304
  }
297
305
  async beginTransaction(_opts) {
298
- if (this.hasOpenTransaction) {
299
- throwPendingTransaction();
300
- }
301
- this.logger.logInfo('beginTransaction');
302
- await this.session?.endSession();
303
- this.session = this.conn.startSession();
304
- this.session.startTransaction();
306
+ return this.serialize(async () => {
307
+ if (this.hasOpenTransaction) {
308
+ throwPendingTransaction();
309
+ }
310
+ this.logger.logInfo('beginTransaction');
311
+ await this.session?.endSession();
312
+ this.session = this.conn.startSession();
313
+ this.session.startTransaction();
314
+ });
305
315
  }
306
316
  async commitTransaction() {
307
- if (!this.hasOpenTransaction) {
308
- throwNoPendingTransaction();
309
- }
310
- this.logger.logInfo('commitTransaction');
311
- await this.session.commitTransaction();
317
+ return this.serialize(async () => {
318
+ if (!this.hasOpenTransaction) {
319
+ throwNoPendingTransaction();
320
+ }
321
+ this.logger.logInfo('commitTransaction');
322
+ await this.session.commitTransaction();
323
+ });
312
324
  }
313
325
  async rollbackTransaction() {
314
- if (!this.hasOpenTransaction) {
315
- throwNoPendingTransaction();
316
- }
317
- this.logger.logInfo('rollbackTransaction');
318
- await this.session.abortTransaction();
326
+ return this.serialize(async () => {
327
+ if (!this.hasOpenTransaction) {
328
+ throwNoPendingTransaction();
329
+ }
330
+ this.logger.logInfo('rollbackTransaction');
331
+ await this.session.abortTransaction();
332
+ });
319
333
  }
320
334
  async internalRelease() {
321
335
  if (this.hasOpenTransaction) {
@@ -325,75 +339,3 @@ export class MongodbQuerier extends AbstractQuerier {
325
339
  this.session = undefined;
326
340
  }
327
341
  }
328
- __decorate([
329
- Serialized(),
330
- __metadata("design:type", Function),
331
- __metadata("design:paramtypes", [Function]),
332
- __metadata("design:returntype", Promise)
333
- ], MongodbQuerier.prototype, "execute", null);
334
- __decorate([
335
- Log(),
336
- __metadata("design:type", Function),
337
- __metadata("design:paramtypes", [Object, Object, Object]),
338
- __metadata("design:returntype", Promise)
339
- ], MongodbQuerier.prototype, "internalFindMany", null);
340
- __decorate([
341
- Log(),
342
- __metadata("design:type", Function),
343
- __metadata("design:paramtypes", [Object, Object, Object]),
344
- __metadata("design:returntype", Promise)
345
- ], MongodbQuerier.prototype, "internalAggregate", null);
346
- __decorate([
347
- Log(),
348
- __metadata("design:type", Function),
349
- __metadata("design:paramtypes", [Object, Object, Object]),
350
- __metadata("design:returntype", Promise)
351
- ], MongodbQuerier.prototype, "internalCount", null);
352
- __decorate([
353
- Log(),
354
- __metadata("design:type", Function),
355
- __metadata("design:paramtypes", [Object, Array]),
356
- __metadata("design:returntype", Promise)
357
- ], MongodbQuerier.prototype, "internalInsertMany", null);
358
- __decorate([
359
- Log(),
360
- __metadata("design:type", Function),
361
- __metadata("design:paramtypes", [Object, Object, Object, Object]),
362
- __metadata("design:returntype", Promise)
363
- ], MongodbQuerier.prototype, "internalUpdateMany", null);
364
- __decorate([
365
- Log(),
366
- __metadata("design:type", Function),
367
- __metadata("design:paramtypes", [Object, Object, typeof (_a = typeof E !== "undefined" && E) === "function" ? _a : Object]),
368
- __metadata("design:returntype", Promise)
369
- ], MongodbQuerier.prototype, "upsertOne", null);
370
- __decorate([
371
- Log(),
372
- __metadata("design:type", Function),
373
- __metadata("design:paramtypes", [Object, Object, Array]),
374
- __metadata("design:returntype", Promise)
375
- ], MongodbQuerier.prototype, "upsertMany", null);
376
- __decorate([
377
- Log(),
378
- __metadata("design:type", Function),
379
- __metadata("design:paramtypes", [Object, Object, Object]),
380
- __metadata("design:returntype", Promise)
381
- ], MongodbQuerier.prototype, "internalDeleteMany", null);
382
- __decorate([
383
- Serialized(),
384
- __metadata("design:type", Function),
385
- __metadata("design:paramtypes", [Object]),
386
- __metadata("design:returntype", Promise)
387
- ], MongodbQuerier.prototype, "beginTransaction", null);
388
- __decorate([
389
- Serialized(),
390
- __metadata("design:type", Function),
391
- __metadata("design:paramtypes", []),
392
- __metadata("design:returntype", Promise)
393
- ], MongodbQuerier.prototype, "commitTransaction", null);
394
- __decorate([
395
- Serialized(),
396
- __metadata("design:type", Function),
397
- __metadata("design:paramtypes", []),
398
- __metadata("design:returntype", Promise)
399
- ], MongodbQuerier.prototype, "rollbackTransaction", null);
@@ -1,11 +1,36 @@
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;
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
6
27
  };
7
- var __metadata = (this && this.__metadata) || function (k, v) {
8
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
9
34
  };
10
35
  import { Injectable } from '@nestjs/common';
11
36
  import { Observable } from 'rxjs';
@@ -15,19 +40,30 @@ import { withContext } from '../context/context.js';
15
40
  * are scoped automatically for every query in the request - including relations, cascades, and
16
41
  * `@Transactional` services. Wired for you by {@link UqlModule.forRoot} when you pass `getContext`.
17
42
  */
18
- let UqlContextInterceptor = class UqlContextInterceptor {
19
- getContext;
20
- constructor(getContext) {
21
- this.getContext = getContext;
22
- }
23
- intercept(execContext, next) {
24
- const context = this.getContext(execContext.switchToHttp().getRequest()) ?? {};
25
- // Subscribe inside the context so the handler's async chain (and its queries) inherit it.
26
- return new Observable((subscriber) => withContext(context, () => next.handle().subscribe(subscriber)));
27
- }
28
- };
29
- UqlContextInterceptor = __decorate([
30
- Injectable(),
31
- __metadata("design:paramtypes", [Function])
32
- ], UqlContextInterceptor);
43
+ let UqlContextInterceptor = (() => {
44
+ let _classDecorators = [Injectable()];
45
+ let _classDescriptor;
46
+ let _classExtraInitializers = [];
47
+ let _classThis;
48
+ var UqlContextInterceptor = class {
49
+ static { _classThis = this; }
50
+ static {
51
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
52
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
53
+ UqlContextInterceptor = _classThis = _classDescriptor.value;
54
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
55
+ __runInitializers(_classThis, _classExtraInitializers);
56
+ }
57
+ getContext;
58
+ constructor(getContext) {
59
+ this.getContext = getContext;
60
+ }
61
+ intercept(execContext, next) {
62
+ const context = this.getContext(execContext.switchToHttp().getRequest()) ?? {};
63
+ // Subscribe inside the context so the handler's async chain (and its queries) inherit it.
64
+ return new Observable((subscriber) => withContext(context, () => next.handle().subscribe(subscriber)));
65
+ }
66
+ };
67
+ return UqlContextInterceptor = _classThis;
68
+ })();
33
69
  export { UqlContextInterceptor };
@@ -1,4 +1,4 @@
1
- import { type DynamicModule, type FactoryProvider, type OnApplicationShutdown } from '@nestjs/common';
1
+ import { type DynamicModule, type FactoryProvider } from '@nestjs/common';
2
2
  import type { QuerierPool, UqlContext } from '../type/index.js';
3
3
  /**
4
4
  * Injection token for the configured {@link QuerierPool} - for injecting into your own
@@ -30,16 +30,7 @@ export type UqlModuleAsyncOptions<Req = unknown> = UqlModuleCommon<Req> & {
30
30
  /** Providers to inject into `useFactory`. */
31
31
  readonly inject?: FactoryProvider['inject'];
32
32
  };
33
- /**
34
- * NestJS integration: provides the pool via DI, sets it as UQL's default pool (so `getQuerier()`,
35
- * `querierMiddleware` (express platform), `createFetchHandler`, and `@Transactional` work unchanged),
36
- * optionally scopes every request to a {@link UqlContext} (multi-tenancy), and ends the pool on
37
- * application shutdown.
38
- */
39
- export declare class UqlModule implements OnApplicationShutdown {
40
- private readonly pool;
41
- constructor(pool: QuerierPool);
42
- onApplicationShutdown(): Promise<void>;
33
+ export declare class UqlModule {
43
34
  /** Configure with an already-built pool. */
44
35
  static forRoot<Req = unknown>({ pool, global, getContext }: UqlModuleOptions<Req>): DynamicModule;
45
36
  /** Configure with a pool built asynchronously from injected providers (e.g. `ConfigService`). */