@classytic/ledger 0.9.1 → 0.10.1

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,253 @@
1
+ import { createEvent, matchEventPattern } from "@classytic/primitives/events";
2
+ import { z } from "zod";
3
+ import { InvalidOutboxEventError, OutboxOwnershipError } from "@classytic/primitives/outbox";
4
+ //#region src/events/event-constants.ts
5
+ /**
6
+ * Ledger domain event names.
7
+ *
8
+ * Convention: `ledger:<resource>.<verb>` — matches the catalog/revenue/flow
9
+ * namespace pattern so hosts can subscribe with a single glob
10
+ * (`ledger:entry.*`, `ledger:*`).
11
+ */
12
+ const LEDGER_EVENTS = {
13
+ ENTRY_CREATED: "ledger:entry.created",
14
+ ENTRY_POSTED: "ledger:entry.posted",
15
+ ENTRY_UNPOSTED: "ledger:entry.unposted",
16
+ ENTRY_ARCHIVED: "ledger:entry.archived",
17
+ ENTRY_DUPLICATED: "ledger:entry.duplicated",
18
+ ENTRY_REVERSED: "ledger:entry.reversed",
19
+ ACCOUNT_SEEDED: "ledger:account.seeded",
20
+ ACCOUNT_BULK_CREATED: "ledger:account.bulk-created",
21
+ JOURNAL_SEEDED: "ledger:journal.seeded",
22
+ RECONCILIATION_MATCHED: "ledger:reconciliation.matched",
23
+ RECONCILIATION_UNMATCHED: "ledger:reconciliation.unmatched"
24
+ };
25
+ //#endregion
26
+ //#region src/events/helpers.ts
27
+ function toIdString(value) {
28
+ if (value == null) return void 0;
29
+ if (typeof value === "string") return value;
30
+ if (typeof value === "number" || typeof value === "bigint") return String(value);
31
+ const obj = value;
32
+ if (typeof obj.toHexString === "function") return obj.toHexString();
33
+ if (typeof obj.toString === "function") {
34
+ const s = obj.toString();
35
+ return s === "[object Object]" ? void 0 : s;
36
+ }
37
+ }
38
+ function createEvent$1(type, payload, ctx, meta) {
39
+ return createEvent(type, payload, {
40
+ userId: toIdString(ctx?.actorId),
41
+ organizationId: toIdString(ctx?.organizationId),
42
+ correlationId: ctx?.correlationId ?? ctx?.traceId,
43
+ ...meta
44
+ });
45
+ }
46
+ //#endregion
47
+ //#region src/events/in-process-bus.ts
48
+ var InProcessLedgerBus = class {
49
+ name = "in-process-ledger";
50
+ handlers = /* @__PURE__ */ new Map();
51
+ logger;
52
+ constructor(options) {
53
+ this.logger = options?.logger ?? console;
54
+ }
55
+ async publish(event) {
56
+ const matched = /* @__PURE__ */ new Set();
57
+ for (const [pattern, set] of this.handlers.entries()) if (matchEventPattern(pattern, event.type)) for (const h of set) matched.add(h);
58
+ for (const handler of matched) try {
59
+ await handler(event);
60
+ } catch (err) {
61
+ this.logger.error(`[InProcessLedgerBus] Handler error for ${event.type}:`, err);
62
+ }
63
+ }
64
+ async publishMany(events) {
65
+ const results = /* @__PURE__ */ new Map();
66
+ for (const event of events) try {
67
+ await this.publish(event);
68
+ results.set(event.meta.id, null);
69
+ } catch (err) {
70
+ results.set(event.meta.id, err instanceof Error ? err : new Error(String(err)));
71
+ }
72
+ return results;
73
+ }
74
+ async subscribe(pattern, handler) {
75
+ if (!this.handlers.has(pattern)) this.handlers.set(pattern, /* @__PURE__ */ new Set());
76
+ this.handlers.get(pattern)?.add(handler);
77
+ return () => {
78
+ const set = this.handlers.get(pattern);
79
+ if (set) {
80
+ set.delete(handler);
81
+ if (set.size === 0) this.handlers.delete(pattern);
82
+ }
83
+ };
84
+ }
85
+ async close() {
86
+ this.handlers.clear();
87
+ }
88
+ };
89
+ //#endregion
90
+ //#region src/events/ledger-event-catalog.ts
91
+ function defineLedgerEvent(input) {
92
+ const { name, version = 1, description, zodSchema } = input;
93
+ const def = {
94
+ name,
95
+ version,
96
+ schema: z.toJSONSchema(zodSchema),
97
+ zodSchema,
98
+ create(payload, meta) {
99
+ return createEvent(name, payload, {
100
+ source: "ledger",
101
+ ...meta
102
+ });
103
+ }
104
+ };
105
+ if (description !== void 0) def.description = description;
106
+ return def;
107
+ }
108
+ /**
109
+ * Mongo ObjectId or string — repositories pass raw ObjectIds which JSON
110
+ * serialise to hex strings. `passthrough()` via union keeps validation
111
+ * permissive without demanding a cast at the emit site.
112
+ */
113
+ const objectIdLike = z.union([z.string(), z.any()]);
114
+ const seedCountsSchema = z.object({
115
+ created: z.number().int().nonnegative(),
116
+ skipped: z.number().int().nonnegative(),
117
+ organizationId: objectIdLike.optional()
118
+ });
119
+ const entryCreatedSchema = z.object({
120
+ entryId: objectIdLike,
121
+ journalType: z.string().optional(),
122
+ state: z.string(),
123
+ referenceNumber: z.string().optional(),
124
+ idempotencyKey: z.string().optional(),
125
+ organizationId: objectIdLike.optional()
126
+ });
127
+ const entryPostedSchema = z.object({
128
+ entryId: objectIdLike,
129
+ referenceNumber: z.string().optional(),
130
+ postedBy: objectIdLike.optional(),
131
+ totalDebit: z.number(),
132
+ totalCredit: z.number(),
133
+ organizationId: objectIdLike.optional()
134
+ });
135
+ const entryUnpostedSchema = z.object({
136
+ entryId: objectIdLike,
137
+ unpostedBy: objectIdLike.optional(),
138
+ organizationId: objectIdLike.optional()
139
+ });
140
+ const entryArchivedSchema = z.object({
141
+ entryId: objectIdLike,
142
+ archivedBy: objectIdLike.optional(),
143
+ organizationId: objectIdLike.optional()
144
+ });
145
+ const entryDuplicatedSchema = z.object({
146
+ sourceEntryId: objectIdLike,
147
+ duplicateEntryId: objectIdLike,
148
+ organizationId: objectIdLike.optional()
149
+ });
150
+ const entryReversedSchema = z.object({
151
+ originalEntryId: objectIdLike,
152
+ reversalEntryId: objectIdLike,
153
+ reversalDate: z.iso.datetime(),
154
+ reversedBy: objectIdLike.optional(),
155
+ organizationId: objectIdLike.optional()
156
+ });
157
+ const accountBulkCreatedSchema = z.object({
158
+ created: z.number().int().nonnegative(),
159
+ skipped: z.number().int().nonnegative(),
160
+ errors: z.number().int().nonnegative(),
161
+ organizationId: objectIdLike.optional()
162
+ });
163
+ const reconciliationMatchedSchema = z.object({
164
+ matchingNumber: z.string(),
165
+ account: objectIdLike,
166
+ itemCount: z.number().int().nonnegative(),
167
+ debitTotal: z.number(),
168
+ creditTotal: z.number(),
169
+ isFullReconcile: z.boolean(),
170
+ currency: z.string().nullable(),
171
+ organizationId: objectIdLike.optional()
172
+ });
173
+ const reconciliationUnmatchedSchema = z.object({
174
+ matchingNumber: z.string(),
175
+ itemCount: z.number().int().nonnegative(),
176
+ organizationId: objectIdLike.optional()
177
+ });
178
+ const EntryCreated = defineLedgerEvent({
179
+ name: LEDGER_EVENTS.ENTRY_CREATED,
180
+ description: "A journal entry was created (may be draft or posted depending on service).",
181
+ zodSchema: entryCreatedSchema
182
+ });
183
+ const EntryPosted = defineLedgerEvent({
184
+ name: LEDGER_EVENTS.ENTRY_POSTED,
185
+ description: "A journal entry was posted — balances are now final.",
186
+ zodSchema: entryPostedSchema
187
+ });
188
+ const EntryUnposted = defineLedgerEvent({
189
+ name: LEDGER_EVENTS.ENTRY_UNPOSTED,
190
+ description: "A previously posted entry was unposted (reverted to draft).",
191
+ zodSchema: entryUnpostedSchema
192
+ });
193
+ const EntryArchived = defineLedgerEvent({
194
+ name: LEDGER_EVENTS.ENTRY_ARCHIVED,
195
+ description: "An entry was archived — hidden from default queries, still on disk.",
196
+ zodSchema: entryArchivedSchema
197
+ });
198
+ const EntryDuplicated = defineLedgerEvent({
199
+ name: LEDGER_EVENTS.ENTRY_DUPLICATED,
200
+ description: "A new draft entry was produced by duplicating an existing one.",
201
+ zodSchema: entryDuplicatedSchema
202
+ });
203
+ const EntryReversed = defineLedgerEvent({
204
+ name: LEDGER_EVENTS.ENTRY_REVERSED,
205
+ description: "A posted entry was reversed — a new offsetting entry was booked.",
206
+ zodSchema: entryReversedSchema
207
+ });
208
+ const AccountSeeded = defineLedgerEvent({
209
+ name: LEDGER_EVENTS.ACCOUNT_SEEDED,
210
+ description: "Chart-of-accounts defaults were seeded for an organization.",
211
+ zodSchema: seedCountsSchema
212
+ });
213
+ const AccountBulkCreated = defineLedgerEvent({
214
+ name: LEDGER_EVENTS.ACCOUNT_BULK_CREATED,
215
+ description: "A bulk import of accounts completed.",
216
+ zodSchema: accountBulkCreatedSchema
217
+ });
218
+ const JournalSeeded = defineLedgerEvent({
219
+ name: LEDGER_EVENTS.JOURNAL_SEEDED,
220
+ description: "Default journals were seeded for an organization.",
221
+ zodSchema: seedCountsSchema
222
+ });
223
+ const ReconciliationMatched = defineLedgerEvent({
224
+ name: LEDGER_EVENTS.RECONCILIATION_MATCHED,
225
+ description: "A set of ledger items was matched under a reconciliation number.",
226
+ zodSchema: reconciliationMatchedSchema
227
+ });
228
+ const ReconciliationUnmatched = defineLedgerEvent({
229
+ name: LEDGER_EVENTS.RECONCILIATION_UNMATCHED,
230
+ description: "A previously matched reconciliation was unmatched.",
231
+ zodSchema: reconciliationUnmatchedSchema
232
+ });
233
+ /**
234
+ * Every ledger event defined in the package — pass to Arc's
235
+ * `EventRegistry`. Hosts wire ONE array; the whole `ledger:*` namespace
236
+ * becomes introspectable via OpenAPI and auto-validated at publish time
237
+ * when `eventPlugin({ validateMode: 'reject' })` is set.
238
+ */
239
+ const ledgerEventDefinitions = [
240
+ EntryCreated,
241
+ EntryPosted,
242
+ EntryUnposted,
243
+ EntryArchived,
244
+ EntryDuplicated,
245
+ EntryReversed,
246
+ AccountSeeded,
247
+ AccountBulkCreated,
248
+ JournalSeeded,
249
+ ReconciliationMatched,
250
+ ReconciliationUnmatched
251
+ ];
252
+ //#endregion
253
+ export { LEDGER_EVENTS as _, EntryArchived as a, EntryPosted as c, JournalSeeded as d, ReconciliationMatched as f, createEvent$1 as g, InProcessLedgerBus as h, AccountSeeded as i, EntryReversed as l, ledgerEventDefinitions as m, OutboxOwnershipError as n, EntryCreated as o, ReconciliationUnmatched as p, AccountBulkCreated as r, EntryDuplicated as s, InvalidOutboxEventError as t, EntryUnposted as u };
@@ -0,0 +1,305 @@
1
+ import { DomainEvent, EventHandler, EventTransport, PublishManyResult } from "@classytic/primitives/events";
2
+ import { z } from "zod";
3
+ import { InvalidOutboxEventError, OutboxAcknowledgeOptions, OutboxClaimOptions, OutboxErrorInfo, OutboxFailOptions, OutboxFailureContext, OutboxFailureDecision, OutboxFailurePolicy, OutboxOwnershipError, OutboxStore, OutboxWriteOptions } from "@classytic/primitives/outbox";
4
+
5
+ //#region src/events/event-constants.d.ts
6
+ /**
7
+ * Ledger domain event names.
8
+ *
9
+ * Convention: `ledger:<resource>.<verb>` — matches the catalog/revenue/flow
10
+ * namespace pattern so hosts can subscribe with a single glob
11
+ * (`ledger:entry.*`, `ledger:*`).
12
+ */
13
+ declare const LEDGER_EVENTS: {
14
+ readonly ENTRY_CREATED: "ledger:entry.created";
15
+ readonly ENTRY_POSTED: "ledger:entry.posted";
16
+ readonly ENTRY_UNPOSTED: "ledger:entry.unposted";
17
+ readonly ENTRY_ARCHIVED: "ledger:entry.archived";
18
+ readonly ENTRY_DUPLICATED: "ledger:entry.duplicated";
19
+ readonly ENTRY_REVERSED: "ledger:entry.reversed";
20
+ readonly ACCOUNT_SEEDED: "ledger:account.seeded";
21
+ readonly ACCOUNT_BULK_CREATED: "ledger:account.bulk-created";
22
+ readonly JOURNAL_SEEDED: "ledger:journal.seeded";
23
+ readonly RECONCILIATION_MATCHED: "ledger:reconciliation.matched";
24
+ readonly RECONCILIATION_UNMATCHED: "ledger:reconciliation.unmatched";
25
+ };
26
+ type LedgerEventName = (typeof LEDGER_EVENTS)[keyof typeof LEDGER_EVENTS];
27
+ //#endregion
28
+ //#region src/events/event-payloads.d.ts
29
+ /**
30
+ * Typed payload definitions for ledger domain events.
31
+ */
32
+ interface EntryCreatedPayload {
33
+ entryId: unknown;
34
+ journalType?: string;
35
+ state: string;
36
+ referenceNumber?: string;
37
+ idempotencyKey?: string;
38
+ organizationId?: unknown;
39
+ }
40
+ interface EntryPostedPayload {
41
+ entryId: unknown;
42
+ referenceNumber?: string;
43
+ postedBy?: unknown;
44
+ totalDebit: number;
45
+ totalCredit: number;
46
+ organizationId?: unknown;
47
+ }
48
+ interface EntryUnpostedPayload {
49
+ entryId: unknown;
50
+ unpostedBy?: unknown;
51
+ organizationId?: unknown;
52
+ }
53
+ interface EntryArchivedPayload {
54
+ entryId: unknown;
55
+ archivedBy?: unknown;
56
+ organizationId?: unknown;
57
+ }
58
+ interface EntryDuplicatedPayload {
59
+ sourceEntryId: unknown;
60
+ duplicateEntryId: unknown;
61
+ organizationId?: unknown;
62
+ }
63
+ interface EntryReversedPayload {
64
+ originalEntryId: unknown;
65
+ reversalEntryId: unknown;
66
+ reversalDate: Date;
67
+ reversedBy?: unknown;
68
+ organizationId?: unknown;
69
+ }
70
+ interface AccountSeededPayload {
71
+ created: number;
72
+ skipped: number;
73
+ organizationId?: unknown;
74
+ }
75
+ interface AccountBulkCreatedPayload {
76
+ created: number;
77
+ skipped: number;
78
+ errors: number;
79
+ organizationId?: unknown;
80
+ }
81
+ interface JournalSeededPayload {
82
+ created: number;
83
+ skipped: number;
84
+ organizationId?: unknown;
85
+ }
86
+ interface ReconciliationMatchedPayload {
87
+ matchingNumber: string;
88
+ account: unknown;
89
+ itemCount: number;
90
+ debitTotal: number;
91
+ creditTotal: number;
92
+ isFullReconcile: boolean;
93
+ currency: string | null;
94
+ organizationId?: unknown;
95
+ }
96
+ interface ReconciliationUnmatchedPayload {
97
+ matchingNumber: string;
98
+ itemCount: number;
99
+ organizationId?: unknown;
100
+ }
101
+ //#endregion
102
+ //#region src/events/helpers.d.ts
103
+ /** Minimal context shape for event metadata. */
104
+ interface EventContext {
105
+ actorId?: unknown;
106
+ organizationId?: unknown;
107
+ correlationId?: string;
108
+ traceId?: string;
109
+ }
110
+ declare function createEvent$1<T>(type: string, payload: T, ctx?: EventContext, meta?: Partial<DomainEvent['meta']>): DomainEvent<T>;
111
+ //#endregion
112
+ //#region src/events/in-process-bus.d.ts
113
+ /** Package-specific logger contract — `console` satisfies it. */
114
+ interface EventLogger {
115
+ warn(message: string, ...args: unknown[]): void;
116
+ error(message: string, ...args: unknown[]): void;
117
+ }
118
+ interface InProcessLedgerBusOptions {
119
+ logger?: EventLogger;
120
+ }
121
+ declare class InProcessLedgerBus implements EventTransport {
122
+ readonly name = "in-process-ledger";
123
+ private handlers;
124
+ private logger;
125
+ constructor(options?: InProcessLedgerBusOptions);
126
+ publish(event: DomainEvent): Promise<void>;
127
+ publishMany(events: readonly DomainEvent[]): Promise<PublishManyResult>;
128
+ subscribe(pattern: string, handler: EventHandler): Promise<() => void>;
129
+ close(): Promise<void>;
130
+ }
131
+ //#endregion
132
+ //#region src/events/ledger-event-catalog.d.ts
133
+ interface LedgerEventSchema {
134
+ type: 'object';
135
+ properties?: Record<string, {
136
+ type?: string;
137
+ format?: string;
138
+ [key: string]: unknown;
139
+ }>;
140
+ required?: string[];
141
+ [key: string]: unknown;
142
+ }
143
+ interface LedgerEventDefinition<TSchema extends z.ZodType = z.ZodType> {
144
+ readonly name: string;
145
+ readonly version: number;
146
+ readonly description?: string;
147
+ readonly schema: LedgerEventSchema;
148
+ readonly zodSchema: TSchema;
149
+ create(payload: z.infer<TSchema>, meta?: Partial<DomainEvent['meta']>): DomainEvent<z.infer<TSchema>>;
150
+ readonly __payload?: z.infer<TSchema>;
151
+ }
152
+ type LedgerEventPayloadOf<D> = D extends LedgerEventDefinition<infer S> ? z.infer<S> : never;
153
+ declare const seedCountsSchema: z.ZodObject<{
154
+ created: z.ZodNumber;
155
+ skipped: z.ZodNumber;
156
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
157
+ }, z.core.$strip>;
158
+ declare const entryCreatedSchema: z.ZodObject<{
159
+ entryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
160
+ journalType: z.ZodOptional<z.ZodString>;
161
+ state: z.ZodString;
162
+ referenceNumber: z.ZodOptional<z.ZodString>;
163
+ idempotencyKey: z.ZodOptional<z.ZodString>;
164
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
165
+ }, z.core.$strip>;
166
+ declare const entryPostedSchema: z.ZodObject<{
167
+ entryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
168
+ referenceNumber: z.ZodOptional<z.ZodString>;
169
+ postedBy: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
170
+ totalDebit: z.ZodNumber;
171
+ totalCredit: z.ZodNumber;
172
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
173
+ }, z.core.$strip>;
174
+ declare const entryUnpostedSchema: z.ZodObject<{
175
+ entryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
176
+ unpostedBy: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
177
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
178
+ }, z.core.$strip>;
179
+ declare const entryArchivedSchema: z.ZodObject<{
180
+ entryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
181
+ archivedBy: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
182
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
183
+ }, z.core.$strip>;
184
+ declare const entryDuplicatedSchema: z.ZodObject<{
185
+ sourceEntryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
186
+ duplicateEntryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
187
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
188
+ }, z.core.$strip>;
189
+ declare const entryReversedSchema: z.ZodObject<{
190
+ originalEntryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
191
+ reversalEntryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
192
+ reversalDate: z.ZodISODateTime;
193
+ reversedBy: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
194
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
195
+ }, z.core.$strip>;
196
+ declare const accountBulkCreatedSchema: z.ZodObject<{
197
+ created: z.ZodNumber;
198
+ skipped: z.ZodNumber;
199
+ errors: z.ZodNumber;
200
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
201
+ }, z.core.$strip>;
202
+ declare const reconciliationMatchedSchema: z.ZodObject<{
203
+ matchingNumber: z.ZodString;
204
+ account: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
205
+ itemCount: z.ZodNumber;
206
+ debitTotal: z.ZodNumber;
207
+ creditTotal: z.ZodNumber;
208
+ isFullReconcile: z.ZodBoolean;
209
+ currency: z.ZodNullable<z.ZodString>;
210
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
211
+ }, z.core.$strip>;
212
+ declare const reconciliationUnmatchedSchema: z.ZodObject<{
213
+ matchingNumber: z.ZodString;
214
+ itemCount: z.ZodNumber;
215
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
216
+ }, z.core.$strip>;
217
+ type EntryCreatedPayloadSchema = z.infer<typeof entryCreatedSchema>;
218
+ type EntryPostedPayloadSchema = z.infer<typeof entryPostedSchema>;
219
+ type EntryUnpostedPayloadSchema = z.infer<typeof entryUnpostedSchema>;
220
+ type EntryArchivedPayloadSchema = z.infer<typeof entryArchivedSchema>;
221
+ type EntryDuplicatedPayloadSchema = z.infer<typeof entryDuplicatedSchema>;
222
+ type EntryReversedPayloadSchema = z.infer<typeof entryReversedSchema>;
223
+ type AccountSeededPayloadSchema = z.infer<typeof seedCountsSchema>;
224
+ type AccountBulkCreatedPayloadSchema = z.infer<typeof accountBulkCreatedSchema>;
225
+ type JournalSeededPayloadSchema = z.infer<typeof seedCountsSchema>;
226
+ type ReconciliationMatchedPayloadSchema = z.infer<typeof reconciliationMatchedSchema>;
227
+ type ReconciliationUnmatchedPayloadSchema = z.infer<typeof reconciliationUnmatchedSchema>;
228
+ declare const EntryCreated: LedgerEventDefinition<z.ZodObject<{
229
+ entryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
230
+ journalType: z.ZodOptional<z.ZodString>;
231
+ state: z.ZodString;
232
+ referenceNumber: z.ZodOptional<z.ZodString>;
233
+ idempotencyKey: z.ZodOptional<z.ZodString>;
234
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
235
+ }, z.core.$strip>>;
236
+ declare const EntryPosted: LedgerEventDefinition<z.ZodObject<{
237
+ entryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
238
+ referenceNumber: z.ZodOptional<z.ZodString>;
239
+ postedBy: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
240
+ totalDebit: z.ZodNumber;
241
+ totalCredit: z.ZodNumber;
242
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
243
+ }, z.core.$strip>>;
244
+ declare const EntryUnposted: LedgerEventDefinition<z.ZodObject<{
245
+ entryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
246
+ unpostedBy: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
247
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
248
+ }, z.core.$strip>>;
249
+ declare const EntryArchived: LedgerEventDefinition<z.ZodObject<{
250
+ entryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
251
+ archivedBy: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
252
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
253
+ }, z.core.$strip>>;
254
+ declare const EntryDuplicated: LedgerEventDefinition<z.ZodObject<{
255
+ sourceEntryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
256
+ duplicateEntryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
257
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
258
+ }, z.core.$strip>>;
259
+ declare const EntryReversed: LedgerEventDefinition<z.ZodObject<{
260
+ originalEntryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
261
+ reversalEntryId: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
262
+ reversalDate: z.ZodISODateTime;
263
+ reversedBy: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
264
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
265
+ }, z.core.$strip>>;
266
+ declare const AccountSeeded: LedgerEventDefinition<z.ZodObject<{
267
+ created: z.ZodNumber;
268
+ skipped: z.ZodNumber;
269
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
270
+ }, z.core.$strip>>;
271
+ declare const AccountBulkCreated: LedgerEventDefinition<z.ZodObject<{
272
+ created: z.ZodNumber;
273
+ skipped: z.ZodNumber;
274
+ errors: z.ZodNumber;
275
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
276
+ }, z.core.$strip>>;
277
+ declare const JournalSeeded: LedgerEventDefinition<z.ZodObject<{
278
+ created: z.ZodNumber;
279
+ skipped: z.ZodNumber;
280
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
281
+ }, z.core.$strip>>;
282
+ declare const ReconciliationMatched: LedgerEventDefinition<z.ZodObject<{
283
+ matchingNumber: z.ZodString;
284
+ account: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
285
+ itemCount: z.ZodNumber;
286
+ debitTotal: z.ZodNumber;
287
+ creditTotal: z.ZodNumber;
288
+ isFullReconcile: z.ZodBoolean;
289
+ currency: z.ZodNullable<z.ZodString>;
290
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
291
+ }, z.core.$strip>>;
292
+ declare const ReconciliationUnmatched: LedgerEventDefinition<z.ZodObject<{
293
+ matchingNumber: z.ZodString;
294
+ itemCount: z.ZodNumber;
295
+ organizationId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodAny]>>;
296
+ }, z.core.$strip>>;
297
+ /**
298
+ * Every ledger event defined in the package — pass to Arc's
299
+ * `EventRegistry`. Hosts wire ONE array; the whole `ledger:*` namespace
300
+ * becomes introspectable via OpenAPI and auto-validated at publish time
301
+ * when `eventPlugin({ validateMode: 'reject' })` is set.
302
+ */
303
+ declare const ledgerEventDefinitions: ReadonlyArray<LedgerEventDefinition>;
304
+ //#endregion
305
+ export { ReconciliationUnmatchedPayload as $, LedgerEventDefinition as A, InProcessLedgerBusOptions as B, EntryPostedPayloadSchema as C, EntryUnpostedPayloadSchema as D, EntryUnposted as E, ReconciliationUnmatched as F, EntryArchivedPayload as G, createEvent$1 as H, ReconciliationUnmatchedPayloadSchema as I, EntryPostedPayload as J, EntryCreatedPayload as K, ledgerEventDefinitions as L, LedgerEventSchema as M, ReconciliationMatched as N, JournalSeeded as O, ReconciliationMatchedPayloadSchema as P, ReconciliationMatchedPayload as Q, EventLogger as R, EntryPosted as S, EntryReversedPayloadSchema as T, AccountBulkCreatedPayload as U, EventContext as V, AccountSeededPayload as W, EntryUnpostedPayload as X, EntryReversedPayload as Y, JournalSeededPayload as Z, EntryArchivedPayloadSchema as _, OutboxFailOptions as a, EntryDuplicated as b, OutboxFailurePolicy as c, OutboxWriteOptions as d, LEDGER_EVENTS as et, AccountBulkCreated as f, EntryArchived as g, AccountSeededPayloadSchema as h, OutboxErrorInfo as i, LedgerEventPayloadOf as j, JournalSeededPayloadSchema as k, OutboxOwnershipError as l, AccountSeeded as m, OutboxAcknowledgeOptions as n, OutboxFailureContext as o, AccountBulkCreatedPayloadSchema as p, EntryDuplicatedPayload as q, OutboxClaimOptions as r, OutboxFailureDecision as s, InvalidOutboxEventError as t, LedgerEventName as tt, OutboxStore as u, EntryCreated as v, EntryReversed as w, EntryDuplicatedPayloadSchema as x, EntryCreatedPayloadSchema as y, InProcessLedgerBus as z };
@@ -1,4 +1,4 @@
1
- import { i as Errors } from "./errors-BI5k4iak.mjs";
1
+ import { i as Errors } from "./errors-vXd932rB.mjs";
2
2
  import { i as extractMainType } from "./categories-FJlrvzcl.mjs";
3
3
  import mongoose from "mongoose";
4
4
  //#region src/utils/tenant-guard.ts
@@ -1,2 +1,2 @@
1
- import { a as watermarkResolver, c as idempotencyPlugin, i as fiscalLockPlugin, l as doubleEntryPlugin, n as creditLimitPlugin, o as periodResolver, r as dailyLockPlugin, s as createLockPlugin, t as fxRealizationPlugin } from "../fx-realization.plugin-Bxlb8cIx.mjs";
1
+ import { a as watermarkResolver, c as idempotencyPlugin, i as fiscalLockPlugin, l as doubleEntryPlugin, n as creditLimitPlugin, o as periodResolver, r as dailyLockPlugin, s as createLockPlugin, t as fxRealizationPlugin } from "../fx-realization.plugin-Dzqzi3u0.mjs";
2
2
  export { createLockPlugin, creditLimitPlugin, dailyLockPlugin, doubleEntryPlugin, fiscalLockPlugin, fxRealizationPlugin, idempotencyPlugin, periodResolver, watermarkResolver };
@@ -1,2 +1,2 @@
1
- import { T as generateAgedBalance, c as generateRevaluation, d as generateIncomeStatement, f as generateGeneralLedger, g as generateBalanceSheet, h as generateBudgetVsActual, m as generateCashFlow, n as closeFiscalPeriod, p as generateDimensionBreakdown, r as reopenFiscalPeriod, s as generateTrialBalance, t as generatePartnerLedger, w as DEFAULT_BUCKETS } from "../partner-ledger-BoebloHk.mjs";
1
+ import { T as generateAgedBalance, c as generateRevaluation, d as generateIncomeStatement, f as generateGeneralLedger, g as generateBalanceSheet, h as generateBudgetVsActual, m as generateCashFlow, n as closeFiscalPeriod, p as generateDimensionBreakdown, r as reopenFiscalPeriod, s as generateTrialBalance, t as generatePartnerLedger, w as DEFAULT_BUCKETS } from "../partner-ledger-CR0geilx.mjs";
2
2
  export { DEFAULT_BUCKETS, closeFiscalPeriod, generateAgedBalance, generateBalanceSheet, generateBudgetVsActual, generateCashFlow, generateDimensionBreakdown, generateGeneralLedger, generateIncomeStatement, generatePartnerLedger, generateRevaluation, generateTrialBalance, reopenFiscalPeriod };
@@ -166,9 +166,19 @@ interface LedgerPaymentInput {
166
166
  */
167
167
  interface LedgerBridge {
168
168
  createJournalEntry(input: LedgerPostInput): Promise<string>;
169
- reverseJournalEntry(journalEntryId: string, reason: string): Promise<string>;
169
+ reverseJournalEntry(journalEntryId: string, reason: string, ctx: LedgerReverseContext): Promise<string>;
170
170
  recordPayment(input: LedgerPaymentInput): Promise<string>;
171
171
  }
172
+ /**
173
+ * Context threaded from the invoice engine into the ledger reversal call.
174
+ * Required so the ledger can stamp `reversedByUser` and satisfy strict-mode
175
+ * `requireActor` / multi-tenant guards.
176
+ */
177
+ interface LedgerReverseContext {
178
+ organizationId?: string;
179
+ actorId?: string;
180
+ session?: unknown;
181
+ }
172
182
  interface AdjustmentLine {
173
183
  account: string;
174
184
  debit?: number;
@@ -200,6 +210,7 @@ interface JournalEntryRepoLike {
200
210
  reverse(id: unknown, orgId?: unknown, options?: {
201
211
  actorId?: string;
202
212
  session?: unknown;
213
+ reversalDate?: Date;
203
214
  }): Promise<{
204
215
  original: {
205
216
  _id: unknown;
@@ -310,4 +321,4 @@ declare function wireImport<TRaw>(args: WireImportArgs<TRaw>): {
310
321
  run(): Promise<ImportReport>;
311
322
  };
312
323
  //#endregion
313
- export { type BankStatementMapperConfig, type ExportReport, type ExportSink, type ImportContext, type ImportError, type ImportMapper, type ImportReport, type InvoiceMapperConfig, type JournalEntryInput, type JournalEntryMapperConfig, type JournalItemInput, type LedgerBridge, type LedgerBridgeAccounts, type LedgerBridgeConfig, type LedgerPaymentInput, type LedgerPostInput, type LedgerPostLine, type OpeningBalanceInput, type OpeningBalanceMapperConfig, type OpeningBalanceResult, type TrialBalanceInput, type WireExportArgs, type WireImportArgs, bankStatementMapper, buildOpeningBalanceEntry, createLedgerBridge, invoiceMapper, journalEntryMapper, openingBalanceMapper, wireExport, wireImport };
324
+ export { type BankStatementMapperConfig, type ExportReport, type ExportSink, type ImportContext, type ImportError, type ImportMapper, type ImportReport, type InvoiceMapperConfig, type JournalEntryInput, type JournalEntryMapperConfig, type JournalItemInput, type LedgerBridge, type LedgerBridgeAccounts, type LedgerBridgeConfig, type LedgerPaymentInput, type LedgerPostInput, type LedgerPostLine, type LedgerReverseContext, type OpeningBalanceInput, type OpeningBalanceMapperConfig, type OpeningBalanceResult, type TrialBalanceInput, type WireExportArgs, type WireImportArgs, bankStatementMapper, buildOpeningBalanceEntry, createLedgerBridge, invoiceMapper, journalEntryMapper, openingBalanceMapper, wireExport, wireImport };
@@ -102,8 +102,11 @@ function createLedgerBridge(engine, config) {
102
102
  }, input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {});
103
103
  return String(result._id);
104
104
  },
105
- async reverseJournalEntry(journalEntryId, _reason) {
106
- const result = await engine.repositories.journalEntries.reverse(journalEntryId);
105
+ async reverseJournalEntry(journalEntryId, _reason, ctx) {
106
+ const options = {};
107
+ if (ctx.actorId) options.actorId = ctx.actorId;
108
+ if (ctx.session) options.session = ctx.session;
109
+ const result = await engine.repositories.journalEntries.reverse(journalEntryId, ctx.organizationId, options);
107
110
  return String(result.reversal._id);
108
111
  },
109
112
  async recordPayment(input) {
@@ -473,7 +476,7 @@ function wireImport(args) {
473
476
  processed: records.length
474
477
  };
475
478
  if (useBulk) try {
476
- await args.journalEntries.createMany(pendingDocs.map((p) => p.doc));
479
+ await args.journalEntries.createMany?.(pendingDocs.map((p) => p.doc));
477
480
  batchInserted += pendingDocs.length;
478
481
  } catch (_err) {
479
482
  for (const { externalId, doc } of pendingDocs) try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/ledger",
3
- "version": "0.9.1",
3
+ "version": "0.10.1",
4
4
  "description": "Production-grade double-entry accounting engine for MongoDB — schemas, reports, tax, multi-tenant",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -94,8 +94,11 @@
94
94
  },
95
95
  "peerDependencies": {
96
96
  "@classytic/fin-io": ">=0.1.0",
97
- "@classytic/mongokit": ">=3.6.4",
98
- "mongoose": ">=9.4.1"
97
+ "@classytic/mongokit": ">=3.11.0",
98
+ "@classytic/primitives": ">=0.1.0",
99
+ "@classytic/repo-core": ">=0.2.0",
100
+ "mongoose": ">=9.4.1",
101
+ "zod": ">=4.0.0"
99
102
  },
100
103
  "peerDependenciesMeta": {
101
104
  "@classytic/fin-io": {
@@ -104,8 +107,10 @@
104
107
  },
105
108
  "devDependencies": {
106
109
  "@biomejs/biome": "^2.4.12",
107
- "@classytic/fin-io": "^0.1.0",
108
- "@classytic/mongokit": "^3.6.4",
110
+ "@classytic/fin-io": ">=0.1.0",
111
+ "@classytic/mongokit": ">=3.11.0",
112
+ "@classytic/primitives": ">=0.1.0",
113
+ "@classytic/repo-core": ">=0.2.0",
109
114
  "@types/node": "^25.5.0",
110
115
  "@vitest/coverage-v8": "^4.1.4",
111
116
  "knip": "^6.4.1",
@@ -113,7 +118,8 @@
113
118
  "mongoose": "^9.4.1",
114
119
  "tsdown": "^0.21.8",
115
120
  "typescript": "^5.7.0",
116
- "vitest": "^4.1.4"
121
+ "vitest": "^4.1.4",
122
+ "zod": "^4.3.6"
117
123
  },
118
124
  "engines": {
119
125
  "node": ">=22"