@ahrowe/mongo 0.1.2 → 0.2.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.
- package/CHANGELOG.md +19 -0
- package/README.md +50 -20
- package/dist/db.d.ts +31 -13
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +177 -156
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/outboxRelay.d.ts +34 -5
- package/dist/outboxRelay.d.ts.map +1 -1
- package/dist/outboxRelay.js +75 -52
- package/dist/outboxRelay.js.map +1 -1
- package/dist/types/outbox.d.ts +14 -2
- package/dist/types/outbox.d.ts.map +1 -1
- package/dist/types/outbox.js +4 -1
- package/dist/types/outbox.js.map +1 -1
- package/docs/CLAUDE.md +116 -86
- package/package.json +5 -2
- package/dist/batchify.d.ts +0 -36
- package/dist/batchify.d.ts.map +0 -1
- package/dist/batchify.js +0 -57
- package/dist/batchify.js.map +0 -1
package/dist/outboxRelay.d.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
+
import { OutboxStatus } from './types/outbox.js';
|
|
1
2
|
export interface OutboxRelayOptions {
|
|
2
|
-
/** Unique per process/pod — identifies the lease holder so
|
|
3
|
+
/** Unique per process/pod — identifies the lease holder, so `stop` releases only its own rows. */
|
|
3
4
|
instanceId: string;
|
|
4
5
|
/** Idle poll interval when the outbox is empty (ms). Default 500. */
|
|
5
6
|
pollIntervalMs?: number;
|
|
6
7
|
/** Max rows claimed+dispatched per tick. Default 20. */
|
|
7
8
|
batchSize?: number;
|
|
8
|
-
/** How long a
|
|
9
|
+
/** How long a claimed row stays hidden from other claims (ms). Default 30_000. */
|
|
9
10
|
leaseMs?: number;
|
|
10
11
|
/** Attempts before a row is dead-lettered to `failed`. Default 10. */
|
|
11
12
|
maxAttempts?: number;
|
|
12
13
|
/** Backoff before a failed row becomes claimable again (ms). Default capped exponential. */
|
|
13
14
|
backoffMs?: (attempt: number) => number;
|
|
15
|
+
/** How long `done` rows are kept (ms). Default 7 days. `Infinity` keeps them forever. */
|
|
16
|
+
keepDoneMs?: number;
|
|
17
|
+
/** How long dead-lettered (`failed`) rows are kept for inspection or replay (ms). Default 90 days. `Infinity` keeps them forever. */
|
|
18
|
+
keepFailedMs?: number;
|
|
14
19
|
/** Start the background poll loop immediately (default true). Set false to drive `tick`/`drain` manually (tests). */
|
|
15
20
|
autoStart?: boolean;
|
|
16
21
|
}
|
|
@@ -22,13 +27,37 @@ export interface OutboxRelay {
|
|
|
22
27
|
/** Process pending rows until none are claimable; returns total processed. (Mainly for tests.) */
|
|
23
28
|
drain: () => Promise<number>;
|
|
24
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Due rows for collections this process has a service for. A claimed row whose lease ran out is
|
|
32
|
+
* due again, so it needs no condition of its own. A row for any other collection waits for a relay
|
|
33
|
+
* that has its listeners, instead of being marked done with nobody notified. Exported for the
|
|
34
|
+
* index test, not part of the public barrel.
|
|
35
|
+
*/
|
|
36
|
+
export declare const claimFilter: (now: Date, collectionNames: string[]) => {
|
|
37
|
+
collectionName: {
|
|
38
|
+
$in: string[];
|
|
39
|
+
};
|
|
40
|
+
status: OutboxStatus;
|
|
41
|
+
nextAttemptAt: {
|
|
42
|
+
$lte: Date;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Longest-due first. nextAttemptAt (equal to createdOn for a fresh row) is in the status index,
|
|
47
|
+
* so the rows come back pre-sorted. Sorting by createdOn instead read and sorted every due row in
|
|
48
|
+
* memory, once per claimed row.
|
|
49
|
+
*/
|
|
50
|
+
export declare const CLAIM_SORT: {
|
|
51
|
+
readonly nextAttemptAt: 1;
|
|
52
|
+
};
|
|
25
53
|
/**
|
|
26
54
|
* Drains the transactional outbox and dispatches each event to the in-process
|
|
27
55
|
* listeners registered on its source collection's bus.
|
|
28
56
|
*
|
|
29
|
-
* Restart-safe and multi-pod-safe:
|
|
30
|
-
*
|
|
31
|
-
* at-least-once (consumers must
|
|
57
|
+
* Restart-safe and multi-pod-safe: a claim is an atomic findOneAndUpdate that
|
|
58
|
+
* pushes the row's nextAttemptAt past a lease, so a row whose holder died is
|
|
59
|
+
* due again once the lease runs out. Delivery is at-least-once (consumers must
|
|
60
|
+
* be idempotent).
|
|
32
61
|
*/
|
|
33
62
|
export declare const startOutboxRelay: (options: OutboxRelayOptions) => OutboxRelay;
|
|
34
63
|
//# sourceMappingURL=outboxRelay.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outboxRelay.d.ts","sourceRoot":"","sources":["../src/outboxRelay.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"outboxRelay.d.ts","sourceRoot":"","sources":["../src/outboxRelay.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAkB,MAAM,mBAAmB,CAAC;AAQjE,MAAM,WAAW,kBAAkB;IACjC,kGAAkG;IAClG,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qIAAqI;IACrI,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qHAAqH;IACrH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,6FAA6F;IAC7F,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,6EAA6E;IAC7E,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5B,kGAAkG;IAClG,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9B;AASD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,IAAI,EAAE,iBAAiB,MAAM,EAAE;;;;;;;;CAI9D,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,UAAU;;CAAgC,CAAC;AAExD;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,kBAAkB,KAAG,WAkK9D,CAAC"}
|
package/dist/outboxRelay.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { getOutboxCollection, getServiceBus } from './db.js';
|
|
1
|
+
import { dispatchEvent, getOutboxCollection, getRegisteredCollections } from './db.js';
|
|
3
2
|
import { OutboxStatus } from './types/outbox.js';
|
|
4
3
|
const logger = {
|
|
5
4
|
info: console.info,
|
|
@@ -8,88 +7,110 @@ const logger = {
|
|
|
8
7
|
};
|
|
9
8
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
9
|
const defaultBackoff = (attempt) => Math.min(30000, 1000 * 2 ** Math.max(0, attempt - 1));
|
|
10
|
+
/** Rows past their retention are removed by the TTL index on `expireAt`. `null` keeps a row forever. */
|
|
11
|
+
const expiryAfter = (keepMs) => (Number.isFinite(keepMs) ? new Date(Date.now() + keepMs) : null);
|
|
12
|
+
/**
|
|
13
|
+
* Due rows for collections this process has a service for. A claimed row whose lease ran out is
|
|
14
|
+
* due again, so it needs no condition of its own. A row for any other collection waits for a relay
|
|
15
|
+
* that has its listeners, instead of being marked done with nobody notified. Exported for the
|
|
16
|
+
* index test, not part of the public barrel.
|
|
17
|
+
*/
|
|
18
|
+
export const claimFilter = (now, collectionNames) => ({
|
|
19
|
+
collectionName: { $in: collectionNames },
|
|
20
|
+
status: OutboxStatus.Pending,
|
|
21
|
+
nextAttemptAt: { $lte: now },
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Longest-due first. nextAttemptAt (equal to createdOn for a fresh row) is in the status index,
|
|
25
|
+
* so the rows come back pre-sorted. Sorting by createdOn instead read and sorted every due row in
|
|
26
|
+
* memory, once per claimed row.
|
|
27
|
+
*/
|
|
28
|
+
export const CLAIM_SORT = { nextAttemptAt: 1 };
|
|
11
29
|
/**
|
|
12
30
|
* Drains the transactional outbox and dispatches each event to the in-process
|
|
13
31
|
* listeners registered on its source collection's bus.
|
|
14
32
|
*
|
|
15
|
-
* Restart-safe and multi-pod-safe:
|
|
16
|
-
*
|
|
17
|
-
* at-least-once (consumers must
|
|
33
|
+
* Restart-safe and multi-pod-safe: a claim is an atomic findOneAndUpdate that
|
|
34
|
+
* pushes the row's nextAttemptAt past a lease, so a row whose holder died is
|
|
35
|
+
* due again once the lease runs out. Delivery is at-least-once (consumers must
|
|
36
|
+
* be idempotent).
|
|
18
37
|
*/
|
|
19
38
|
export const startOutboxRelay = (options) => {
|
|
20
|
-
const { instanceId, pollIntervalMs = 500, batchSize = 20, leaseMs = 30000, maxAttempts = 10, backoffMs = defaultBackoff, autoStart = true, } = options;
|
|
39
|
+
const { instanceId, pollIntervalMs = 500, batchSize = 20, leaseMs = 30000, maxAttempts = 10, backoffMs = defaultBackoff, keepDoneMs = 7 * 24 * 60 * 60 * 1000, keepFailedMs = 90 * 24 * 60 * 60 * 1000, autoStart = true, } = options;
|
|
21
40
|
let running = true;
|
|
22
41
|
let loopPromise = null;
|
|
23
42
|
let indexesReady = null;
|
|
24
43
|
const ensureIndexes = () => {
|
|
25
44
|
if (!indexesReady) {
|
|
26
45
|
const col = getOutboxCollection();
|
|
27
|
-
indexesReady =
|
|
28
|
-
col.createIndex({ status: 1, nextAttemptAt: 1
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
col.createIndex({
|
|
32
|
-
|
|
46
|
+
indexesReady = (async () => {
|
|
47
|
+
await col.createIndex({ status: 1, nextAttemptAt: 1 });
|
|
48
|
+
// Retention is per row: markDone/markFailure set expireAt, so changing keepDoneMs
|
|
49
|
+
// needs no index change (createIndex can't alter expireAfterSeconds of an existing index).
|
|
50
|
+
await col.createIndex({ expireAt: 1 }, { expireAfterSeconds: 0 });
|
|
51
|
+
})().catch((err) => {
|
|
52
|
+
// Don't cache a failure: the next tick tries again instead of failing forever.
|
|
53
|
+
indexesReady = null;
|
|
54
|
+
throw err;
|
|
55
|
+
});
|
|
33
56
|
}
|
|
34
57
|
return indexesReady;
|
|
35
58
|
};
|
|
36
59
|
const claimOne = async () => {
|
|
37
60
|
const col = getOutboxCollection();
|
|
38
61
|
const now = new Date();
|
|
39
|
-
const claimed = await col.findOneAndUpdate({
|
|
40
|
-
$
|
|
41
|
-
{ status: OutboxStatus.Pending, nextAttemptAt: { $lte: now } },
|
|
42
|
-
{ status: OutboxStatus.Processing, leasedUntil: { $lt: now } },
|
|
43
|
-
],
|
|
44
|
-
}, {
|
|
45
|
-
$set: {
|
|
46
|
-
status: OutboxStatus.Processing,
|
|
47
|
-
leasedBy: instanceId,
|
|
48
|
-
leasedUntil: new Date(now.getTime() + leaseMs),
|
|
49
|
-
},
|
|
62
|
+
const claimed = await col.findOneAndUpdate(claimFilter(now, getRegisteredCollections()), {
|
|
63
|
+
$set: { leasedBy: instanceId, nextAttemptAt: new Date(now.getTime() + leaseMs) },
|
|
50
64
|
$inc: { attempts: 1 },
|
|
51
|
-
}, { sort:
|
|
65
|
+
}, { sort: CLAIM_SORT, returnDocument: 'after' });
|
|
52
66
|
return claimed ?? null;
|
|
53
67
|
};
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
await Promise.all(listeners.map(async (l) => l(payload)));
|
|
68
|
+
/**
|
|
69
|
+
* Writes a claimed row's outcome, but only while this claim still holds it. attempts goes up
|
|
70
|
+
* on every claim, so a holder whose lease ran out (and whose row another instance reclaimed)
|
|
71
|
+
* matches nothing instead of overwriting the newer claim's outcome.
|
|
72
|
+
*/
|
|
73
|
+
const settle = async (row, set) => {
|
|
74
|
+
const { matchedCount } = await getOutboxCollection().updateOne({ _id: row._id, leasedBy: instanceId, attempts: row.attempts }, { $set: { ...set, leasedBy: null } });
|
|
75
|
+
if (matchedCount === 0) {
|
|
76
|
+
logger.warn(`Outbox row ${row._id} (${row.collectionName}/${row.op}) was reclaimed after its lease ran out. ` +
|
|
77
|
+
'Raise leaseMs above your slowest listener.');
|
|
78
|
+
}
|
|
79
|
+
return matchedCount > 0;
|
|
67
80
|
};
|
|
68
81
|
const markDone = async (row) => {
|
|
69
|
-
await
|
|
82
|
+
await settle(row, { status: OutboxStatus.Done, processedOn: new Date(), expireAt: expiryAfter(keepDoneMs), lastError: null });
|
|
70
83
|
};
|
|
71
84
|
const markFailure = async (row, err) => {
|
|
72
85
|
const message = err instanceof Error ? err.message : String(err);
|
|
73
86
|
if (row.attempts >= maxAttempts) {
|
|
74
|
-
await
|
|
75
|
-
|
|
87
|
+
if (await settle(row, { status: OutboxStatus.Failed, lastError: message, expireAt: expiryAfter(keepFailedMs) })) {
|
|
88
|
+
logger.error(`Outbox row ${row._id} (${row.collectionName}/${row.op}) dead-lettered after ${row.attempts} attempts: ${message}`);
|
|
89
|
+
}
|
|
76
90
|
}
|
|
77
91
|
else {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
lastError: message,
|
|
83
|
-
leasedBy: null,
|
|
84
|
-
leasedUntil: null,
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
logger.warn(`Outbox row ${row._id} (${row.collectionName}/${row.op}) failed (attempt ${row.attempts}), will retry: ${message}`);
|
|
92
|
+
const retry = { status: OutboxStatus.Pending, nextAttemptAt: new Date(Date.now() + backoffMs(row.attempts)), lastError: message };
|
|
93
|
+
if (await settle(row, retry)) {
|
|
94
|
+
logger.warn(`Outbox row ${row._id} (${row.collectionName}/${row.op}) failed (attempt ${row.attempts}), will retry: ${message}`);
|
|
95
|
+
}
|
|
88
96
|
}
|
|
89
97
|
};
|
|
90
98
|
const processRow = async (row) => {
|
|
99
|
+
// markFailure dead-letters at maxAttempts, so only claims whose lease ran out get past it: a
|
|
100
|
+
// listener that crashes the process or outlasts leaseMs. Dispatching again would repeat that.
|
|
101
|
+
if (row.attempts > maxAttempts) {
|
|
102
|
+
await markFailure(row, new Error(`lease of ${leaseMs}ms ran out on earlier attempts; not dispatched again`));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
91
105
|
try {
|
|
92
|
-
await
|
|
106
|
+
await dispatchEvent(row.collectionName, {
|
|
107
|
+
op: row.op,
|
|
108
|
+
eventId: row._id,
|
|
109
|
+
doc: row.doc,
|
|
110
|
+
prevDoc: row.prevDoc,
|
|
111
|
+
meta: row.meta,
|
|
112
|
+
context: row.context,
|
|
113
|
+
});
|
|
93
114
|
await markDone(row);
|
|
94
115
|
}
|
|
95
116
|
catch (err) {
|
|
@@ -137,7 +158,9 @@ export const startOutboxRelay = (options) => {
|
|
|
137
158
|
if (loopPromise)
|
|
138
159
|
await loopPromise;
|
|
139
160
|
try {
|
|
140
|
-
|
|
161
|
+
// A held row's nextAttemptAt is its lease end, still ahead: the condition keeps this off the due backlog.
|
|
162
|
+
const now = new Date();
|
|
163
|
+
await getOutboxCollection().updateMany({ status: OutboxStatus.Pending, nextAttemptAt: { $gt: now }, leasedBy: instanceId }, { $set: { leasedBy: null, nextAttemptAt: now } });
|
|
141
164
|
}
|
|
142
165
|
catch (err) {
|
|
143
166
|
logger.error('Outbox relay failed to release leased rows on stop:', err);
|
package/dist/outboxRelay.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outboxRelay.js","sourceRoot":"","sources":["../src/outboxRelay.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"outboxRelay.js","sourceRoot":"","sources":["../src/outboxRelay.ts"],"names":[],"mappings":"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"}
|
package/dist/types/outbox.d.ts
CHANGED
|
@@ -11,12 +11,24 @@ export declare enum OutboxOp {
|
|
|
11
11
|
Updated = "updated",
|
|
12
12
|
Removed = "removed"
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* A claimed row stays `pending`: the claim pushes `nextAttemptAt` past its lease, so a row whose
|
|
16
|
+
* holder died is due again through the same query that finds new rows.
|
|
17
|
+
*/
|
|
14
18
|
export declare enum OutboxStatus {
|
|
15
19
|
Pending = "pending",
|
|
16
|
-
Processing = "processing",
|
|
17
20
|
Done = "done",
|
|
18
21
|
Failed = "failed"
|
|
19
22
|
}
|
|
23
|
+
/** What `dispatchEvent` delivers, and listeners receive. `eventId` is the outbox row's `_id`. */
|
|
24
|
+
export interface OutboxEvent {
|
|
25
|
+
op: `${OutboxOp}`;
|
|
26
|
+
eventId: string;
|
|
27
|
+
doc: unknown;
|
|
28
|
+
prevDoc?: unknown;
|
|
29
|
+
meta?: Record<string, unknown> | null;
|
|
30
|
+
context?: unknown;
|
|
31
|
+
}
|
|
20
32
|
export interface OutboxRow<T = Record<string, unknown>> {
|
|
21
33
|
_id: string;
|
|
22
34
|
collectionName: string;
|
|
@@ -28,10 +40,10 @@ export interface OutboxRow<T = Record<string, unknown>> {
|
|
|
28
40
|
status: OutboxStatus;
|
|
29
41
|
attempts: number;
|
|
30
42
|
nextAttemptAt: Date;
|
|
31
|
-
leasedUntil?: Date | null;
|
|
32
43
|
leasedBy?: string | null;
|
|
33
44
|
lastError?: string | null;
|
|
34
45
|
createdOn: Date;
|
|
35
46
|
processedOn?: Date | null;
|
|
47
|
+
expireAt?: Date | null;
|
|
36
48
|
}
|
|
37
49
|
//# sourceMappingURL=outbox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/types/outbox.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,oBAAY,QAAQ;IAClB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,OAAO,YAAY;CACpB;AAED,oBAAY,YAAY;IACtB,OAAO,YAAY;IACnB,
|
|
1
|
+
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/types/outbox.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,oBAAY,QAAQ;IAClB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,OAAO,YAAY;CACpB;AAED;;;GAGG;AACH,oBAAY,YAAY;IACtB,OAAO,YAAY;IACnB,IAAI,SAAS;IACb,MAAM,WAAW;CAClB;AAED,iGAAiG;AACjG,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,GAAG,QAAQ,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,EAAE,EAAE,QAAQ,CAAC;IACb,GAAG,EAAE,CAAC,CAAC;IACP,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,IAAI,CAAC;IAChB,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB"}
|
package/dist/types/outbox.js
CHANGED
|
@@ -12,10 +12,13 @@ export var OutboxOp;
|
|
|
12
12
|
OutboxOp["Updated"] = "updated";
|
|
13
13
|
OutboxOp["Removed"] = "removed";
|
|
14
14
|
})(OutboxOp || (OutboxOp = {}));
|
|
15
|
+
/**
|
|
16
|
+
* A claimed row stays `pending`: the claim pushes `nextAttemptAt` past its lease, so a row whose
|
|
17
|
+
* holder died is due again through the same query that finds new rows.
|
|
18
|
+
*/
|
|
15
19
|
export var OutboxStatus;
|
|
16
20
|
(function (OutboxStatus) {
|
|
17
21
|
OutboxStatus["Pending"] = "pending";
|
|
18
|
-
OutboxStatus["Processing"] = "processing";
|
|
19
22
|
OutboxStatus["Done"] = "done";
|
|
20
23
|
OutboxStatus["Failed"] = "failed";
|
|
21
24
|
})(OutboxStatus || (OutboxStatus = {}));
|
package/dist/types/outbox.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/types/outbox.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAE1C,MAAM,CAAN,IAAY,QAIX;AAJD,WAAY,QAAQ;IAClB,+BAAmB,CAAA;IACnB,+BAAmB,CAAA;IACnB,+BAAmB,CAAA;AACrB,CAAC,EAJW,QAAQ,KAAR,QAAQ,QAInB;AAED,MAAM,CAAN,IAAY,
|
|
1
|
+
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/types/outbox.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAE1C,MAAM,CAAN,IAAY,QAIX;AAJD,WAAY,QAAQ;IAClB,+BAAmB,CAAA;IACnB,+BAAmB,CAAA;IACnB,+BAAmB,CAAA;AACrB,CAAC,EAJW,QAAQ,KAAR,QAAQ,QAInB;AAED;;;GAGG;AACH,MAAM,CAAN,IAAY,YAIX;AAJD,WAAY,YAAY;IACtB,mCAAmB,CAAA;IACnB,6BAAa,CAAA;IACb,iCAAiB,CAAA;AACnB,CAAC,EAJW,YAAY,KAAZ,YAAY,QAIvB"}
|