@tstdl/base 0.92.85 → 0.92.87

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 (152) hide show
  1. package/ai/ai.service.d.ts +4 -4
  2. package/ai/ai.service.js +27 -14
  3. package/ai/types.d.ts +5 -4
  4. package/api/server/gateway.js +1 -1
  5. package/authentication/authentication.api.d.ts +9 -9
  6. package/authentication/models/schemas.d.ts +2 -2
  7. package/authentication/server/authentication-ancillary.service.d.ts +6 -4
  8. package/authentication/server/authentication-ancillary.service.js +5 -5
  9. package/cancellation/token.d.ts +1 -1
  10. package/context/context.d.ts +1 -9
  11. package/context/context.js +8 -5
  12. package/document-management/api/document-management.api.d.ts +142 -110
  13. package/document-management/models/document-category.model.d.ts +1 -0
  14. package/document-management/models/document-category.model.js +2 -0
  15. package/document-management/models/document-collection-document.model.js +7 -3
  16. package/document-management/models/document-property-value.model.d.ts +13 -14
  17. package/document-management/models/document-property-value.model.js +60 -27
  18. package/document-management/models/document-property.model.d.ts +2 -0
  19. package/document-management/models/document-property.model.js +4 -1
  20. package/document-management/models/document-request-assignment-task-collection.model.d.ts +7 -0
  21. package/document-management/models/document-request-assignment-task-collection.model.js +32 -0
  22. package/document-management/models/document-request-assignment-task.model.d.ts +14 -0
  23. package/document-management/models/document-request-assignment-task.model.js +72 -0
  24. package/document-management/models/document-request-collection.model.d.ts +1 -0
  25. package/document-management/models/document-request-collection.model.js +7 -3
  26. package/document-management/models/document-request-file.model.d.ts +7 -2
  27. package/document-management/models/document-request-file.model.js +29 -4
  28. package/document-management/models/document-request.model.d.ts +1 -0
  29. package/document-management/models/document-requests-template.js +2 -0
  30. package/document-management/models/document-type-property.model.js +7 -3
  31. package/document-management/models/document-type.model.d.ts +1 -0
  32. package/document-management/models/document-type.model.js +7 -3
  33. package/document-management/models/document.model.d.ts +5 -2
  34. package/document-management/models/document.model.js +21 -6
  35. package/document-management/models/index.d.ts +2 -0
  36. package/document-management/models/index.js +2 -0
  37. package/document-management/models/service-models/document.service-model.d.ts +84 -65
  38. package/document-management/models/service-models/document.service-model.js +11 -6
  39. package/document-management/models/service-models/document.view-model.d.ts +1 -1
  40. package/document-management/models/service-models/document.view-model.js +2 -2
  41. package/document-management/server/drizzle/{0000_sloppy_fenris.sql → 0000_cool_victor_mancha.sql} +99 -43
  42. package/document-management/server/drizzle/meta/0000_snapshot.json +518 -130
  43. package/document-management/server/drizzle/meta/_journal.json +2 -2
  44. package/document-management/server/drizzle.config.js +1 -1
  45. package/document-management/server/module.d.ts +3 -2
  46. package/document-management/server/module.js +4 -2
  47. package/document-management/server/schemas.d.ts +36 -0
  48. package/document-management/server/schemas.js +37 -0
  49. package/document-management/server/services/document-management-ancillary.service.d.ts +4 -0
  50. package/document-management/server/services/document-management-ancillary.service.js +13 -0
  51. package/document-management/server/services/document-management.service.d.ts +71 -22
  52. package/document-management/server/services/document-management.service.js +528 -81
  53. package/document-management/server/services/index.d.ts +1 -0
  54. package/document-management/server/services/index.js +1 -0
  55. package/eslint.config.js +1 -0
  56. package/examples/document-management/main.d.ts +5 -0
  57. package/examples/document-management/main.js +20 -2
  58. package/examples/orm/schemas.d.ts +1 -1
  59. package/file/index.d.ts +1 -0
  60. package/file/index.js +1 -0
  61. package/file/temporary-file.d.ts +17 -0
  62. package/file/temporary-file.js +49 -0
  63. package/http/server/http-server-response.d.ts +2 -0
  64. package/http/server/http-server-response.js +13 -0
  65. package/injector/index.d.ts +1 -0
  66. package/injector/index.js +1 -0
  67. package/injector/injector.js +19 -7
  68. package/injector/interfaces.d.ts +1 -1
  69. package/injector/interfaces.js +1 -1
  70. package/injector/resolution.d.ts +15 -0
  71. package/injector/resolution.js +6 -0
  72. package/logger/console/logger.d.ts +1 -1
  73. package/logger/logger.d.ts +1 -1
  74. package/mail/drizzle.config.js +1 -1
  75. package/mail/models/schemas.d.ts +1 -1
  76. package/object-storage/object-storage.d.ts +5 -7
  77. package/object-storage/s3/s3.object-storage.d.ts +0 -1
  78. package/object-storage/s3/s3.object-storage.js +0 -3
  79. package/orm/{server/data-types → data-types}/numeric-date.js +2 -3
  80. package/orm/decorators.d.ts +17 -8
  81. package/orm/decorators.js +13 -7
  82. package/orm/entity.d.ts +5 -7
  83. package/orm/entity.js +11 -7
  84. package/orm/index.d.ts +2 -0
  85. package/orm/index.js +2 -0
  86. package/orm/query.d.ts +1 -3
  87. package/orm/query.js +0 -1
  88. package/orm/repository.types.d.ts +32 -0
  89. package/orm/repository.types.js +1 -0
  90. package/orm/server/database-schema.d.ts +4 -4
  91. package/orm/server/drizzle/schema-converter.d.ts +1 -1
  92. package/orm/server/drizzle/schema-converter.js +48 -19
  93. package/orm/server/index.d.ts +1 -0
  94. package/orm/server/index.js +1 -0
  95. package/orm/server/query-converter.d.ts +1 -2
  96. package/orm/server/query-converter.js +66 -61
  97. package/orm/server/repository.d.ts +80 -43
  98. package/orm/server/repository.js +219 -112
  99. package/orm/server/sqls.d.ts +15 -0
  100. package/orm/server/sqls.js +19 -0
  101. package/orm/server/types.d.ts +3 -3
  102. package/orm/types.d.ts +4 -4
  103. package/orm/utils.d.ts +3 -0
  104. package/orm/utils.js +6 -0
  105. package/package.json +23 -19
  106. package/pdf/pdf.service.d.ts +0 -1
  107. package/pdf/pdf.service.js +1 -95
  108. package/pdf/utils.d.ts +3 -1
  109. package/pdf/utils.js +129 -4
  110. package/promise/lazy-promise.d.ts +3 -3
  111. package/queue/enqueue-batch.d.ts +1 -0
  112. package/queue/enqueue-batch.js +1 -1
  113. package/queue/mongo/queue.d.ts +9 -4
  114. package/queue/mongo/queue.js +5 -6
  115. package/queue/postgres/drizzle/0000_zippy_moondragon.sql +11 -0
  116. package/queue/postgres/drizzle/meta/0000_snapshot.json +90 -0
  117. package/queue/postgres/drizzle/meta/_journal.json +13 -0
  118. package/queue/postgres/drizzle.config.d.ts +2 -0
  119. package/queue/postgres/drizzle.config.js +11 -0
  120. package/queue/postgres/index.d.ts +4 -0
  121. package/queue/postgres/index.js +4 -0
  122. package/queue/postgres/job.model.d.ts +13 -0
  123. package/queue/postgres/job.model.js +55 -0
  124. package/queue/postgres/module.d.ts +9 -0
  125. package/queue/postgres/module.js +29 -0
  126. package/queue/postgres/queue.d.ts +28 -0
  127. package/queue/postgres/queue.js +147 -0
  128. package/queue/postgres/queue.provider.d.ts +7 -0
  129. package/queue/postgres/queue.provider.js +21 -0
  130. package/queue/postgres/schemas.d.ts +3 -0
  131. package/queue/postgres/schemas.js +4 -0
  132. package/queue/provider.d.ts +2 -1
  133. package/queue/queue.d.ts +32 -6
  134. package/queue/queue.js +43 -0
  135. package/schema/schemas/object.d.ts +1 -1
  136. package/utils/date-time.d.ts +4 -2
  137. package/utils/date-time.js +10 -3
  138. package/utils/format-error.js +0 -1
  139. package/utils/object/lazy-property.js +0 -1
  140. package/utils/timing.d.ts +4 -3
  141. package/utils/timing.js +3 -1
  142. package/utils/try-ignore.d.ts +9 -2
  143. package/utils/try-ignore.js +30 -6
  144. package/document-management/models/schemas.d.ts +0 -33
  145. package/document-management/models/schemas.js +0 -34
  146. /package/orm/{server/data-types → data-types}/bytea.d.ts +0 -0
  147. /package/orm/{server/data-types → data-types}/bytea.js +0 -0
  148. /package/orm/{server/data-types → data-types}/index.d.ts +0 -0
  149. /package/orm/{server/data-types → data-types}/index.js +0 -0
  150. /package/orm/{server/data-types → data-types}/numeric-date.d.ts +0 -0
  151. /package/orm/{server/data-types → data-types}/timestamp.d.ts +0 -0
  152. /package/orm/{server/data-types → data-types}/timestamp.js +0 -0
@@ -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, fromEntries, objectEntries } from '../../utils/object/object.js';
26
+ import { assertDefinedPass, isArray, isDefined, isString, 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,16 @@ 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
- });
247
- const columns = await this.mapManyToColumns(entities, transformContext);
248
- const mappedUpdate = await this.mapUpdate(update, transformContext);
251
+ const targetColumns = toArray(target).map((path) => this.$getColumn(path));
252
+ const columns = await this.mapManyToInsertColumns(entities, transformContext);
253
+ const mappedUpdate = isDefined(update)
254
+ ? await this.mapUpdate(update, transformContext)
255
+ : {
256
+ ...fromEntries(this.#columnDefinitions.map((column) => [column.name, sql `excluded.${sql.identifier(this.$getColumn(column).name)}`])),
257
+ ...this.getMetadataUpdate(update)
258
+ };
249
259
  const rows = await this.session
250
- .insert(this.table)
260
+ .insert(this.#table)
251
261
  .values(columns)
252
262
  .onConflictDoUpdate({
253
263
  target: targetColumns,
@@ -259,7 +269,7 @@ let EntityRepository = class EntityRepository {
259
269
  async update(id, update) {
260
270
  const entity = await this.tryUpdate(id, update);
261
271
  if (isUndefined(entity)) {
262
- throw new NotFoundError(`${this.type.entityName} ${id} not found.`);
272
+ throw new NotFoundError(`${this.typeName} ${id} not found.`);
263
273
  }
264
274
  return entity;
265
275
  }
@@ -267,9 +277,9 @@ let EntityRepository = class EntityRepository {
267
277
  const transformContext = await this.getTransformContext();
268
278
  const mappedUpdate = await this.mapUpdate(update, transformContext);
269
279
  const [row] = await this.session
270
- .update(this.table)
280
+ .update(this.#table)
271
281
  .set(mappedUpdate)
272
- .where(eq(this.table.id, id))
282
+ .where(eq(this.#table.id, id))
273
283
  .returning();
274
284
  if (isUndefined(row)) {
275
285
  return undefined;
@@ -279,18 +289,18 @@ let EntityRepository = class EntityRepository {
279
289
  async updateByQuery(query, update) {
280
290
  const entity = await this.tryUpdateByQuery(query, update);
281
291
  if (isUndefined(entity)) {
282
- throw new NotFoundError(`${this.type.entityName} not found.`);
292
+ throw new NotFoundError(`${this.typeName} not found.`);
283
293
  }
284
294
  return entity;
285
295
  }
286
296
  async tryUpdateByQuery(query, update) {
287
297
  const transformContext = await this.getTransformContext();
288
298
  const mappedUpdate = await this.mapUpdate(update, transformContext);
289
- const idQuery = this.getIdLimitQuery(query);
299
+ const idQuery = this.getIdLimitSelect(query);
290
300
  const [row] = await this.session
291
- .update(this.table)
301
+ .update(this.#table)
292
302
  .set(mappedUpdate)
293
- .where(inArray(this.table.id, idQuery))
303
+ .where(inArray(this.#table.id, idQuery.for('update')))
294
304
  .returning();
295
305
  if (isUndefined(row)) {
296
306
  return undefined;
@@ -298,14 +308,14 @@ let EntityRepository = class EntityRepository {
298
308
  return this.mapToEntity(row, transformContext);
299
309
  }
300
310
  async updateMany(ids, update) {
301
- return this.updateManyByQuery(inArray(this.table.id, ids), update);
311
+ return this.updateManyByQuery(inArray(this.#table.id, ids), update);
302
312
  }
303
313
  async updateManyByQuery(query, update) {
304
314
  const transformContext = await this.getTransformContext();
305
- const sqlQuery = this.convertQuery(query);
315
+ const sqlQuery = this.$convertQuery(query);
306
316
  const mappedUpdate = await this.mapUpdate(update, transformContext);
307
317
  const rows = await this.session
308
- .update(this.table)
318
+ .update(this.#table)
309
319
  .set(mappedUpdate)
310
320
  .where(sqlQuery)
311
321
  .returning();
@@ -314,18 +324,21 @@ let EntityRepository = class EntityRepository {
314
324
  async delete(id, metadataUpdate) {
315
325
  const entity = await this.tryDelete(id, metadataUpdate);
316
326
  if (isUndefined(entity)) {
317
- throw new NotFoundError(`${this.type.entityName} ${id} not found.`);
327
+ throw new NotFoundError(`${this.typeName} ${id} not found.`);
318
328
  }
319
329
  return entity;
320
330
  }
321
331
  async tryDelete(id, metadataUpdate) {
332
+ if (!this.hasMetadata) {
333
+ return this.tryHardDelete(id);
334
+ }
322
335
  const [row] = await this.session
323
- .update(this.table)
336
+ .update(this.#tableWithMetadata)
324
337
  .set({
325
338
  deleteTimestamp: TRANSACTION_TIMESTAMP,
326
339
  attributes: this.getAttributesUpdate(metadataUpdate?.attributes)
327
340
  })
328
- .where(eq(this.table.id, id))
341
+ .where(eq(this.#table.id, id))
329
342
  .returning();
330
343
  if (isUndefined(row)) {
331
344
  return undefined;
@@ -336,19 +349,22 @@ let EntityRepository = class EntityRepository {
336
349
  async deleteByQuery(query, metadataUpdate) {
337
350
  const entity = await this.tryDeleteByQuery(query, metadataUpdate);
338
351
  if (isUndefined(entity)) {
339
- throw new NotFoundError(`${this.type.entityName} not found.`);
352
+ throw new NotFoundError(`${this.typeName} not found.`);
340
353
  }
341
354
  return entity;
342
355
  }
343
356
  async tryDeleteByQuery(query, metadataUpdate) {
344
- const idQuery = this.getIdLimitQuery(query);
357
+ if (!this.hasMetadata) {
358
+ return this.tryHardDeleteByQuery(query);
359
+ }
360
+ const idQuery = this.getIdLimitSelect(query);
345
361
  const [row] = await this.session
346
- .update(this.table)
362
+ .update(this.#tableWithMetadata)
347
363
  .set({
348
364
  deleteTimestamp: TRANSACTION_TIMESTAMP,
349
365
  attributes: this.getAttributesUpdate(metadataUpdate?.attributes)
350
366
  })
351
- .where(inArray(this.table.id, idQuery))
367
+ .where(inArray(this.#table.id, idQuery.for('update')))
352
368
  .returning();
353
369
  if (isUndefined(row)) {
354
370
  return undefined;
@@ -357,12 +373,15 @@ let EntityRepository = class EntityRepository {
357
373
  return this.mapToEntity(row, transformContext);
358
374
  }
359
375
  async deleteMany(ids, metadataUpdate) {
360
- return this.deleteManyByQuery(inArray(this.table.id, ids), metadataUpdate);
376
+ return this.deleteManyByQuery(inArray(this.#table.id, ids), metadataUpdate);
361
377
  }
362
378
  async deleteManyByQuery(query, metadataUpdate) {
363
- const sqlQuery = this.convertQuery(query);
379
+ if (!this.hasMetadata) {
380
+ return this.hardDeleteManyByQuery(query);
381
+ }
382
+ const sqlQuery = this.$convertQuery(query);
364
383
  const rows = await this.session
365
- .update(this.table)
384
+ .update(this.#tableWithMetadata)
366
385
  .set({
367
386
  deleteTimestamp: TRANSACTION_TIMESTAMP,
368
387
  attributes: this.getAttributesUpdate(metadataUpdate?.attributes)
@@ -375,47 +394,118 @@ let EntityRepository = class EntityRepository {
375
394
  async hardDelete(id) {
376
395
  const result = await this.tryHardDelete(id);
377
396
  if (!result) {
378
- throw new NotFoundError(`${this.type.entityName} ${id} not found.`);
397
+ throw new NotFoundError(`${this.typeName} ${id} not found.`);
379
398
  }
399
+ return result;
380
400
  }
381
401
  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);
402
+ const [row] = await this.session
403
+ .delete(this.#table)
404
+ .where(eq(this.#table.id, id))
405
+ .returning();
406
+ if (isUndefined(row)) {
407
+ return undefined;
408
+ }
409
+ const transformContext = await this.getTransformContext();
410
+ return this.mapToEntity(row, transformContext);
386
411
  }
387
412
  async hardDeleteByQuery(query) {
388
413
  const result = await this.tryHardDeleteByQuery(query);
389
414
  if (!result) {
390
- throw new NotFoundError(`${this.type.entityName} not found.`);
415
+ throw new NotFoundError(`${this.typeName} not found.`);
391
416
  }
417
+ return result;
392
418
  }
393
419
  async tryHardDeleteByQuery(query) {
394
- 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);
420
+ const idQuery = this.getIdLimitSelect(query);
421
+ const [row] = await this.session
422
+ .delete(this.#table)
423
+ .where(inArray(this.#table.id, idQuery.for('update')))
424
+ .returning();
425
+ if (isUndefined(row)) {
426
+ return undefined;
427
+ }
428
+ const transformContext = await this.getTransformContext();
429
+ return this.mapToEntity(row, transformContext);
399
430
  }
400
431
  async hardDeleteMany(ids) {
401
- return this.hardDeleteManyByQuery(inArray(this.table.id, ids));
432
+ return this.hardDeleteManyByQuery(inArray(this.#table.id, ids));
402
433
  }
403
434
  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);
435
+ const sqlQuery = this.$convertQuery(query);
436
+ const rows = await this.session
437
+ .delete(this.#table)
438
+ .where(sqlQuery)
439
+ .returning();
440
+ const transformContext = await this.getTransformContext();
441
+ return this.mapManyToEntity(rows, transformContext);
409
442
  }
410
- convertQuery(query) {
411
- return convertQuery(query, this.table, this.columnDefinitionsMap);
443
+ $getColumn(pathOrColumn) {
444
+ if (isString(pathOrColumn)) {
445
+ const columnName = assertDefinedPass(this.#columnDefinitionsMap.get(pathOrColumn), `Could not map ${pathOrColumn} to column.`).name;
446
+ return this.#table[columnName];
447
+ }
448
+ return this.#table[pathOrColumn.name];
449
+ }
450
+ $convertOrderBy(orderBy) {
451
+ if (isArray(orderBy)) {
452
+ return orderBy.map((item) => {
453
+ const itemIsArray = isArray(item);
454
+ const target = itemIsArray ? item[0] : item;
455
+ const column = isSQLWrapper(target) ? target : this.$getColumn(target);
456
+ const direction = itemIsArray ? item[1] : 'asc';
457
+ return direction == 'asc' ? asc(column) : desc(column);
458
+ });
459
+ }
460
+ return objectEntries(orderBy)
461
+ .map(([path, direction]) => {
462
+ const column = this.$getColumn(path);
463
+ return direction == 'asc' ? asc(column) : desc(column);
464
+ });
465
+ }
466
+ $convertQuery(query) {
467
+ return convertQuery(query, this.#table, this.#columnDefinitionsMap);
468
+ }
469
+ async $mapManyToEntity(columns) {
470
+ const transformContext = await this.getTransformContext();
471
+ return this.mapManyToEntity(columns, transformContext);
472
+ }
473
+ async $mapToEntity(columns) {
474
+ const transformContext = await this.getTransformContext();
475
+ return this.mapToEntity(columns, transformContext);
476
+ }
477
+ async $mapManyToColumns(objects) {
478
+ const transformContext = await this.getTransformContext();
479
+ return this.mapManyToColumns(objects, transformContext);
480
+ }
481
+ async $mapToColumns(obj) {
482
+ const transformContext = await this.getTransformContext();
483
+ return this.mapToColumns(obj, transformContext);
484
+ }
485
+ async $mapManyToInsertColumns(objects) {
486
+ const transformContext = await this.getTransformContext();
487
+ return this.mapManyToInsertColumns(objects, transformContext);
488
+ }
489
+ async $mapToInsertColumns(obj) {
490
+ const transformContext = await this.getTransformContext();
491
+ return this.mapToInsertColumns(obj, transformContext);
492
+ }
493
+ async $mapUpdate(update) {
494
+ const transformContext = await this.getTransformContext();
495
+ return this.mapUpdate(update, transformContext);
496
+ }
497
+ $getIdLimitQuery(query) {
498
+ return this.getIdLimitSelect(query);
499
+ }
500
+ $getAttributesUpdate(attributes) {
501
+ return this.getAttributesUpdate(attributes);
412
502
  }
413
503
  async mapManyToEntity(columns, transformContext) {
414
504
  return toArrayAsync(mapAsync(columns, async (column) => this.mapToEntity(column, transformContext)));
415
505
  }
416
506
  async mapToEntity(columns, transformContext) {
417
507
  const entries = [];
418
- for (const def of this.columnDefinitions) {
508
+ for (const def of this.#columnDefinitions) {
419
509
  const rawValue = columns[def.name];
420
510
  const transformed = await def.fromDatabase(rawValue, transformContext);
421
511
  entries.push([def.objectPath, transformed]); // eslint-disable-line @typescript-eslint/no-unsafe-argument
@@ -428,7 +518,7 @@ let EntityRepository = class EntityRepository {
428
518
  }
429
519
  async mapToColumns(obj, transformContext) {
430
520
  const columns = {};
431
- for (const def of this.columnDefinitions) {
521
+ for (const def of this.#columnDefinitions) {
432
522
  const rawValue = def.dereferenceObjectPath(obj);
433
523
  columns[def.name] = await def.toDatabase(rawValue, transformContext);
434
524
  }
@@ -441,14 +531,18 @@ let EntityRepository = class EntityRepository {
441
531
  const mapped = await this.mapToColumns(obj, transformContext);
442
532
  return {
443
533
  ...mapped,
444
- revision: 1,
445
- revisionTimestamp: TRANSACTION_TIMESTAMP,
446
- createTimestamp: TRANSACTION_TIMESTAMP
534
+ ...(this.hasMetadata
535
+ ? {
536
+ revision: 1,
537
+ revisionTimestamp: TRANSACTION_TIMESTAMP,
538
+ createTimestamp: TRANSACTION_TIMESTAMP
539
+ }
540
+ : undefined)
447
541
  };
448
542
  }
449
543
  async mapUpdate(update, transformContext) {
450
544
  const mappedUpdate = {};
451
- for (const column of this.columnDefinitions) {
545
+ for (const column of this.#columnDefinitions) {
452
546
  const value = column.dereferenceObjectPath(update);
453
547
  if (isUndefined(value)) {
454
548
  continue;
@@ -457,15 +551,22 @@ let EntityRepository = class EntityRepository {
457
551
  }
458
552
  return {
459
553
  ...mappedUpdate,
460
- attributes: this.getAttributesUpdate(update.metadata?.attributes),
461
- revision: sql `${this.table.revision} + 1`,
462
- revisionTimestamp: TRANSACTION_TIMESTAMP
554
+ ...this.getMetadataUpdate(update)
463
555
  };
464
556
  }
465
- getIdLimitQuery(query) {
466
- const sqlQuery = this.convertQuery(query);
467
- return this.session.select({ id: this.table.id })
468
- .from(this.table)
557
+ getMetadataUpdate(update) {
558
+ return this.hasMetadata
559
+ ? {
560
+ attributes: this.getAttributesUpdate(update?.metadata?.attributes),
561
+ revision: sql `${this.#tableWithMetadata.revision} + 1`,
562
+ revisionTimestamp: TRANSACTION_TIMESTAMP
563
+ }
564
+ : undefined;
565
+ }
566
+ getIdLimitSelect(query) {
567
+ const sqlQuery = this.$convertQuery(query);
568
+ return this.session.select({ id: this.#table.id })
569
+ .from(this.#table)
469
570
  .where(sqlQuery)
470
571
  .limit(1);
471
572
  }
@@ -473,11 +574,17 @@ let EntityRepository = class EntityRepository {
473
574
  if (isUndefined(attributes)) {
474
575
  return undefined;
475
576
  }
476
- return sql `${this.table.attributes} || ${JSON.stringify(attributes)}::jsonb`;
577
+ if (attributes instanceof SQL) {
578
+ return attributes;
579
+ }
580
+ return sql `${this.#tableWithMetadata.attributes} || ${JSON.stringify(attributes)}::jsonb`;
477
581
  }
478
582
  async getTransformContext() {
479
583
  if (isUndefined(this.#transformContext)) {
480
- assertDefined(this.#encryptionSecret, 'Missing database encryption secret');
584
+ if (isUndefined(this.#encryptionSecret)) {
585
+ this.#transformContext = {};
586
+ return this.#transformContext;
587
+ }
481
588
  this.#transformContext = importSymmetricKey('AES-GCM', 256, this.#encryptionSecret, false).then((encryptionKey) => ({ encryptionKey }));
482
589
  const transformContext = await this.#transformContext;
483
590
  this.#transformContext = transformContext;
@@ -0,0 +1,15 @@
1
+ import { type Column, type SQL } from 'drizzle-orm';
2
+ import type { GetSelectTableSelection, SelectResultField, TableLike } from 'drizzle-orm/query-builders/select.types';
3
+ import type { Uuid } from '../types.js';
4
+ export declare const TRANSACTION_TIMESTAMP: SQL<Date>;
5
+ export declare const RANDOM_UUID: SQL<Uuid>;
6
+ type IntervalUnit = 'millennium' | 'millenniums' | 'millennia' | 'century' | 'centuries' | 'decade' | 'decades' | 'year' | 'years' | 'day' | 'days' | 'hour' | 'hours' | 'minute' | 'minutes' | 'second' | 'seconds' | 'millisecond' | 'milliseconds' | 'microsecond' | 'microseconds';
7
+ export declare function interval(value: number, unit: IntervalUnit): SQL;
8
+ export declare function arrayAgg<T extends Column>(column: T): SQL<SelectResultField<T>[]>;
9
+ export declare function jsonAgg<T extends TableLike>(tableOrColumn: T): SQL<SelectResultField<GetSelectTableSelection<T>>[]>;
10
+ export declare namespace jsonAgg {
11
+ var withNull: <T extends TableLike>(tableOrColumn: T) => SQL<(SelectResultField<GetSelectTableSelection<T>> | null)[]>;
12
+ }
13
+ export declare function coalesce<T extends (Column | SQL)[]>(...columns: T): SQL<SelectResultField<T>[number]>;
14
+ export declare function toJsonb<T extends (Column | SQL)>(column: T): SQL<SelectResultField<T>>;
15
+ export {};
@@ -0,0 +1,19 @@
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
+ }
7
+ export function arrayAgg(column) {
8
+ return sql `array_agg(${column})`;
9
+ }
10
+ export function jsonAgg(tableOrColumn) {
11
+ return sql `json_agg(${tableOrColumn})`;
12
+ }
13
+ jsonAgg.withNull = jsonAgg;
14
+ export function coalesce(...columns) {
15
+ return sql `coalesce(${sql.join(columns, sql.raw(', '))})`;
16
+ }
17
+ export function toJsonb(column) {
18
+ return sql `to_jsonb(${column})`;
19
+ }