@outbox-event-bus/postgres-drizzle-outbox 1.0.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.
@@ -0,0 +1,482 @@
1
+ import * as drizzle_orm_pg_core0 from "drizzle-orm/pg-core";
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+ import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
4
+
5
+ //#region ../../core/src/errors/errors.d.ts
6
+ interface OutboxErrorContext {
7
+ event?: BusEvent | FailedBusEvent;
8
+ cause?: unknown;
9
+ [key: string]: unknown;
10
+ }
11
+ declare abstract class OutboxError<TContext extends OutboxErrorContext = OutboxErrorContext> extends Error {
12
+ context?: TContext | undefined;
13
+ constructor(message: string, name: string, context?: TContext);
14
+ }
15
+ //#endregion
16
+ //#region ../../core/src/types/types.d.ts
17
+ type BusEvent<T extends string = string, P = unknown> = {
18
+ id: string;
19
+ type: T;
20
+ payload: P;
21
+ occurredAt: Date;
22
+ metadata?: Record<string, unknown>;
23
+ };
24
+ type FailedBusEvent<T extends string = string, P = unknown> = BusEvent<T, P> & {
25
+ error?: string;
26
+ retryCount: number;
27
+ lastAttemptAt?: Date;
28
+ };
29
+ type ErrorHandler = (error: OutboxError) => void;
30
+ //#endregion
31
+ //#region ../../core/src/types/interfaces.d.ts
32
+ interface IOutbox<TTransaction> {
33
+ publish: (events: BusEvent[], transaction?: TTransaction) => Promise<void>;
34
+ start: (handler: (event: BusEvent) => Promise<void>, onError: ErrorHandler) => void;
35
+ stop: () => Promise<void>;
36
+ getFailedEvents: () => Promise<FailedBusEvent[]>;
37
+ retryEvents: (eventIds: string[]) => Promise<void>;
38
+ }
39
+ interface OutboxConfig {
40
+ maxRetries?: number;
41
+ baseBackoffMs?: number;
42
+ pollIntervalMs?: number;
43
+ batchSize?: number;
44
+ processingTimeoutMs?: number;
45
+ maxErrorBackoffMs?: number;
46
+ }
47
+ //#endregion
48
+ //#region src/schema.d.ts
49
+ declare const outboxEvents: drizzle_orm_pg_core0.PgTableWithColumns<{
50
+ name: "outbox_events";
51
+ schema: undefined;
52
+ columns: {
53
+ id: drizzle_orm_pg_core0.PgColumn<{
54
+ name: "id";
55
+ tableName: "outbox_events";
56
+ dataType: "string";
57
+ columnType: "PgUUID";
58
+ data: string;
59
+ driverParam: string;
60
+ notNull: true;
61
+ hasDefault: false;
62
+ isPrimaryKey: true;
63
+ isAutoincrement: false;
64
+ hasRuntimeDefault: false;
65
+ enumValues: undefined;
66
+ baseColumn: never;
67
+ identity: undefined;
68
+ generated: undefined;
69
+ }, {}, {}>;
70
+ type: drizzle_orm_pg_core0.PgColumn<{
71
+ name: "type";
72
+ tableName: "outbox_events";
73
+ dataType: "string";
74
+ columnType: "PgText";
75
+ data: string;
76
+ driverParam: string;
77
+ notNull: true;
78
+ hasDefault: false;
79
+ isPrimaryKey: false;
80
+ isAutoincrement: false;
81
+ hasRuntimeDefault: false;
82
+ enumValues: [string, ...string[]];
83
+ baseColumn: never;
84
+ identity: undefined;
85
+ generated: undefined;
86
+ }, {}, {}>;
87
+ payload: drizzle_orm_pg_core0.PgColumn<{
88
+ name: "payload";
89
+ tableName: "outbox_events";
90
+ dataType: "json";
91
+ columnType: "PgJsonb";
92
+ data: unknown;
93
+ driverParam: unknown;
94
+ notNull: true;
95
+ hasDefault: false;
96
+ isPrimaryKey: false;
97
+ isAutoincrement: false;
98
+ hasRuntimeDefault: false;
99
+ enumValues: undefined;
100
+ baseColumn: never;
101
+ identity: undefined;
102
+ generated: undefined;
103
+ }, {}, {}>;
104
+ occurredAt: drizzle_orm_pg_core0.PgColumn<{
105
+ name: "occurred_at";
106
+ tableName: "outbox_events";
107
+ dataType: "date";
108
+ columnType: "PgTimestamp";
109
+ data: Date;
110
+ driverParam: string;
111
+ notNull: true;
112
+ hasDefault: false;
113
+ isPrimaryKey: false;
114
+ isAutoincrement: false;
115
+ hasRuntimeDefault: false;
116
+ enumValues: undefined;
117
+ baseColumn: never;
118
+ identity: undefined;
119
+ generated: undefined;
120
+ }, {}, {}>;
121
+ status: drizzle_orm_pg_core0.PgColumn<{
122
+ name: "status";
123
+ tableName: "outbox_events";
124
+ dataType: "string";
125
+ columnType: "PgEnumColumn";
126
+ data: "created" | "active" | "completed" | "failed";
127
+ driverParam: string;
128
+ notNull: true;
129
+ hasDefault: true;
130
+ isPrimaryKey: false;
131
+ isAutoincrement: false;
132
+ hasRuntimeDefault: false;
133
+ enumValues: ["created", "active", "completed", "failed"];
134
+ baseColumn: never;
135
+ identity: undefined;
136
+ generated: undefined;
137
+ }, {}, {}>;
138
+ retryCount: drizzle_orm_pg_core0.PgColumn<{
139
+ name: "retry_count";
140
+ tableName: "outbox_events";
141
+ dataType: "number";
142
+ columnType: "PgInteger";
143
+ data: number;
144
+ driverParam: string | number;
145
+ notNull: true;
146
+ hasDefault: true;
147
+ isPrimaryKey: false;
148
+ isAutoincrement: false;
149
+ hasRuntimeDefault: false;
150
+ enumValues: undefined;
151
+ baseColumn: never;
152
+ identity: undefined;
153
+ generated: undefined;
154
+ }, {}, {}>;
155
+ lastError: drizzle_orm_pg_core0.PgColumn<{
156
+ name: "last_error";
157
+ tableName: "outbox_events";
158
+ dataType: "string";
159
+ columnType: "PgText";
160
+ data: string;
161
+ driverParam: string;
162
+ notNull: false;
163
+ hasDefault: false;
164
+ isPrimaryKey: false;
165
+ isAutoincrement: false;
166
+ hasRuntimeDefault: false;
167
+ enumValues: [string, ...string[]];
168
+ baseColumn: never;
169
+ identity: undefined;
170
+ generated: undefined;
171
+ }, {}, {}>;
172
+ nextRetryAt: drizzle_orm_pg_core0.PgColumn<{
173
+ name: "next_retry_at";
174
+ tableName: "outbox_events";
175
+ dataType: "date";
176
+ columnType: "PgTimestamp";
177
+ data: Date;
178
+ driverParam: string;
179
+ notNull: false;
180
+ hasDefault: false;
181
+ isPrimaryKey: false;
182
+ isAutoincrement: false;
183
+ hasRuntimeDefault: false;
184
+ enumValues: undefined;
185
+ baseColumn: never;
186
+ identity: undefined;
187
+ generated: undefined;
188
+ }, {}, {}>;
189
+ createdOn: drizzle_orm_pg_core0.PgColumn<{
190
+ name: "created_on";
191
+ tableName: "outbox_events";
192
+ dataType: "date";
193
+ columnType: "PgTimestamp";
194
+ data: Date;
195
+ driverParam: string;
196
+ notNull: true;
197
+ hasDefault: true;
198
+ isPrimaryKey: false;
199
+ isAutoincrement: false;
200
+ hasRuntimeDefault: false;
201
+ enumValues: undefined;
202
+ baseColumn: never;
203
+ identity: undefined;
204
+ generated: undefined;
205
+ }, {}, {}>;
206
+ startedOn: drizzle_orm_pg_core0.PgColumn<{
207
+ name: "started_on";
208
+ tableName: "outbox_events";
209
+ dataType: "date";
210
+ columnType: "PgTimestamp";
211
+ data: Date;
212
+ driverParam: string;
213
+ notNull: false;
214
+ hasDefault: false;
215
+ isPrimaryKey: false;
216
+ isAutoincrement: false;
217
+ hasRuntimeDefault: false;
218
+ enumValues: undefined;
219
+ baseColumn: never;
220
+ identity: undefined;
221
+ generated: undefined;
222
+ }, {}, {}>;
223
+ completedOn: drizzle_orm_pg_core0.PgColumn<{
224
+ name: "completed_on";
225
+ tableName: "outbox_events";
226
+ dataType: "date";
227
+ columnType: "PgTimestamp";
228
+ data: Date;
229
+ driverParam: string;
230
+ notNull: false;
231
+ hasDefault: false;
232
+ isPrimaryKey: false;
233
+ isAutoincrement: false;
234
+ hasRuntimeDefault: false;
235
+ enumValues: undefined;
236
+ baseColumn: never;
237
+ identity: undefined;
238
+ generated: undefined;
239
+ }, {}, {}>;
240
+ keepAlive: drizzle_orm_pg_core0.PgColumn<{
241
+ name: "keep_alive";
242
+ tableName: "outbox_events";
243
+ dataType: "date";
244
+ columnType: "PgTimestamp";
245
+ data: Date;
246
+ driverParam: string;
247
+ notNull: false;
248
+ hasDefault: false;
249
+ isPrimaryKey: false;
250
+ isAutoincrement: false;
251
+ hasRuntimeDefault: false;
252
+ enumValues: undefined;
253
+ baseColumn: never;
254
+ identity: undefined;
255
+ generated: undefined;
256
+ }, {}, {}>;
257
+ expireInSeconds: drizzle_orm_pg_core0.PgColumn<{
258
+ name: "expire_in_seconds";
259
+ tableName: "outbox_events";
260
+ dataType: "number";
261
+ columnType: "PgInteger";
262
+ data: number;
263
+ driverParam: string | number;
264
+ notNull: true;
265
+ hasDefault: true;
266
+ isPrimaryKey: false;
267
+ isAutoincrement: false;
268
+ hasRuntimeDefault: false;
269
+ enumValues: undefined;
270
+ baseColumn: never;
271
+ identity: undefined;
272
+ generated: undefined;
273
+ }, {}, {}>;
274
+ };
275
+ dialect: "pg";
276
+ }>;
277
+ declare const outboxEventsArchive: drizzle_orm_pg_core0.PgTableWithColumns<{
278
+ name: "outbox_events_archive";
279
+ schema: undefined;
280
+ columns: {
281
+ id: drizzle_orm_pg_core0.PgColumn<{
282
+ name: "id";
283
+ tableName: "outbox_events_archive";
284
+ dataType: "string";
285
+ columnType: "PgUUID";
286
+ data: string;
287
+ driverParam: string;
288
+ notNull: true;
289
+ hasDefault: false;
290
+ isPrimaryKey: true;
291
+ isAutoincrement: false;
292
+ hasRuntimeDefault: false;
293
+ enumValues: undefined;
294
+ baseColumn: never;
295
+ identity: undefined;
296
+ generated: undefined;
297
+ }, {}, {}>;
298
+ type: drizzle_orm_pg_core0.PgColumn<{
299
+ name: "type";
300
+ tableName: "outbox_events_archive";
301
+ dataType: "string";
302
+ columnType: "PgText";
303
+ data: string;
304
+ driverParam: string;
305
+ notNull: true;
306
+ hasDefault: false;
307
+ isPrimaryKey: false;
308
+ isAutoincrement: false;
309
+ hasRuntimeDefault: false;
310
+ enumValues: [string, ...string[]];
311
+ baseColumn: never;
312
+ identity: undefined;
313
+ generated: undefined;
314
+ }, {}, {}>;
315
+ payload: drizzle_orm_pg_core0.PgColumn<{
316
+ name: "payload";
317
+ tableName: "outbox_events_archive";
318
+ dataType: "json";
319
+ columnType: "PgJsonb";
320
+ data: unknown;
321
+ driverParam: unknown;
322
+ notNull: true;
323
+ hasDefault: false;
324
+ isPrimaryKey: false;
325
+ isAutoincrement: false;
326
+ hasRuntimeDefault: false;
327
+ enumValues: undefined;
328
+ baseColumn: never;
329
+ identity: undefined;
330
+ generated: undefined;
331
+ }, {}, {}>;
332
+ occurredAt: drizzle_orm_pg_core0.PgColumn<{
333
+ name: "occurred_at";
334
+ tableName: "outbox_events_archive";
335
+ dataType: "date";
336
+ columnType: "PgTimestamp";
337
+ data: Date;
338
+ driverParam: string;
339
+ notNull: true;
340
+ hasDefault: false;
341
+ isPrimaryKey: false;
342
+ isAutoincrement: false;
343
+ hasRuntimeDefault: false;
344
+ enumValues: undefined;
345
+ baseColumn: never;
346
+ identity: undefined;
347
+ generated: undefined;
348
+ }, {}, {}>;
349
+ status: drizzle_orm_pg_core0.PgColumn<{
350
+ name: "status";
351
+ tableName: "outbox_events_archive";
352
+ dataType: "string";
353
+ columnType: "PgEnumColumn";
354
+ data: "created" | "active" | "completed" | "failed";
355
+ driverParam: string;
356
+ notNull: true;
357
+ hasDefault: false;
358
+ isPrimaryKey: false;
359
+ isAutoincrement: false;
360
+ hasRuntimeDefault: false;
361
+ enumValues: ["created", "active", "completed", "failed"];
362
+ baseColumn: never;
363
+ identity: undefined;
364
+ generated: undefined;
365
+ }, {}, {}>;
366
+ retryCount: drizzle_orm_pg_core0.PgColumn<{
367
+ name: "retry_count";
368
+ tableName: "outbox_events_archive";
369
+ dataType: "number";
370
+ columnType: "PgInteger";
371
+ data: number;
372
+ driverParam: string | number;
373
+ notNull: true;
374
+ hasDefault: false;
375
+ isPrimaryKey: false;
376
+ isAutoincrement: false;
377
+ hasRuntimeDefault: false;
378
+ enumValues: undefined;
379
+ baseColumn: never;
380
+ identity: undefined;
381
+ generated: undefined;
382
+ }, {}, {}>;
383
+ lastError: drizzle_orm_pg_core0.PgColumn<{
384
+ name: "last_error";
385
+ tableName: "outbox_events_archive";
386
+ dataType: "string";
387
+ columnType: "PgText";
388
+ data: string;
389
+ driverParam: string;
390
+ notNull: false;
391
+ hasDefault: false;
392
+ isPrimaryKey: false;
393
+ isAutoincrement: false;
394
+ hasRuntimeDefault: false;
395
+ enumValues: [string, ...string[]];
396
+ baseColumn: never;
397
+ identity: undefined;
398
+ generated: undefined;
399
+ }, {}, {}>;
400
+ createdOn: drizzle_orm_pg_core0.PgColumn<{
401
+ name: "created_on";
402
+ tableName: "outbox_events_archive";
403
+ dataType: "date";
404
+ columnType: "PgTimestamp";
405
+ data: Date;
406
+ driverParam: string;
407
+ notNull: true;
408
+ hasDefault: false;
409
+ isPrimaryKey: false;
410
+ isAutoincrement: false;
411
+ hasRuntimeDefault: false;
412
+ enumValues: undefined;
413
+ baseColumn: never;
414
+ identity: undefined;
415
+ generated: undefined;
416
+ }, {}, {}>;
417
+ startedOn: drizzle_orm_pg_core0.PgColumn<{
418
+ name: "started_on";
419
+ tableName: "outbox_events_archive";
420
+ dataType: "date";
421
+ columnType: "PgTimestamp";
422
+ data: Date;
423
+ driverParam: string;
424
+ notNull: false;
425
+ hasDefault: false;
426
+ isPrimaryKey: false;
427
+ isAutoincrement: false;
428
+ hasRuntimeDefault: false;
429
+ enumValues: undefined;
430
+ baseColumn: never;
431
+ identity: undefined;
432
+ generated: undefined;
433
+ }, {}, {}>;
434
+ completedOn: drizzle_orm_pg_core0.PgColumn<{
435
+ name: "completed_on";
436
+ tableName: "outbox_events_archive";
437
+ dataType: "date";
438
+ columnType: "PgTimestamp";
439
+ data: Date;
440
+ driverParam: string;
441
+ notNull: true;
442
+ hasDefault: false;
443
+ isPrimaryKey: false;
444
+ isAutoincrement: false;
445
+ hasRuntimeDefault: false;
446
+ enumValues: undefined;
447
+ baseColumn: never;
448
+ identity: undefined;
449
+ generated: undefined;
450
+ }, {}, {}>;
451
+ };
452
+ dialect: "pg";
453
+ }>;
454
+ //#endregion
455
+ //#region src/postgres-drizzle-outbox.d.ts
456
+ interface PostgresDrizzleOutboxConfig extends OutboxConfig {
457
+ db: PostgresJsDatabase<Record<string, unknown>>;
458
+ getTransaction?: (() => PostgresJsDatabase<Record<string, unknown>> | undefined) | undefined;
459
+ tables?: {
460
+ outboxEvents: typeof outboxEvents;
461
+ outboxEventsArchive: typeof outboxEventsArchive;
462
+ };
463
+ }
464
+ declare class PostgresDrizzleOutbox implements IOutbox<PostgresJsDatabase<Record<string, unknown>>> {
465
+ private readonly config;
466
+ private readonly poller;
467
+ constructor(config: PostgresDrizzleOutboxConfig);
468
+ publish(events: BusEvent[], transaction?: PostgresJsDatabase<Record<string, unknown>>): Promise<void>;
469
+ getFailedEvents(): Promise<FailedBusEvent[]>;
470
+ retryEvents(eventIds: string[]): Promise<void>;
471
+ start(handler: (event: BusEvent) => Promise<void>, onError: ErrorHandler): void;
472
+ stop(): Promise<void>;
473
+ private processBatch;
474
+ }
475
+ //#endregion
476
+ //#region src/transaction-storage.d.ts
477
+ declare const drizzleTransactionStorage: AsyncLocalStorage<PostgresJsDatabase<Record<string, unknown>>>;
478
+ declare function withDrizzleTransaction<T>(db: PostgresJsDatabase<Record<string, unknown>>, fn: (tx: PostgresJsDatabase<Record<string, unknown>>) => Promise<T>): Promise<T>;
479
+ declare function getDrizzleTransaction(): () => PostgresJsDatabase<Record<string, unknown>> | undefined;
480
+ //#endregion
481
+ export { PostgresDrizzleOutbox, PostgresDrizzleOutboxConfig, drizzleTransactionStorage, getDrizzleTransaction, withDrizzleTransaction };
482
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../core/src/errors/errors.ts","../../../core/src/types/types.ts","../../../core/src/types/interfaces.ts","../src/schema.ts","../src/postgres-drizzle-outbox.ts","../src/transaction-storage.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.mjs ADDED
@@ -0,0 +1,159 @@
1
+ import { and, eq, inArray, lt, or, sql } from "drizzle-orm";
2
+ import { EventStatus, PollingService, formatErrorMessage, reportEventError } from "outbox-event-bus";
3
+ import { integer, jsonb, pgEnum, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
4
+ import { AsyncLocalStorage } from "node:async_hooks";
5
+
6
+ //#region src/schema.ts
7
+ const outboxStatusEnum = pgEnum("outbox_status", [
8
+ "created",
9
+ "active",
10
+ "completed",
11
+ "failed"
12
+ ]);
13
+ const outboxEvents = pgTable("outbox_events", {
14
+ id: uuid("id").primaryKey(),
15
+ type: text("type").notNull(),
16
+ payload: jsonb("payload").notNull(),
17
+ occurredAt: timestamp("occurred_at").notNull(),
18
+ status: outboxStatusEnum("status").notNull().default("created"),
19
+ retryCount: integer("retry_count").notNull().default(0),
20
+ lastError: text("last_error"),
21
+ nextRetryAt: timestamp("next_retry_at"),
22
+ createdOn: timestamp("created_on").notNull().defaultNow(),
23
+ startedOn: timestamp("started_on"),
24
+ completedOn: timestamp("completed_on"),
25
+ keepAlive: timestamp("keep_alive"),
26
+ expireInSeconds: integer("expire_in_seconds").notNull().default(300)
27
+ });
28
+ const outboxEventsArchive = pgTable("outbox_events_archive", {
29
+ id: uuid("id").primaryKey(),
30
+ type: text("type").notNull(),
31
+ payload: jsonb("payload").notNull(),
32
+ occurredAt: timestamp("occurred_at").notNull(),
33
+ status: outboxStatusEnum("status").notNull(),
34
+ retryCount: integer("retry_count").notNull(),
35
+ lastError: text("last_error"),
36
+ createdOn: timestamp("created_on").notNull(),
37
+ startedOn: timestamp("started_on"),
38
+ completedOn: timestamp("completed_on").notNull()
39
+ });
40
+
41
+ //#endregion
42
+ //#region src/postgres-drizzle-outbox.ts
43
+ var PostgresDrizzleOutbox = class {
44
+ config;
45
+ poller;
46
+ constructor(config) {
47
+ this.config = {
48
+ batchSize: config.batchSize ?? 50,
49
+ pollIntervalMs: config.pollIntervalMs ?? 1e3,
50
+ maxRetries: config.maxRetries ?? 5,
51
+ baseBackoffMs: config.baseBackoffMs ?? 1e3,
52
+ processingTimeoutMs: config.processingTimeoutMs ?? 3e4,
53
+ maxErrorBackoffMs: config.maxErrorBackoffMs ?? 3e4,
54
+ db: config.db,
55
+ getTransaction: config.getTransaction,
56
+ tables: config.tables ?? {
57
+ outboxEvents,
58
+ outboxEventsArchive
59
+ }
60
+ };
61
+ this.poller = new PollingService({
62
+ pollIntervalMs: this.config.pollIntervalMs,
63
+ baseBackoffMs: this.config.baseBackoffMs,
64
+ maxErrorBackoffMs: this.config.maxErrorBackoffMs,
65
+ processBatch: (handler) => this.processBatch(handler)
66
+ });
67
+ }
68
+ async publish(events, transaction) {
69
+ await (transaction ?? this.config.getTransaction?.() ?? this.config.db).insert(this.config.tables.outboxEvents).values(events.map((event) => ({
70
+ id: event.id,
71
+ type: event.type,
72
+ payload: event.payload,
73
+ occurredAt: event.occurredAt,
74
+ status: EventStatus.CREATED
75
+ })));
76
+ }
77
+ async getFailedEvents() {
78
+ return (await this.config.db.select().from(this.config.tables.outboxEvents).where(eq(this.config.tables.outboxEvents.status, EventStatus.FAILED)).orderBy(sql`${this.config.tables.outboxEvents.occurredAt} DESC`).limit(100)).map((event) => {
79
+ const failedEvent = {
80
+ id: event.id,
81
+ type: event.type,
82
+ payload: event.payload,
83
+ occurredAt: event.occurredAt,
84
+ retryCount: event.retryCount
85
+ };
86
+ if (event.lastError) failedEvent.error = event.lastError;
87
+ if (event.startedOn) failedEvent.lastAttemptAt = event.startedOn;
88
+ return failedEvent;
89
+ });
90
+ }
91
+ async retryEvents(eventIds) {
92
+ await this.config.db.update(this.config.tables.outboxEvents).set({
93
+ status: EventStatus.CREATED,
94
+ retryCount: 0,
95
+ nextRetryAt: null,
96
+ lastError: null
97
+ }).where(inArray(this.config.tables.outboxEvents.id, eventIds));
98
+ }
99
+ start(handler, onError) {
100
+ this.poller.start(handler, onError);
101
+ }
102
+ async stop() {
103
+ await this.poller.stop();
104
+ }
105
+ async processBatch(handler) {
106
+ await this.config.db.transaction(async (transaction) => {
107
+ const now = /* @__PURE__ */ new Date();
108
+ const events = await transaction.select().from(this.config.tables.outboxEvents).where(or(eq(this.config.tables.outboxEvents.status, EventStatus.CREATED), and(eq(this.config.tables.outboxEvents.status, EventStatus.FAILED), lt(this.config.tables.outboxEvents.retryCount, this.config.maxRetries), lt(this.config.tables.outboxEvents.nextRetryAt, now)), and(eq(this.config.tables.outboxEvents.status, EventStatus.ACTIVE), lt(this.config.tables.outboxEvents.keepAlive, sql`${now.toISOString()}::timestamp - make_interval(secs => ${this.config.tables.outboxEvents.expireInSeconds})`)))).limit(this.config.batchSize).for("update", { skipLocked: true });
109
+ if (events.length === 0) return;
110
+ const eventIds = events.map((event) => event.id);
111
+ await transaction.update(this.config.tables.outboxEvents).set({
112
+ status: EventStatus.ACTIVE,
113
+ startedOn: now,
114
+ keepAlive: now
115
+ }).where(inArray(this.config.tables.outboxEvents.id, eventIds));
116
+ for (const event of events) try {
117
+ await handler(event);
118
+ await transaction.insert(this.config.tables.outboxEventsArchive).values({
119
+ id: event.id,
120
+ type: event.type,
121
+ payload: event.payload,
122
+ occurredAt: event.occurredAt,
123
+ status: EventStatus.COMPLETED,
124
+ retryCount: event.retryCount,
125
+ createdOn: event.createdOn,
126
+ startedOn: now,
127
+ completedOn: /* @__PURE__ */ new Date()
128
+ });
129
+ await transaction.delete(this.config.tables.outboxEvents).where(eq(this.config.tables.outboxEvents.id, event.id));
130
+ } catch (error) {
131
+ const retryCount = event.retryCount + 1;
132
+ reportEventError(this.poller.onError, error, event, retryCount, this.config.maxRetries);
133
+ const delay = this.poller.calculateBackoff(retryCount);
134
+ await transaction.update(this.config.tables.outboxEvents).set({
135
+ status: EventStatus.FAILED,
136
+ retryCount,
137
+ lastError: formatErrorMessage(error),
138
+ nextRetryAt: new Date(Date.now() + delay)
139
+ }).where(eq(this.config.tables.outboxEvents.id, event.id));
140
+ }
141
+ });
142
+ }
143
+ };
144
+
145
+ //#endregion
146
+ //#region src/transaction-storage.ts
147
+ const drizzleTransactionStorage = new AsyncLocalStorage();
148
+ async function withDrizzleTransaction(db, fn) {
149
+ return db.transaction(async (tx) => {
150
+ return drizzleTransactionStorage.run(tx, () => fn(tx));
151
+ });
152
+ }
153
+ function getDrizzleTransaction() {
154
+ return () => drizzleTransactionStorage.getStore();
155
+ }
156
+
157
+ //#endregion
158
+ export { PostgresDrizzleOutbox, drizzleTransactionStorage, getDrizzleTransaction, withDrizzleTransaction };
159
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["failedEvent: FailedBusEvent","error: unknown"],"sources":["../src/schema.ts","../src/postgres-drizzle-outbox.ts","../src/transaction-storage.ts"],"sourcesContent":["import { integer, jsonb, pgEnum, pgTable, text, timestamp, uuid } from \"drizzle-orm/pg-core\"\n\nexport const outboxStatusEnum = pgEnum(\"outbox_status\", [\n \"created\",\n \"active\",\n \"completed\",\n \"failed\",\n])\n\nexport const outboxEvents = pgTable(\"outbox_events\", {\n id: uuid(\"id\").primaryKey(),\n type: text(\"type\").notNull(),\n payload: jsonb(\"payload\").notNull(),\n occurredAt: timestamp(\"occurred_at\").notNull(),\n status: outboxStatusEnum(\"status\").notNull().default(\"created\"),\n retryCount: integer(\"retry_count\").notNull().default(0),\n lastError: text(\"last_error\"),\n nextRetryAt: timestamp(\"next_retry_at\"),\n createdOn: timestamp(\"created_on\").notNull().defaultNow(),\n startedOn: timestamp(\"started_on\"),\n completedOn: timestamp(\"completed_on\"),\n keepAlive: timestamp(\"keep_alive\"),\n expireInSeconds: integer(\"expire_in_seconds\").notNull().default(300),\n})\n\nexport const outboxEventsArchive = pgTable(\"outbox_events_archive\", {\n id: uuid(\"id\").primaryKey(),\n type: text(\"type\").notNull(),\n payload: jsonb(\"payload\").notNull(),\n occurredAt: timestamp(\"occurred_at\").notNull(),\n status: outboxStatusEnum(\"status\").notNull(),\n retryCount: integer(\"retry_count\").notNull(),\n lastError: text(\"last_error\"),\n createdOn: timestamp(\"created_on\").notNull(),\n startedOn: timestamp(\"started_on\"),\n completedOn: timestamp(\"completed_on\").notNull(),\n})\n","import { and, eq, inArray, lt, or, sql } from \"drizzle-orm\"\nimport type { PostgresJsDatabase } from \"drizzle-orm/postgres-js\"\nimport {\n type BusEvent,\n type ErrorHandler,\n EventStatus,\n type FailedBusEvent,\n formatErrorMessage,\n type IOutbox,\n type OutboxConfig,\n PollingService,\n reportEventError,\n} from \"outbox-event-bus\"\nimport { outboxEvents, outboxEventsArchive } from \"./schema\"\n\nexport interface PostgresDrizzleOutboxConfig extends OutboxConfig {\n db: PostgresJsDatabase<Record<string, unknown>>\n getTransaction?: (() => PostgresJsDatabase<Record<string, unknown>> | undefined) | undefined\n tables?: {\n outboxEvents: typeof outboxEvents\n outboxEventsArchive: typeof outboxEventsArchive\n }\n}\n\nexport class PostgresDrizzleOutbox implements IOutbox<PostgresJsDatabase<Record<string, unknown>>> {\n private readonly config: Required<PostgresDrizzleOutboxConfig>\n private readonly poller: PollingService\n\n constructor(config: PostgresDrizzleOutboxConfig) {\n this.config = {\n batchSize: config.batchSize ?? 50,\n pollIntervalMs: config.pollIntervalMs ?? 1000,\n maxRetries: config.maxRetries ?? 5,\n baseBackoffMs: config.baseBackoffMs ?? 1000,\n processingTimeoutMs: config.processingTimeoutMs ?? 30000,\n maxErrorBackoffMs: config.maxErrorBackoffMs ?? 30000,\n db: config.db,\n getTransaction: config.getTransaction,\n tables: config.tables ?? {\n outboxEvents,\n outboxEventsArchive,\n },\n }\n\n this.poller = new PollingService({\n pollIntervalMs: this.config.pollIntervalMs,\n baseBackoffMs: this.config.baseBackoffMs,\n maxErrorBackoffMs: this.config.maxErrorBackoffMs,\n processBatch: (handler) => this.processBatch(handler),\n })\n }\n\n async publish(\n events: BusEvent[],\n transaction?: PostgresJsDatabase<Record<string, unknown>>\n ): Promise<void> {\n const executor = transaction ?? this.config.getTransaction?.() ?? this.config.db\n\n await executor.insert(this.config.tables.outboxEvents).values(\n events.map((event) => ({\n id: event.id,\n type: event.type,\n payload: event.payload,\n occurredAt: event.occurredAt,\n status: EventStatus.CREATED,\n }))\n )\n }\n\n async getFailedEvents(): Promise<FailedBusEvent[]> {\n const events = await this.config.db\n .select()\n .from(this.config.tables.outboxEvents)\n .where(eq(this.config.tables.outboxEvents.status, EventStatus.FAILED))\n .orderBy(sql`${this.config.tables.outboxEvents.occurredAt} DESC`)\n .limit(100)\n\n return events.map((event) => {\n const failedEvent: FailedBusEvent = {\n id: event.id,\n type: event.type,\n payload: event.payload as any,\n occurredAt: event.occurredAt,\n retryCount: event.retryCount,\n }\n if (event.lastError) failedEvent.error = event.lastError\n if (event.startedOn) failedEvent.lastAttemptAt = event.startedOn\n return failedEvent\n })\n }\n\n async retryEvents(eventIds: string[]): Promise<void> {\n await this.config.db\n .update(this.config.tables.outboxEvents)\n .set({\n status: EventStatus.CREATED,\n retryCount: 0,\n nextRetryAt: null,\n lastError: null,\n })\n .where(inArray(this.config.tables.outboxEvents.id, eventIds))\n }\n\n start(handler: (event: BusEvent) => Promise<void>, onError: ErrorHandler): void {\n this.poller.start(handler, onError)\n }\n\n async stop(): Promise<void> {\n await this.poller.stop()\n }\n\n private async processBatch(handler: (event: BusEvent) => Promise<void>) {\n await this.config.db.transaction(async (transaction) => {\n const now = new Date()\n\n // Select events that are:\n // 1. New (status = created)\n // 2. Failed but can be retried (retry count < max AND retry time has passed)\n // 3. Active but stuck/timed out (keepAlive is older than now - expireInSeconds)\n const events = await transaction\n .select()\n .from(this.config.tables.outboxEvents)\n .where(\n or(\n eq(this.config.tables.outboxEvents.status, EventStatus.CREATED),\n and(\n eq(this.config.tables.outboxEvents.status, EventStatus.FAILED),\n lt(this.config.tables.outboxEvents.retryCount, this.config.maxRetries),\n lt(this.config.tables.outboxEvents.nextRetryAt, now)\n ),\n and(\n eq(this.config.tables.outboxEvents.status, EventStatus.ACTIVE),\n // Check if event is stuck: keepAlive is older than (now - expireInSeconds)\n // This uses PostgreSQL's make_interval to subtract expireInSeconds from current timestamp\n lt(\n this.config.tables.outboxEvents.keepAlive,\n sql`${now.toISOString()}::timestamp - make_interval(secs => ${this.config.tables.outboxEvents.expireInSeconds})`\n )\n )\n )\n )\n .limit(this.config.batchSize)\n .for(\"update\", { skipLocked: true })\n\n if (events.length === 0) return\n\n const eventIds = events.map((event) => event.id)\n\n await transaction\n .update(this.config.tables.outboxEvents)\n .set({\n status: EventStatus.ACTIVE,\n startedOn: now,\n keepAlive: now,\n })\n .where(inArray(this.config.tables.outboxEvents.id, eventIds))\n\n for (const event of events) {\n try {\n await handler(event)\n // Archive successful event immediately\n await transaction.insert(this.config.tables.outboxEventsArchive).values({\n id: event.id,\n type: event.type,\n payload: event.payload,\n occurredAt: event.occurredAt,\n status: EventStatus.COMPLETED,\n retryCount: event.retryCount,\n createdOn: event.createdOn,\n startedOn: now,\n completedOn: new Date(),\n })\n await transaction\n .delete(this.config.tables.outboxEvents)\n .where(eq(this.config.tables.outboxEvents.id, event.id))\n } catch (error: unknown) {\n const retryCount = event.retryCount + 1\n reportEventError(this.poller.onError, error, event, retryCount, this.config.maxRetries)\n\n // Mark this specific event as failed\n const delay = this.poller.calculateBackoff(retryCount)\n await transaction\n .update(this.config.tables.outboxEvents)\n .set({\n status: EventStatus.FAILED,\n retryCount,\n lastError: formatErrorMessage(error),\n nextRetryAt: new Date(Date.now() + delay),\n })\n .where(eq(this.config.tables.outboxEvents.id, event.id))\n }\n }\n })\n }\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\"\nimport type { PostgresJsDatabase } from \"drizzle-orm/postgres-js\"\n\nexport const drizzleTransactionStorage = new AsyncLocalStorage<\n PostgresJsDatabase<Record<string, unknown>>\n>()\n\nexport async function withDrizzleTransaction<T>(\n db: PostgresJsDatabase<Record<string, unknown>>,\n fn: (tx: PostgresJsDatabase<Record<string, unknown>>) => Promise<T>\n): Promise<T> {\n return db.transaction(async (tx) => {\n return drizzleTransactionStorage.run(tx, () => fn(tx))\n })\n}\n\nexport function getDrizzleTransaction(): () =>\n | PostgresJsDatabase<Record<string, unknown>>\n | undefined {\n return () => drizzleTransactionStorage.getStore()\n}\n"],"mappings":"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"}