@decaf-ts/for-typeorm 0.0.6
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/LICENSE.md +21 -0
- package/README.md +93 -0
- package/dist/for-typeorm.cjs +2553 -0
- package/dist/for-typeorm.esm.cjs +2538 -0
- package/lib/TypeORMAdapter.cjs +1129 -0
- package/lib/TypeORMAdapter.d.ts +221 -0
- package/lib/TypeORMDispatch.cjs +134 -0
- package/lib/TypeORMDispatch.d.ts +87 -0
- package/lib/TypeORMEventSubscriber.cjs +96 -0
- package/lib/TypeORMEventSubscriber.d.ts +56 -0
- package/lib/TypeORMRepository.cjs +209 -0
- package/lib/TypeORMRepository.d.ts +125 -0
- package/lib/constants.cjs +43 -0
- package/lib/constants.d.ts +39 -0
- package/lib/errors.cjs +28 -0
- package/lib/errors.d.ts +21 -0
- package/lib/esm/TypeORMAdapter.d.ts +221 -0
- package/lib/esm/TypeORMAdapter.js +1124 -0
- package/lib/esm/TypeORMDispatch.d.ts +87 -0
- package/lib/esm/TypeORMDispatch.js +130 -0
- package/lib/esm/TypeORMEventSubscriber.d.ts +56 -0
- package/lib/esm/TypeORMEventSubscriber.js +93 -0
- package/lib/esm/TypeORMRepository.d.ts +125 -0
- package/lib/esm/TypeORMRepository.js +206 -0
- package/lib/esm/constants.d.ts +39 -0
- package/lib/esm/constants.js +40 -0
- package/lib/esm/errors.d.ts +21 -0
- package/lib/esm/errors.js +24 -0
- package/lib/esm/index.d.ts +22 -0
- package/lib/esm/index.js +25 -0
- package/lib/esm/indexes/generator.d.ts +50 -0
- package/lib/esm/indexes/generator.js +95 -0
- package/lib/esm/indexes/index.d.ts +1 -0
- package/lib/esm/indexes/index.js +2 -0
- package/lib/esm/overrides/Column.d.ts +74 -0
- package/lib/esm/overrides/Column.js +70 -0
- package/lib/esm/overrides/CreateDateColumn.d.ts +2 -0
- package/lib/esm/overrides/CreateDateColumn.js +9 -0
- package/lib/esm/overrides/Entity.d.ts +11 -0
- package/lib/esm/overrides/Entity.js +28 -0
- package/lib/esm/overrides/PrimaryColumn.d.ts +20 -0
- package/lib/esm/overrides/PrimaryColumn.js +53 -0
- package/lib/esm/overrides/PrimaryGeneratedColumn.d.ts +24 -0
- package/lib/esm/overrides/PrimaryGeneratedColumn.js +51 -0
- package/lib/esm/overrides/UpdateDateColumn.d.ts +2 -0
- package/lib/esm/overrides/UpdateDateColumn.js +9 -0
- package/lib/esm/overrides/utils.d.ts +2 -0
- package/lib/esm/overrides/utils.js +29 -0
- package/lib/esm/query/Paginator.d.ts +86 -0
- package/lib/esm/query/Paginator.js +124 -0
- package/lib/esm/query/Statement.d.ts +131 -0
- package/lib/esm/query/Statement.js +242 -0
- package/lib/esm/query/constants.d.ts +52 -0
- package/lib/esm/query/constants.js +74 -0
- package/lib/esm/query/index.d.ts +4 -0
- package/lib/esm/query/index.js +5 -0
- package/lib/esm/query/translate.d.ts +34 -0
- package/lib/esm/query/translate.js +42 -0
- package/lib/esm/raw/postgres.d.ts +36 -0
- package/lib/esm/raw/postgres.js +2 -0
- package/lib/esm/sequences/Sequence.d.ts +67 -0
- package/lib/esm/sequences/Sequence.js +117 -0
- package/lib/esm/sequences/index.d.ts +1 -0
- package/lib/esm/sequences/index.js +2 -0
- package/lib/esm/types.d.ts +67 -0
- package/lib/esm/types.js +28 -0
- package/lib/esm/utils.d.ts +16 -0
- package/lib/esm/utils.js +29 -0
- package/lib/index.cjs +42 -0
- package/lib/index.d.ts +22 -0
- package/lib/indexes/generator.cjs +98 -0
- package/lib/indexes/generator.d.ts +50 -0
- package/lib/indexes/index.cjs +18 -0
- package/lib/indexes/index.d.ts +1 -0
- package/lib/overrides/Column.cjs +73 -0
- package/lib/overrides/Column.d.ts +74 -0
- package/lib/overrides/CreateDateColumn.cjs +12 -0
- package/lib/overrides/CreateDateColumn.d.ts +2 -0
- package/lib/overrides/Entity.cjs +31 -0
- package/lib/overrides/Entity.d.ts +11 -0
- package/lib/overrides/PrimaryColumn.cjs +56 -0
- package/lib/overrides/PrimaryColumn.d.ts +20 -0
- package/lib/overrides/PrimaryGeneratedColumn.cjs +54 -0
- package/lib/overrides/PrimaryGeneratedColumn.d.ts +24 -0
- package/lib/overrides/UpdateDateColumn.cjs +12 -0
- package/lib/overrides/UpdateDateColumn.d.ts +2 -0
- package/lib/overrides/utils.cjs +32 -0
- package/lib/overrides/utils.d.ts +2 -0
- package/lib/query/Paginator.cjs +128 -0
- package/lib/query/Paginator.d.ts +86 -0
- package/lib/query/Statement.cjs +246 -0
- package/lib/query/Statement.d.ts +131 -0
- package/lib/query/constants.cjs +77 -0
- package/lib/query/constants.d.ts +52 -0
- package/lib/query/index.cjs +21 -0
- package/lib/query/index.d.ts +4 -0
- package/lib/query/translate.cjs +45 -0
- package/lib/query/translate.d.ts +34 -0
- package/lib/raw/postgres.cjs +3 -0
- package/lib/raw/postgres.d.ts +36 -0
- package/lib/sequences/Sequence.cjs +121 -0
- package/lib/sequences/Sequence.d.ts +67 -0
- package/lib/sequences/index.cjs +18 -0
- package/lib/sequences/index.d.ts +1 -0
- package/lib/types.cjs +31 -0
- package/lib/types.d.ts +67 -0
- package/lib/utils.cjs +32 -0
- package/lib/utils.d.ts +16 -0
- package/package.json +128 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { Adapter, RelationsMetadata, Sequence, type SequenceOptions } from "@decaf-ts/core";
|
|
2
|
+
import { BaseError, Context, OperationKeys } from "@decaf-ts/db-decorators";
|
|
3
|
+
import "reflect-metadata";
|
|
4
|
+
import { type Constructor, Model } from "@decaf-ts/decorator-validation";
|
|
5
|
+
import { TypeORMStatement } from "./query";
|
|
6
|
+
import { TypeORMFlags, TypeORMQuery, TypeORMTableSpec } from "./types";
|
|
7
|
+
import { TypeORMRepository } from "./TypeORMRepository";
|
|
8
|
+
import { TypeORMDispatch } from "./TypeORMDispatch";
|
|
9
|
+
import { DataSource } from "typeorm";
|
|
10
|
+
import { DataSourceOptions } from "typeorm/data-source/DataSourceOptions";
|
|
11
|
+
export declare function createdByOnPostgresCreateUpdate<M extends Model, R extends TypeORMRepository<M>, V extends RelationsMetadata>(this: R, context: Context<TypeORMFlags>, data: V, key: keyof M, model: M): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* @description Adapter for TypeORM-backed persistence operations.
|
|
14
|
+
* @summary Implements the Decaf.ts Adapter over a TypeORM DataSource, providing CRUD operations, query/statement factories, sequence management, error parsing, and decoration helpers.
|
|
15
|
+
* @template Y The native configuration type (TypeORM DataSourceOptions).
|
|
16
|
+
* @template F The repository flags type.
|
|
17
|
+
* @template C The context type.
|
|
18
|
+
* @param {DataSourceOptions} scope The DataSource options for the adapter.
|
|
19
|
+
* @param {string} flavour The flavour of the adapter.
|
|
20
|
+
* @param {string} [alias] Optional alias for the adapter.
|
|
21
|
+
* @class TypeORMAdapter
|
|
22
|
+
* @example
|
|
23
|
+
* const adapter = new TypeORMAdapter({ type: 'postgres', /* ... *\/ });
|
|
24
|
+
* await adapter.initialize();
|
|
25
|
+
* const repo = new (adapter.repository<User>())(adapter, User);
|
|
26
|
+
* const created = await repo.create(new User({ name: 'Alice' }));
|
|
27
|
+
*
|
|
28
|
+
* @mermaid
|
|
29
|
+
* sequenceDiagram
|
|
30
|
+
* participant App
|
|
31
|
+
* participant Adapter as TypeORMAdapter
|
|
32
|
+
* participant Repo as TypeORMRepository
|
|
33
|
+
* participant DS as TypeORM DataSource
|
|
34
|
+
*
|
|
35
|
+
* App->>Adapter: new TypeORMAdapter(opts)
|
|
36
|
+
* Adapter->>DS: initialize()
|
|
37
|
+
* App->>Adapter: repository()
|
|
38
|
+
* Adapter-->>App: TypeORMRepository
|
|
39
|
+
* App->>Repo: create(model)
|
|
40
|
+
* Repo->>Adapter: prepare/create/revert
|
|
41
|
+
* Adapter-->>Repo: Model
|
|
42
|
+
* Repo-->>App: Model
|
|
43
|
+
*/
|
|
44
|
+
export declare class TypeORMAdapter extends Adapter<DataSourceOptions, TypeORMQuery, TypeORMFlags, Context<TypeORMFlags>> {
|
|
45
|
+
private _dataSource?;
|
|
46
|
+
get dataSource(): DataSource;
|
|
47
|
+
constructor(options: DataSourceOptions, alias?: string);
|
|
48
|
+
protected flags<M extends Model>(operation: OperationKeys, model: Constructor<M>, flags: Partial<TypeORMFlags>): Promise<TypeORMFlags>;
|
|
49
|
+
protected Dispatch(): TypeORMDispatch;
|
|
50
|
+
repository<M extends Model>(): Constructor<TypeORMRepository<M>>;
|
|
51
|
+
/**
|
|
52
|
+
* @description Creates a new Postgres statement for querying
|
|
53
|
+
* @summary Factory method that creates a new PostgresStatement instance for building queries
|
|
54
|
+
* @template M - The model type
|
|
55
|
+
* @return {TypeORMStatement<M, any>} A new PostgresStatement instance
|
|
56
|
+
*/
|
|
57
|
+
Statement<M extends Model>(): TypeORMStatement<M, any>;
|
|
58
|
+
/**
|
|
59
|
+
* @description Creates a new PostgreSQL sequence
|
|
60
|
+
* @summary Factory method that creates a new PostgreSQLSequence instance for managing sequences
|
|
61
|
+
* @param {SequenceOptions} options - The options for the sequence
|
|
62
|
+
* @return {Promise<Sequence>} A promise that resolves to a new Sequence instance
|
|
63
|
+
*/
|
|
64
|
+
Sequence(options: SequenceOptions): Promise<Sequence>;
|
|
65
|
+
/**
|
|
66
|
+
* @description Initializes the adapter by creating indexes for all managed models
|
|
67
|
+
* @summary Sets up the necessary database indexes for all models managed by this adapter
|
|
68
|
+
* @return {Promise<void>} A promise that resolves when initialization is complete
|
|
69
|
+
*/
|
|
70
|
+
initialize(): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* @description Creates indexes for the given models
|
|
73
|
+
* @summary Abstract method that must be implemented to create database indexes for the specified models
|
|
74
|
+
* @template M - The model type
|
|
75
|
+
* @param {...Constructor<M>} models - The model constructors to create indexes for
|
|
76
|
+
* @return {Promise<void>} A promise that resolves when all indexes are created
|
|
77
|
+
*/
|
|
78
|
+
protected index<M extends Model>(...models: Constructor<M>[]): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* @description Executes a raw SQL query against the database
|
|
81
|
+
* @summary Abstract method that must be implemented to execute raw SQL queries
|
|
82
|
+
* @template R - The result type
|
|
83
|
+
* @param {TypeORMQuery} q - The query to execute
|
|
84
|
+
* @return {Promise<R>} A promise that resolves to the query result
|
|
85
|
+
*/
|
|
86
|
+
raw<R>(q: TypeORMQuery): Promise<R>;
|
|
87
|
+
prepare<M extends Model>(model: M, pk: keyof M, child?: boolean): {
|
|
88
|
+
record: Record<string, any>;
|
|
89
|
+
id: string;
|
|
90
|
+
transient?: Record<string, any>;
|
|
91
|
+
};
|
|
92
|
+
revert<M extends Model>(obj: Record<string, any>, clazz: string | Constructor<M>, pk: keyof M, id: string | number | bigint, transient?: Record<string, any>): M;
|
|
93
|
+
/**
|
|
94
|
+
* @description Creates a new record in the database
|
|
95
|
+
* @summary Abstract method that must be implemented to create a new record
|
|
96
|
+
* @param {string} tableName - The name of the table
|
|
97
|
+
* @param {string|number} id - The ID of the record
|
|
98
|
+
* @param {Record<string, any>} model - The model to create
|
|
99
|
+
* @param {...any[]} args - Additional arguments
|
|
100
|
+
* @return {Promise<Record<string, any>>} A promise that resolves to the created record
|
|
101
|
+
*/
|
|
102
|
+
create(tableName: string, id: string | number, model: Record<string, any>, ...args: any[]): Promise<Record<string, any>>;
|
|
103
|
+
/**
|
|
104
|
+
* @description Reads a record from the database
|
|
105
|
+
* @summary Abstract method that must be implemented to read a record
|
|
106
|
+
* @param {string} tableName - The name of the table
|
|
107
|
+
* @param {string|number} id - The ID of the record
|
|
108
|
+
* @param {string} pk - primary key colum
|
|
109
|
+
* @return {Promise<Record<string, any>>} A promise that resolves to the read record
|
|
110
|
+
*/
|
|
111
|
+
read(tableName: string, id: string | number, pk: string): Promise<Record<string, any>>;
|
|
112
|
+
/**
|
|
113
|
+
* @description Updates a record in the database
|
|
114
|
+
* @summary Abstract method that must be implemented to update a record
|
|
115
|
+
* @param {string} tableName - The name of the table
|
|
116
|
+
* @param {string|number} id - The ID of the record
|
|
117
|
+
* @param {Record<string, any>} model - The model to update
|
|
118
|
+
* @param {string} pk - Additional arguments
|
|
119
|
+
* @return A promise that resolves to the updated record
|
|
120
|
+
*/
|
|
121
|
+
update(tableName: string, id: string | number, model: Record<string, any>, ...args: any[]): Promise<Record<string, any>>;
|
|
122
|
+
/**
|
|
123
|
+
* @description Deletes a record from the database
|
|
124
|
+
* @summary Abstract method that must be implemented to delete a record
|
|
125
|
+
* @param {string} tableName - The name of the table
|
|
126
|
+
* @param {string|number} id - The ID of the record
|
|
127
|
+
* @param {string} pk - Additional arguments
|
|
128
|
+
* @return A promise that resolves to the deleted record
|
|
129
|
+
*/
|
|
130
|
+
delete(tableName: string, id: string | number, pk: string, ...args: any[]): Promise<Record<string, any>>;
|
|
131
|
+
createAll(tableName: string, id: (string | number)[], model: Record<string, any>[], ...args: any[]): Promise<Record<string, any>[]>;
|
|
132
|
+
readAll(tableName: string, id: (string | number | bigint)[], pk: string, ...args: any[]): Promise<Record<string, any>[]>;
|
|
133
|
+
updateAll(tableName: string, ids: string[] | number[], model: Record<string, any>[], pk: string, ...args: any[]): Promise<Record<string, any>[]>;
|
|
134
|
+
deleteAll(tableName: string, ids: (string | number | bigint)[], pk: string, ...args: any[]): Promise<Record<string, any>[]>;
|
|
135
|
+
/**
|
|
136
|
+
* @description Parses an error and converts it to a BaseError
|
|
137
|
+
* @summary Converts various error types to appropriate BaseError subtypes
|
|
138
|
+
* @param {Error|string} err - The error to parse
|
|
139
|
+
* @param {string} [reason] - Optional reason for the error
|
|
140
|
+
* @return {BaseError} The parsed error as a BaseError
|
|
141
|
+
*/
|
|
142
|
+
parseError(err: Error | string, reason?: string): BaseError;
|
|
143
|
+
/**
|
|
144
|
+
* @description Checks if an attribute is reserved
|
|
145
|
+
* @summary Determines if an attribute name is reserved in PostgreSQL
|
|
146
|
+
* @param {string} attr - The attribute name to check
|
|
147
|
+
* @return {boolean} True if the attribute is reserved, false otherwise
|
|
148
|
+
*/
|
|
149
|
+
protected isReserved(attr: string): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* @description Static method to parse an error and convert it to a BaseError
|
|
152
|
+
* @summary Converts various error types to appropriate BaseError subtypes based on PostgreSQL error codes and messages
|
|
153
|
+
* @param {Error|string} err - The error to parse
|
|
154
|
+
* @param {string} [reason] - Optional reason for the error
|
|
155
|
+
* @return {BaseError} The parsed error as a BaseError
|
|
156
|
+
* @mermaid
|
|
157
|
+
* sequenceDiagram
|
|
158
|
+
* participant Caller
|
|
159
|
+
* participant parseError
|
|
160
|
+
* participant ErrorTypes
|
|
161
|
+
*
|
|
162
|
+
* Caller->>parseError: err, reason
|
|
163
|
+
* Note over parseError: Check if err is already a BaseError
|
|
164
|
+
* alt err is BaseError
|
|
165
|
+
* parseError-->>Caller: return err
|
|
166
|
+
* else err is string
|
|
167
|
+
* Note over parseError: Extract code from string
|
|
168
|
+
* alt code matches "duplicate key|already exists"
|
|
169
|
+
* parseError->>ErrorTypes: new ConflictError(code)
|
|
170
|
+
* ErrorTypes-->>Caller: ConflictError
|
|
171
|
+
* else code matches "does not exist|not found"
|
|
172
|
+
* parseError->>ErrorTypes: new NotFoundError(code)
|
|
173
|
+
* ErrorTypes-->>Caller: NotFoundError
|
|
174
|
+
* end
|
|
175
|
+
* else err has code property
|
|
176
|
+
* Note over parseError: Extract code and reason
|
|
177
|
+
* else
|
|
178
|
+
* Note over parseError: Use err.message as code
|
|
179
|
+
* end
|
|
180
|
+
*
|
|
181
|
+
* Note over parseError: Switch on PostgreSQL error code
|
|
182
|
+
* alt code is 23505 (unique_violation)
|
|
183
|
+
* parseError->>ErrorTypes: new ConflictError(reason)
|
|
184
|
+
* ErrorTypes-->>Caller: ConflictError
|
|
185
|
+
* else code is 23503 (foreign_key_violation)
|
|
186
|
+
* parseError->>ErrorTypes: new ConflictError(reason)
|
|
187
|
+
* ErrorTypes-->>Caller: ConflictError
|
|
188
|
+
* else code is 42P01 (undefined_table)
|
|
189
|
+
* parseError->>ErrorTypes: new NotFoundError(reason)
|
|
190
|
+
* ErrorTypes-->>Caller: NotFoundError
|
|
191
|
+
* else code is 42703 (undefined_column)
|
|
192
|
+
* parseError->>ErrorTypes: new NotFoundError(reason)
|
|
193
|
+
* ErrorTypes-->>Caller: NotFoundError
|
|
194
|
+
* else code is 42P07 (duplicate_table)
|
|
195
|
+
* parseError->>ErrorTypes: new ConflictError(reason)
|
|
196
|
+
* ErrorTypes-->>Caller: ConflictError
|
|
197
|
+
* else code is 42P16 (invalid_table_definition)
|
|
198
|
+
* parseError->>ErrorTypes: new IndexError(err)
|
|
199
|
+
* ErrorTypes-->>Caller: IndexError
|
|
200
|
+
* else code matches "ECONNREFUSED"
|
|
201
|
+
* parseError->>ErrorTypes: new ConnectionError(err)
|
|
202
|
+
* ErrorTypes-->>Caller: ConnectionError
|
|
203
|
+
* else
|
|
204
|
+
* parseError->>ErrorTypes: new InternalError(err)
|
|
205
|
+
* ErrorTypes-->>Caller: InternalError
|
|
206
|
+
* end
|
|
207
|
+
*/
|
|
208
|
+
protected static parseError(err: Error | string, reason?: string): BaseError;
|
|
209
|
+
static connect(config: DataSourceOptions): Promise<DataSource>;
|
|
210
|
+
static createDatabase(dataSource: DataSource, dbName: string): Promise<void>;
|
|
211
|
+
static createNotifyFunction(dataSource: DataSource, user: string): Promise<void>;
|
|
212
|
+
static deleteDatabase(dataSource: DataSource, dbName: string, user?: string): Promise<void>;
|
|
213
|
+
static createUser(dataSource: DataSource, dbName: string, user: string, password: string): Promise<void>;
|
|
214
|
+
static deleteUser(client: DataSource, user: string, admin: string): Promise<void>;
|
|
215
|
+
private static parseTypeToPostgres;
|
|
216
|
+
private static parseValidationToPostgres;
|
|
217
|
+
private static parseRelationsToPostgres;
|
|
218
|
+
static createTable<M extends Model>(client: DataSource, model: Constructor<M>): Promise<Record<string, TypeORMTableSpec>>;
|
|
219
|
+
static getCurrentUser(client: DataSource): Promise<string>;
|
|
220
|
+
static decoration(): void;
|
|
221
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TypeORMDispatch = void 0;
|
|
4
|
+
const core_1 = require("@decaf-ts/core");
|
|
5
|
+
const db_decorators_1 = require("@decaf-ts/db-decorators");
|
|
6
|
+
const TypeORMEventSubscriber_1 = require("./TypeORMEventSubscriber.cjs");
|
|
7
|
+
/**
|
|
8
|
+
* @description Dispatcher for TypeORM-driven change events.
|
|
9
|
+
* @summary Subscribes a TypeORM DataSource with a custom EntitySubscriber to notify observers when records are created, updated, or deleted.
|
|
10
|
+
* @param {number} [timeout=5000] Timeout in milliseconds for initialization retries.
|
|
11
|
+
* @class TypeORMDispatch
|
|
12
|
+
* @example
|
|
13
|
+
* // Create a dispatcher for a TypeORM DataSource
|
|
14
|
+
* const dispatch = new TypeORMDispatch();
|
|
15
|
+
* await dispatch.observe(adapter, adapter.dataSource.options);
|
|
16
|
+
*
|
|
17
|
+
* // The dispatcher registers a TypeORMEventSubscriber and notifies observers when entities change.
|
|
18
|
+
* @mermaid
|
|
19
|
+
* classDiagram
|
|
20
|
+
* class Dispatch {
|
|
21
|
+
* +initialize()
|
|
22
|
+
* +updateObservers()
|
|
23
|
+
* }
|
|
24
|
+
* class TypeORMDispatch {
|
|
25
|
+
* -observerLastUpdate?: string
|
|
26
|
+
* -attemptCounter: number
|
|
27
|
+
* -timeout: number
|
|
28
|
+
* +constructor(timeout)
|
|
29
|
+
* #notificationHandler()
|
|
30
|
+
* #initialize()
|
|
31
|
+
* }
|
|
32
|
+
* Dispatch <|-- TypeORMDispatch
|
|
33
|
+
*/
|
|
34
|
+
class TypeORMDispatch extends core_1.Dispatch {
|
|
35
|
+
constructor(timeout = 5000) {
|
|
36
|
+
super();
|
|
37
|
+
this.timeout = timeout;
|
|
38
|
+
this.attemptCounter = 0;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* @description Processes TypeORM notification events.
|
|
42
|
+
* @summary Handles change notifications (translated from TypeORM events) and notifies observers about record changes.
|
|
43
|
+
* @param {string} table The notification payload.
|
|
44
|
+
* @param {OperationKeys} operation The notification payload.
|
|
45
|
+
* @param {EventIds} ids The notification payload.
|
|
46
|
+
* @return {Promise<void>} A promise that resolves when all notifications have been processed.
|
|
47
|
+
* @mermaid
|
|
48
|
+
* sequenceDiagram
|
|
49
|
+
* participant D as PostgreSQLDispatch
|
|
50
|
+
* participant L as Logger
|
|
51
|
+
* participant O as Observers
|
|
52
|
+
* Note over D: Receive notification from PostgreSQL
|
|
53
|
+
* D->>D: Parse notification payload
|
|
54
|
+
* D->>D: Extract table, operation, and ids
|
|
55
|
+
* D->>O: updateObservers(table, operation, ids)
|
|
56
|
+
* D->>D: Update observerLastUpdate
|
|
57
|
+
* D->>L: Log successful dispatch
|
|
58
|
+
*/
|
|
59
|
+
async notificationHandler(table, operation, ids) {
|
|
60
|
+
const log = this.log.for(this.notificationHandler);
|
|
61
|
+
try {
|
|
62
|
+
// Notify observers
|
|
63
|
+
await this.updateObservers(table, operation, ids);
|
|
64
|
+
this.observerLastUpdate = new Date().toISOString();
|
|
65
|
+
log.verbose(`Observer refresh dispatched by ${operation} for ${table}`);
|
|
66
|
+
log.debug(`pks: ${ids}`);
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
log.error(`Failed to process notification: ${e}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* @description Initializes the dispatcher and subscribes to TypeORM notifications.
|
|
74
|
+
* @summary Registers the TypeORMEventSubscriber on the DataSource and logs the subscription lifecycle.
|
|
75
|
+
* @return {Promise<void>} A promise that resolves when the subscription is established.
|
|
76
|
+
* @mermaid
|
|
77
|
+
* sequenceDiagram
|
|
78
|
+
* participant D as TypeORMDispatch
|
|
79
|
+
* participant S as subscribeToTypeORM
|
|
80
|
+
* participant DS as TypeORM DataSource
|
|
81
|
+
* participant L as Logger
|
|
82
|
+
* D->>S: Call subscribeToTypeORM
|
|
83
|
+
* S->>S: Check adapter and native
|
|
84
|
+
* alt No adapter or native
|
|
85
|
+
* S-->>S: throw InternalError
|
|
86
|
+
* end
|
|
87
|
+
* S->>DS: initialize()
|
|
88
|
+
* S->>DS: subscribers.push(TypeORMEventSubscriber)
|
|
89
|
+
* alt Success
|
|
90
|
+
* DS-->>S: Subscription established
|
|
91
|
+
* S-->>D: Promise resolves
|
|
92
|
+
* D->>L: Log successful subscription
|
|
93
|
+
* else Error
|
|
94
|
+
* DS-->>S: Error
|
|
95
|
+
* S-->>D: Promise rejects
|
|
96
|
+
* end
|
|
97
|
+
*/
|
|
98
|
+
async initialize() {
|
|
99
|
+
async function subscribeToTypeORM() {
|
|
100
|
+
if (!this.adapter || !this.native) {
|
|
101
|
+
throw new db_decorators_1.InternalError(`No adapter/native observed for dispatch`);
|
|
102
|
+
}
|
|
103
|
+
const adapter = this.adapter;
|
|
104
|
+
try {
|
|
105
|
+
if (!adapter.dataSource.isInitialized)
|
|
106
|
+
await adapter.dataSource.initialize();
|
|
107
|
+
adapter.dataSource.subscribers.push(new TypeORMEventSubscriber_1.TypeORMEventSubscriber(this.notificationHandler.bind(this)));
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
throw new db_decorators_1.InternalError(e);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
subscribeToTypeORM
|
|
114
|
+
.call(this)
|
|
115
|
+
.then(() => {
|
|
116
|
+
this.log.info(`Subscribed to TypeORM notifications`);
|
|
117
|
+
})
|
|
118
|
+
.catch((e) => {
|
|
119
|
+
throw new db_decorators_1.InternalError(`Failed to subscribe to TypeORM notifications: ${e}`);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Cleanup method to release resources when the dispatcher is no longer needed
|
|
124
|
+
*/
|
|
125
|
+
cleanup() {
|
|
126
|
+
// if (this.adapter) {
|
|
127
|
+
//
|
|
128
|
+
// const adapter = this.adapter as TypeORMAdapter;
|
|
129
|
+
// await adapter.dataSource.destroy();
|
|
130
|
+
// }
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.TypeORMDispatch = TypeORMDispatch;
|
|
134
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHlwZU9STURpc3BhdGNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1R5cGVPUk1EaXNwYXRjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSx5Q0FBb0Q7QUFDcEQsMkRBQXVFO0FBR3ZFLHlFQUFrRTtBQUVsRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EwQkc7QUFDSCxNQUFhLGVBQWdCLFNBQVEsZUFBMkI7SUFJOUQsWUFBb0IsVUFBVSxJQUFJO1FBQ2hDLEtBQUssRUFBRSxDQUFDO1FBRFUsWUFBTyxHQUFQLE9BQU8sQ0FBTztRQUYxQixtQkFBYyxHQUFXLENBQUMsQ0FBQztJQUluQyxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7OztPQWtCRztJQUNPLEtBQUssQ0FBQyxtQkFBbUIsQ0FDakMsS0FBYSxFQUNiLFNBQXdCLEVBQ3hCLEdBQWE7UUFFYixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsQ0FBQztRQUNuRCxJQUFJLENBQUM7WUFDSCxtQkFBbUI7WUFDbkIsTUFBTSxJQUFJLENBQUMsZUFBZSxDQUFDLEtBQUssRUFBRSxTQUFTLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDbEQsSUFBSSxDQUFDLGtCQUFrQixHQUFHLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDbkQsR0FBRyxDQUFDLE9BQU8sQ0FBQyxrQ0FBa0MsU0FBUyxRQUFRLEtBQUssRUFBRSxDQUFDLENBQUM7WUFDeEUsR0FBRyxDQUFDLEtBQUssQ0FBQyxRQUFRLEdBQUcsRUFBRSxDQUFDLENBQUM7UUFDM0IsQ0FBQztRQUFDLE9BQU8sQ0FBVSxFQUFFLENBQUM7WUFDcEIsR0FBRyxDQUFDLEtBQUssQ0FBQyxtQ0FBbUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNwRCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O09BeUJHO0lBQ2dCLEtBQUssQ0FBQyxVQUFVO1FBQ2pDLEtBQUssVUFBVSxrQkFBa0I7WUFDL0IsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7Z0JBQ2xDLE1BQU0sSUFBSSw2QkFBYSxDQUFDLHlDQUF5QyxDQUFDLENBQUM7WUFDckUsQ0FBQztZQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxPQUF5QixDQUFDO1lBRS9DLElBQUksQ0FBQztnQkFDSCxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxhQUFhO29CQUNuQyxNQUFNLE9BQU8sQ0FBQyxVQUFVLENBQUMsVUFBVSxFQUFFLENBQUM7Z0JBRXhDLE9BQU8sQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLElBQUksQ0FDakMsSUFBSSwrQ0FBc0IsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQ2hFLENBQUM7WUFDSixDQUFDO1lBQUMsT0FBTyxDQUFVLEVBQUUsQ0FBQztnQkFDcEIsTUFBTSxJQUFJLDZCQUFhLENBQUMsQ0FBVSxDQUFDLENBQUM7WUFDdEMsQ0FBQztRQUNILENBQUM7UUFFRCxrQkFBa0I7YUFDZixJQUFJLENBQUMsSUFBSSxDQUFDO2FBQ1YsSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUNULElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLHFDQUFxQyxDQUFDLENBQUM7UUFDdkQsQ0FBQyxDQUFDO2FBQ0QsS0FBSyxDQUFDLENBQUMsQ0FBVSxFQUFFLEVBQUU7WUFDcEIsTUFBTSxJQUFJLDZCQUFhLENBQ3JCLGlEQUFpRCxDQUFDLEVBQUUsQ0FDckQsQ0FBQztRQUNKLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVEOztPQUVHO0lBQ0ksT0FBTztRQUNaLHNCQUFzQjtRQUN0QixFQUFFO1FBQ0Ysb0RBQW9EO1FBQ3BELHdDQUF3QztRQUN4QyxJQUFJO0lBQ04sQ0FBQztDQUNGO0FBaEhELDBDQWdIQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IERpc3BhdGNoLCBFdmVudElkcyB9IGZyb20gXCJAZGVjYWYtdHMvY29yZVwiO1xuaW1wb3J0IHsgSW50ZXJuYWxFcnJvciwgT3BlcmF0aW9uS2V5cyB9IGZyb20gXCJAZGVjYWYtdHMvZGItZGVjb3JhdG9yc1wiO1xuaW1wb3J0IHsgRGF0YVNvdXJjZU9wdGlvbnMgfSBmcm9tIFwidHlwZW9ybS9kYXRhLXNvdXJjZS9EYXRhU291cmNlT3B0aW9uc1wiO1xuaW1wb3J0IHsgVHlwZU9STUFkYXB0ZXIgfSBmcm9tIFwiLi9UeXBlT1JNQWRhcHRlclwiO1xuaW1wb3J0IHsgVHlwZU9STUV2ZW50U3Vic2NyaWJlciB9IGZyb20gXCIuL1R5cGVPUk1FdmVudFN1YnNjcmliZXJcIjtcblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gRGlzcGF0Y2hlciBmb3IgVHlwZU9STS1kcml2ZW4gY2hhbmdlIGV2ZW50cy5cbiAqIEBzdW1tYXJ5IFN1YnNjcmliZXMgYSBUeXBlT1JNIERhdGFTb3VyY2Ugd2l0aCBhIGN1c3RvbSBFbnRpdHlTdWJzY3JpYmVyIHRvIG5vdGlmeSBvYnNlcnZlcnMgd2hlbiByZWNvcmRzIGFyZSBjcmVhdGVkLCB1cGRhdGVkLCBvciBkZWxldGVkLlxuICogQHBhcmFtIHtudW1iZXJ9IFt0aW1lb3V0PTUwMDBdIFRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBpbml0aWFsaXphdGlvbiByZXRyaWVzLlxuICogQGNsYXNzIFR5cGVPUk1EaXNwYXRjaFxuICogQGV4YW1wbGVcbiAqIC8vIENyZWF0ZSBhIGRpc3BhdGNoZXIgZm9yIGEgVHlwZU9STSBEYXRhU291cmNlXG4gKiBjb25zdCBkaXNwYXRjaCA9IG5ldyBUeXBlT1JNRGlzcGF0Y2goKTtcbiAqIGF3YWl0IGRpc3BhdGNoLm9ic2VydmUoYWRhcHRlciwgYWRhcHRlci5kYXRhU291cmNlLm9wdGlvbnMpO1xuICpcbiAqIC8vIFRoZSBkaXNwYXRjaGVyIHJlZ2lzdGVycyBhIFR5cGVPUk1FdmVudFN1YnNjcmliZXIgYW5kIG5vdGlmaWVzIG9ic2VydmVycyB3aGVuIGVudGl0aWVzIGNoYW5nZS5cbiAqIEBtZXJtYWlkXG4gKiBjbGFzc0RpYWdyYW1cbiAqICAgY2xhc3MgRGlzcGF0Y2gge1xuICogICAgICtpbml0aWFsaXplKClcbiAqICAgICArdXBkYXRlT2JzZXJ2ZXJzKClcbiAqICAgfVxuICogICBjbGFzcyBUeXBlT1JNRGlzcGF0Y2gge1xuICogICAgIC1vYnNlcnZlckxhc3RVcGRhdGU/OiBzdHJpbmdcbiAqICAgICAtYXR0ZW1wdENvdW50ZXI6IG51bWJlclxuICogICAgIC10aW1lb3V0OiBudW1iZXJcbiAqICAgICArY29uc3RydWN0b3IodGltZW91dClcbiAqICAgICAjbm90aWZpY2F0aW9uSGFuZGxlcigpXG4gKiAgICAgI2luaXRpYWxpemUoKVxuICogICB9XG4gKiAgIERpc3BhdGNoIDx8LS0gVHlwZU9STURpc3BhdGNoXG4gKi9cbmV4cG9ydCBjbGFzcyBUeXBlT1JNRGlzcGF0Y2ggZXh0ZW5kcyBEaXNwYXRjaDxEYXRhU291cmNlT3B0aW9ucz4ge1xuICBwcml2YXRlIG9ic2VydmVyTGFzdFVwZGF0ZT86IHN0cmluZztcbiAgcHJpdmF0ZSBhdHRlbXB0Q291bnRlcjogbnVtYmVyID0gMDtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHRpbWVvdXQgPSA1MDAwKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gUHJvY2Vzc2VzIFR5cGVPUk0gbm90aWZpY2F0aW9uIGV2ZW50cy5cbiAgICogQHN1bW1hcnkgSGFuZGxlcyBjaGFuZ2Ugbm90aWZpY2F0aW9ucyAodHJhbnNsYXRlZCBmcm9tIFR5cGVPUk0gZXZlbnRzKSBhbmQgbm90aWZpZXMgb2JzZXJ2ZXJzIGFib3V0IHJlY29yZCBjaGFuZ2VzLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdGFibGUgVGhlIG5vdGlmaWNhdGlvbiBwYXlsb2FkLlxuICAgKiBAcGFyYW0ge09wZXJhdGlvbktleXN9IG9wZXJhdGlvbiBUaGUgbm90aWZpY2F0aW9uIHBheWxvYWQuXG4gICAqIEBwYXJhbSB7RXZlbnRJZHN9IGlkcyBUaGUgbm90aWZpY2F0aW9uIHBheWxvYWQuXG4gICAqIEByZXR1cm4ge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gYWxsIG5vdGlmaWNhdGlvbnMgaGF2ZSBiZWVuIHByb2Nlc3NlZC5cbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgRCBhcyBQb3N0Z3JlU1FMRGlzcGF0Y2hcbiAgICogICBwYXJ0aWNpcGFudCBMIGFzIExvZ2dlclxuICAgKiAgIHBhcnRpY2lwYW50IE8gYXMgT2JzZXJ2ZXJzXG4gICAqICAgTm90ZSBvdmVyIEQ6IFJlY2VpdmUgbm90aWZpY2F0aW9uIGZyb20gUG9zdGdyZVNRTFxuICAgKiAgIEQtPj5EOiBQYXJzZSBub3RpZmljYXRpb24gcGF5bG9hZFxuICAgKiAgIEQtPj5EOiBFeHRyYWN0IHRhYmxlLCBvcGVyYXRpb24sIGFuZCBpZHNcbiAgICogICBELT4+TzogdXBkYXRlT2JzZXJ2ZXJzKHRhYmxlLCBvcGVyYXRpb24sIGlkcylcbiAgICogICBELT4+RDogVXBkYXRlIG9ic2VydmVyTGFzdFVwZGF0ZVxuICAgKiAgIEQtPj5MOiBMb2cgc3VjY2Vzc2Z1bCBkaXNwYXRjaFxuICAgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIG5vdGlmaWNhdGlvbkhhbmRsZXIoXG4gICAgdGFibGU6IHN0cmluZyxcbiAgICBvcGVyYXRpb246IE9wZXJhdGlvbktleXMsXG4gICAgaWRzOiBFdmVudElkc1xuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBsb2cgPSB0aGlzLmxvZy5mb3IodGhpcy5ub3RpZmljYXRpb25IYW5kbGVyKTtcbiAgICB0cnkge1xuICAgICAgLy8gTm90aWZ5IG9ic2VydmVyc1xuICAgICAgYXdhaXQgdGhpcy51cGRhdGVPYnNlcnZlcnModGFibGUsIG9wZXJhdGlvbiwgaWRzKTtcbiAgICAgIHRoaXMub2JzZXJ2ZXJMYXN0VXBkYXRlID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpO1xuICAgICAgbG9nLnZlcmJvc2UoYE9ic2VydmVyIHJlZnJlc2ggZGlzcGF0Y2hlZCBieSAke29wZXJhdGlvbn0gZm9yICR7dGFibGV9YCk7XG4gICAgICBsb2cuZGVidWcoYHBrczogJHtpZHN9YCk7XG4gICAgfSBjYXRjaCAoZTogdW5rbm93bikge1xuICAgICAgbG9nLmVycm9yKGBGYWlsZWQgdG8gcHJvY2VzcyBub3RpZmljYXRpb246ICR7ZX1gKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEluaXRpYWxpemVzIHRoZSBkaXNwYXRjaGVyIGFuZCBzdWJzY3JpYmVzIHRvIFR5cGVPUk0gbm90aWZpY2F0aW9ucy5cbiAgICogQHN1bW1hcnkgUmVnaXN0ZXJzIHRoZSBUeXBlT1JNRXZlbnRTdWJzY3JpYmVyIG9uIHRoZSBEYXRhU291cmNlIGFuZCBsb2dzIHRoZSBzdWJzY3JpcHRpb24gbGlmZWN5Y2xlLlxuICAgKiBAcmV0dXJuIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzdWJzY3JpcHRpb24gaXMgZXN0YWJsaXNoZWQuXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEQgYXMgVHlwZU9STURpc3BhdGNoXG4gICAqICAgcGFydGljaXBhbnQgUyBhcyBzdWJzY3JpYmVUb1R5cGVPUk1cbiAgICogICBwYXJ0aWNpcGFudCBEUyBhcyBUeXBlT1JNIERhdGFTb3VyY2VcbiAgICogICBwYXJ0aWNpcGFudCBMIGFzIExvZ2dlclxuICAgKiAgIEQtPj5TOiBDYWxsIHN1YnNjcmliZVRvVHlwZU9STVxuICAgKiAgIFMtPj5TOiBDaGVjayBhZGFwdGVyIGFuZCBuYXRpdmVcbiAgICogICBhbHQgTm8gYWRhcHRlciBvciBuYXRpdmVcbiAgICogICAgIFMtLT4+UzogdGhyb3cgSW50ZXJuYWxFcnJvclxuICAgKiAgIGVuZFxuICAgKiAgIFMtPj5EUzogaW5pdGlhbGl6ZSgpXG4gICAqICAgUy0+PkRTOiBzdWJzY3JpYmVycy5wdXNoKFR5cGVPUk1FdmVudFN1YnNjcmliZXIpXG4gICAqICAgYWx0IFN1Y2Nlc3NcbiAgICogICAgIERTLS0+PlM6IFN1YnNjcmlwdGlvbiBlc3RhYmxpc2hlZFxuICAgKiAgICAgUy0tPj5EOiBQcm9taXNlIHJlc29sdmVzXG4gICAqICAgICBELT4+TDogTG9nIHN1Y2Nlc3NmdWwgc3Vic2NyaXB0aW9uXG4gICAqICAgZWxzZSBFcnJvclxuICAgKiAgICAgRFMtLT4+UzogRXJyb3JcbiAgICogICAgIFMtLT4+RDogUHJvbWlzZSByZWplY3RzXG4gICAqICAgZW5kXG4gICAqL1xuICBwcm90ZWN0ZWQgb3ZlcnJpZGUgYXN5bmMgaW5pdGlhbGl6ZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhc3luYyBmdW5jdGlvbiBzdWJzY3JpYmVUb1R5cGVPUk0odGhpczogVHlwZU9STURpc3BhdGNoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICBpZiAoIXRoaXMuYWRhcHRlciB8fCAhdGhpcy5uYXRpdmUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoYE5vIGFkYXB0ZXIvbmF0aXZlIG9ic2VydmVkIGZvciBkaXNwYXRjaGApO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBhZGFwdGVyID0gdGhpcy5hZGFwdGVyIGFzIFR5cGVPUk1BZGFwdGVyO1xuXG4gICAgICB0cnkge1xuICAgICAgICBpZiAoIWFkYXB0ZXIuZGF0YVNvdXJjZS5pc0luaXRpYWxpemVkKVxuICAgICAgICAgIGF3YWl0IGFkYXB0ZXIuZGF0YVNvdXJjZS5pbml0aWFsaXplKCk7XG5cbiAgICAgICAgYWRhcHRlci5kYXRhU291cmNlLnN1YnNjcmliZXJzLnB1c2goXG4gICAgICAgICAgbmV3IFR5cGVPUk1FdmVudFN1YnNjcmliZXIodGhpcy5ub3RpZmljYXRpb25IYW5kbGVyLmJpbmQodGhpcykpXG4gICAgICAgICk7XG4gICAgICB9IGNhdGNoIChlOiB1bmtub3duKSB7XG4gICAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKGUgYXMgRXJyb3IpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHN1YnNjcmliZVRvVHlwZU9STVxuICAgICAgLmNhbGwodGhpcylcbiAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgdGhpcy5sb2cuaW5mbyhgU3Vic2NyaWJlZCB0byBUeXBlT1JNIG5vdGlmaWNhdGlvbnNgKTtcbiAgICAgIH0pXG4gICAgICAuY2F0Y2goKGU6IHVua25vd24pID0+IHtcbiAgICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoXG4gICAgICAgICAgYEZhaWxlZCB0byBzdWJzY3JpYmUgdG8gVHlwZU9STSBub3RpZmljYXRpb25zOiAke2V9YFxuICAgICAgICApO1xuICAgICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQ2xlYW51cCBtZXRob2QgdG8gcmVsZWFzZSByZXNvdXJjZXMgd2hlbiB0aGUgZGlzcGF0Y2hlciBpcyBubyBsb25nZXIgbmVlZGVkXG4gICAqL1xuICBwdWJsaWMgY2xlYW51cCgpOiB2b2lkIHtcbiAgICAvLyBpZiAodGhpcy5hZGFwdGVyKSB7XG4gICAgLy9cbiAgICAvLyAgIGNvbnN0IGFkYXB0ZXIgPSB0aGlzLmFkYXB0ZXIgYXMgVHlwZU9STUFkYXB0ZXI7XG4gICAgLy8gICBhd2FpdCBhZGFwdGVyLmRhdGFTb3VyY2UuZGVzdHJveSgpO1xuICAgIC8vIH1cbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Dispatch, EventIds } from "@decaf-ts/core";
|
|
2
|
+
import { OperationKeys } from "@decaf-ts/db-decorators";
|
|
3
|
+
import { DataSourceOptions } from "typeorm/data-source/DataSourceOptions";
|
|
4
|
+
/**
|
|
5
|
+
* @description Dispatcher for TypeORM-driven change events.
|
|
6
|
+
* @summary Subscribes a TypeORM DataSource with a custom EntitySubscriber to notify observers when records are created, updated, or deleted.
|
|
7
|
+
* @param {number} [timeout=5000] Timeout in milliseconds for initialization retries.
|
|
8
|
+
* @class TypeORMDispatch
|
|
9
|
+
* @example
|
|
10
|
+
* // Create a dispatcher for a TypeORM DataSource
|
|
11
|
+
* const dispatch = new TypeORMDispatch();
|
|
12
|
+
* await dispatch.observe(adapter, adapter.dataSource.options);
|
|
13
|
+
*
|
|
14
|
+
* // The dispatcher registers a TypeORMEventSubscriber and notifies observers when entities change.
|
|
15
|
+
* @mermaid
|
|
16
|
+
* classDiagram
|
|
17
|
+
* class Dispatch {
|
|
18
|
+
* +initialize()
|
|
19
|
+
* +updateObservers()
|
|
20
|
+
* }
|
|
21
|
+
* class TypeORMDispatch {
|
|
22
|
+
* -observerLastUpdate?: string
|
|
23
|
+
* -attemptCounter: number
|
|
24
|
+
* -timeout: number
|
|
25
|
+
* +constructor(timeout)
|
|
26
|
+
* #notificationHandler()
|
|
27
|
+
* #initialize()
|
|
28
|
+
* }
|
|
29
|
+
* Dispatch <|-- TypeORMDispatch
|
|
30
|
+
*/
|
|
31
|
+
export declare class TypeORMDispatch extends Dispatch<DataSourceOptions> {
|
|
32
|
+
private timeout;
|
|
33
|
+
private observerLastUpdate?;
|
|
34
|
+
private attemptCounter;
|
|
35
|
+
constructor(timeout?: number);
|
|
36
|
+
/**
|
|
37
|
+
* @description Processes TypeORM notification events.
|
|
38
|
+
* @summary Handles change notifications (translated from TypeORM events) and notifies observers about record changes.
|
|
39
|
+
* @param {string} table The notification payload.
|
|
40
|
+
* @param {OperationKeys} operation The notification payload.
|
|
41
|
+
* @param {EventIds} ids The notification payload.
|
|
42
|
+
* @return {Promise<void>} A promise that resolves when all notifications have been processed.
|
|
43
|
+
* @mermaid
|
|
44
|
+
* sequenceDiagram
|
|
45
|
+
* participant D as PostgreSQLDispatch
|
|
46
|
+
* participant L as Logger
|
|
47
|
+
* participant O as Observers
|
|
48
|
+
* Note over D: Receive notification from PostgreSQL
|
|
49
|
+
* D->>D: Parse notification payload
|
|
50
|
+
* D->>D: Extract table, operation, and ids
|
|
51
|
+
* D->>O: updateObservers(table, operation, ids)
|
|
52
|
+
* D->>D: Update observerLastUpdate
|
|
53
|
+
* D->>L: Log successful dispatch
|
|
54
|
+
*/
|
|
55
|
+
protected notificationHandler(table: string, operation: OperationKeys, ids: EventIds): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* @description Initializes the dispatcher and subscribes to TypeORM notifications.
|
|
58
|
+
* @summary Registers the TypeORMEventSubscriber on the DataSource and logs the subscription lifecycle.
|
|
59
|
+
* @return {Promise<void>} A promise that resolves when the subscription is established.
|
|
60
|
+
* @mermaid
|
|
61
|
+
* sequenceDiagram
|
|
62
|
+
* participant D as TypeORMDispatch
|
|
63
|
+
* participant S as subscribeToTypeORM
|
|
64
|
+
* participant DS as TypeORM DataSource
|
|
65
|
+
* participant L as Logger
|
|
66
|
+
* D->>S: Call subscribeToTypeORM
|
|
67
|
+
* S->>S: Check adapter and native
|
|
68
|
+
* alt No adapter or native
|
|
69
|
+
* S-->>S: throw InternalError
|
|
70
|
+
* end
|
|
71
|
+
* S->>DS: initialize()
|
|
72
|
+
* S->>DS: subscribers.push(TypeORMEventSubscriber)
|
|
73
|
+
* alt Success
|
|
74
|
+
* DS-->>S: Subscription established
|
|
75
|
+
* S-->>D: Promise resolves
|
|
76
|
+
* D->>L: Log successful subscription
|
|
77
|
+
* else Error
|
|
78
|
+
* DS-->>S: Error
|
|
79
|
+
* S-->>D: Promise rejects
|
|
80
|
+
* end
|
|
81
|
+
*/
|
|
82
|
+
protected initialize(): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Cleanup method to release resources when the dispatcher is no longer needed
|
|
85
|
+
*/
|
|
86
|
+
cleanup(): void;
|
|
87
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TypeORMEventSubscriber = void 0;
|
|
13
|
+
const typeorm_1 = require("typeorm");
|
|
14
|
+
const core_1 = require("@decaf-ts/core");
|
|
15
|
+
const db_decorators_1 = require("@decaf-ts/db-decorators");
|
|
16
|
+
const decorator_validation_1 = require("@decaf-ts/decorator-validation");
|
|
17
|
+
/**
|
|
18
|
+
* @description TypeORM event subscriber that forwards entity lifecycle events to the adapter.
|
|
19
|
+
* @summary Listens for insert, update, and remove events emitted by TypeORM and notifies the Decaf.ts adapter so that observers can be updated accordingly.
|
|
20
|
+
* @param {TypeORMAdapter} adapter The TypeORM adapter used to propagate events and look up metadata.
|
|
21
|
+
* @class
|
|
22
|
+
* @example
|
|
23
|
+
* // Registering the subscriber when creating a DataSource
|
|
24
|
+
* // dataSourceOptions.subscribers = [new TypeORMEventSubscriber(adapter)];
|
|
25
|
+
*
|
|
26
|
+
* @mermaid
|
|
27
|
+
* sequenceDiagram
|
|
28
|
+
* participant TypeORM
|
|
29
|
+
* participant Subscriber as TypeORMEventSubscriber
|
|
30
|
+
* participant Adapter as TypeORMAdapter
|
|
31
|
+
* participant Observers
|
|
32
|
+
*
|
|
33
|
+
* TypeORM->>Subscriber: afterInsert(entity)
|
|
34
|
+
* Subscriber->>Adapter: updateObservers(table, CREATE, [id])
|
|
35
|
+
* Adapter->>Observers: notify(table, CREATE, [id])
|
|
36
|
+
*
|
|
37
|
+
* TypeORM->>Subscriber: afterUpdate(event)
|
|
38
|
+
* Subscriber->>Adapter: updateObservers(table, UPDATE, [id])
|
|
39
|
+
* Adapter->>Observers: notify(table, UPDATE, [id])
|
|
40
|
+
*
|
|
41
|
+
* TypeORM->>Subscriber: afterRemove(event)
|
|
42
|
+
* Subscriber->>Adapter: updateObservers(table, DELETE, [id])
|
|
43
|
+
* Adapter->>Observers: notify(table, DELETE, [id])
|
|
44
|
+
*/
|
|
45
|
+
let TypeORMEventSubscriber = class TypeORMEventSubscriber {
|
|
46
|
+
constructor(handler) {
|
|
47
|
+
this.handler = handler;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* @description Handles post-insert events.
|
|
51
|
+
* @summary Notifies observers about a create operation for the inserted entity.
|
|
52
|
+
* @param {InsertEvent<any>} event The TypeORM insert event.
|
|
53
|
+
* @return {Promise<any>|void} A promise when async or void otherwise.
|
|
54
|
+
*/
|
|
55
|
+
afterInsert(event) {
|
|
56
|
+
const constructor = decorator_validation_1.Model.get(event.entity.constructor.name);
|
|
57
|
+
if (!constructor)
|
|
58
|
+
throw new db_decorators_1.InternalError(`No registered model found for ${event.entity.constructor.name}`);
|
|
59
|
+
const tableName = core_1.Repository.table(constructor);
|
|
60
|
+
this.handler(tableName, db_decorators_1.OperationKeys.CREATE, [event.entityId]);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* @description Handles post-remove events.
|
|
64
|
+
* @summary Notifies observers about a delete operation for the removed entity.
|
|
65
|
+
* @param {RemoveEvent<any>} event The TypeORM remove event.
|
|
66
|
+
* @return {Promise<any>|void} A promise when async or void otherwise.
|
|
67
|
+
*/
|
|
68
|
+
afterRemove(event) {
|
|
69
|
+
const constructor = decorator_validation_1.Model.get(event.entity.constructor.name);
|
|
70
|
+
if (!constructor)
|
|
71
|
+
throw new db_decorators_1.InternalError(`No registered model found for ${event.entity.constructor.name}`);
|
|
72
|
+
const tableName = core_1.Repository.table(constructor);
|
|
73
|
+
this.handler(tableName, db_decorators_1.OperationKeys.DELETE, [event.entityId]);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* @description Handles post-update events.
|
|
77
|
+
* @summary Notifies observers about an update operation for the modified entity.
|
|
78
|
+
* @param {UpdateEvent<any>} event The TypeORM update event.
|
|
79
|
+
* @return {Promise<any>|void} A promise when async or void otherwise.
|
|
80
|
+
*/
|
|
81
|
+
afterUpdate(event) {
|
|
82
|
+
const constructor = decorator_validation_1.Model.get(event.databaseEntity.constructor.name);
|
|
83
|
+
if (!constructor)
|
|
84
|
+
throw new db_decorators_1.InternalError(`No registered model found for ${event.databaseEntity.constructor.name}`);
|
|
85
|
+
const tableName = core_1.Repository.table(constructor);
|
|
86
|
+
return this.handler(tableName, db_decorators_1.OperationKeys.UPDATE, [
|
|
87
|
+
event.entity["id"],
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
exports.TypeORMEventSubscriber = TypeORMEventSubscriber;
|
|
92
|
+
exports.TypeORMEventSubscriber = TypeORMEventSubscriber = __decorate([
|
|
93
|
+
(0, typeorm_1.EventSubscriber)(),
|
|
94
|
+
__metadata("design:paramtypes", [Function])
|
|
95
|
+
], TypeORMEventSubscriber);
|
|
96
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHlwZU9STUV2ZW50U3Vic2NyaWJlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9UeXBlT1JNRXZlbnRTdWJzY3JpYmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7OztBQUFBLHFDQU1pQjtBQUNqQix5Q0FBc0Q7QUFDdEQsMkRBQXVFO0FBQ3ZFLHlFQUF1RDtBQUV2RDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMkJHO0FBRUksSUFBTSxzQkFBc0IsR0FBNUIsTUFBTSxzQkFBc0I7SUFDakMsWUFDcUIsT0FJVjtRQUpVLFlBQU8sR0FBUCxPQUFPLENBSWpCO0lBQ1IsQ0FBQztJQUVKOzs7OztPQUtHO0lBQ0gsV0FBVyxDQUFDLEtBQXVCO1FBQ2pDLE1BQU0sV0FBVyxHQUFHLDRCQUFLLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzdELElBQUksQ0FBQyxXQUFXO1lBQ2QsTUFBTSxJQUFJLDZCQUFhLENBQ3JCLGlDQUFpQyxLQUFLLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FDakUsQ0FBQztRQUNKLE1BQU0sU0FBUyxHQUFHLGlCQUFVLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBRWhELElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLDZCQUFhLENBQUMsTUFBTSxFQUFFLENBQUMsS0FBSyxDQUFDLFFBQWUsQ0FBQyxDQUFDLENBQUM7SUFDekUsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsV0FBVyxDQUFDLEtBQXVCO1FBQ2pDLE1BQU0sV0FBVyxHQUFHLDRCQUFLLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzdELElBQUksQ0FBQyxXQUFXO1lBQ2QsTUFBTSxJQUFJLDZCQUFhLENBQ3JCLGlDQUFpQyxLQUFLLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FDakUsQ0FBQztRQUNKLE1BQU0sU0FBUyxHQUFHLGlCQUFVLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBRWhELElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLDZCQUFhLENBQUMsTUFBTSxFQUFFLENBQUMsS0FBSyxDQUFDLFFBQWUsQ0FBQyxDQUFDLENBQUM7SUFDekUsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsV0FBVyxDQUFDLEtBQXVCO1FBQ2pDLE1BQU0sV0FBVyxHQUFHLDRCQUFLLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3JFLElBQUksQ0FBQyxXQUFXO1lBQ2QsTUFBTSxJQUFJLDZCQUFhLENBQ3JCLGlDQUFpQyxLQUFLLENBQUMsY0FBYyxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FDekUsQ0FBQztRQUNKLE1BQU0sU0FBUyxHQUFHLGlCQUFVLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBRWhELE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsNkJBQWEsQ0FBQyxNQUFNLEVBQUU7WUFDbEQsS0FBSyxDQUFDLE1BQWMsQ0FBQyxJQUFJLENBQVE7U0FDbkMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztDQUNGLENBQUE7QUE3RFksd0RBQXNCO2lDQUF0QixzQkFBc0I7SUFEbEMsSUFBQSx5QkFBZSxHQUFFOztHQUNMLHNCQUFzQixDQTZEbEMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBFbnRpdHlTdWJzY3JpYmVySW50ZXJmYWNlLFxuICBFdmVudFN1YnNjcmliZXIsXG4gIEluc2VydEV2ZW50LFxuICBSZW1vdmVFdmVudCxcbiAgVXBkYXRlRXZlbnQsXG59IGZyb20gXCJ0eXBlb3JtXCI7XG5pbXBvcnQgeyBFdmVudElkcywgUmVwb3NpdG9yeSB9IGZyb20gXCJAZGVjYWYtdHMvY29yZVwiO1xuaW1wb3J0IHsgSW50ZXJuYWxFcnJvciwgT3BlcmF0aW9uS2V5cyB9IGZyb20gXCJAZGVjYWYtdHMvZGItZGVjb3JhdG9yc1wiO1xuaW1wb3J0IHsgTW9kZWwgfSBmcm9tIFwiQGRlY2FmLXRzL2RlY29yYXRvci12YWxpZGF0aW9uXCI7XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIFR5cGVPUk0gZXZlbnQgc3Vic2NyaWJlciB0aGF0IGZvcndhcmRzIGVudGl0eSBsaWZlY3ljbGUgZXZlbnRzIHRvIHRoZSBhZGFwdGVyLlxuICogQHN1bW1hcnkgTGlzdGVucyBmb3IgaW5zZXJ0LCB1cGRhdGUsIGFuZCByZW1vdmUgZXZlbnRzIGVtaXR0ZWQgYnkgVHlwZU9STSBhbmQgbm90aWZpZXMgdGhlIERlY2FmLnRzIGFkYXB0ZXIgc28gdGhhdCBvYnNlcnZlcnMgY2FuIGJlIHVwZGF0ZWQgYWNjb3JkaW5nbHkuXG4gKiBAcGFyYW0ge1R5cGVPUk1BZGFwdGVyfSBhZGFwdGVyIFRoZSBUeXBlT1JNIGFkYXB0ZXIgdXNlZCB0byBwcm9wYWdhdGUgZXZlbnRzIGFuZCBsb29rIHVwIG1ldGFkYXRhLlxuICogQGNsYXNzXG4gKiBAZXhhbXBsZVxuICogLy8gUmVnaXN0ZXJpbmcgdGhlIHN1YnNjcmliZXIgd2hlbiBjcmVhdGluZyBhIERhdGFTb3VyY2VcbiAqIC8vIGRhdGFTb3VyY2VPcHRpb25zLnN1YnNjcmliZXJzID0gW25ldyBUeXBlT1JNRXZlbnRTdWJzY3JpYmVyKGFkYXB0ZXIpXTtcbiAqXG4gKiBAbWVybWFpZFxuICogc2VxdWVuY2VEaWFncmFtXG4gKiAgIHBhcnRpY2lwYW50IFR5cGVPUk1cbiAqICAgcGFydGljaXBhbnQgU3Vic2NyaWJlciBhcyBUeXBlT1JNRXZlbnRTdWJzY3JpYmVyXG4gKiAgIHBhcnRpY2lwYW50IEFkYXB0ZXIgYXMgVHlwZU9STUFkYXB0ZXJcbiAqICAgcGFydGljaXBhbnQgT2JzZXJ2ZXJzXG4gKlxuICogICBUeXBlT1JNLT4+U3Vic2NyaWJlcjogYWZ0ZXJJbnNlcnQoZW50aXR5KVxuICogICBTdWJzY3JpYmVyLT4+QWRhcHRlcjogdXBkYXRlT2JzZXJ2ZXJzKHRhYmxlLCBDUkVBVEUsIFtpZF0pXG4gKiAgIEFkYXB0ZXItPj5PYnNlcnZlcnM6IG5vdGlmeSh0YWJsZSwgQ1JFQVRFLCBbaWRdKVxuICpcbiAqICAgVHlwZU9STS0+PlN1YnNjcmliZXI6IGFmdGVyVXBkYXRlKGV2ZW50KVxuICogICBTdWJzY3JpYmVyLT4+QWRhcHRlcjogdXBkYXRlT2JzZXJ2ZXJzKHRhYmxlLCBVUERBVEUsIFtpZF0pXG4gKiAgIEFkYXB0ZXItPj5PYnNlcnZlcnM6IG5vdGlmeSh0YWJsZSwgVVBEQVRFLCBbaWRdKVxuICpcbiAqICAgVHlwZU9STS0+PlN1YnNjcmliZXI6IGFmdGVyUmVtb3ZlKGV2ZW50KVxuICogICBTdWJzY3JpYmVyLT4+QWRhcHRlcjogdXBkYXRlT2JzZXJ2ZXJzKHRhYmxlLCBERUxFVEUsIFtpZF0pXG4gKiAgIEFkYXB0ZXItPj5PYnNlcnZlcnM6IG5vdGlmeSh0YWJsZSwgREVMRVRFLCBbaWRdKVxuICovXG5ARXZlbnRTdWJzY3JpYmVyKClcbmV4cG9ydCBjbGFzcyBUeXBlT1JNRXZlbnRTdWJzY3JpYmVyIGltcGxlbWVudHMgRW50aXR5U3Vic2NyaWJlckludGVyZmFjZSB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByb3RlY3RlZCByZWFkb25seSBoYW5kbGVyOiAoXG4gICAgICB0YWJsZU5hbWU6IHN0cmluZyxcbiAgICAgIG9wZXJhdGlvbjogT3BlcmF0aW9uS2V5cyxcbiAgICAgIGlkczogRXZlbnRJZHNcbiAgICApID0+IHZvaWRcbiAgKSB7fVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSGFuZGxlcyBwb3N0LWluc2VydCBldmVudHMuXG4gICAqIEBzdW1tYXJ5IE5vdGlmaWVzIG9ic2VydmVycyBhYm91dCBhIGNyZWF0ZSBvcGVyYXRpb24gZm9yIHRoZSBpbnNlcnRlZCBlbnRpdHkuXG4gICAqIEBwYXJhbSB7SW5zZXJ0RXZlbnQ8YW55Pn0gZXZlbnQgVGhlIFR5cGVPUk0gaW5zZXJ0IGV2ZW50LlxuICAgKiBAcmV0dXJuIHtQcm9taXNlPGFueT58dm9pZH0gQSBwcm9taXNlIHdoZW4gYXN5bmMgb3Igdm9pZCBvdGhlcndpc2UuXG4gICAqL1xuICBhZnRlckluc2VydChldmVudDogSW5zZXJ0RXZlbnQ8YW55Pik6IFByb21pc2U8YW55PiB8IHZvaWQge1xuICAgIGNvbnN0IGNvbnN0cnVjdG9yID0gTW9kZWwuZ2V0KGV2ZW50LmVudGl0eS5jb25zdHJ1Y3Rvci5uYW1lKTtcbiAgICBpZiAoIWNvbnN0cnVjdG9yKVxuICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoXG4gICAgICAgIGBObyByZWdpc3RlcmVkIG1vZGVsIGZvdW5kIGZvciAke2V2ZW50LmVudGl0eS5jb25zdHJ1Y3Rvci5uYW1lfWBcbiAgICAgICk7XG4gICAgY29uc3QgdGFibGVOYW1lID0gUmVwb3NpdG9yeS50YWJsZShjb25zdHJ1Y3Rvcik7XG5cbiAgICB0aGlzLmhhbmRsZXIodGFibGVOYW1lLCBPcGVyYXRpb25LZXlzLkNSRUFURSwgW2V2ZW50LmVudGl0eUlkIGFzIGFueV0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBIYW5kbGVzIHBvc3QtcmVtb3ZlIGV2ZW50cy5cbiAgICogQHN1bW1hcnkgTm90aWZpZXMgb2JzZXJ2ZXJzIGFib3V0IGEgZGVsZXRlIG9wZXJhdGlvbiBmb3IgdGhlIHJlbW92ZWQgZW50aXR5LlxuICAgKiBAcGFyYW0ge1JlbW92ZUV2ZW50PGFueT59IGV2ZW50IFRoZSBUeXBlT1JNIHJlbW92ZSBldmVudC5cbiAgICogQHJldHVybiB7UHJvbWlzZTxhbnk+fHZvaWR9IEEgcHJvbWlzZSB3aGVuIGFzeW5jIG9yIHZvaWQgb3RoZXJ3aXNlLlxuICAgKi9cbiAgYWZ0ZXJSZW1vdmUoZXZlbnQ6IFJlbW92ZUV2ZW50PGFueT4pOiBQcm9taXNlPGFueT4gfCB2b2lkIHtcbiAgICBjb25zdCBjb25zdHJ1Y3RvciA9IE1vZGVsLmdldChldmVudC5lbnRpdHkuY29uc3RydWN0b3IubmFtZSk7XG4gICAgaWYgKCFjb25zdHJ1Y3RvcilcbiAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKFxuICAgICAgICBgTm8gcmVnaXN0ZXJlZCBtb2RlbCBmb3VuZCBmb3IgJHtldmVudC5lbnRpdHkuY29uc3RydWN0b3IubmFtZX1gXG4gICAgICApO1xuICAgIGNvbnN0IHRhYmxlTmFtZSA9IFJlcG9zaXRvcnkudGFibGUoY29uc3RydWN0b3IpO1xuXG4gICAgdGhpcy5oYW5kbGVyKHRhYmxlTmFtZSwgT3BlcmF0aW9uS2V5cy5ERUxFVEUsIFtldmVudC5lbnRpdHlJZCBhcyBhbnldKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSGFuZGxlcyBwb3N0LXVwZGF0ZSBldmVudHMuXG4gICAqIEBzdW1tYXJ5IE5vdGlmaWVzIG9ic2VydmVycyBhYm91dCBhbiB1cGRhdGUgb3BlcmF0aW9uIGZvciB0aGUgbW9kaWZpZWQgZW50aXR5LlxuICAgKiBAcGFyYW0ge1VwZGF0ZUV2ZW50PGFueT59IGV2ZW50IFRoZSBUeXBlT1JNIHVwZGF0ZSBldmVudC5cbiAgICogQHJldHVybiB7UHJvbWlzZTxhbnk+fHZvaWR9IEEgcHJvbWlzZSB3aGVuIGFzeW5jIG9yIHZvaWQgb3RoZXJ3aXNlLlxuICAgKi9cbiAgYWZ0ZXJVcGRhdGUoZXZlbnQ6IFVwZGF0ZUV2ZW50PGFueT4pOiBQcm9taXNlPGFueT4gfCB2b2lkIHtcbiAgICBjb25zdCBjb25zdHJ1Y3RvciA9IE1vZGVsLmdldChldmVudC5kYXRhYmFzZUVudGl0eS5jb25zdHJ1Y3Rvci5uYW1lKTtcbiAgICBpZiAoIWNvbnN0cnVjdG9yKVxuICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoXG4gICAgICAgIGBObyByZWdpc3RlcmVkIG1vZGVsIGZvdW5kIGZvciAke2V2ZW50LmRhdGFiYXNlRW50aXR5LmNvbnN0cnVjdG9yLm5hbWV9YFxuICAgICAgKTtcbiAgICBjb25zdCB0YWJsZU5hbWUgPSBSZXBvc2l0b3J5LnRhYmxlKGNvbnN0cnVjdG9yKTtcblxuICAgIHJldHVybiB0aGlzLmhhbmRsZXIodGFibGVOYW1lLCBPcGVyYXRpb25LZXlzLlVQREFURSwgW1xuICAgICAgKGV2ZW50LmVudGl0eSBhcyBhbnkpW1wiaWRcIl0gYXMgYW55LFxuICAgIF0pO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { EntitySubscriberInterface, InsertEvent, RemoveEvent, UpdateEvent } from "typeorm";
|
|
2
|
+
import { EventIds } from "@decaf-ts/core";
|
|
3
|
+
import { OperationKeys } from "@decaf-ts/db-decorators";
|
|
4
|
+
/**
|
|
5
|
+
* @description TypeORM event subscriber that forwards entity lifecycle events to the adapter.
|
|
6
|
+
* @summary Listens for insert, update, and remove events emitted by TypeORM and notifies the Decaf.ts adapter so that observers can be updated accordingly.
|
|
7
|
+
* @param {TypeORMAdapter} adapter The TypeORM adapter used to propagate events and look up metadata.
|
|
8
|
+
* @class
|
|
9
|
+
* @example
|
|
10
|
+
* // Registering the subscriber when creating a DataSource
|
|
11
|
+
* // dataSourceOptions.subscribers = [new TypeORMEventSubscriber(adapter)];
|
|
12
|
+
*
|
|
13
|
+
* @mermaid
|
|
14
|
+
* sequenceDiagram
|
|
15
|
+
* participant TypeORM
|
|
16
|
+
* participant Subscriber as TypeORMEventSubscriber
|
|
17
|
+
* participant Adapter as TypeORMAdapter
|
|
18
|
+
* participant Observers
|
|
19
|
+
*
|
|
20
|
+
* TypeORM->>Subscriber: afterInsert(entity)
|
|
21
|
+
* Subscriber->>Adapter: updateObservers(table, CREATE, [id])
|
|
22
|
+
* Adapter->>Observers: notify(table, CREATE, [id])
|
|
23
|
+
*
|
|
24
|
+
* TypeORM->>Subscriber: afterUpdate(event)
|
|
25
|
+
* Subscriber->>Adapter: updateObservers(table, UPDATE, [id])
|
|
26
|
+
* Adapter->>Observers: notify(table, UPDATE, [id])
|
|
27
|
+
*
|
|
28
|
+
* TypeORM->>Subscriber: afterRemove(event)
|
|
29
|
+
* Subscriber->>Adapter: updateObservers(table, DELETE, [id])
|
|
30
|
+
* Adapter->>Observers: notify(table, DELETE, [id])
|
|
31
|
+
*/
|
|
32
|
+
export declare class TypeORMEventSubscriber implements EntitySubscriberInterface {
|
|
33
|
+
protected readonly handler: (tableName: string, operation: OperationKeys, ids: EventIds) => void;
|
|
34
|
+
constructor(handler: (tableName: string, operation: OperationKeys, ids: EventIds) => void);
|
|
35
|
+
/**
|
|
36
|
+
* @description Handles post-insert events.
|
|
37
|
+
* @summary Notifies observers about a create operation for the inserted entity.
|
|
38
|
+
* @param {InsertEvent<any>} event The TypeORM insert event.
|
|
39
|
+
* @return {Promise<any>|void} A promise when async or void otherwise.
|
|
40
|
+
*/
|
|
41
|
+
afterInsert(event: InsertEvent<any>): Promise<any> | void;
|
|
42
|
+
/**
|
|
43
|
+
* @description Handles post-remove events.
|
|
44
|
+
* @summary Notifies observers about a delete operation for the removed entity.
|
|
45
|
+
* @param {RemoveEvent<any>} event The TypeORM remove event.
|
|
46
|
+
* @return {Promise<any>|void} A promise when async or void otherwise.
|
|
47
|
+
*/
|
|
48
|
+
afterRemove(event: RemoveEvent<any>): Promise<any> | void;
|
|
49
|
+
/**
|
|
50
|
+
* @description Handles post-update events.
|
|
51
|
+
* @summary Notifies observers about an update operation for the modified entity.
|
|
52
|
+
* @param {UpdateEvent<any>} event The TypeORM update event.
|
|
53
|
+
* @return {Promise<any>|void} A promise when async or void otherwise.
|
|
54
|
+
*/
|
|
55
|
+
afterUpdate(event: UpdateEvent<any>): Promise<any> | void;
|
|
56
|
+
}
|