@classytic/ledger 0.9.1 → 0.10.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/dist/bridges/index.d.mts +1 -1
- package/dist/{errors-BI5k4iak.mjs → errors-vXd932rB.mjs} +1 -1
- package/dist/events/index.d.mts +3 -2
- package/dist/events/index.mjs +2 -2
- package/dist/{fx-realization.plugin-Bxlb8cIx.mjs → fx-realization.plugin-Dzqzi3u0.mjs} +1 -1
- package/dist/{index-Dih0lM65.d.mts → index-Bl0gP9lD.d.mts} +3 -3
- package/dist/index.d.mts +93 -73
- package/dist/index.mjs +180 -198
- package/dist/outbox-store-BbKdQ2eT.mjs +253 -0
- package/dist/outbox-store-BcCiHMPw.d.mts +305 -0
- package/dist/{partner-ledger-BoebloHk.mjs → partner-ledger-CR0geilx.mjs} +1 -1
- package/dist/plugins/index.mjs +1 -1
- package/dist/reports/index.mjs +1 -1
- package/dist/sync/index.d.mts +13 -2
- package/dist/sync/index.mjs +6 -3
- package/package.json +12 -6
- package/dist/outbox-store-DQbL-KYT.mjs +0 -132
- package/dist/outbox-store-UYC4eZpI.d.mts +0 -249
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
//#region src/events/event-constants.ts
|
|
3
|
-
/**
|
|
4
|
-
* Ledger domain event names.
|
|
5
|
-
*
|
|
6
|
-
* Convention: `ledger:<resource>.<verb>` — matches the catalog/revenue/flow
|
|
7
|
-
* namespace pattern so hosts can subscribe with a single glob
|
|
8
|
-
* (`ledger:entry.*`, `ledger:*`).
|
|
9
|
-
*/
|
|
10
|
-
const LEDGER_EVENTS = {
|
|
11
|
-
ENTRY_CREATED: "ledger:entry.created",
|
|
12
|
-
ENTRY_POSTED: "ledger:entry.posted",
|
|
13
|
-
ENTRY_UNPOSTED: "ledger:entry.unposted",
|
|
14
|
-
ENTRY_ARCHIVED: "ledger:entry.archived",
|
|
15
|
-
ENTRY_DUPLICATED: "ledger:entry.duplicated",
|
|
16
|
-
ENTRY_REVERSED: "ledger:entry.reversed",
|
|
17
|
-
ACCOUNT_SEEDED: "ledger:account.seeded",
|
|
18
|
-
ACCOUNT_BULK_CREATED: "ledger:account.bulk-created",
|
|
19
|
-
JOURNAL_SEEDED: "ledger:journal.seeded",
|
|
20
|
-
RECONCILIATION_MATCHED: "ledger:reconciliation.matched",
|
|
21
|
-
RECONCILIATION_UNMATCHED: "ledger:reconciliation.unmatched"
|
|
22
|
-
};
|
|
23
|
-
//#endregion
|
|
24
|
-
//#region src/events/helpers.ts
|
|
25
|
-
/**
|
|
26
|
-
* Event creation helper.
|
|
27
|
-
*
|
|
28
|
-
* Builds a well-formed DomainEvent with auto-generated `meta.id` + `meta.timestamp`.
|
|
29
|
-
* Threads optional context fields (userId, organizationId, correlationId) that
|
|
30
|
-
* hosts rely on for audit trails and distributed tracing.
|
|
31
|
-
*/
|
|
32
|
-
function toIdString(value) {
|
|
33
|
-
if (value == null) return void 0;
|
|
34
|
-
if (typeof value === "string") return value;
|
|
35
|
-
if (typeof value === "number" || typeof value === "bigint") return String(value);
|
|
36
|
-
const obj = value;
|
|
37
|
-
if (typeof obj.toHexString === "function") return obj.toHexString();
|
|
38
|
-
if (typeof obj.toString === "function") {
|
|
39
|
-
const s = obj.toString();
|
|
40
|
-
return s === "[object Object]" ? void 0 : s;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function createEvent(type, payload, ctx, meta) {
|
|
44
|
-
return {
|
|
45
|
-
type,
|
|
46
|
-
payload,
|
|
47
|
-
meta: {
|
|
48
|
-
id: randomUUID(),
|
|
49
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
50
|
-
userId: toIdString(ctx?.actorId),
|
|
51
|
-
organizationId: toIdString(ctx?.organizationId),
|
|
52
|
-
correlationId: ctx?.correlationId ?? ctx?.traceId,
|
|
53
|
-
...meta
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
//#endregion
|
|
58
|
-
//#region src/events/in-process-bus.ts
|
|
59
|
-
var InProcessLedgerBus = class {
|
|
60
|
-
name = "in-process-ledger";
|
|
61
|
-
handlers = /* @__PURE__ */ new Map();
|
|
62
|
-
logger;
|
|
63
|
-
constructor(options) {
|
|
64
|
-
this.logger = options?.logger ?? console;
|
|
65
|
-
}
|
|
66
|
-
async publish(event) {
|
|
67
|
-
const exact = this.handlers.get(event.type) ?? /* @__PURE__ */ new Set();
|
|
68
|
-
const wildcard = this.handlers.get("*") ?? /* @__PURE__ */ new Set();
|
|
69
|
-
const pattern = /* @__PURE__ */ new Set();
|
|
70
|
-
for (const [p, handlers] of this.handlers.entries()) if (p.endsWith(".*")) {
|
|
71
|
-
const prefix = p.slice(0, -2);
|
|
72
|
-
if (event.type.startsWith(`${prefix}.`)) for (const h of handlers) pattern.add(h);
|
|
73
|
-
} else if (p.endsWith(":*")) {
|
|
74
|
-
const prefix = p.slice(0, -2);
|
|
75
|
-
if (event.type.startsWith(`${prefix}:`)) for (const h of handlers) pattern.add(h);
|
|
76
|
-
}
|
|
77
|
-
const all = new Set([
|
|
78
|
-
...exact,
|
|
79
|
-
...wildcard,
|
|
80
|
-
...pattern
|
|
81
|
-
]);
|
|
82
|
-
for (const handler of all) try {
|
|
83
|
-
await handler(event);
|
|
84
|
-
} catch (err) {
|
|
85
|
-
this.logger.error(`[InProcessLedgerBus] Handler error for ${event.type}:`, err);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
async publishMany(events) {
|
|
89
|
-
const results = /* @__PURE__ */ new Map();
|
|
90
|
-
for (const event of events) try {
|
|
91
|
-
await this.publish(event);
|
|
92
|
-
results.set(event.meta.id, null);
|
|
93
|
-
} catch (err) {
|
|
94
|
-
results.set(event.meta.id, err instanceof Error ? err : new Error(String(err)));
|
|
95
|
-
}
|
|
96
|
-
return results;
|
|
97
|
-
}
|
|
98
|
-
async subscribe(pattern, handler) {
|
|
99
|
-
if (!this.handlers.has(pattern)) this.handlers.set(pattern, /* @__PURE__ */ new Set());
|
|
100
|
-
this.handlers.get(pattern)?.add(handler);
|
|
101
|
-
return () => {
|
|
102
|
-
const set = this.handlers.get(pattern);
|
|
103
|
-
if (set) {
|
|
104
|
-
set.delete(handler);
|
|
105
|
-
if (set.size === 0) this.handlers.delete(pattern);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
async close() {
|
|
110
|
-
this.handlers.clear();
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
//#endregion
|
|
114
|
-
//#region src/events/outbox-store.ts
|
|
115
|
-
/**
|
|
116
|
-
* Thrown by a store when `acknowledge` / `fail` is called by a consumer that
|
|
117
|
-
* does not own the event's current lease.
|
|
118
|
-
*/
|
|
119
|
-
var OutboxOwnershipError = class extends Error {
|
|
120
|
-
eventId;
|
|
121
|
-
attemptedBy;
|
|
122
|
-
currentOwner;
|
|
123
|
-
constructor(eventId, attemptedBy, currentOwner) {
|
|
124
|
-
super(`Outbox ownership mismatch for event "${eventId}": attempted by "${attemptedBy}", current owner is "${currentOwner ?? "none"}". The lease may have expired and been reclaimed by another worker.`);
|
|
125
|
-
this.name = "OutboxOwnershipError";
|
|
126
|
-
this.eventId = eventId;
|
|
127
|
-
this.attemptedBy = attemptedBy;
|
|
128
|
-
this.currentOwner = currentOwner;
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
//#endregion
|
|
132
|
-
export { LEDGER_EVENTS as i, InProcessLedgerBus as n, createEvent as r, OutboxOwnershipError as t };
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
//#region src/events/event-constants.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Ledger domain event names.
|
|
4
|
-
*
|
|
5
|
-
* Convention: `ledger:<resource>.<verb>` — matches the catalog/revenue/flow
|
|
6
|
-
* namespace pattern so hosts can subscribe with a single glob
|
|
7
|
-
* (`ledger:entry.*`, `ledger:*`).
|
|
8
|
-
*/
|
|
9
|
-
declare const LEDGER_EVENTS: {
|
|
10
|
-
readonly ENTRY_CREATED: "ledger:entry.created";
|
|
11
|
-
readonly ENTRY_POSTED: "ledger:entry.posted";
|
|
12
|
-
readonly ENTRY_UNPOSTED: "ledger:entry.unposted";
|
|
13
|
-
readonly ENTRY_ARCHIVED: "ledger:entry.archived";
|
|
14
|
-
readonly ENTRY_DUPLICATED: "ledger:entry.duplicated";
|
|
15
|
-
readonly ENTRY_REVERSED: "ledger:entry.reversed";
|
|
16
|
-
readonly ACCOUNT_SEEDED: "ledger:account.seeded";
|
|
17
|
-
readonly ACCOUNT_BULK_CREATED: "ledger:account.bulk-created";
|
|
18
|
-
readonly JOURNAL_SEEDED: "ledger:journal.seeded";
|
|
19
|
-
readonly RECONCILIATION_MATCHED: "ledger:reconciliation.matched";
|
|
20
|
-
readonly RECONCILIATION_UNMATCHED: "ledger:reconciliation.unmatched";
|
|
21
|
-
};
|
|
22
|
-
type LedgerEventName = (typeof LEDGER_EVENTS)[keyof typeof LEDGER_EVENTS];
|
|
23
|
-
//#endregion
|
|
24
|
-
//#region src/events/event-payloads.d.ts
|
|
25
|
-
/**
|
|
26
|
-
* Typed payload definitions for ledger domain events.
|
|
27
|
-
*/
|
|
28
|
-
interface EntryCreatedPayload {
|
|
29
|
-
entryId: unknown;
|
|
30
|
-
journalType?: string;
|
|
31
|
-
state: string;
|
|
32
|
-
referenceNumber?: string;
|
|
33
|
-
idempotencyKey?: string;
|
|
34
|
-
organizationId?: unknown;
|
|
35
|
-
}
|
|
36
|
-
interface EntryPostedPayload {
|
|
37
|
-
entryId: unknown;
|
|
38
|
-
referenceNumber?: string;
|
|
39
|
-
postedBy?: unknown;
|
|
40
|
-
totalDebit: number;
|
|
41
|
-
totalCredit: number;
|
|
42
|
-
organizationId?: unknown;
|
|
43
|
-
}
|
|
44
|
-
interface EntryUnpostedPayload {
|
|
45
|
-
entryId: unknown;
|
|
46
|
-
unpostedBy?: unknown;
|
|
47
|
-
organizationId?: unknown;
|
|
48
|
-
}
|
|
49
|
-
interface EntryArchivedPayload {
|
|
50
|
-
entryId: unknown;
|
|
51
|
-
archivedBy?: unknown;
|
|
52
|
-
organizationId?: unknown;
|
|
53
|
-
}
|
|
54
|
-
interface EntryDuplicatedPayload {
|
|
55
|
-
sourceEntryId: unknown;
|
|
56
|
-
duplicateEntryId: unknown;
|
|
57
|
-
organizationId?: unknown;
|
|
58
|
-
}
|
|
59
|
-
interface EntryReversedPayload {
|
|
60
|
-
originalEntryId: unknown;
|
|
61
|
-
reversalEntryId: unknown;
|
|
62
|
-
reversalDate: Date;
|
|
63
|
-
reversedBy?: unknown;
|
|
64
|
-
organizationId?: unknown;
|
|
65
|
-
}
|
|
66
|
-
interface AccountSeededPayload {
|
|
67
|
-
created: number;
|
|
68
|
-
skipped: number;
|
|
69
|
-
organizationId?: unknown;
|
|
70
|
-
}
|
|
71
|
-
interface AccountBulkCreatedPayload {
|
|
72
|
-
created: number;
|
|
73
|
-
skipped: number;
|
|
74
|
-
errors: number;
|
|
75
|
-
organizationId?: unknown;
|
|
76
|
-
}
|
|
77
|
-
interface JournalSeededPayload {
|
|
78
|
-
created: number;
|
|
79
|
-
skipped: number;
|
|
80
|
-
organizationId?: unknown;
|
|
81
|
-
}
|
|
82
|
-
interface ReconciliationMatchedPayload {
|
|
83
|
-
matchingNumber: string;
|
|
84
|
-
account: unknown;
|
|
85
|
-
itemCount: number;
|
|
86
|
-
debitTotal: number;
|
|
87
|
-
creditTotal: number;
|
|
88
|
-
isFullReconcile: boolean;
|
|
89
|
-
currency: string | null;
|
|
90
|
-
organizationId?: unknown;
|
|
91
|
-
}
|
|
92
|
-
interface ReconciliationUnmatchedPayload {
|
|
93
|
-
matchingNumber: string;
|
|
94
|
-
itemCount: number;
|
|
95
|
-
organizationId?: unknown;
|
|
96
|
-
}
|
|
97
|
-
//#endregion
|
|
98
|
-
//#region src/events/transport.d.ts
|
|
99
|
-
/**
|
|
100
|
-
* EventTransport — structurally identical to @classytic/arc's EventTransport.
|
|
101
|
-
*
|
|
102
|
-
* DO NOT import from @classytic/arc at runtime. TypeScript structural typing
|
|
103
|
-
* means any arc transport (Memory, Redis, BullMQ, Kafka) is assignable to this
|
|
104
|
-
* interface with zero adapter code, as long as the shape matches exactly.
|
|
105
|
-
*
|
|
106
|
-
* See: packages/arc/src/events/EventTransport.ts
|
|
107
|
-
*/
|
|
108
|
-
interface DomainEvent<T = unknown> {
|
|
109
|
-
type: string;
|
|
110
|
-
payload: T;
|
|
111
|
-
meta: {
|
|
112
|
-
id: string;
|
|
113
|
-
timestamp: Date;
|
|
114
|
-
resource?: string;
|
|
115
|
-
resourceId?: string;
|
|
116
|
-
userId?: string;
|
|
117
|
-
organizationId?: string;
|
|
118
|
-
correlationId?: string;
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
type EventHandler<T = unknown> = (event: DomainEvent<T>) => void | Promise<void>;
|
|
122
|
-
/**
|
|
123
|
-
* Per-event outcome returned by `EventTransport.publishMany`.
|
|
124
|
-
* Key is `event.meta.id`; value is `null` for success or `Error` for per-event failure.
|
|
125
|
-
*/
|
|
126
|
-
type PublishManyResult = ReadonlyMap<string, Error | null>;
|
|
127
|
-
interface EventLogger {
|
|
128
|
-
warn(message: string, ...args: unknown[]): void;
|
|
129
|
-
error(message: string, ...args: unknown[]): void;
|
|
130
|
-
}
|
|
131
|
-
interface EventTransport {
|
|
132
|
-
readonly name: string;
|
|
133
|
-
publish(event: DomainEvent): Promise<void>;
|
|
134
|
-
publishMany?(events: readonly DomainEvent[]): Promise<PublishManyResult>;
|
|
135
|
-
subscribe(pattern: string, handler: EventHandler): Promise<() => void>;
|
|
136
|
-
close?(): Promise<void>;
|
|
137
|
-
}
|
|
138
|
-
//#endregion
|
|
139
|
-
//#region src/events/helpers.d.ts
|
|
140
|
-
/** Minimal context shape for event metadata. */
|
|
141
|
-
interface EventContext {
|
|
142
|
-
actorId?: unknown;
|
|
143
|
-
organizationId?: unknown;
|
|
144
|
-
correlationId?: string;
|
|
145
|
-
traceId?: string;
|
|
146
|
-
}
|
|
147
|
-
declare function createEvent<T>(type: string, payload: T, ctx?: EventContext, meta?: Partial<DomainEvent['meta']>): DomainEvent<T>;
|
|
148
|
-
//#endregion
|
|
149
|
-
//#region src/events/in-process-bus.d.ts
|
|
150
|
-
interface InProcessLedgerBusOptions {
|
|
151
|
-
logger?: EventLogger;
|
|
152
|
-
}
|
|
153
|
-
declare class InProcessLedgerBus implements EventTransport {
|
|
154
|
-
readonly name = "in-process-ledger";
|
|
155
|
-
private handlers;
|
|
156
|
-
private logger;
|
|
157
|
-
constructor(options?: InProcessLedgerBusOptions);
|
|
158
|
-
publish(event: DomainEvent): Promise<void>;
|
|
159
|
-
publishMany(events: readonly DomainEvent[]): Promise<PublishManyResult>;
|
|
160
|
-
subscribe(pattern: string, handler: EventHandler): Promise<() => void>;
|
|
161
|
-
close(): Promise<void>;
|
|
162
|
-
}
|
|
163
|
-
//#endregion
|
|
164
|
-
//#region src/events/outbox-store.d.ts
|
|
165
|
-
/** Options passed to `OutboxStore.save` for richer write semantics. */
|
|
166
|
-
interface OutboxWriteOptions {
|
|
167
|
-
/** Host-provided DB session/transaction handle for atomic writes. */
|
|
168
|
-
readonly session?: unknown;
|
|
169
|
-
/** Earliest time the event should be visible to relay workers. */
|
|
170
|
-
readonly visibleAt?: Date;
|
|
171
|
-
/** Idempotency key — stores that support it should dedupe on this. */
|
|
172
|
-
readonly dedupeKey?: string;
|
|
173
|
-
/** Partition/routing key for sharded transports (Kafka, Redis Streams). */
|
|
174
|
-
readonly partitionKey?: string;
|
|
175
|
-
/** Arbitrary headers propagated to the transport layer. */
|
|
176
|
-
readonly headers?: Readonly<Record<string, string>>;
|
|
177
|
-
}
|
|
178
|
-
/** Options for lease-based work claim. */
|
|
179
|
-
interface OutboxClaimOptions {
|
|
180
|
-
readonly limit?: number;
|
|
181
|
-
readonly consumerId?: string;
|
|
182
|
-
readonly leaseMs?: number;
|
|
183
|
-
readonly types?: readonly string[];
|
|
184
|
-
}
|
|
185
|
-
/** Options for `OutboxStore.acknowledge`. */
|
|
186
|
-
interface OutboxAcknowledgeOptions {
|
|
187
|
-
readonly consumerId?: string;
|
|
188
|
-
}
|
|
189
|
-
/** Options for `OutboxStore.fail`. */
|
|
190
|
-
interface OutboxFailOptions {
|
|
191
|
-
readonly consumerId?: string;
|
|
192
|
-
readonly retryAt?: Date;
|
|
193
|
-
readonly deadLetter?: boolean;
|
|
194
|
-
}
|
|
195
|
-
/** Normalized error info passed to `OutboxStore.fail`. */
|
|
196
|
-
interface OutboxErrorInfo {
|
|
197
|
-
readonly message: string;
|
|
198
|
-
readonly code?: string;
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Thrown by a store when `acknowledge` / `fail` is called by a consumer that
|
|
202
|
-
* does not own the event's current lease.
|
|
203
|
-
*/
|
|
204
|
-
declare class OutboxOwnershipError extends Error {
|
|
205
|
-
readonly eventId: string;
|
|
206
|
-
readonly attemptedBy: string;
|
|
207
|
-
readonly currentOwner: string | null;
|
|
208
|
-
constructor(eventId: string, attemptedBy: string, currentOwner: string | null);
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Durable storage contract for the transactional outbox pattern.
|
|
212
|
-
*
|
|
213
|
-
* **Required**: `save`, `getPending`, `acknowledge`.
|
|
214
|
-
* **Optional** (stores opt-in): `claimPending`, `fail`, `purge`.
|
|
215
|
-
*
|
|
216
|
-
* Structurally identical to arc's `OutboxStore` — assignable in both
|
|
217
|
-
* directions via TypeScript structural typing.
|
|
218
|
-
*/
|
|
219
|
-
interface OutboxStore {
|
|
220
|
-
/**
|
|
221
|
-
* Save event to outbox (typically inside a business transaction via `options.session`).
|
|
222
|
-
* MUST reject events missing `type` or `meta.id` — throw rather than persist.
|
|
223
|
-
*/
|
|
224
|
-
save(event: DomainEvent, options?: OutboxWriteOptions): Promise<void>;
|
|
225
|
-
/**
|
|
226
|
-
* Get pending (unrelayed) events, FIFO ordered.
|
|
227
|
-
* Multi-worker deployments should prefer `claimPending` if supported.
|
|
228
|
-
*/
|
|
229
|
-
getPending(limit: number): Promise<DomainEvent[]>;
|
|
230
|
-
/**
|
|
231
|
-
* Atomically claim pending events with a lease.
|
|
232
|
-
* Two concurrent callers MUST never receive overlapping events.
|
|
233
|
-
*/
|
|
234
|
-
claimPending?(options?: OutboxClaimOptions): Promise<DomainEvent[]>;
|
|
235
|
-
/**
|
|
236
|
-
* Mark event as successfully relayed. Unknown eventId is a no-op (idempotent).
|
|
237
|
-
* Ownership mismatch MUST throw `OutboxOwnershipError`.
|
|
238
|
-
*/
|
|
239
|
-
acknowledge(eventId: string, options?: OutboxAcknowledgeOptions): Promise<void>;
|
|
240
|
-
/**
|
|
241
|
-
* Record a relay failure. Enables retry scheduling and dead-letter flow.
|
|
242
|
-
* Ownership mismatch MUST throw `OutboxOwnershipError`.
|
|
243
|
-
*/
|
|
244
|
-
fail?(eventId: string, error: OutboxErrorInfo, options?: OutboxFailOptions): Promise<void>;
|
|
245
|
-
/** Purge old delivered events (older than `olderThanMs`). Returns count. */
|
|
246
|
-
purge?(olderThanMs: number): Promise<number>;
|
|
247
|
-
}
|
|
248
|
-
//#endregion
|
|
249
|
-
export { EntryReversedPayload as C, ReconciliationUnmatchedPayload as D, ReconciliationMatchedPayload as E, LEDGER_EVENTS as O, EntryPostedPayload as S, JournalSeededPayload as T, AccountBulkCreatedPayload as _, OutboxOwnershipError as a, EntryCreatedPayload as b, InProcessLedgerBus as c, createEvent as d, DomainEvent as f, PublishManyResult as g, EventTransport as h, OutboxFailOptions as i, LedgerEventName as k, InProcessLedgerBusOptions as l, EventLogger as m, OutboxClaimOptions as n, OutboxStore as o, EventHandler as p, OutboxErrorInfo as r, OutboxWriteOptions as s, OutboxAcknowledgeOptions as t, EventContext as u, AccountSeededPayload as v, EntryUnpostedPayload as w, EntryDuplicatedPayload as x, EntryArchivedPayload as y };
|