@tstdl/base 0.92.85 → 0.92.86

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 (79) hide show
  1. package/ai/ai.service.d.ts +1 -1
  2. package/ai/ai.service.js +3 -3
  3. package/ai/types.d.ts +1 -1
  4. package/authentication/authentication.api.d.ts +9 -9
  5. package/authentication/models/schemas.d.ts +2 -2
  6. package/cancellation/token.d.ts +1 -1
  7. package/document-management/api/document-management.api.d.ts +94 -94
  8. package/document-management/models/document-category.model.d.ts +1 -0
  9. package/document-management/models/document-index.model.d.ts +7 -0
  10. package/document-management/models/document-index.model.js +32 -0
  11. package/document-management/models/document-request-file.model.d.ts +1 -1
  12. package/document-management/models/document-request-file.model.js +2 -2
  13. package/document-management/models/document-type.model.d.ts +1 -0
  14. package/document-management/models/document.model.d.ts +1 -1
  15. package/document-management/models/document.model.js +2 -2
  16. package/document-management/models/service-models/document.service-model.d.ts +51 -51
  17. package/document-management/models/service-models/document.service-model.js +2 -2
  18. package/document-management/server/drizzle/{0000_sloppy_fenris.sql → 0000_useful_overlord.sql} +2 -2
  19. package/document-management/server/drizzle/meta/0000_snapshot.json +5 -5
  20. package/document-management/server/drizzle/meta/_journal.json +2 -2
  21. package/document-management/server/drizzle.config.js +1 -1
  22. package/document-management/server/module.d.ts +1 -1
  23. package/document-management/server/module.js +1 -1
  24. package/document-management/server/schemas.d.ts +33 -0
  25. package/document-management/{models → server}/schemas.js +14 -14
  26. package/document-management/server/services/document-management.service.d.ts +17 -3
  27. package/document-management/server/services/document-management.service.js +65 -17
  28. package/examples/orm/schemas.d.ts +1 -1
  29. package/mail/drizzle.config.js +1 -1
  30. package/mail/models/schemas.d.ts +1 -1
  31. package/orm/decorators.d.ts +4 -4
  32. package/orm/entity.d.ts +5 -7
  33. package/orm/entity.js +9 -1
  34. package/orm/index.d.ts +1 -0
  35. package/orm/index.js +1 -0
  36. package/orm/query.d.ts +1 -3
  37. package/orm/query.js +0 -1
  38. package/orm/repository.types.d.ts +32 -0
  39. package/orm/repository.types.js +1 -0
  40. package/orm/server/database-schema.d.ts +4 -4
  41. package/orm/server/drizzle/schema-converter.d.ts +1 -1
  42. package/orm/server/index.d.ts +1 -0
  43. package/orm/server/index.js +1 -0
  44. package/orm/server/query-converter.d.ts +1 -2
  45. package/orm/server/query-converter.js +66 -61
  46. package/orm/server/repository.d.ts +78 -42
  47. package/orm/server/repository.js +202 -106
  48. package/orm/server/sqls.d.ts +7 -0
  49. package/orm/server/sqls.js +6 -0
  50. package/orm/server/types.d.ts +3 -3
  51. package/orm/types.d.ts +1 -1
  52. package/package.json +11 -9
  53. package/queue/enqueue-batch.d.ts +1 -0
  54. package/queue/enqueue-batch.js +1 -1
  55. package/queue/mongo/queue.d.ts +9 -4
  56. package/queue/mongo/queue.js +5 -6
  57. package/queue/postgres/drizzle/0000_zippy_moondragon.sql +11 -0
  58. package/queue/postgres/drizzle/meta/0000_snapshot.json +90 -0
  59. package/queue/postgres/drizzle/meta/_journal.json +13 -0
  60. package/queue/postgres/drizzle.config.d.ts +2 -0
  61. package/queue/postgres/drizzle.config.js +11 -0
  62. package/queue/postgres/index.d.ts +4 -0
  63. package/queue/postgres/index.js +4 -0
  64. package/queue/postgres/job.model.d.ts +13 -0
  65. package/queue/postgres/job.model.js +55 -0
  66. package/queue/postgres/module.d.ts +9 -0
  67. package/queue/postgres/module.js +29 -0
  68. package/queue/postgres/queue.d.ts +28 -0
  69. package/queue/postgres/queue.js +149 -0
  70. package/queue/postgres/queue.provider.d.ts +7 -0
  71. package/queue/postgres/queue.provider.js +21 -0
  72. package/queue/postgres/schemas.d.ts +3 -0
  73. package/queue/postgres/schemas.js +4 -0
  74. package/queue/provider.d.ts +2 -1
  75. package/queue/queue.d.ts +18 -6
  76. package/schema/schemas/object.d.ts +1 -1
  77. package/utils/timing.d.ts +4 -3
  78. package/utils/timing.js +3 -1
  79. package/document-management/models/schemas.d.ts +0 -33
@@ -8,7 +8,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
8
8
  var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
- import { count, eq, inArray, sql } from 'drizzle-orm';
11
+ import { asc, count, desc, eq, inArray, isSQLWrapper, SQL, sql } from 'drizzle-orm';
12
12
  import { PgTransaction as DrizzlePgTransaction } from 'drizzle-orm/pg-core';
13
13
  import { createContextProvider } from '../../context/context.js';
14
14
  import { NotFoundError } from '../../errors/not-found.error.js';
@@ -21,11 +21,14 @@ import { toArray } from '../../utils/array/array.js';
21
21
  import { mapAsync } from '../../utils/async-iterable-helpers/map.js';
22
22
  import { toArrayAsync } from '../../utils/async-iterable-helpers/to-array.js';
23
23
  import { importSymmetricKey } from '../../utils/cryptography.js';
24
- import { fromDeepObjectEntries } from '../../utils/object/object.js';
25
- import { assertDefined, assertDefinedPass, assertNotNullPass, isNotNull, isUndefined } from '../../utils/type-guards.js';
24
+ import { typeExtends } from '../../utils/index.js';
25
+ import { fromDeepObjectEntries, objectEntries } from '../../utils/object/object.js';
26
+ import { assertDefinedPass, isArray, isDefined, isUndefined } from '../../utils/type-guards.js';
27
+ import { Entity } from '../entity.js';
26
28
  import { Database } from './database.js';
27
29
  import { getColumnDefinitions, getDrizzleTableFromType } from './drizzle/schema-converter.js';
28
30
  import { convertQuery } from './query-converter.js';
31
+ import { TRANSACTION_TIMESTAMP } from './sqls.js';
29
32
  import { ENCRYPTION_SECRET } from './tokens.js';
30
33
  import { DrizzleTransaction } from './transaction.js';
31
34
  export const repositoryType = Symbol('repositoryType');
@@ -33,30 +36,28 @@ export class EntityRepositoryConfig {
33
36
  schema;
34
37
  }
35
38
  const entityTypeToken = Symbol('EntityType');
36
- const TRANSACTION_TIMESTAMP = sql `transaction_timestamp()`;
37
39
  const { getCurrentEntityRepositoryContext, runInEntityRepositoryContext, isInEntityRepositoryContext } = createContextProvider('EntityRepository');
38
40
  let EntityRepository = class EntityRepository {
39
41
  #injector = inject(Injector);
40
42
  #repositoryConstructor;
41
43
  #withTransactionCache = new WeakMap();
42
44
  #encryptionSecret = isInEntityRepositoryContext() ? getCurrentEntityRepositoryContext()?.encryptionSecret : inject(ENCRYPTION_SECRET, undefined, { optional: true });
43
- #transformContext;
44
- type;
45
- table;
46
- columnDefinitions;
47
- columnDefinitionsMap;
48
- session;
49
- isInTransaction;
45
+ #context = getCurrentEntityRepositoryContext() ?? {};
46
+ #transformContext = this.#context.transformContext;
47
+ type = this.#context.type ?? injectArgument(this, { optional: true }) ?? assertDefinedPass(this.constructor[entityTypeToken], 'Missing entity type.');
48
+ typeName = this.type.entityName ?? this.type.name;
49
+ #table = this.#context.table ?? getDrizzleTableFromType(this.type, inject(EntityRepositoryConfig).schema);
50
+ #tableWithMetadata = this.#table;
51
+ #columnDefinitions = this.#context.columnDefinitions ?? getColumnDefinitions(this.#table);
52
+ #columnDefinitionsMap = this.#context.columnDefinitionsMap ?? new Map(this.#columnDefinitions.map((column) => [column.objectPath.path, column]));
53
+ session = this.#context.session ?? inject(Database);
54
+ isInTransaction = this.session instanceof DrizzlePgTransaction;
55
+ hasMetadata = typeExtends(this.type, Entity);
56
+ get table() {
57
+ return this.#table;
58
+ }
50
59
  constructor() {
51
60
  this.#repositoryConstructor = new.target;
52
- const { type, table, columnDefinitions, columnDefinitionsMap, session, transformContext } = getCurrentEntityRepositoryContext() ?? {};
53
- this.type = type ?? injectArgument(this, { optional: true }) ?? assertDefinedPass(new.target[entityTypeToken], 'Missing entity type.');
54
- this.table = table ?? getDrizzleTableFromType(this.type, inject(EntityRepositoryConfig).schema);
55
- this.columnDefinitions = columnDefinitions ?? getColumnDefinitions(this.table);
56
- this.columnDefinitionsMap = columnDefinitionsMap ?? new Map(this.columnDefinitions.map((column) => [column.objectPath.path, column]));
57
- this.session = session ?? inject(Database);
58
- this.isInTransaction = this.session instanceof DrizzlePgTransaction;
59
- this.#transformContext = transformContext;
60
61
  }
61
62
  withOptionalTransaction(transaction) {
62
63
  if (isUndefined(transaction)) {
@@ -70,9 +71,9 @@ let EntityRepository = class EntityRepository {
70
71
  }
71
72
  const context = {
72
73
  type: this.type,
73
- table: this.table,
74
- columnDefinitions: this.columnDefinitions,
75
- columnDefinitionsMap: this.columnDefinitionsMap,
74
+ table: this.#table,
75
+ columnDefinitions: this.#columnDefinitions,
76
+ columnDefinitionsMap: this.#columnDefinitionsMap,
76
77
  session: transaction.transaction,
77
78
  encryptionSecret: this.#encryptionSecret,
78
79
  transformContext: this.#transformContext
@@ -99,26 +100,30 @@ let EntityRepository = class EntityRepository {
99
100
  async load(id) {
100
101
  const entity = await this.tryLoad(id);
101
102
  if (isUndefined(entity)) {
102
- throw new NotFoundError(`${this.type.entityName} ${id} not found.`);
103
+ throw new NotFoundError(`${this.typeName} ${id} not found.`);
103
104
  }
104
105
  return entity;
105
106
  }
106
107
  async tryLoad(id) {
107
- return this.tryLoadByQuery(eq(this.table.id, id));
108
+ return this.tryLoadByQuery(eq(this.#table.id, id));
108
109
  }
109
110
  async loadByQuery(query, options) {
110
111
  const entity = await this.tryLoadByQuery(query, options);
111
112
  if (isUndefined(entity)) {
112
- throw new NotFoundError(`${this.type.entityName} not found.`);
113
+ throw new NotFoundError(`${this.typeName} not found.`);
113
114
  }
114
115
  return entity;
115
116
  }
116
117
  async tryLoadByQuery(query, options) {
117
- const sqlQuery = this.convertQuery(query);
118
- const dbQuery = this.session.select()
119
- .from(this.table)
118
+ const sqlQuery = this.$convertQuery(query);
119
+ let dbQuery = this.session.select()
120
+ .from(this.#table)
120
121
  .where(sqlQuery)
121
- .offset(options?.offset);
122
+ .offset(options?.offset)
123
+ .$dynamic();
124
+ if (isDefined(options?.order)) {
125
+ dbQuery = dbQuery.orderBy(...this.$convertOrderBy(options.order));
126
+ }
122
127
  const [row] = await dbQuery;
123
128
  if (isUndefined(row)) {
124
129
  return undefined;
@@ -127,19 +132,25 @@ let EntityRepository = class EntityRepository {
127
132
  return this.mapToEntity(row, transformContext);
128
133
  }
129
134
  async loadMany(ids, options) {
130
- return this.loadManyByQuery(inArray(this.table.id, ids), options);
135
+ return this.loadManyByQuery(inArray(this.#table.id, ids), options);
131
136
  }
132
137
  async *loadManyCursor(ids, options) {
133
138
  const entities = await this.loadMany(ids, options);
134
139
  yield* entities;
135
140
  }
136
141
  async loadManyByQuery(query, options) {
137
- const sqlQuery = this.convertQuery(query);
138
- const rows = await this.session.select()
139
- .from(this.table)
142
+ const sqlQuery = this.$convertQuery(query);
143
+ let dbQuery = this.session.select()
144
+ .from(this.#table)
140
145
  .where(sqlQuery)
146
+ .orderBy()
141
147
  .offset(options?.offset)
142
- .limit(options?.limit);
148
+ .limit(options?.limit)
149
+ .$dynamic();
150
+ if (isDefined(options?.order)) {
151
+ dbQuery = dbQuery.orderBy(...this.$convertOrderBy(options.order));
152
+ }
153
+ const rows = await dbQuery;
143
154
  const transformContext = await this.getTransformContext();
144
155
  return this.mapManyToEntity(rows, transformContext);
145
156
  }
@@ -157,34 +168,34 @@ let EntityRepository = class EntityRepository {
157
168
  async count() {
158
169
  const dbQuery = this.session
159
170
  .select({ count: count() })
160
- .from(this.table);
171
+ .from(this.#table);
161
172
  const [result] = await dbQuery;
162
173
  return assertDefinedPass(result).count;
163
174
  }
164
175
  async countByQuery(query) {
165
- const sqlQuery = this.convertQuery(query);
176
+ const sqlQuery = this.$convertQuery(query);
166
177
  const dbQuery = this.session
167
178
  .select({ count: count() })
168
- .from(this.table)
179
+ .from(this.#table)
169
180
  .where(sqlQuery);
170
181
  const [result] = await dbQuery;
171
182
  return assertDefinedPass(result).count;
172
183
  }
173
184
  async has(id) {
174
- return this.hasByQuery(eq(this.table.id, id));
185
+ return this.hasByQuery(eq(this.#table.id, id));
175
186
  }
176
187
  async hasByQuery(query) {
177
- const sqlQuery = this.convertQuery(query);
188
+ const sqlQuery = this.$convertQuery(query);
178
189
  const dbQuery = this.session
179
- .select({ exists: sql `SELECT EXISTS(SELECT 1 FROM ${this.table} WHERE ${sqlQuery})` })
180
- .from(this.table);
190
+ .select({ exists: sql `SELECT EXISTS(SELECT 1 FROM ${this.#table} WHERE ${sqlQuery})` })
191
+ .from(this.#table);
181
192
  const [result] = await dbQuery;
182
193
  return assertDefinedPass(result).exists;
183
194
  }
184
195
  async hasAll(ids) {
185
196
  const result = await this.session
186
- .select({ contains: sql `array_agg(${this.table.id}) @> ${ids}`.as('contains') })
187
- .from(this.table);
197
+ .select({ contains: sql `array_agg(${this.#table.id}) @> ${ids}`.as('contains') })
198
+ .from(this.#table);
188
199
  return assertDefinedPass(result[0]).contains;
189
200
  }
190
201
  /**
@@ -196,7 +207,7 @@ let EntityRepository = class EntityRepository {
196
207
  const transformContext = await this.getTransformContext();
197
208
  const columns = await this.mapToInsertColumns(entity, transformContext);
198
209
  const [row] = await this.session
199
- .insert(this.table)
210
+ .insert(this.#table)
200
211
  .values(columns)
201
212
  .onConflictDoNothing()
202
213
  .returning();
@@ -209,7 +220,7 @@ let EntityRepository = class EntityRepository {
209
220
  const transformContext = await this.getTransformContext();
210
221
  const columns = await this.mapToInsertColumns(entity, transformContext);
211
222
  const [row] = await this.session
212
- .insert(this.table)
223
+ .insert(this.#table)
213
224
  .values(columns)
214
225
  .returning();
215
226
  return this.mapToEntity(row, transformContext);
@@ -217,19 +228,16 @@ let EntityRepository = class EntityRepository {
217
228
  async insertMany(entities) {
218
229
  const transformContext = await this.getTransformContext();
219
230
  const columns = await this.mapManyToInsertColumns(entities, transformContext);
220
- const rows = await this.session.insert(this.table).values(columns).returning();
231
+ const rows = await this.session.insert(this.#table).values(columns).returning();
221
232
  return this.mapManyToEntity(rows, transformContext);
222
233
  }
223
234
  async upsert(target, entity, update) {
224
235
  const transformContext = await this.getTransformContext();
225
- const targetColumns = toArray(target).map((path) => {
226
- const columnName = assertDefinedPass(this.columnDefinitionsMap.get(path), `Could not map ${path} to column.`).name;
227
- return this.table[columnName];
228
- });
236
+ const targetColumns = toArray(target).map((path) => this.$getColumn(path));
229
237
  const columns = await this.mapToInsertColumns(entity, transformContext);
230
238
  const mappedUpdate = await this.mapUpdate(update ?? entity, transformContext);
231
239
  const [row] = await this.session
232
- .insert(this.table)
240
+ .insert(this.#table)
233
241
  .values(columns)
234
242
  .onConflictDoUpdate({
235
243
  target: targetColumns,
@@ -240,14 +248,11 @@ let EntityRepository = class EntityRepository {
240
248
  }
241
249
  async upsertMany(target, entities, update) {
242
250
  const transformContext = await this.getTransformContext();
243
- const targetColumns = toArray(target).map((path) => {
244
- const columnName = assertDefinedPass(this.columnDefinitionsMap.get(path), `Could not map ${path} to column.`).name;
245
- return this.table[columnName];
246
- });
251
+ const targetColumns = toArray(target).map((path) => this.$getColumn(path));
247
252
  const columns = await this.mapManyToColumns(entities, transformContext);
248
253
  const mappedUpdate = await this.mapUpdate(update, transformContext);
249
254
  const rows = await this.session
250
- .insert(this.table)
255
+ .insert(this.#table)
251
256
  .values(columns)
252
257
  .onConflictDoUpdate({
253
258
  target: targetColumns,
@@ -259,7 +264,7 @@ let EntityRepository = class EntityRepository {
259
264
  async update(id, update) {
260
265
  const entity = await this.tryUpdate(id, update);
261
266
  if (isUndefined(entity)) {
262
- throw new NotFoundError(`${this.type.entityName} ${id} not found.`);
267
+ throw new NotFoundError(`${this.typeName} ${id} not found.`);
263
268
  }
264
269
  return entity;
265
270
  }
@@ -267,9 +272,9 @@ let EntityRepository = class EntityRepository {
267
272
  const transformContext = await this.getTransformContext();
268
273
  const mappedUpdate = await this.mapUpdate(update, transformContext);
269
274
  const [row] = await this.session
270
- .update(this.table)
275
+ .update(this.#table)
271
276
  .set(mappedUpdate)
272
- .where(eq(this.table.id, id))
277
+ .where(eq(this.#table.id, id))
273
278
  .returning();
274
279
  if (isUndefined(row)) {
275
280
  return undefined;
@@ -279,7 +284,7 @@ let EntityRepository = class EntityRepository {
279
284
  async updateByQuery(query, update) {
280
285
  const entity = await this.tryUpdateByQuery(query, update);
281
286
  if (isUndefined(entity)) {
282
- throw new NotFoundError(`${this.type.entityName} not found.`);
287
+ throw new NotFoundError(`${this.typeName} not found.`);
283
288
  }
284
289
  return entity;
285
290
  }
@@ -288,9 +293,9 @@ let EntityRepository = class EntityRepository {
288
293
  const mappedUpdate = await this.mapUpdate(update, transformContext);
289
294
  const idQuery = this.getIdLimitQuery(query);
290
295
  const [row] = await this.session
291
- .update(this.table)
296
+ .update(this.#table)
292
297
  .set(mappedUpdate)
293
- .where(inArray(this.table.id, idQuery))
298
+ .where(inArray(this.#table.id, idQuery))
294
299
  .returning();
295
300
  if (isUndefined(row)) {
296
301
  return undefined;
@@ -298,14 +303,14 @@ let EntityRepository = class EntityRepository {
298
303
  return this.mapToEntity(row, transformContext);
299
304
  }
300
305
  async updateMany(ids, update) {
301
- return this.updateManyByQuery(inArray(this.table.id, ids), update);
306
+ return this.updateManyByQuery(inArray(this.#table.id, ids), update);
302
307
  }
303
308
  async updateManyByQuery(query, update) {
304
309
  const transformContext = await this.getTransformContext();
305
- const sqlQuery = this.convertQuery(query);
310
+ const sqlQuery = this.$convertQuery(query);
306
311
  const mappedUpdate = await this.mapUpdate(update, transformContext);
307
312
  const rows = await this.session
308
- .update(this.table)
313
+ .update(this.#table)
309
314
  .set(mappedUpdate)
310
315
  .where(sqlQuery)
311
316
  .returning();
@@ -314,18 +319,21 @@ let EntityRepository = class EntityRepository {
314
319
  async delete(id, metadataUpdate) {
315
320
  const entity = await this.tryDelete(id, metadataUpdate);
316
321
  if (isUndefined(entity)) {
317
- throw new NotFoundError(`${this.type.entityName} ${id} not found.`);
322
+ throw new NotFoundError(`${this.typeName} ${id} not found.`);
318
323
  }
319
324
  return entity;
320
325
  }
321
326
  async tryDelete(id, metadataUpdate) {
327
+ if (!this.hasMetadata) {
328
+ return this.tryHardDelete(id);
329
+ }
322
330
  const [row] = await this.session
323
- .update(this.table)
331
+ .update(this.#tableWithMetadata)
324
332
  .set({
325
333
  deleteTimestamp: TRANSACTION_TIMESTAMP,
326
334
  attributes: this.getAttributesUpdate(metadataUpdate?.attributes)
327
335
  })
328
- .where(eq(this.table.id, id))
336
+ .where(eq(this.#table.id, id))
329
337
  .returning();
330
338
  if (isUndefined(row)) {
331
339
  return undefined;
@@ -336,19 +344,22 @@ let EntityRepository = class EntityRepository {
336
344
  async deleteByQuery(query, metadataUpdate) {
337
345
  const entity = await this.tryDeleteByQuery(query, metadataUpdate);
338
346
  if (isUndefined(entity)) {
339
- throw new NotFoundError(`${this.type.entityName} not found.`);
347
+ throw new NotFoundError(`${this.typeName} not found.`);
340
348
  }
341
349
  return entity;
342
350
  }
343
351
  async tryDeleteByQuery(query, metadataUpdate) {
352
+ if (!this.hasMetadata) {
353
+ return this.tryHardDeleteByQuery(query);
354
+ }
344
355
  const idQuery = this.getIdLimitQuery(query);
345
356
  const [row] = await this.session
346
- .update(this.table)
357
+ .update(this.#tableWithMetadata)
347
358
  .set({
348
359
  deleteTimestamp: TRANSACTION_TIMESTAMP,
349
360
  attributes: this.getAttributesUpdate(metadataUpdate?.attributes)
350
361
  })
351
- .where(inArray(this.table.id, idQuery))
362
+ .where(inArray(this.#table.id, idQuery))
352
363
  .returning();
353
364
  if (isUndefined(row)) {
354
365
  return undefined;
@@ -357,12 +368,15 @@ let EntityRepository = class EntityRepository {
357
368
  return this.mapToEntity(row, transformContext);
358
369
  }
359
370
  async deleteMany(ids, metadataUpdate) {
360
- return this.deleteManyByQuery(inArray(this.table.id, ids), metadataUpdate);
371
+ return this.deleteManyByQuery(inArray(this.#table.id, ids), metadataUpdate);
361
372
  }
362
373
  async deleteManyByQuery(query, metadataUpdate) {
363
- const sqlQuery = this.convertQuery(query);
374
+ if (!this.hasMetadata) {
375
+ return this.hardDeleteManyByQuery(query);
376
+ }
377
+ const sqlQuery = this.$convertQuery(query);
364
378
  const rows = await this.session
365
- .update(this.table)
379
+ .update(this.#tableWithMetadata)
366
380
  .set({
367
381
  deleteTimestamp: TRANSACTION_TIMESTAMP,
368
382
  attributes: this.getAttributesUpdate(metadataUpdate?.attributes)
@@ -375,47 +389,115 @@ let EntityRepository = class EntityRepository {
375
389
  async hardDelete(id) {
376
390
  const result = await this.tryHardDelete(id);
377
391
  if (!result) {
378
- throw new NotFoundError(`${this.type.entityName} ${id} not found.`);
392
+ throw new NotFoundError(`${this.typeName} ${id} not found.`);
379
393
  }
394
+ return result;
380
395
  }
381
396
  async tryHardDelete(id) {
382
- const result = await this.session
383
- .delete(this.table)
384
- .where(eq(this.table.id, id));
385
- return isNotNull(result.rowCount) && (result.rowCount > 0);
397
+ const [row] = await this.session
398
+ .delete(this.#table)
399
+ .where(eq(this.#table.id, id))
400
+ .returning();
401
+ if (isUndefined(row)) {
402
+ return undefined;
403
+ }
404
+ const transformContext = await this.getTransformContext();
405
+ return this.mapToEntity(row, transformContext);
386
406
  }
387
407
  async hardDeleteByQuery(query) {
388
408
  const result = await this.tryHardDeleteByQuery(query);
389
409
  if (!result) {
390
- throw new NotFoundError(`${this.type.entityName} not found.`);
410
+ throw new NotFoundError(`${this.typeName} not found.`);
391
411
  }
412
+ return result;
392
413
  }
393
414
  async tryHardDeleteByQuery(query) {
394
415
  const idQuery = this.getIdLimitQuery(query);
395
- const result = await this.session
396
- .delete(this.table)
397
- .where(inArray(this.table.id, idQuery));
398
- return isNotNull(result.rowCount) && (result.rowCount > 0);
416
+ const [row] = await this.session
417
+ .delete(this.#table)
418
+ .where(inArray(this.#table.id, idQuery))
419
+ .returning();
420
+ if (isUndefined(row)) {
421
+ return undefined;
422
+ }
423
+ const transformContext = await this.getTransformContext();
424
+ return this.mapToEntity(row, transformContext);
399
425
  }
400
426
  async hardDeleteMany(ids) {
401
- return this.hardDeleteManyByQuery(inArray(this.table.id, ids));
427
+ return this.hardDeleteManyByQuery(inArray(this.#table.id, ids));
402
428
  }
403
429
  async hardDeleteManyByQuery(query) {
404
- const sqlQuery = this.convertQuery(query);
405
- const result = await this.session
406
- .delete(this.table)
407
- .where(sqlQuery);
408
- return assertNotNullPass(result.rowCount);
430
+ const sqlQuery = this.$convertQuery(query);
431
+ const rows = await this.session
432
+ .delete(this.#table)
433
+ .where(sqlQuery)
434
+ .returning();
435
+ const transformContext = await this.getTransformContext();
436
+ return this.mapManyToEntity(rows, transformContext);
437
+ }
438
+ $getColumn(path) {
439
+ const columnName = assertDefinedPass(this.#columnDefinitionsMap.get(path), `Could not map ${path} to column.`).name;
440
+ return this.#table[columnName];
441
+ }
442
+ $convertOrderBy(orderBy) {
443
+ if (isArray(orderBy)) {
444
+ return orderBy.map((item) => {
445
+ const itemIsArray = isArray(item);
446
+ const target = itemIsArray ? item[0] : item;
447
+ const column = isSQLWrapper(target) ? target : this.$getColumn(target);
448
+ const direction = itemIsArray ? item[1] : 'asc';
449
+ return direction == 'asc' ? asc(column) : desc(column);
450
+ });
451
+ }
452
+ return objectEntries(orderBy)
453
+ .map(([path, direction]) => {
454
+ const column = this.$getColumn(path);
455
+ return direction == 'asc' ? asc(column) : desc(column);
456
+ });
457
+ }
458
+ $convertQuery(query) {
459
+ return convertQuery(query, this.#table, this.#columnDefinitionsMap);
460
+ }
461
+ async $mapManyToEntity(columns) {
462
+ const transformContext = await this.getTransformContext();
463
+ return this.mapManyToEntity(columns, transformContext);
464
+ }
465
+ async $mapToEntity(columns) {
466
+ const transformContext = await this.getTransformContext();
467
+ return this.mapToEntity(columns, transformContext);
409
468
  }
410
- convertQuery(query) {
411
- return convertQuery(query, this.table, this.columnDefinitionsMap);
469
+ async $mapManyToColumns(objects) {
470
+ const transformContext = await this.getTransformContext();
471
+ return this.mapManyToColumns(objects, transformContext);
472
+ }
473
+ async $mapToColumns(obj) {
474
+ const transformContext = await this.getTransformContext();
475
+ return this.mapToColumns(obj, transformContext);
476
+ }
477
+ async $mapManyToInsertColumns(objects) {
478
+ const transformContext = await this.getTransformContext();
479
+ return this.mapManyToInsertColumns(objects, transformContext);
480
+ }
481
+ async $mapToInsertColumns(obj) {
482
+ const transformContext = await this.getTransformContext();
483
+ return this.mapToInsertColumns(obj, transformContext);
484
+ }
485
+ async $mapUpdate(update) {
486
+ const transformContext = await this.getTransformContext();
487
+ return this.mapUpdate(update, transformContext);
488
+ }
489
+ $getIdLimitQuery(query) {
490
+ return this.getIdLimitQuery(query);
491
+ }
492
+ $getAttributesUpdate(attributes) {
493
+ return this.getAttributesUpdate(attributes);
412
494
  }
413
495
  async mapManyToEntity(columns, transformContext) {
414
496
  return toArrayAsync(mapAsync(columns, async (column) => this.mapToEntity(column, transformContext)));
415
497
  }
416
498
  async mapToEntity(columns, transformContext) {
417
499
  const entries = [];
418
- for (const def of this.columnDefinitions) {
500
+ for (const def of this.#columnDefinitions) {
419
501
  const rawValue = columns[def.name];
420
502
  const transformed = await def.fromDatabase(rawValue, transformContext);
421
503
  entries.push([def.objectPath, transformed]); // eslint-disable-line @typescript-eslint/no-unsafe-argument
@@ -428,7 +510,7 @@ let EntityRepository = class EntityRepository {
428
510
  }
429
511
  async mapToColumns(obj, transformContext) {
430
512
  const columns = {};
431
- for (const def of this.columnDefinitions) {
513
+ for (const def of this.#columnDefinitions) {
432
514
  const rawValue = def.dereferenceObjectPath(obj);
433
515
  columns[def.name] = await def.toDatabase(rawValue, transformContext);
434
516
  }
@@ -441,14 +523,18 @@ let EntityRepository = class EntityRepository {
441
523
  const mapped = await this.mapToColumns(obj, transformContext);
442
524
  return {
443
525
  ...mapped,
444
- revision: 1,
445
- revisionTimestamp: TRANSACTION_TIMESTAMP,
446
- createTimestamp: TRANSACTION_TIMESTAMP
526
+ ...(this.hasMetadata
527
+ ? {
528
+ revision: 1,
529
+ revisionTimestamp: TRANSACTION_TIMESTAMP,
530
+ createTimestamp: TRANSACTION_TIMESTAMP
531
+ }
532
+ : undefined)
447
533
  };
448
534
  }
449
535
  async mapUpdate(update, transformContext) {
450
536
  const mappedUpdate = {};
451
- for (const column of this.columnDefinitions) {
537
+ for (const column of this.#columnDefinitions) {
452
538
  const value = column.dereferenceObjectPath(update);
453
539
  if (isUndefined(value)) {
454
540
  continue;
@@ -457,15 +543,19 @@ let EntityRepository = class EntityRepository {
457
543
  }
458
544
  return {
459
545
  ...mappedUpdate,
460
- attributes: this.getAttributesUpdate(update.metadata?.attributes),
461
- revision: sql `${this.table.revision} + 1`,
462
- revisionTimestamp: TRANSACTION_TIMESTAMP
546
+ ...(this.hasMetadata
547
+ ? {
548
+ attributes: this.getAttributesUpdate(update?.metadata?.attributes),
549
+ revision: sql `${this.#tableWithMetadata.revision} + 1`,
550
+ revisionTimestamp: TRANSACTION_TIMESTAMP
551
+ }
552
+ : undefined)
463
553
  };
464
554
  }
465
555
  getIdLimitQuery(query) {
466
- const sqlQuery = this.convertQuery(query);
467
- return this.session.select({ id: this.table.id })
468
- .from(this.table)
556
+ const sqlQuery = this.$convertQuery(query);
557
+ return this.session.select({ id: this.#table.id })
558
+ .from(this.#table)
469
559
  .where(sqlQuery)
470
560
  .limit(1);
471
561
  }
@@ -473,11 +563,17 @@ let EntityRepository = class EntityRepository {
473
563
  if (isUndefined(attributes)) {
474
564
  return undefined;
475
565
  }
476
- return sql `${this.table.attributes} || ${JSON.stringify(attributes)}::jsonb`;
566
+ if (attributes instanceof SQL) {
567
+ return attributes;
568
+ }
569
+ return sql `${this.#tableWithMetadata.attributes} || ${JSON.stringify(attributes)}::jsonb`;
477
570
  }
478
571
  async getTransformContext() {
479
572
  if (isUndefined(this.#transformContext)) {
480
- assertDefined(this.#encryptionSecret, 'Missing database encryption secret');
573
+ if (isUndefined(this.#encryptionSecret)) {
574
+ this.#transformContext = {};
575
+ return this.#transformContext;
576
+ }
481
577
  this.#transformContext = importSymmetricKey('AES-GCM', 256, this.#encryptionSecret, false).then((encryptionKey) => ({ encryptionKey }));
482
578
  const transformContext = await this.#transformContext;
483
579
  this.#transformContext = transformContext;
@@ -0,0 +1,7 @@
1
+ import { type SQL } from 'drizzle-orm';
2
+ import type { Uuid } from '../types.js';
3
+ export declare const TRANSACTION_TIMESTAMP: SQL<Date>;
4
+ export declare const RANDOM_UUID: SQL<Uuid>;
5
+ type IntervalUnit = 'millennium' | 'millenniums' | 'millennia' | 'century' | 'centuries' | 'decade' | 'decades' | 'year' | 'years' | 'day' | 'days' | 'hour' | 'hours' | 'minute' | 'minutes' | 'second' | 'seconds' | 'millisecond' | 'milliseconds' | 'microsecond' | 'microseconds';
6
+ export declare function interval(value: number, unit: IntervalUnit): SQL;
7
+ export {};
@@ -0,0 +1,6 @@
1
+ import { sql } from 'drizzle-orm';
2
+ export const TRANSACTION_TIMESTAMP = sql `transaction_timestamp()`;
3
+ export const RANDOM_UUID = sql `gen_random_uuid()`;
4
+ export function interval(value, unit) {
5
+ return sql `(${value} ||' ${sql.raw(unit)}')::interval`;
6
+ }
@@ -3,7 +3,7 @@ import type { PgColumnBuilder, PgTableWithColumns } from 'drizzle-orm/pg-core';
3
3
  import type { CamelCase, ConditionalPick, SnakeCase } from 'type-fest';
4
4
  import type { JsonPath } from '../../json-path/json-path.js';
5
5
  import type { Record } from '../../schema/index.js';
6
- import type { AbstractConstructor, UnionToIntersection } from '../../types.js';
6
+ import type { UnionToIntersection } from '../../types.js';
7
7
  import type { Tagged } from '../../types/index.js';
8
8
  import type { OrmColumnReflectionData } from '../decorators.js';
9
9
  import type { EntityType } from '../entity.js';
@@ -23,11 +23,11 @@ export type ColumnDefinition = {
23
23
  export type TransformContext = {
24
24
  encryptionKey?: CryptoKey;
25
25
  };
26
- type Column<Name extends string, T> = null extends T ? ColumnBuilder<T, Name> : NotNull<ColumnBuilder<T, Name>>;
26
+ type Column<Name extends string, T> = null extends T ? ColumnBuilder<Exclude<T, null>, Name> : NotNull<ColumnBuilder<T, Name>>;
27
27
  export type ColumnPrefix<T> = T extends Tagged<unknown, EmbeddedConfigTag, {
28
28
  prefix: infer Prefix;
29
29
  }> ? Prefix extends string ? Prefix : '' : '';
30
- export type PgTableFromType<S extends string, T extends AbstractConstructor, TableName extends string = T extends Required<EntityType> ? SnakeCase<T['entityName']> : string> = PgTableWithColumns<{
30
+ export type PgTableFromType<T extends EntityType = EntityType, S extends string = string, TableName extends string = T extends Required<EntityType> ? SnakeCase<T['entityName']> : string> = PgTableWithColumns<{
31
31
  name: TableName;
32
32
  schema: S;
33
33
  columns: BuildColumns<TableName, {
package/orm/types.d.ts CHANGED
@@ -16,7 +16,7 @@ export type Embedded<T = AbstractConstructor, P extends string = ''> = Tagged<T,
16
16
  prefix: P;
17
17
  }>;
18
18
  export type Array<T extends Tagged<ObjectLiteral, ColumnTypeTag, PgColumnBuilder<any>>> = Tagged<UnwrapTagged<T>[], ColumnTypeTag, ReturnType<GetTagMetadata<T, ColumnTypeTag>['array']>>;
19
- export type Json<T extends ObjectLiteral = ObjectLiteral> = Tagged<T, ColumnTypeTag, $Type<ReturnType<typeof jsonb>, T>>;
19
+ export type Json<T> = Tagged<T, ColumnTypeTag, $Type<ReturnType<typeof jsonb>, T>>;
20
20
  export type Enum<T extends EnumerationObject> = Tagged<EnumerationValue<T>, ColumnTypeTag, EnumColumn<T>>;
21
21
  export type Text = Tagged<string, ColumnTypeTag, ReturnType<typeof text<string, [string, ...string[]]>>>;
22
22
  export type Uuid = Tagged<string, ColumnTypeTag, ReturnType<typeof uuid>>;