@tstdl/base 0.92.54 → 0.92.56

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 (52) hide show
  1. package/authentication/models/index.d.ts +1 -0
  2. package/authentication/models/index.js +1 -0
  3. package/authentication/models/schemas.d.ts +2 -2
  4. package/document-management/models/schemas.d.ts +17 -17
  5. package/document-management/server/drizzle/{0000_wakeful_firebrand.sql → 0000_sloppy_fenris.sql} +1 -1
  6. package/document-management/server/drizzle/meta/0000_snapshot.json +5 -5
  7. package/document-management/server/drizzle/meta/_journal.json +2 -2
  8. package/examples/orm/schemas.d.ts +1 -1
  9. package/mail/drizzle/0000_empty_jubilee.sql +13 -0
  10. package/mail/drizzle/meta/0000_snapshot.json +100 -0
  11. package/mail/drizzle/meta/_journal.json +13 -0
  12. package/mail/drizzle.config.d.ts +2 -0
  13. package/mail/drizzle.config.js +11 -0
  14. package/mail/index.d.ts +0 -1
  15. package/mail/index.js +0 -1
  16. package/mail/mail.service.d.ts +1 -1
  17. package/mail/mail.service.js +21 -21
  18. package/mail/models/index.d.ts +1 -0
  19. package/mail/models/index.js +1 -0
  20. package/mail/models/mail-log.model.d.ts +6 -7
  21. package/mail/models/mail-log.model.js +38 -1
  22. package/mail/models/schemas.d.ts +3 -0
  23. package/mail/models/schemas.js +4 -0
  24. package/mail/module.d.ts +9 -8
  25. package/mail/module.js +25 -11
  26. package/orm/decorators.d.ts +2 -0
  27. package/orm/decorators.js +3 -0
  28. package/orm/server/database-schema.d.ts +1 -1
  29. package/orm/server/drizzle/schema-converter.d.ts +3 -36
  30. package/orm/server/drizzle/schema-converter.js +18 -1
  31. package/orm/server/encryption.d.ts +2 -0
  32. package/orm/server/encryption.js +30 -0
  33. package/orm/server/module.d.ts +3 -1
  34. package/orm/server/module.js +4 -0
  35. package/orm/server/query-converter.d.ts +1 -1
  36. package/orm/server/query-converter.js +0 -1
  37. package/orm/server/repository.d.ts +9 -5
  38. package/orm/server/repository.js +72 -30
  39. package/orm/server/tokens.d.ts +1 -0
  40. package/orm/server/tokens.js +2 -0
  41. package/orm/server/types.d.ts +43 -0
  42. package/orm/server/types.js +1 -0
  43. package/orm/types.d.ts +2 -1
  44. package/package.json +6 -4
  45. package/mail/repositories/index.d.ts +0 -2
  46. package/mail/repositories/index.js +0 -2
  47. package/mail/repositories/mail-log.repository.d.ts +0 -4
  48. package/mail/repositories/mail-log.repository.js +0 -3
  49. package/mail/repositories/mongo/index.d.ts +0 -1
  50. package/mail/repositories/mongo/index.js +0 -1
  51. package/mail/repositories/mongo/mongo-mail-log.repository.d.ts +0 -16
  52. package/mail/repositories/mongo/mongo-mail-log.repository.js +0 -33
@@ -6,6 +6,7 @@ import { JsonPath } from '../../../json-path/json-path.js';
6
6
  import { reflectionRegistry } from '../../../reflection/registry.js';
7
7
  import { ArraySchema, BooleanSchema, DefaultSchema, EnumerationSchema, getObjectSchema, NullableSchema, NumberSchema, ObjectSchema, OptionalSchema, StringSchema, Uint8ArraySchema } from '../../../schema/index.js';
8
8
  import { compareByValueSelectionToOrder, orderRest } from '../../../utils/comparison.js';
9
+ import { decodeText, encodeUtf8 } from '../../../utils/encoding.js';
9
10
  import { enumValues } from '../../../utils/enum.js';
10
11
  import { memoize, memoizeSingle } from '../../../utils/function/memoize.js';
11
12
  import { compileDereferencer } from '../../../utils/object/dereference.js';
@@ -16,6 +17,7 @@ import { NumericDateSchema } from '../../schemas/numeric-date.js';
16
17
  import { TimestampSchema } from '../../schemas/timestamp.js';
17
18
  import { UuidSchema } from '../../schemas/uuid.js';
18
19
  import { bytea } from '../data-types/bytea.js';
20
+ import { decryptBytes, encryptBytes } from '../encryption.js';
19
21
  const getDbSchema = memoizeSingle(pgSchema);
20
22
  export const getDrizzleTableFromType = memoize(_getDrizzleTableFromType);
21
23
  const columnDefinitionsSymbol = Symbol('columnDefinitions');
@@ -86,13 +88,28 @@ function getPostgresColumnEntries(type, tableName, dbSchema, path = new JsonPath
86
88
  return getPostgresColumnEntries(columnReflectionData?.embedded?.type ?? propertyMetadata.type, tableName, dbSchema, path.add(property), nestedPrefix);
87
89
  }
88
90
  const objectPath = path.add(property);
91
+ const encrypted = columnReflectionData?.encrypted == true;
92
+ const toDatabase = encrypted
93
+ ? async (value, context) => {
94
+ const bytes = encodeUtf8(value);
95
+ return encryptBytes(bytes, context.encryptionKey);
96
+ }
97
+ : (value) => value;
98
+ const fromDatabase = encrypted
99
+ ? async (value, context) => {
100
+ const decrypted = await decryptBytes(value, context.encryptionKey);
101
+ return decodeText(decrypted);
102
+ }
103
+ : (value) => value;
89
104
  return [
90
105
  {
91
106
  name: toCamelCase([prefix, columnName].join('')),
92
107
  objectPath,
93
108
  type: getPostgresColumn(columnName, dbSchema, schema, columnReflectionData ?? {}, { type, property }),
94
109
  reflectionData: columnReflectionData,
95
- dereferenceObjectPath: compileDereferencer(objectPath, { optional: true })
110
+ dereferenceObjectPath: compileDereferencer(objectPath, { optional: true }),
111
+ toDatabase,
112
+ fromDatabase
96
113
  }
97
114
  ];
98
115
  });
@@ -0,0 +1,2 @@
1
+ export declare function encryptBytes(bytes: Uint8Array, key: CryptoKey): Promise<Uint8Array>;
2
+ export declare function decryptBytes(bytes: Uint8Array, key: CryptoKey): Promise<Uint8Array>;
@@ -0,0 +1,30 @@
1
+ import { DetailsError } from '../../errors/index.js';
2
+ import { decrypt, encrypt } from '../../utils/cryptography.js';
3
+ import { getRandomBytes } from '../../utils/random.js';
4
+ import { assert } from '../../utils/type-guards.js';
5
+ const encryptionVersion = 1;
6
+ const encryptionVersionBytes = 2;
7
+ const ivBytes = 12;
8
+ export async function encryptBytes(bytes, key) {
9
+ const iv = getRandomBytes(ivBytes);
10
+ const encrypted = await encrypt({ name: 'AES-GCM', iv }, key, bytes).toBuffer();
11
+ const result = new Uint8Array(encryptionVersionBytes + ivBytes + encrypted.byteLength);
12
+ const resultView = new DataView(result.buffer);
13
+ resultView.setUint16(0, encryptionVersion);
14
+ result.set(iv, encryptionVersionBytes);
15
+ result.set(new Uint8Array(encrypted), encryptionVersionBytes + ivBytes);
16
+ return result;
17
+ }
18
+ export async function decryptBytes(bytes, key) {
19
+ const bytesView = new DataView(bytes.buffer, bytes.byteOffset, bytes.length);
20
+ const version = bytesView.getUint16(0);
21
+ const iv = bytes.slice(encryptionVersionBytes, encryptionVersionBytes + ivBytes);
22
+ assert(version == encryptionVersion, 'Invalid encryption version.');
23
+ try {
24
+ const decrypted = await decrypt({ name: 'AES-GCM', iv }, key, bytes.slice(encryptionVersionBytes + ivBytes)).toBuffer();
25
+ return new Uint8Array(decrypted);
26
+ }
27
+ catch (error) {
28
+ throw new DetailsError('Decrypt error', error);
29
+ }
30
+ }
@@ -7,4 +7,6 @@ export type OrmModuleOptions = {
7
7
  connection?: string | PoolConfig;
8
8
  repositoryConfig?: EntityRepositoryConfig;
9
9
  };
10
- export declare function configureOrm(options: OrmModuleOptions): void;
10
+ export declare function configureOrm(options: OrmModuleOptions & {
11
+ encryptionSecret?: Uint8Array;
12
+ }): void;
@@ -1,6 +1,7 @@
1
1
  import { Injector } from '../../injector/injector.js';
2
2
  import { isDefined } from '../../utils/type-guards.js';
3
3
  import { EntityRepositoryConfig } from './repository.js';
4
+ import { ENCRYPTION_SECRET } from './tokens.js';
4
5
  export class DatabaseConfig {
5
6
  connection;
6
7
  }
@@ -11,4 +12,7 @@ export function configureOrm(options) {
11
12
  if (isDefined(options.repositoryConfig)) {
12
13
  Injector.register(EntityRepositoryConfig, { useValue: options.repositoryConfig });
13
14
  }
15
+ if (isDefined(options.encryptionSecret)) {
16
+ Injector.register(ENCRYPTION_SECRET, { useValue: options.encryptionSecret });
17
+ }
14
18
  }
@@ -1,5 +1,5 @@
1
1
  import { SQL } from 'drizzle-orm';
2
- import type { ColumnDefinition, PgTableFromType } from './drizzle/schema-converter.js';
3
2
  import type { EntityType } from '../entity.js';
4
3
  import type { Query } from '../query.js';
4
+ import type { ColumnDefinition, PgTableFromType } from './types.js';
5
5
  export declare function convertQuery(query: Query, table: PgTableFromType<string, EntityType>, columnDefinitionsMap: Map<string, ColumnDefinition>): SQL;
@@ -14,7 +14,6 @@ export function convertQuery(query, table, columnDefinitionsMap) {
14
14
  if (queryEntries.length == 0) {
15
15
  return sqlTrue;
16
16
  }
17
- // eslint-disable-next-line no-unreachable-loop
18
17
  for (const [property, value] of queryEntries) {
19
18
  const isPrimitiveValue = isPrimitive(value);
20
19
  if (property == '$and') {
@@ -7,8 +7,8 @@ import type { UntaggedDeep } from '../../types/index.js';
7
7
  import type { Entity, EntityMetadata, EntityMetadataAttributes, EntityType, NewEntity } from '../entity.js';
8
8
  import type { Query } from '../query.js';
9
9
  import { Database } from './database.js';
10
- import { type ColumnDefinition, type PgTableFromType } from './drizzle/schema-converter.js';
11
10
  import { type Transaction, type TransactionConfig } from './transaction.js';
11
+ import type { ColumnDefinition, PgTableFromType, TransformContext } from './types.js';
12
12
  type PgTransaction = DrizzlePgTransaction<PgQueryResultHKT, Record, Record>;
13
13
  export declare const repositoryType: unique symbol;
14
14
  export type OrderOptions<T extends Entity> = {
@@ -83,10 +83,13 @@ export declare class EntityRepository<T extends Entity = Entity> implements Reso
83
83
  hardDeleteMany(ids: string[]): Promise<number>;
84
84
  hardDeleteManyByQuery(query: Query<T>): Promise<number>;
85
85
  protected convertQuery(query: Query<T>): SQL;
86
- protected mapToEntity(columns: this['table']['$inferSelect']): T;
87
- protected mapToColumns(obj: DeepPartial<T> | NewEntity<T>): PgInsertValue<PgTableFromType<string, EntityType>>;
88
- protected mapToInsertColumns(obj: DeepPartial<T> | NewEntity<T>): PgInsertValue<PgTableFromType<string, EntityType>>;
89
- protected mapUpdate(update: EntityUpdate<T>): PgUpdateSetSource<PgTableFromType<string, EntityType>>;
86
+ protected mapManyToEntity(columns: this['table']['$inferSelect'][], transformContext: TransformContext): Promise<T[]>;
87
+ protected mapToEntity(columns: this['table']['$inferSelect'], transformContext: TransformContext): Promise<T>;
88
+ protected mapManyToColumns(objects: (DeepPartial<T> | NewEntity<T>)[], transformContext: TransformContext): Promise<PgInsertValue<PgTableFromType<string, EntityType>>[]>;
89
+ protected mapToColumns(obj: DeepPartial<T> | NewEntity<T>, transformContext: TransformContext): Promise<PgInsertValue<PgTableFromType<string, EntityType>>>;
90
+ protected mapManyToInsertColumns(objects: (DeepPartial<T> | NewEntity<T>)[], transformContext: TransformContext): Promise<PgInsertValue<PgTableFromType<string, EntityType>>[]>;
91
+ protected mapToInsertColumns(obj: DeepPartial<T> | NewEntity<T>, transformContext: TransformContext): Promise<PgInsertValue<PgTableFromType<string, EntityType>>>;
92
+ protected mapUpdate(update: EntityUpdate<T>, transformContext: TransformContext): Promise<PgUpdateSetSource<PgTableFromType<string, EntityType>>>;
90
93
  protected getIdLimitQuery(query: Query<T>): import("drizzle-orm/pg-core").WithSubqueryWithSelection<{
91
94
  id: PgColumn<{
92
95
  name: string;
@@ -107,6 +110,7 @@ export declare class EntityRepository<T extends Entity = Entity> implements Reso
107
110
  }, {}, {}>;
108
111
  }, "id">;
109
112
  protected getAttributesUpdate(attributes: EntityMetadataAttributes | undefined): SQL<unknown> | undefined;
113
+ protected getTransformContext(): Promise<TransformContext>;
110
114
  }
111
115
  export declare function injectRepository<T extends Entity>(type: EntityType<T>): EntityRepository<T>;
112
116
  export declare function getRepository<T extends Entity>(type: EntityType<T>, config?: EntityRepositoryConfig): Type<EntityRepository<T>>;
@@ -17,11 +17,15 @@ import { resolveArgumentType } from '../../injector/interfaces.js';
17
17
  import { injectionToken } from '../../injector/token.js';
18
18
  import { Schema } from '../../schema/schema.js';
19
19
  import { toArray } from '../../utils/array/array.js';
20
+ import { mapAsync } from '../../utils/async-iterable-helpers/map.js';
21
+ import { toArrayAsync } from '../../utils/async-iterable-helpers/to-array.js';
22
+ import { importSymmetricKey } from '../../utils/cryptography.js';
20
23
  import { fromDeepObjectEntries } from '../../utils/object/object.js';
21
- import { assertDefinedPass, assertNotNullPass, isNotNull, isUndefined } from '../../utils/type-guards.js';
24
+ import { assertDefined, assertDefinedPass, assertNotNullPass, isNotNull, isUndefined } from '../../utils/type-guards.js';
22
25
  import { Database } from './database.js';
23
26
  import { getColumnDefinitions, getDrizzleTableFromType } from './drizzle/schema-converter.js';
24
27
  import { convertQuery } from './query-converter.js';
28
+ import { ENCRYPTION_SECRET } from './tokens.js';
25
29
  import { DrizzleTransaction } from './transaction.js';
26
30
  export const repositoryType = Symbol('repositoryType');
27
31
  export class EntityRepositoryConfig {
@@ -32,6 +36,8 @@ const TRANSACTION_TIMESTAMP = sql `transaction_timestamp()`;
32
36
  let EntityRepository = class EntityRepository {
33
37
  #repositoryConstructor;
34
38
  #withTransactionCache = new WeakMap();
39
+ #encryptionSecret = inject(ENCRYPTION_SECRET, undefined, { optional: true });
40
+ #transformContext;
35
41
  type;
36
42
  table;
37
43
  columnDefinitions;
@@ -103,7 +109,8 @@ let EntityRepository = class EntityRepository {
103
109
  if (isUndefined(row)) {
104
110
  return undefined;
105
111
  }
106
- return this.mapToEntity(row);
112
+ const transformContext = await this.getTransformContext();
113
+ return this.mapToEntity(row, transformContext);
107
114
  }
108
115
  async loadMany(ids, options) {
109
116
  return this.loadManyByQuery(inArray(this.table.id, ids), options);
@@ -119,7 +126,8 @@ let EntityRepository = class EntityRepository {
119
126
  .where(sqlQuery)
120
127
  .offset(options?.offset)
121
128
  .limit(options?.limit);
122
- return rows.map((entity) => this.mapToEntity(entity));
129
+ const transformContext = await this.getTransformContext();
130
+ return this.mapManyToEntity(rows, transformContext);
123
131
  }
124
132
  async *loadManyByQueryCursor(query, options) {
125
133
  const entities = await this.loadManyByQuery(query, options);
@@ -166,25 +174,28 @@ let EntityRepository = class EntityRepository {
166
174
  return assertDefinedPass(result[0]).contains;
167
175
  }
168
176
  async insert(entity) {
169
- const columns = this.mapToInsertColumns(entity);
177
+ const transformContext = await this.getTransformContext();
178
+ const columns = await this.mapToInsertColumns(entity, transformContext);
170
179
  const [row] = await this.session
171
180
  .insert(this.table)
172
181
  .values(columns)
173
182
  .returning();
174
- return this.mapToEntity(row);
183
+ return this.mapToEntity(row, transformContext);
175
184
  }
176
185
  async insertMany(entities) {
177
- const columns = entities.map((entity) => this.mapToInsertColumns(entity));
186
+ const transformContext = await this.getTransformContext();
187
+ const columns = await this.mapManyToInsertColumns(entities, transformContext);
178
188
  const rows = await this.session.insert(this.table).values(columns).returning();
179
- return rows.map((row) => this.mapToEntity(row));
189
+ return this.mapManyToEntity(rows, transformContext);
180
190
  }
181
191
  async upsert(target, entity, update) {
192
+ const transformContext = await this.getTransformContext();
182
193
  const targetColumns = toArray(target).map((path) => {
183
194
  const columnName = assertDefinedPass(this.columnDefinitionsMap.get(path), `Could not map ${path} to column.`).name;
184
195
  return this.table[columnName];
185
196
  });
186
- const columns = this.mapToInsertColumns(entity);
187
- const mappedUpdate = this.mapUpdate(update ?? entity);
197
+ const columns = await this.mapToInsertColumns(entity, transformContext);
198
+ const mappedUpdate = await this.mapUpdate(update ?? entity, transformContext);
188
199
  const [row] = await this.session
189
200
  .insert(this.table)
190
201
  .values(columns)
@@ -193,15 +204,16 @@ let EntityRepository = class EntityRepository {
193
204
  set: mappedUpdate
194
205
  })
195
206
  .returning();
196
- return this.mapToEntity(row);
207
+ return this.mapToEntity(row, transformContext);
197
208
  }
198
209
  async upsertMany(target, entities, update) {
210
+ const transformContext = await this.getTransformContext();
199
211
  const targetColumns = toArray(target).map((path) => {
200
212
  const columnName = assertDefinedPass(this.columnDefinitionsMap.get(path), `Could not map ${path} to column.`).name;
201
213
  return this.table[columnName];
202
214
  });
203
- const columns = entities.map((entity) => this.mapToColumns(entity));
204
- const mappedUpdate = this.mapUpdate(update);
215
+ const columns = await this.mapManyToColumns(entities, transformContext);
216
+ const mappedUpdate = await this.mapUpdate(update, transformContext);
205
217
  const rows = await this.session
206
218
  .insert(this.table)
207
219
  .values(columns)
@@ -210,7 +222,7 @@ let EntityRepository = class EntityRepository {
210
222
  set: mappedUpdate
211
223
  })
212
224
  .returning();
213
- return rows.map((row) => this.mapToEntity(row));
225
+ return this.mapManyToEntity(rows, transformContext);
214
226
  }
215
227
  async update(id, update) {
216
228
  const entity = await this.tryUpdate(id, update);
@@ -220,7 +232,8 @@ let EntityRepository = class EntityRepository {
220
232
  return entity;
221
233
  }
222
234
  async tryUpdate(id, update) {
223
- const mappedUpdate = this.mapUpdate(update);
235
+ const transformContext = await this.getTransformContext();
236
+ const mappedUpdate = await this.mapUpdate(update, transformContext);
224
237
  const [row] = await this.session
225
238
  .update(this.table)
226
239
  .set(mappedUpdate)
@@ -229,7 +242,7 @@ let EntityRepository = class EntityRepository {
229
242
  if (isUndefined(row)) {
230
243
  return undefined;
231
244
  }
232
- return this.mapToEntity(row);
245
+ return this.mapToEntity(row, transformContext);
233
246
  }
234
247
  async updateByQuery(query, update) {
235
248
  const entity = await this.tryUpdateByQuery(query, update);
@@ -239,7 +252,8 @@ let EntityRepository = class EntityRepository {
239
252
  return entity;
240
253
  }
241
254
  async tryUpdateByQuery(query, update) {
242
- const mappedUpdate = this.mapUpdate(update);
255
+ const transformContext = await this.getTransformContext();
256
+ const mappedUpdate = await this.mapUpdate(update, transformContext);
243
257
  const idQuery = this.getIdLimitQuery(query);
244
258
  const [row] = await this.session
245
259
  .with(idQuery)
@@ -250,20 +264,21 @@ let EntityRepository = class EntityRepository {
250
264
  if (isUndefined(row)) {
251
265
  return undefined;
252
266
  }
253
- return this.mapToEntity(row);
267
+ return this.mapToEntity(row, transformContext);
254
268
  }
255
269
  async updateMany(ids, update) {
256
270
  return this.updateManyByQuery(inArray(this.table.id, ids), update);
257
271
  }
258
272
  async updateManyByQuery(query, update) {
273
+ const transformContext = await this.getTransformContext();
259
274
  const sqlQuery = this.convertQuery(query);
260
- const mappedUpdate = this.mapUpdate(update);
275
+ const mappedUpdate = await this.mapUpdate(update, transformContext);
261
276
  const rows = await this.session
262
277
  .update(this.table)
263
278
  .set(mappedUpdate)
264
279
  .where(sqlQuery)
265
280
  .returning();
266
- return rows.map((entity) => this.mapToEntity(entity));
281
+ return this.mapManyToEntity(rows, transformContext);
267
282
  }
268
283
  async delete(id, metadataUpdate) {
269
284
  const entity = await this.tryDelete(id, metadataUpdate);
@@ -284,7 +299,8 @@ let EntityRepository = class EntityRepository {
284
299
  if (isUndefined(row)) {
285
300
  return undefined;
286
301
  }
287
- return this.mapToEntity(row);
302
+ const transformContext = await this.getTransformContext();
303
+ return this.mapToEntity(row, transformContext);
288
304
  }
289
305
  async deleteByQuery(query, metadataUpdate) {
290
306
  const entity = await this.tryDeleteByQuery(query, metadataUpdate);
@@ -306,7 +322,8 @@ let EntityRepository = class EntityRepository {
306
322
  if (isUndefined(row)) {
307
323
  return undefined;
308
324
  }
309
- return this.mapToEntity(row);
325
+ const transformContext = await this.getTransformContext();
326
+ return this.mapToEntity(row, transformContext);
310
327
  }
311
328
  async deleteMany(ids, metadataUpdate) {
312
329
  return this.deleteManyByQuery(inArray(this.table.id, ids), metadataUpdate);
@@ -321,7 +338,8 @@ let EntityRepository = class EntityRepository {
321
338
  })
322
339
  .where(sqlQuery)
323
340
  .returning();
324
- return rows.map((row) => this.mapToEntity(row));
341
+ const transformContext = await this.getTransformContext();
342
+ return this.mapManyToEntity(rows, transformContext);
325
343
  }
326
344
  async hardDelete(id) {
327
345
  const result = await this.tryHardDelete(id);
@@ -355,20 +373,35 @@ let EntityRepository = class EntityRepository {
355
373
  convertQuery(query) {
356
374
  return convertQuery(query, this.table, this.columnDefinitionsMap);
357
375
  }
358
- mapToEntity(columns) {
359
- const entries = this.columnDefinitions.map((def) => [def.objectPath, columns[def.name]]);
376
+ async mapManyToEntity(columns, transformContext) {
377
+ return toArrayAsync(mapAsync(columns, async (column) => this.mapToEntity(column, transformContext)));
378
+ }
379
+ async mapToEntity(columns, transformContext) {
380
+ const entries = [];
381
+ for (const def of this.columnDefinitions) {
382
+ const rawValue = columns[def.name];
383
+ const transformed = await def.fromDatabase(rawValue, transformContext);
384
+ entries.push([def.objectPath, transformed]); // eslint-disable-line @typescript-eslint/no-unsafe-argument
385
+ }
360
386
  const obj = fromDeepObjectEntries(entries);
361
387
  return Schema.parse(this.type, obj);
362
388
  }
363
- mapToColumns(obj) {
389
+ async mapManyToColumns(objects, transformContext) {
390
+ return toArrayAsync(mapAsync(objects, async (obj) => this.mapToColumns(obj, transformContext)));
391
+ }
392
+ async mapToColumns(obj, transformContext) {
364
393
  const columns = {};
365
394
  for (const def of this.columnDefinitions) {
366
- columns[def.name] = def.dereferenceObjectPath(obj);
395
+ const rawValue = def.dereferenceObjectPath(obj);
396
+ columns[def.name] = await def.toDatabase(rawValue, transformContext);
367
397
  }
368
398
  return columns;
369
399
  }
370
- mapToInsertColumns(obj) {
371
- const mapped = this.mapToColumns(obj);
400
+ async mapManyToInsertColumns(objects, transformContext) {
401
+ return toArrayAsync(mapAsync(objects, async (obj) => this.mapToInsertColumns(obj, transformContext)));
402
+ }
403
+ async mapToInsertColumns(obj, transformContext) {
404
+ const mapped = await this.mapToColumns(obj, transformContext);
372
405
  return {
373
406
  ...mapped,
374
407
  revision: 1,
@@ -376,14 +409,14 @@ let EntityRepository = class EntityRepository {
376
409
  createTimestamp: TRANSACTION_TIMESTAMP
377
410
  };
378
411
  }
379
- mapUpdate(update) {
412
+ async mapUpdate(update, transformContext) {
380
413
  const mappedUpdate = {};
381
414
  for (const column of this.columnDefinitions) {
382
415
  const value = column.dereferenceObjectPath(update);
383
416
  if (isUndefined(value)) {
384
417
  continue;
385
418
  }
386
- mappedUpdate[column.name] = value;
419
+ mappedUpdate[column.name] = await column.toDatabase(value, transformContext);
387
420
  }
388
421
  return {
389
422
  ...mappedUpdate,
@@ -405,6 +438,15 @@ let EntityRepository = class EntityRepository {
405
438
  }
406
439
  return sql `${this.table.attributes} || '${JSON.stringify(attributes)}'::jsonb`;
407
440
  }
441
+ async getTransformContext() {
442
+ if (isUndefined(this.#transformContext)) {
443
+ assertDefined(this.#encryptionSecret, 'Missing database encryption secret');
444
+ this.#transformContext = importSymmetricKey('AES-GCM', 256, this.#encryptionSecret, false).then((encryptionKey) => ({ encryptionKey }));
445
+ const transformContext = await this.#transformContext;
446
+ this.#transformContext = transformContext;
447
+ }
448
+ return this.#transformContext;
449
+ }
408
450
  };
409
451
  EntityRepository = __decorate([
410
452
  Singleton({
@@ -0,0 +1 @@
1
+ export declare const ENCRYPTION_SECRET: import("../../injector/token.js").InjectionToken<Uint8Array<ArrayBufferLike>, never>;
@@ -0,0 +1,2 @@
1
+ import { injectionToken } from '../../injector/token.js';
2
+ export const ENCRYPTION_SECRET = injectionToken('EncryptionSecret');
@@ -0,0 +1,43 @@
1
+ import type { BuildColumns, NotNull } from 'drizzle-orm';
2
+ import type { PgColumnBuilder, PgTableWithColumns } from 'drizzle-orm/pg-core';
3
+ import type { CamelCase, ConditionalPick, SnakeCase } from 'type-fest';
4
+ import type { JsonPath } from '../../json-path/json-path.js';
5
+ import type { Record } from '../../schema/index.js';
6
+ import type { AbstractConstructor, UnionToIntersection } from '../../types.js';
7
+ import type { Tagged } from '../../types/index.js';
8
+ import type { OrmColumnReflectionData } from '../decorators.js';
9
+ import type { EntityType } from '../entity.js';
10
+ import type { ColumnBuilder, EmbeddedConfigTag } from '../types.js';
11
+ export type ColumnDefinition = {
12
+ name: string;
13
+ objectPath: JsonPath;
14
+ type: PgColumnBuilder<any, any, any, any>;
15
+ reflectionData: OrmColumnReflectionData | undefined;
16
+ dereferenceObjectPath: (obj: Record) => any;
17
+ toDatabase: (value: unknown, context: TransformContext) => any;
18
+ fromDatabase: (value: unknown, context: TransformContext) => any;
19
+ };
20
+ export type TransformContext = {
21
+ encryptionKey?: CryptoKey;
22
+ };
23
+ type Column<Name extends string, T> = null extends T ? ColumnBuilder<T, Name> : NotNull<ColumnBuilder<T, Name>>;
24
+ export type ColumnPrefix<T> = T extends Tagged<unknown, EmbeddedConfigTag, {
25
+ prefix: infer Prefix;
26
+ }> ? Prefix extends string ? Prefix : '' : '';
27
+ export type PgTableFromType<S extends string, T extends AbstractConstructor, TableName extends string = T extends Required<EntityType> ? SnakeCase<T['entityName']> : string> = PgTableWithColumns<{
28
+ name: TableName;
29
+ schema: S;
30
+ columns: BuildColumns<TableName, {
31
+ [P in Exclude<keyof InstanceType<T>, keyof EmbeddedProperties<InstanceType<T>>>]: Column<CamelCase<Extract<P, string>>, InstanceType<T>[P]>;
32
+ } & UnionToIntersection<{
33
+ [P in keyof EmbeddedProperties<InstanceType<T>>]: EmbeddedColumns<InstanceType<T>[P], ColumnPrefix<InstanceType<T>[P]>>;
34
+ }[keyof EmbeddedProperties<InstanceType<T>>]>, 'pg'>;
35
+ dialect: 'pg';
36
+ }>;
37
+ export type EmbeddedProperties<T> = ConditionalPick<T, Tagged<unknown, EmbeddedConfigTag, {
38
+ prefix: any;
39
+ }>>;
40
+ export type EmbeddedColumns<T, Prefix extends string> = {
41
+ [P in keyof T as CamelCase<`${Prefix}${Extract<P, string>}`>]: Column<CamelCase<`${Prefix}${Extract<P, string>}`>, T[P]>;
42
+ };
43
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/orm/types.d.ts CHANGED
@@ -25,5 +25,6 @@ export type DoublePrecision = Tagged<number, ColumnTypeTag, ReturnType<typeof do
25
25
  export type Boolean = Tagged<number, ColumnTypeTag, ReturnType<typeof boolean>>;
26
26
  export type NumericDate = Tagged<number, ColumnTypeTag, ReturnType<typeof date>>;
27
27
  export type Timestamp = Tagged<number, ColumnTypeTag, ReturnType<typeof timestamp>>;
28
- export type Bytea = Tagged<number, ColumnTypeTag, ReturnType<typeof bytea>>;
28
+ export type Bytea = Tagged<Uint8Array, ColumnTypeTag, ReturnType<typeof bytea>>;
29
+ export type Encrypted<T> = Tagged<T, ColumnTypeTag, ReturnType<typeof bytea>>;
29
30
  export { Array, Column, Embedded, Index, Integer, Json, NumericDate, PrimaryKey, References, Timestamp, Unique, Uuid };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.92.54",
3
+ "version": "0.92.56",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -17,12 +17,14 @@
17
17
  "pub": "npm run build:production && rm -vf dist/test* && rm -vrf dist/tools/ && npm publish dist/",
18
18
  "tsc:watch": "tsc --watch",
19
19
  "tsc-alias:watch": "tsc-alias --watch",
20
- "generate:orm": "npm run generate:drizzle:document-management && npm run generate:drizzle:authentication",
20
+ "generate:orm": "npm run generate:drizzle:document-management && npm run generate:drizzle:authentication && npm run generate:drizzle:mail",
21
21
  "generate:drizzle:document-management": "drizzle-kit generate --config dist/document-management/server/drizzle.config.js",
22
22
  "generate:drizzle:authentication": "drizzle-kit generate --config dist/authentication/server/drizzle.config.js",
23
- "copy:orm": "npm run copy:document-management && npm run copy:authentication",
23
+ "generate:drizzle:mail": "drizzle-kit generate --config dist/mail/drizzle.config.js",
24
+ "copy:orm": "npm run copy:document-management && npm run copy:authentication && npm run copy:mail",
24
25
  "copy:document-management": "rm -rf ./dist/document-management/server/drizzle && cp -r ./source/document-management/server/drizzle ./dist/document-management/server/",
25
- "copy:authentication": "rm -rf ./dist/authentication/server/drizzle && cp -r ./source/authentication/server/drizzle ./dist/authentication/server/"
26
+ "copy:authentication": "rm -rf ./dist/authentication/server/drizzle && cp -r ./source/authentication/server/drizzle ./dist/authentication/server/",
27
+ "copy:mail": "rm -rf ./dist/mail/drizzle && cp -r ./source/mail/drizzle ./dist/mail/"
26
28
  },
27
29
  "exports": {
28
30
  "./tsconfig.json": "./tsconfig.json",
@@ -1,2 +0,0 @@
1
- export * from './mail-log.repository.js';
2
- export * from './mongo/index.js';
@@ -1,2 +0,0 @@
1
- export * from './mail-log.repository.js';
2
- export * from './mongo/index.js';
@@ -1,4 +0,0 @@
1
- import { EntityRepository } from '../../database/index.js';
2
- import type { MailLog } from '../models/mail-log.model.js';
3
- export declare abstract class MailLogRepository extends EntityRepository<MailLog> {
4
- }
@@ -1,3 +0,0 @@
1
- import { EntityRepository } from '../../database/index.js';
2
- export class MailLogRepository extends EntityRepository {
3
- }
@@ -1 +0,0 @@
1
- export * from './mongo-mail-log.repository.js';
@@ -1 +0,0 @@
1
- export * from './mongo-mail-log.repository.js';
@@ -1,16 +0,0 @@
1
- import type { CollectionArgument } from '../../../database/mongo/index.js';
2
- import { Collection, MongoEntityRepository } from '../../../database/mongo/index.js';
3
- import { resolveArgumentType } from '../../../injector/index.js';
4
- import type { Resolvable } from '../../../injector/interfaces.js';
5
- import { Logger } from '../../../logger/index.js';
6
- import type { MailLog } from '../../models/mail-log.model.js';
7
- export type MongoMailLogRepositoryConfig = {
8
- config?: MongoMailLogRepositoryArgument;
9
- };
10
- export type MongoMailLogRepositoryArgument = CollectionArgument<MailLog>;
11
- export declare const mongoMailLogRepositoryConfig: MongoMailLogRepositoryConfig;
12
- export declare class MongoMailLogRepository extends MongoEntityRepository<MailLog> implements Resolvable<MongoMailLogRepositoryArgument> {
13
- readonly [resolveArgumentType]: MongoMailLogRepositoryArgument;
14
- constructor(collection: Collection<MailLog>, logger: Logger);
15
- }
16
- export declare function configureMongoMailLogRepository(config?: Partial<MongoMailLogRepositoryConfig>): void;
@@ -1,33 +0,0 @@
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 __param = (this && this.__param) || function (paramIndex, decorator) {
11
- return function (target, key) { decorator(target, key, paramIndex); }
12
- };
13
- import { Collection, MongoEntityRepository, noopTransformer } from '../../../database/mongo/index.js';
14
- import { ForwardArg, Singleton, resolveArgumentType } from '../../../injector/index.js';
15
- import { Logger } from '../../../logger/index.js';
16
- export const mongoMailLogRepositoryConfig = {};
17
- const indexes = [];
18
- let MongoMailLogRepository = class MongoMailLogRepository extends MongoEntityRepository {
19
- constructor(collection, logger) {
20
- super(collection, noopTransformer, { logger, indexes });
21
- }
22
- };
23
- MongoMailLogRepository = __decorate([
24
- Singleton({
25
- defaultArgumentProvider: () => mongoMailLogRepositoryConfig.config
26
- }),
27
- __param(0, ForwardArg()),
28
- __metadata("design:paramtypes", [Collection, Logger])
29
- ], MongoMailLogRepository);
30
- export { MongoMailLogRepository };
31
- export function configureMongoMailLogRepository(config = {}) {
32
- mongoMailLogRepositoryConfig.config = config.config ?? mongoMailLogRepositoryConfig.config;
33
- }