@tstdl/base 0.92.55 → 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.
- package/authentication/models/schemas.d.ts +2 -2
- package/document-management/models/schemas.d.ts +17 -17
- package/examples/orm/schemas.d.ts +1 -1
- package/mail/models/schemas.d.ts +1 -1
- package/orm/decorators.d.ts +2 -0
- package/orm/decorators.js +3 -0
- package/orm/server/database-schema.d.ts +1 -1
- package/orm/server/drizzle/schema-converter.d.ts +3 -36
- package/orm/server/drizzle/schema-converter.js +18 -1
- package/orm/server/encryption.d.ts +2 -0
- package/orm/server/encryption.js +30 -0
- package/orm/server/module.d.ts +3 -1
- package/orm/server/module.js +4 -0
- package/orm/server/query-converter.d.ts +1 -1
- package/orm/server/query-converter.js +0 -1
- package/orm/server/repository.d.ts +9 -5
- package/orm/server/repository.js +72 -30
- package/orm/server/tokens.d.ts +1 -0
- package/orm/server/tokens.js +2 -0
- package/orm/server/types.d.ts +43 -0
- package/orm/server/types.js +1 -0
- package/orm/types.d.ts +2 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AuthenticationCredentials } from './authentication-credentials.model.js';
|
|
2
2
|
import { AuthenticationSession } from './authentication-session.model.js';
|
|
3
3
|
export declare const authenticationSchema: import("../../orm/server/database-schema.js").DatabaseSchema<"authentication">;
|
|
4
|
-
export declare const authenticationCredentials: import("../../orm/server/
|
|
5
|
-
export declare const authenticationSession: import("../../orm/server/
|
|
4
|
+
export declare const authenticationCredentials: import("../../orm/server/types.js").PgTableFromType<"authentication", typeof AuthenticationCredentials>;
|
|
5
|
+
export declare const authenticationSession: import("../../orm/server/types.js").PgTableFromType<"authentication", typeof AuthenticationSession>;
|
|
@@ -14,20 +14,20 @@ import { DocumentType } from './document-type.model.js';
|
|
|
14
14
|
import { Document } from './document.model.js';
|
|
15
15
|
export declare const documentManagementSchema: import("../../orm/server/database-schema.js").DatabaseSchema<"document_management">;
|
|
16
16
|
export declare const dataType: import("drizzle-orm/pg-core").PgEnum<["boolean", "decimal", "text", "integer"]>;
|
|
17
|
-
export declare const documentCategory: import("../../orm/server/
|
|
18
|
-
export declare const documentCollectionDocument: import("../../orm/server/
|
|
19
|
-
export declare const documentCollection: import("../../orm/server/
|
|
20
|
-
export declare const documentFile: import("../../orm/server/
|
|
21
|
-
export declare const documentPropertyTextValue: import("../../orm/server/
|
|
22
|
-
export declare const documentPropertyIntegerValue: import("../../orm/server/
|
|
23
|
-
export declare const documentPropertyDecimalValue: import("../../orm/server/
|
|
24
|
-
export declare const documentPropertyBooleanValue: import("../../orm/server/
|
|
25
|
-
export declare const documentProperty: import("../../orm/server/
|
|
26
|
-
export declare const documentRequestCollection: import("../../orm/server/
|
|
27
|
-
export declare const documentRequestFile: import("../../orm/server/
|
|
28
|
-
export declare const documentRequestTemplate: import("../../orm/server/
|
|
29
|
-
export declare const documentRequest: import("../../orm/server/
|
|
30
|
-
export declare const documentRequestsTemplate: import("../../orm/server/
|
|
31
|
-
export declare const documentTypeProperty: import("../../orm/server/
|
|
32
|
-
export declare const documentType: import("../../orm/server/
|
|
33
|
-
export declare const document: import("../../orm/server/
|
|
17
|
+
export declare const documentCategory: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentCategory>;
|
|
18
|
+
export declare const documentCollectionDocument: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentCollectionDocument>;
|
|
19
|
+
export declare const documentCollection: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentCollection>;
|
|
20
|
+
export declare const documentFile: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentFile>;
|
|
21
|
+
export declare const documentPropertyTextValue: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentPropertyTextValue>;
|
|
22
|
+
export declare const documentPropertyIntegerValue: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentPropertyIntegerValue>;
|
|
23
|
+
export declare const documentPropertyDecimalValue: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentPropertyDecimalValue>;
|
|
24
|
+
export declare const documentPropertyBooleanValue: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentPropertyBooleanValue>;
|
|
25
|
+
export declare const documentProperty: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentProperty>;
|
|
26
|
+
export declare const documentRequestCollection: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentRequestCollection>;
|
|
27
|
+
export declare const documentRequestFile: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentRequestFile>;
|
|
28
|
+
export declare const documentRequestTemplate: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentRequestTemplate>;
|
|
29
|
+
export declare const documentRequest: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentRequest>;
|
|
30
|
+
export declare const documentRequestsTemplate: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentRequestsTemplate>;
|
|
31
|
+
export declare const documentTypeProperty: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentTypeProperty>;
|
|
32
|
+
export declare const documentType: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof DocumentType>;
|
|
33
|
+
export declare const document: import("../../orm/server/types.js").PgTableFromType<"document_management", typeof Document>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { User } from './user.model.js';
|
|
2
2
|
export declare const mySchema: import("../../orm/server/database-schema.js").DatabaseSchema<"my_application">;
|
|
3
|
-
export declare const user: import("../../orm/server/
|
|
3
|
+
export declare const user: import("../../orm/server/types.js").PgTableFromType<"my_application", typeof User>;
|
package/mail/models/schemas.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { MailLog } from './mail-log.model.js';
|
|
2
2
|
export declare const mailSchema: import("../../orm/server/database-schema.js").DatabaseSchema<"mail">;
|
|
3
|
-
export declare const mailLog: import("../../orm/server/
|
|
3
|
+
export declare const mailLog: import("../../orm/server/types.js").PgTableFromType<"mail", typeof MailLog>;
|
package/orm/decorators.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export type OrmColumnReflectionData = {
|
|
|
20
20
|
prefix?: string | null;
|
|
21
21
|
};
|
|
22
22
|
references?: () => EntityType;
|
|
23
|
+
encrypted?: boolean;
|
|
23
24
|
};
|
|
24
25
|
export type UniqueReflectionData = {
|
|
25
26
|
name?: string;
|
|
@@ -44,6 +45,7 @@ export declare function createTableAndColumnDecorator(data?: OrmColumnReflection
|
|
|
44
45
|
export declare function Column(options: OrmColumnReflectionData): PropertyDecorator;
|
|
45
46
|
export declare function PrimaryKey(): PropertyDecorator;
|
|
46
47
|
export declare function References(type: () => EntityType): PropertyDecorator;
|
|
48
|
+
export declare function Encrypted(): PropertyDecorator;
|
|
47
49
|
export declare function Embedded(type: AbstractConstructor, options?: TypedOmit<NonNullable<OrmColumnReflectionData['embedded']>, 'type'>): PropertyDecorator;
|
|
48
50
|
export declare function Unique(name?: string, options?: UniqueReflectionData['options']): PropertyDecorator;
|
|
49
51
|
export declare function Unique(name: string | undefined, columns: [string, ...string[]], options?: UniqueReflectionData['options']): ClassDecorator;
|
package/orm/decorators.js
CHANGED
|
@@ -19,6 +19,9 @@ export function PrimaryKey() {
|
|
|
19
19
|
export function References(type) {
|
|
20
20
|
return createColumnDecorator({ references: type });
|
|
21
21
|
}
|
|
22
|
+
export function Encrypted() {
|
|
23
|
+
return createColumnDecorator({ encrypted: true });
|
|
24
|
+
}
|
|
22
25
|
export function Embedded(type, options) {
|
|
23
26
|
return createPropertyDecorator({
|
|
24
27
|
include: [Property(type), createColumnDecorator({ embedded: { type, ...options } })]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PgEnum } from 'drizzle-orm/pg-core';
|
|
2
2
|
import type { Enumeration, EnumerationValue, UnionToTuple } from '../../types.js';
|
|
3
3
|
import type { EntityType } from '../entity.js';
|
|
4
|
-
import {
|
|
4
|
+
import type { PgTableFromType } from './types.js';
|
|
5
5
|
export declare class DatabaseSchema<Name extends string> {
|
|
6
6
|
readonly name: Name;
|
|
7
7
|
constructor(name: Name);
|
|
@@ -1,45 +1,12 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import
|
|
3
|
-
import type { CamelCase, ConditionalPick, SnakeCase } from 'type-fest';
|
|
4
|
-
import { JsonPath } from '../../../json-path/json-path.js';
|
|
5
|
-
import { type Record } from '../../../schema/index.js';
|
|
6
|
-
import type { AbstractConstructor, Enumeration, UnionToIntersection } from '../../../types.js';
|
|
7
|
-
import type { Tagged } from '../../../types/index.js';
|
|
8
|
-
import type { OrmColumnReflectionData } from '../../decorators.js';
|
|
1
|
+
import { type PgEnum, type PgSchema, type PgTableWithColumns } from 'drizzle-orm/pg-core';
|
|
2
|
+
import type { AbstractConstructor, Enumeration } from '../../../types.js';
|
|
9
3
|
import type { EntityType } from '../../entity.js';
|
|
10
|
-
import type {
|
|
11
|
-
type Column<Name extends string, T> = null extends T ? ColumnBuilder<T, Name> : NotNull<ColumnBuilder<T, Name>>;
|
|
4
|
+
import type { ColumnDefinition, PgTableFromType } from '../types.js';
|
|
12
5
|
type ConverterContext = {
|
|
13
6
|
type: AbstractConstructor;
|
|
14
7
|
property: string;
|
|
15
8
|
};
|
|
16
|
-
export type ColumnDefinition = {
|
|
17
|
-
name: string;
|
|
18
|
-
objectPath: JsonPath;
|
|
19
|
-
type: PgColumnBuilder<any, any, any, any>;
|
|
20
|
-
reflectionData: OrmColumnReflectionData | undefined;
|
|
21
|
-
dereferenceObjectPath: (obj: Record) => any;
|
|
22
|
-
};
|
|
23
9
|
export declare const getDrizzleTableFromType: typeof _getDrizzleTableFromType;
|
|
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
10
|
export declare function getColumnDefinitions(table: PgTableWithColumns<any>): ColumnDefinition[];
|
|
44
11
|
export declare function _getDrizzleTableFromType<T extends EntityType, S extends string>(type: T, schemaName: S, tableName?: string): PgTableFromType<S, T>;
|
|
45
12
|
export declare function registerEnum(enumeration: Enumeration, name: string): void;
|
|
@@ -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,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
|
+
}
|
package/orm/server/module.d.ts
CHANGED
|
@@ -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
|
|
10
|
+
export declare function configureOrm(options: OrmModuleOptions & {
|
|
11
|
+
encryptionSecret?: Uint8Array;
|
|
12
|
+
}): void;
|
package/orm/server/module.js
CHANGED
|
@@ -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
|
|
87
|
-
protected
|
|
88
|
-
protected
|
|
89
|
-
protected
|
|
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>>;
|
package/orm/server/repository.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
359
|
-
|
|
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
|
-
|
|
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
|
-
|
|
395
|
+
const rawValue = def.dereferenceObjectPath(obj);
|
|
396
|
+
columns[def.name] = await def.toDatabase(rawValue, transformContext);
|
|
367
397
|
}
|
|
368
398
|
return columns;
|
|
369
399
|
}
|
|
370
|
-
|
|
371
|
-
|
|
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,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<
|
|
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 };
|