@nestjs-transactional/outbox-typeorm 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +347 -0
  3. package/dist/entity/event-publication-archive.entity.d.ts +24 -0
  4. package/dist/entity/event-publication-archive.entity.js +84 -0
  5. package/dist/entity/event-publication-archive.entity.js.map +1 -0
  6. package/dist/entity/event-publication.entity.d.ts +33 -0
  7. package/dist/entity/event-publication.entity.js +98 -0
  8. package/dist/entity/event-publication.entity.js.map +1 -0
  9. package/dist/index.d.ts +9 -0
  10. package/dist/index.js +25 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/migrations/1700000000000-create-event-publication.d.ts +18 -0
  13. package/dist/migrations/1700000000000-create-event-publication.js +26 -0
  14. package/dist/migrations/1700000000000-create-event-publication.js.map +1 -0
  15. package/dist/module/outbox-typeorm.module.d.ts +272 -0
  16. package/dist/module/outbox-typeorm.module.js +335 -0
  17. package/dist/module/outbox-typeorm.module.js.map +1 -0
  18. package/dist/repository/typeorm-event-publication.repository.d.ts +50 -0
  19. package/dist/repository/typeorm-event-publication.repository.js +237 -0
  20. package/dist/repository/typeorm-event-publication.repository.js.map +1 -0
  21. package/dist/schema/event-publication-schema.d.ts +30 -0
  22. package/dist/schema/event-publication-schema.js +124 -0
  23. package/dist/schema/event-publication-schema.js.map +1 -0
  24. package/dist/schema/schema-initialization-options.d.ts +24 -0
  25. package/dist/schema/schema-initialization-options.js +10 -0
  26. package/dist/schema/schema-initialization-options.js.map +1 -0
  27. package/dist/schema/schema-initializer.d.ts +31 -0
  28. package/dist/schema/schema-initializer.js +72 -0
  29. package/dist/schema/schema-initializer.js.map +1 -0
  30. package/package.json +86 -0
@@ -0,0 +1,50 @@
1
+ import { PublicationStatus, type EventPublication, type EventPublicationRepository, type FindCompletedOptions, type FindFailedOptions, type NewEventPublication, type UpdateStatusOptions } from '@nestjs-transactional/outbox';
2
+ import { type DataSource } from 'typeorm';
3
+ /**
4
+ * TypeORM-backed implementation of
5
+ * {@link EventPublicationRepository}. Reads and writes go through the
6
+ * {@link EntityManager} bound to the ambient transaction (via
7
+ * `@nestjs-transactional/core`'s `AsyncLocalStorage`), so publication
8
+ * rows commit atomically with the business data.
9
+ *
10
+ * `findReadyForProcessing` returns ready rows in publication-date
11
+ * order WITHOUT per-row locking. The earlier design used
12
+ * `SELECT ... FOR UPDATE SKIP LOCKED` to give concurrent workers
13
+ * disjoint row sets; that approach was dropped because pessimistic
14
+ * locks require an enclosing transaction whose lifetime did not fit
15
+ * the worker's `find → tryClaim → invoke → finalize` flow (a
16
+ * transaction wide enough to hold the lock would have to wrap the
17
+ * listener invocation, which is unsafe for long-running listeners).
18
+ * Concurrent workers may now SEE the same rows; correctness comes
19
+ * from {@link tryClaim}, whose conditional `UPDATE` transitions
20
+ * `PUBLISHED`/`RESUBMITTED` → `PROCESSING` atomically and returns
21
+ * whether the row was actually claimed. A losing worker simply moves
22
+ * on. With small worker counts (typical: 1–3) the duplicate-fetch
23
+ * cost is negligible.
24
+ *
25
+ * The repository is dataSource-aware: pass a non-default
26
+ * `dataSourceName` when the application uses multiple DataSources
27
+ * (`'billing'`, `'inventory'`, ...). The fallback {@link DataSource}
28
+ * passed to the constructor is used when `getCurrentEntityManager` is
29
+ * called outside any active transaction — typical for operator-facing
30
+ * read APIs.
31
+ */
32
+ export declare class TypeOrmEventPublicationRepository implements EventPublicationRepository {
33
+ private readonly dataSource;
34
+ private readonly dataSourceName;
35
+ constructor(dataSource: DataSource, dataSourceName?: string);
36
+ private get em();
37
+ createAll(inputs: NewEventPublication[]): Promise<EventPublication[]>;
38
+ findById(id: string): Promise<EventPublication | null>;
39
+ updateStatus(id: string, status: PublicationStatus, options?: UpdateStatusOptions): Promise<void>;
40
+ tryClaim(id: string): Promise<boolean>;
41
+ findReadyForProcessing(limit: number): Promise<EventPublication[]>;
42
+ findStale(beforeDate: Date, statuses: PublicationStatus[]): Promise<EventPublication[]>;
43
+ findCompleted(options?: FindCompletedOptions): Promise<EventPublication[]>;
44
+ findIncomplete(): Promise<EventPublication[]>;
45
+ findFailed(options?: FindFailedOptions): Promise<EventPublication[]>;
46
+ deleteCompleted(olderThan?: Date): Promise<number>;
47
+ archiveCompleted(id: string): Promise<void>;
48
+ delete(id: string): Promise<void>;
49
+ }
50
+ //# sourceMappingURL=typeorm-event-publication.repository.d.ts.map
@@ -0,0 +1,237 @@
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.TypeOrmEventPublicationRepository = void 0;
13
+ const node_crypto_1 = require("node:crypto");
14
+ const common_1 = require("@nestjs/common");
15
+ const outbox_1 = require("@nestjs-transactional/outbox");
16
+ const typeorm_1 = require("@nestjs-transactional/typeorm");
17
+ const typeorm_2 = require("typeorm");
18
+ const event_publication_archive_entity_1 = require("../entity/event-publication-archive.entity");
19
+ const event_publication_entity_1 = require("../entity/event-publication.entity");
20
+ /**
21
+ * TypeORM-backed implementation of
22
+ * {@link EventPublicationRepository}. Reads and writes go through the
23
+ * {@link EntityManager} bound to the ambient transaction (via
24
+ * `@nestjs-transactional/core`'s `AsyncLocalStorage`), so publication
25
+ * rows commit atomically with the business data.
26
+ *
27
+ * `findReadyForProcessing` returns ready rows in publication-date
28
+ * order WITHOUT per-row locking. The earlier design used
29
+ * `SELECT ... FOR UPDATE SKIP LOCKED` to give concurrent workers
30
+ * disjoint row sets; that approach was dropped because pessimistic
31
+ * locks require an enclosing transaction whose lifetime did not fit
32
+ * the worker's `find → tryClaim → invoke → finalize` flow (a
33
+ * transaction wide enough to hold the lock would have to wrap the
34
+ * listener invocation, which is unsafe for long-running listeners).
35
+ * Concurrent workers may now SEE the same rows; correctness comes
36
+ * from {@link tryClaim}, whose conditional `UPDATE` transitions
37
+ * `PUBLISHED`/`RESUBMITTED` → `PROCESSING` atomically and returns
38
+ * whether the row was actually claimed. A losing worker simply moves
39
+ * on. With small worker counts (typical: 1–3) the duplicate-fetch
40
+ * cost is negligible.
41
+ *
42
+ * The repository is dataSource-aware: pass a non-default
43
+ * `dataSourceName` when the application uses multiple DataSources
44
+ * (`'billing'`, `'inventory'`, ...). The fallback {@link DataSource}
45
+ * passed to the constructor is used when `getCurrentEntityManager` is
46
+ * called outside any active transaction — typical for operator-facing
47
+ * read APIs.
48
+ */
49
+ let TypeOrmEventPublicationRepository = class TypeOrmEventPublicationRepository {
50
+ dataSource;
51
+ dataSourceName;
52
+ constructor(dataSource, dataSourceName = 'default') {
53
+ this.dataSource = dataSource;
54
+ this.dataSourceName = dataSourceName;
55
+ }
56
+ get em() {
57
+ return (0, typeorm_1.getCurrentEntityManager)(this.dataSourceName, this.dataSource);
58
+ }
59
+ async createAll(inputs) {
60
+ const entities = inputs.map((input) => {
61
+ const entity = new event_publication_entity_1.EventPublicationEntity();
62
+ entity.id = (0, node_crypto_1.randomUUID)();
63
+ entity.listenerId = input.listenerId;
64
+ entity.eventType = input.eventType;
65
+ entity.serializedEvent = input.serializedEvent;
66
+ entity.publicationDate = input.publicationDate ?? new Date();
67
+ entity.status = outbox_1.PublicationStatus.PUBLISHED;
68
+ entity.completionDate = null;
69
+ entity.lastResubmissionDate = null;
70
+ entity.completionAttempts = 0;
71
+ entity.failureReason = null;
72
+ return entity;
73
+ });
74
+ await this.em.save(event_publication_entity_1.EventPublicationEntity, entities);
75
+ return entities.map(toDomain);
76
+ }
77
+ async findById(id) {
78
+ const entity = await this.em.findOne(event_publication_entity_1.EventPublicationEntity, { where: { id } });
79
+ return entity ? toDomain(entity) : null;
80
+ }
81
+ async updateStatus(id, status, options = {}) {
82
+ // Build the SET clause inline: TypeORM's `.set()` accepts raw SQL
83
+ // fragments as `() => string`, so we can bump `completionAttempts`
84
+ // atomically without a separate `em.increment()` call.
85
+ await this.em
86
+ .createQueryBuilder()
87
+ .update(event_publication_entity_1.EventPublicationEntity)
88
+ .set({
89
+ status,
90
+ ...(options.completionDate !== undefined
91
+ ? { completionDate: options.completionDate }
92
+ : {}),
93
+ ...(options.failureReason !== undefined ? { failureReason: options.failureReason } : {}),
94
+ ...(options.lastResubmissionDate !== undefined
95
+ ? { lastResubmissionDate: options.lastResubmissionDate }
96
+ : {}),
97
+ ...(options.incrementAttempts
98
+ ? { completionAttempts: () => 'completion_attempts + 1' }
99
+ : {}),
100
+ })
101
+ .where('id = :id', { id })
102
+ .execute();
103
+ }
104
+ async tryClaim(id) {
105
+ // Atomic conditional update: only transitions the row when the
106
+ // current status is one the worker is allowed to claim. `affected`
107
+ // tells us whether we won the race — losers get 0 and back off.
108
+ const result = await this.em
109
+ .createQueryBuilder()
110
+ .update(event_publication_entity_1.EventPublicationEntity)
111
+ .set({
112
+ status: outbox_1.PublicationStatus.PROCESSING,
113
+ completionAttempts: () => 'completion_attempts + 1',
114
+ })
115
+ .where('id = :id AND status IN (:...statuses)', {
116
+ id,
117
+ statuses: [outbox_1.PublicationStatus.PUBLISHED, outbox_1.PublicationStatus.RESUBMITTED],
118
+ })
119
+ .execute();
120
+ return (result.affected ?? 0) > 0;
121
+ }
122
+ async findReadyForProcessing(limit) {
123
+ const entities = await this.em
124
+ .createQueryBuilder(event_publication_entity_1.EventPublicationEntity, 'p')
125
+ .where('p.status IN (:...statuses)', {
126
+ statuses: [outbox_1.PublicationStatus.PUBLISHED, outbox_1.PublicationStatus.RESUBMITTED],
127
+ })
128
+ .orderBy('p.publication_date', 'ASC')
129
+ .limit(limit)
130
+ .getMany();
131
+ return entities.map(toDomain);
132
+ }
133
+ async findStale(beforeDate, statuses) {
134
+ if (statuses.length === 0) {
135
+ return [];
136
+ }
137
+ const entities = await this.em.find(event_publication_entity_1.EventPublicationEntity, {
138
+ where: {
139
+ status: (0, typeorm_2.In)(statuses),
140
+ publicationDate: (0, typeorm_2.LessThan)(beforeDate),
141
+ },
142
+ });
143
+ return entities.map(toDomain);
144
+ }
145
+ async findCompleted(options) {
146
+ const where = {
147
+ status: outbox_1.PublicationStatus.COMPLETED,
148
+ };
149
+ if (options?.olderThan !== undefined) {
150
+ where.completionDate = (0, typeorm_2.LessThan)(options.olderThan);
151
+ }
152
+ const entities = await this.em.find(event_publication_entity_1.EventPublicationEntity, {
153
+ where,
154
+ ...(options?.limit !== undefined ? { take: options.limit } : {}),
155
+ order: { completionDate: 'DESC' },
156
+ });
157
+ return entities.map(toDomain);
158
+ }
159
+ async findIncomplete() {
160
+ const entities = await this.em.find(event_publication_entity_1.EventPublicationEntity, {
161
+ where: { status: (0, typeorm_2.Not)(outbox_1.PublicationStatus.COMPLETED) },
162
+ });
163
+ return entities.map(toDomain);
164
+ }
165
+ async findFailed(options) {
166
+ const where = {
167
+ status: outbox_1.PublicationStatus.FAILED,
168
+ };
169
+ if (options?.minAge !== undefined) {
170
+ where.publicationDate = (0, typeorm_2.LessThan)(new Date(Date.now() - options.minAge));
171
+ }
172
+ if (options?.maxAttempts !== undefined) {
173
+ where.completionAttempts = (0, typeorm_2.LessThanOrEqual)(options.maxAttempts);
174
+ }
175
+ const entities = await this.em.find(event_publication_entity_1.EventPublicationEntity, { where });
176
+ return entities.map(toDomain);
177
+ }
178
+ async deleteCompleted(olderThan) {
179
+ const where = {
180
+ status: outbox_1.PublicationStatus.COMPLETED,
181
+ };
182
+ if (olderThan !== undefined) {
183
+ where.completionDate = (0, typeorm_2.LessThan)(olderThan);
184
+ }
185
+ const result = await this.em.delete(event_publication_entity_1.EventPublicationEntity, where);
186
+ return result.affected ?? 0;
187
+ }
188
+ async archiveCompleted(id) {
189
+ // Deliberately does not open a nested TypeORM transaction — the
190
+ // ambient `@Transactional` scope (the processor always wraps the
191
+ // listener invocation in one) gives us atomicity between the
192
+ // archive insert and the hot-queue delete. If called outside a
193
+ // transaction, TypeORM executes both statements autocommit; the
194
+ // window of inconsistency is the round-trip between the two
195
+ // statements, which is acceptable for an archive operation.
196
+ const entity = await this.em.findOne(event_publication_entity_1.EventPublicationEntity, { where: { id } });
197
+ if (entity === null) {
198
+ throw new outbox_1.PublicationNotFoundError(id);
199
+ }
200
+ const archive = new event_publication_archive_entity_1.EventPublicationArchiveEntity();
201
+ archive.id = entity.id;
202
+ archive.listenerId = entity.listenerId;
203
+ archive.eventType = entity.eventType;
204
+ archive.serializedEvent = entity.serializedEvent;
205
+ archive.publicationDate = entity.publicationDate;
206
+ archive.status = entity.status;
207
+ archive.completionDate = entity.completionDate ?? new Date();
208
+ archive.lastResubmissionDate = entity.lastResubmissionDate;
209
+ archive.completionAttempts = entity.completionAttempts;
210
+ archive.failureReason = entity.failureReason;
211
+ await this.em.save(event_publication_archive_entity_1.EventPublicationArchiveEntity, archive);
212
+ await this.em.delete(event_publication_entity_1.EventPublicationEntity, { id });
213
+ }
214
+ async delete(id) {
215
+ await this.em.delete(event_publication_entity_1.EventPublicationEntity, { id });
216
+ }
217
+ };
218
+ exports.TypeOrmEventPublicationRepository = TypeOrmEventPublicationRepository;
219
+ exports.TypeOrmEventPublicationRepository = TypeOrmEventPublicationRepository = __decorate([
220
+ (0, common_1.Injectable)(),
221
+ __metadata("design:paramtypes", [Function, Object])
222
+ ], TypeOrmEventPublicationRepository);
223
+ function toDomain(entity) {
224
+ return {
225
+ id: entity.id,
226
+ listenerId: entity.listenerId,
227
+ eventType: entity.eventType,
228
+ serializedEvent: entity.serializedEvent,
229
+ publicationDate: entity.publicationDate,
230
+ status: entity.status,
231
+ completionDate: entity.completionDate,
232
+ lastResubmissionDate: entity.lastResubmissionDate,
233
+ completionAttempts: entity.completionAttempts,
234
+ failureReason: entity.failureReason,
235
+ };
236
+ }
237
+ //# sourceMappingURL=typeorm-event-publication.repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeorm-event-publication.repository.js","sourceRoot":"","sources":["../../src/repository/typeorm-event-publication.repository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { type QueryRunner } from 'typeorm';
2
+ /**
3
+ * Canonical table name for the hot queue. Exposed so downstream tools
4
+ * (e.g. custom queries, observability) can avoid hardcoding the string.
5
+ */
6
+ export declare const EVENT_PUBLICATION_TABLE = "event_publication";
7
+ /**
8
+ * Canonical table name for the archive trail. Used only by the
9
+ * `ARCHIVE` completion mode.
10
+ */
11
+ export declare const EVENT_PUBLICATION_ARCHIVE_TABLE = "event_publication_archive";
12
+ /**
13
+ * Create the hot and archive tables plus every index. Shared by
14
+ * {@link CreateEventPublication1700000000000} (the TypeORM migration)
15
+ * and `SchemaInitializer` (development-only auto-init) so the two paths
16
+ * cannot drift.
17
+ *
18
+ * Strict mode: fails if the tables already exist — callers are
19
+ * responsible for checking existence and skipping this call when
20
+ * needed.
21
+ */
22
+ export declare function applyEventPublicationSchema(qr: QueryRunner): Promise<void>;
23
+ /**
24
+ * Drop the archive first (to avoid referential surprises if a future
25
+ * revision adds a FK between the two) and then the hot table. Both
26
+ * drops are tolerant — `dropTable(..., true)` adds `IF EXISTS`, so
27
+ * running `down()` on a partially-applied schema still succeeds.
28
+ */
29
+ export declare function revertEventPublicationSchema(qr: QueryRunner): Promise<void>;
30
+ //# sourceMappingURL=event-publication-schema.d.ts.map
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EVENT_PUBLICATION_ARCHIVE_TABLE = exports.EVENT_PUBLICATION_TABLE = void 0;
4
+ exports.applyEventPublicationSchema = applyEventPublicationSchema;
5
+ exports.revertEventPublicationSchema = revertEventPublicationSchema;
6
+ const outbox_1 = require("@nestjs-transactional/outbox");
7
+ const typeorm_1 = require("typeorm");
8
+ /**
9
+ * Canonical table name for the hot queue. Exposed so downstream tools
10
+ * (e.g. custom queries, observability) can avoid hardcoding the string.
11
+ */
12
+ exports.EVENT_PUBLICATION_TABLE = 'event_publication';
13
+ /**
14
+ * Canonical table name for the archive trail. Used only by the
15
+ * `ARCHIVE` completion mode.
16
+ */
17
+ exports.EVENT_PUBLICATION_ARCHIVE_TABLE = 'event_publication_archive';
18
+ function buildHotTable() {
19
+ return new typeorm_1.Table({
20
+ name: exports.EVENT_PUBLICATION_TABLE,
21
+ columns: [
22
+ { name: 'id', type: 'uuid', isPrimary: true },
23
+ { name: 'listener_id', type: 'varchar', length: '512', isNullable: false },
24
+ { name: 'event_type', type: 'varchar', length: '256', isNullable: false },
25
+ { name: 'serialized_event', type: 'text', isNullable: false },
26
+ { name: 'publication_date', type: 'timestamptz', isNullable: false },
27
+ {
28
+ name: 'status',
29
+ type: 'varchar',
30
+ length: '32',
31
+ default: `'${outbox_1.PublicationStatus.PUBLISHED}'`,
32
+ isNullable: false,
33
+ },
34
+ { name: 'completion_date', type: 'timestamptz', isNullable: true },
35
+ { name: 'last_resubmission_date', type: 'timestamptz', isNullable: true },
36
+ { name: 'completion_attempts', type: 'int', default: 0, isNullable: false },
37
+ { name: 'failure_reason', type: 'text', isNullable: true },
38
+ ],
39
+ });
40
+ }
41
+ function buildArchiveTable() {
42
+ return new typeorm_1.Table({
43
+ name: exports.EVENT_PUBLICATION_ARCHIVE_TABLE,
44
+ columns: [
45
+ { name: 'id', type: 'uuid', isPrimary: true },
46
+ { name: 'listener_id', type: 'varchar', length: '512', isNullable: false },
47
+ { name: 'event_type', type: 'varchar', length: '256', isNullable: false },
48
+ { name: 'serialized_event', type: 'text', isNullable: false },
49
+ { name: 'publication_date', type: 'timestamptz', isNullable: false },
50
+ { name: 'status', type: 'varchar', length: '32', isNullable: false },
51
+ { name: 'completion_date', type: 'timestamptz', isNullable: false },
52
+ { name: 'last_resubmission_date', type: 'timestamptz', isNullable: true },
53
+ { name: 'completion_attempts', type: 'int', isNullable: false },
54
+ { name: 'failure_reason', type: 'text', isNullable: true },
55
+ ],
56
+ });
57
+ }
58
+ function buildHotIndexes() {
59
+ return [
60
+ new typeorm_1.TableIndex({
61
+ name: 'idx_event_publication_status_date',
62
+ columnNames: ['status', 'publication_date'],
63
+ }),
64
+ new typeorm_1.TableIndex({
65
+ name: 'idx_event_publication_status_listener',
66
+ columnNames: ['status', 'listener_id'],
67
+ }),
68
+ new typeorm_1.TableIndex({
69
+ name: 'idx_event_publication_event_type',
70
+ columnNames: ['event_type'],
71
+ }),
72
+ new typeorm_1.TableIndex({
73
+ name: 'idx_event_publication_completion_date',
74
+ columnNames: ['completion_date'],
75
+ }),
76
+ ];
77
+ }
78
+ function buildArchiveIndexes() {
79
+ return [
80
+ new typeorm_1.TableIndex({
81
+ name: 'idx_event_publication_archive_completion_date',
82
+ columnNames: ['completion_date'],
83
+ }),
84
+ new typeorm_1.TableIndex({
85
+ name: 'idx_event_publication_archive_listener',
86
+ columnNames: ['listener_id'],
87
+ }),
88
+ new typeorm_1.TableIndex({
89
+ name: 'idx_event_publication_archive_event_type',
90
+ columnNames: ['event_type'],
91
+ }),
92
+ ];
93
+ }
94
+ /**
95
+ * Create the hot and archive tables plus every index. Shared by
96
+ * {@link CreateEventPublication1700000000000} (the TypeORM migration)
97
+ * and `SchemaInitializer` (development-only auto-init) so the two paths
98
+ * cannot drift.
99
+ *
100
+ * Strict mode: fails if the tables already exist — callers are
101
+ * responsible for checking existence and skipping this call when
102
+ * needed.
103
+ */
104
+ async function applyEventPublicationSchema(qr) {
105
+ await qr.createTable(buildHotTable());
106
+ for (const index of buildHotIndexes()) {
107
+ await qr.createIndex(exports.EVENT_PUBLICATION_TABLE, index);
108
+ }
109
+ await qr.createTable(buildArchiveTable());
110
+ for (const index of buildArchiveIndexes()) {
111
+ await qr.createIndex(exports.EVENT_PUBLICATION_ARCHIVE_TABLE, index);
112
+ }
113
+ }
114
+ /**
115
+ * Drop the archive first (to avoid referential surprises if a future
116
+ * revision adds a FK between the two) and then the hot table. Both
117
+ * drops are tolerant — `dropTable(..., true)` adds `IF EXISTS`, so
118
+ * running `down()` on a partially-applied schema still succeeds.
119
+ */
120
+ async function revertEventPublicationSchema(qr) {
121
+ await qr.dropTable(exports.EVENT_PUBLICATION_ARCHIVE_TABLE, true);
122
+ await qr.dropTable(exports.EVENT_PUBLICATION_TABLE, true);
123
+ }
124
+ //# sourceMappingURL=event-publication-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-publication-schema.js","sourceRoot":"","sources":["../../src/schema/event-publication-schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Configuration for {@link SchemaInitializer}. Deliberately minimal —
3
+ * the initializer's sole job is to decide whether to create the
4
+ * `event_publication` schema at bootstrap time.
5
+ */
6
+ export interface SchemaInitializationOptions {
7
+ /**
8
+ * When `true`, the initializer creates the `event_publication` and
9
+ * `event_publication_archive` tables on application bootstrap if
10
+ * they are missing. When `false` (the default), bootstrap is a
11
+ * no-op and the user is expected to have applied the TypeORM
12
+ * migration shipped by this package (or their own equivalent).
13
+ *
14
+ * **Development only.** Production systems should apply schema
15
+ * changes through a reviewed migration step, never at process
16
+ * startup.
17
+ */
18
+ readonly enabled: boolean;
19
+ }
20
+ /** DI token for {@link SchemaInitializationOptions}. */
21
+ export declare const SCHEMA_INITIALIZATION_OPTIONS: unique symbol;
22
+ /** Safe defaults — auto-init off, migrations preferred. */
23
+ export declare const DEFAULT_SCHEMA_INITIALIZATION_OPTIONS: SchemaInitializationOptions;
24
+ //# sourceMappingURL=schema-initialization-options.d.ts.map
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_SCHEMA_INITIALIZATION_OPTIONS = exports.SCHEMA_INITIALIZATION_OPTIONS = void 0;
4
+ /** DI token for {@link SchemaInitializationOptions}. */
5
+ exports.SCHEMA_INITIALIZATION_OPTIONS = Symbol('SCHEMA_INITIALIZATION_OPTIONS');
6
+ /** Safe defaults — auto-init off, migrations preferred. */
7
+ exports.DEFAULT_SCHEMA_INITIALIZATION_OPTIONS = {
8
+ enabled: false,
9
+ };
10
+ //# sourceMappingURL=schema-initialization-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-initialization-options.js","sourceRoot":"","sources":["../../src/schema/schema-initialization-options.ts"],"names":[],"mappings":";;;AAoBA,wDAAwD;AAC3C,QAAA,6BAA6B,GAAG,MAAM,CAAC,+BAA+B,CAAC,CAAC;AAErF,2DAA2D;AAC9C,QAAA,qCAAqC,GAAgC;IAChF,OAAO,EAAE,KAAK;CACf,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { type OnApplicationBootstrap } from '@nestjs/common';
2
+ import type { DataSource } from 'typeorm';
3
+ import type { SchemaInitializationOptions } from './schema-initialization-options';
4
+ /**
5
+ * Development-time helper that creates the `event_publication` schema
6
+ * at application bootstrap, so developers can spin up a fresh app
7
+ * against an empty database without a separate migration step.
8
+ *
9
+ * **Not intended for production.** Production deployments should apply
10
+ * the schema via the TypeORM migration shipped by this package
11
+ * (`CreateEventPublication1700000000000`) or their own equivalent,
12
+ * and leave `enabled: false`.
13
+ *
14
+ * The equivalent Spring Modulith switch is
15
+ * `spring.modulith.events.jdbc.schema-initialization.enabled`.
16
+ */
17
+ export declare class SchemaInitializer implements OnApplicationBootstrap {
18
+ private readonly dataSource;
19
+ private readonly options;
20
+ private readonly logger;
21
+ constructor(dataSource: DataSource, options: SchemaInitializationOptions);
22
+ onApplicationBootstrap(): Promise<void>;
23
+ /**
24
+ * Check for the hot table via `to_regclass` — Postgres-specific and
25
+ * respects the current `search_path`. Returns `null` when the table
26
+ * is missing, a regclass name (as text after the `::text` cast) when
27
+ * it exists.
28
+ */
29
+ private hotTableExists;
30
+ }
31
+ //# sourceMappingURL=schema-initializer.d.ts.map
@@ -0,0 +1,72 @@
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
+ var SchemaInitializer_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.SchemaInitializer = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const event_publication_schema_1 = require("./event-publication-schema");
16
+ /**
17
+ * Development-time helper that creates the `event_publication` schema
18
+ * at application bootstrap, so developers can spin up a fresh app
19
+ * against an empty database without a separate migration step.
20
+ *
21
+ * **Not intended for production.** Production deployments should apply
22
+ * the schema via the TypeORM migration shipped by this package
23
+ * (`CreateEventPublication1700000000000`) or their own equivalent,
24
+ * and leave `enabled: false`.
25
+ *
26
+ * The equivalent Spring Modulith switch is
27
+ * `spring.modulith.events.jdbc.schema-initialization.enabled`.
28
+ */
29
+ let SchemaInitializer = SchemaInitializer_1 = class SchemaInitializer {
30
+ dataSource;
31
+ options;
32
+ logger = new common_1.Logger(SchemaInitializer_1.name);
33
+ constructor(dataSource, options) {
34
+ this.dataSource = dataSource;
35
+ this.options = options;
36
+ }
37
+ async onApplicationBootstrap() {
38
+ if (!this.options.enabled) {
39
+ return;
40
+ }
41
+ if (await this.hotTableExists()) {
42
+ this.logger.debug(`Table '${event_publication_schema_1.EVENT_PUBLICATION_TABLE}' already exists — skipping auto schema init`);
43
+ return;
44
+ }
45
+ this.logger.log(`Initialising '${event_publication_schema_1.EVENT_PUBLICATION_TABLE}' schema (development auto-init)`);
46
+ const queryRunner = this.dataSource.createQueryRunner();
47
+ try {
48
+ await queryRunner.connect();
49
+ await (0, event_publication_schema_1.applyEventPublicationSchema)(queryRunner);
50
+ this.logger.log(`'${event_publication_schema_1.EVENT_PUBLICATION_TABLE}' schema initialised`);
51
+ }
52
+ finally {
53
+ await queryRunner.release();
54
+ }
55
+ }
56
+ /**
57
+ * Check for the hot table via `to_regclass` — Postgres-specific and
58
+ * respects the current `search_path`. Returns `null` when the table
59
+ * is missing, a regclass name (as text after the `::text` cast) when
60
+ * it exists.
61
+ */
62
+ async hotTableExists() {
63
+ const rows = await this.dataSource.query(`SELECT to_regclass($1)::text AS exists`, [event_publication_schema_1.EVENT_PUBLICATION_TABLE]);
64
+ return rows[0]?.exists != null;
65
+ }
66
+ };
67
+ exports.SchemaInitializer = SchemaInitializer;
68
+ exports.SchemaInitializer = SchemaInitializer = SchemaInitializer_1 = __decorate([
69
+ (0, common_1.Injectable)(),
70
+ __metadata("design:paramtypes", [Function, Object])
71
+ ], SchemaInitializer);
72
+ //# sourceMappingURL=schema-initializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-initializer.js","sourceRoot":"","sources":["../../src/schema/schema-initializer.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAiF;AAGjF,yEAGoC;AAOpC;;;;;;;;;;;;GAYG;AAEI,IAAM,iBAAiB,yBAAvB,MAAM,iBAAiB;IAIT;IACA;IAJF,MAAM,GAAG,IAAI,eAAM,CAAC,mBAAiB,CAAC,IAAI,CAAC,CAAC;IAE7D,YACmB,UAAsB,EACtB,OAAoC;QADpC,eAAU,GAAV,UAAU,CAAY;QACtB,YAAO,GAAP,OAAO,CAA6B;IACpD,CAAC;IAEJ,KAAK,CAAC,sBAAsB;QAC1B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,IAAI,MAAM,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,UAAU,kDAAuB,8CAA8C,CAChF,CAAC;YACF,OAAO;QACT,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,GAAG,CACb,iBAAiB,kDAAuB,kCAAkC,CAC3E,CAAC;QAEF,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC;QACxD,IAAI,CAAC;YACH,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAA,sDAA2B,EAAC,WAAW,CAAC,CAAC;YAC/C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,kDAAuB,sBAAsB,CAAC,CAAC;QACrE,CAAC;gBAAS,CAAC;YACT,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,cAAc;QAC1B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CACtC,wCAAwC,EACxC,CAAC,kDAAuB,CAAC,CAC1B,CAAC;QACF,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,IAAI,CAAC;IACjC,CAAC;CACF,CAAA;AA/CY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;;GACA,iBAAiB,CA+C7B"}