@book.dev/sdk 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/backup.d.ts +75 -1
  2. package/dist/backup.js +8 -1
  3. package/dist/backup.js.map +1 -1
  4. package/dist/client.d.ts +134 -2
  5. package/dist/client.js +171 -0
  6. package/dist/client.js.map +1 -1
  7. package/dist/csv.d.ts +49 -0
  8. package/dist/csv.js +116 -0
  9. package/dist/csv.js.map +1 -0
  10. package/dist/importAssets.d.ts +10 -0
  11. package/dist/importAssets.js +17 -0
  12. package/dist/importAssets.js.map +1 -1
  13. package/dist/index.d.ts +10 -4
  14. package/dist/index.js +9 -3
  15. package/dist/index.js.map +1 -1
  16. package/dist/ledger.d.ts +772 -0
  17. package/dist/ledger.js +493 -0
  18. package/dist/ledger.js.map +1 -0
  19. package/dist/ledgerBeancount.d.ts +117 -0
  20. package/dist/ledgerBeancount.js +309 -0
  21. package/dist/ledgerBeancount.js.map +1 -0
  22. package/dist/ledgerBeancountFixture.d.ts +61 -0
  23. package/dist/ledgerBeancountFixture.js +459 -0
  24. package/dist/ledgerBeancountFixture.js.map +1 -0
  25. package/dist/ledgerCsv.d.ts +76 -0
  26. package/dist/ledgerCsv.js +197 -0
  27. package/dist/ledgerCsv.js.map +1 -0
  28. package/dist/money.d.ts +179 -0
  29. package/dist/money.js +283 -0
  30. package/dist/money.js.map +1 -0
  31. package/dist/notionImport.d.ts +5 -3
  32. package/dist/notionImport.js +6 -56
  33. package/dist/notionImport.js.map +1 -1
  34. package/dist/plugins.d.ts +24 -0
  35. package/dist/plugins.js +25 -0
  36. package/dist/plugins.js.map +1 -1
  37. package/dist/provenance.d.ts +17 -0
  38. package/dist/provenance.js.map +1 -1
  39. package/dist/routes.d.ts +121 -0
  40. package/dist/routes.js +122 -0
  41. package/dist/routes.js.map +1 -1
  42. package/dist/types.d.ts +8 -0
  43. package/dist/types.js.map +1 -1
  44. package/package.json +1 -1
@@ -0,0 +1,459 @@
1
+ /**
2
+ * Deterministic fixture books for the Beancount reference harness (LGR-13).
3
+ *
4
+ * The parity/bean-check gate needs a 500-transaction book; committing the
5
+ * serialized fixture would be megabytes of noise, so the GENERATOR is the
6
+ * artifact: seeded, pure, no clock — every call reproduces the identical book,
7
+ * so the exported journal is byte-identical across machines and runs (which is
8
+ * itself asserted by the byte-stability tests).
9
+ *
10
+ * Two books:
11
+ * - {@link buildBeancountMiniBook} — small and hostile: names that need
12
+ * mangling (spaces, unicode, lowercase, a bare-root name, a mangling
13
+ * collision), quote/backslash/newline text, a reversal pair, evidence,
14
+ * a closed period with its closing entry, and a draft (which must NOT
15
+ * export);
16
+ * - {@link buildBeancountParityBook} — exactly 500 reported (posted + void)
17
+ * transactions across two currencies, with reversal pairs, a closed period
18
+ * (closing entry + balance assertions), a reopened period (void closing
19
+ * entry + reversal), evidence, and drafts.
20
+ *
21
+ * Exported from the SDK because three consumers must share ONE book: the sdk
22
+ * unit tests, the ui parity gate (which runs the plugin's LGR-8 fold through
23
+ * the real loader), and the CI bean-check job.
24
+ */
25
+ import { sumAmounts } from './money';
26
+ /** mulberry32 — tiny deterministic PRNG; good enough to spread fixture data. */
27
+ function mulberry32(seed) {
28
+ let a = seed >>> 0;
29
+ return () => {
30
+ a = (a + 0x6d2b79f5) >>> 0;
31
+ let t = a;
32
+ t = Math.imul(t ^ (t >>> 15), t | 1);
33
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
34
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
35
+ };
36
+ }
37
+ /** `base + days`, as ISO `YYYY-MM-DD` (UTC). */
38
+ function isoDay(base, days) {
39
+ const d = new Date(`${base}T00:00:00Z`);
40
+ d.setUTCDate(d.getUTCDate() + days);
41
+ return d.toISOString().slice(0, 10);
42
+ }
43
+ /** A stable fake row timestamp — index-ordered so creation order is total. */
44
+ function stamp(index) {
45
+ return new Date(Date.UTC(2026, 0, 1, 0, 0, 0) + index * 1000).toISOString();
46
+ }
47
+ /** Book assembler: sequential ids/entry numbers, balanced-by-construction. */
48
+ class BookBuilder {
49
+ constructor() {
50
+ this.accounts = [];
51
+ this.transactions = [];
52
+ this.periods = [];
53
+ this.seq = 0;
54
+ }
55
+ account(name, type, currency = 'USD') {
56
+ this.seq += 1;
57
+ const account = {
58
+ id: `fx-a-${String(this.accounts.length + 1).padStart(2, '0')}`,
59
+ name,
60
+ type,
61
+ status: 'open',
62
+ currency,
63
+ evidenceRequired: false,
64
+ createdAt: stamp(this.seq),
65
+ updatedAt: stamp(this.seq),
66
+ };
67
+ this.accounts.push(account);
68
+ return account;
69
+ }
70
+ tx(spec) {
71
+ this.seq += 1;
72
+ const state = spec.state ?? 'posted';
73
+ const id = `fx-t-${String(this.transactions.length + 1).padStart(4, '0')}`;
74
+ const postings = spec.postings.map((p, i) => ({
75
+ id: `${id}-p${i + 1}`,
76
+ transactionId: id,
77
+ accountId: p.accountId,
78
+ amountMinor: p.amountMinor,
79
+ cleared: p.cleared ?? 'pending',
80
+ reconciliationId: null,
81
+ memo: p.memo ?? null,
82
+ }));
83
+ const drift = sumAmounts(postings.map((p) => p.amountMinor));
84
+ if (state !== 'draft' && drift !== 0) {
85
+ throw new Error(`fixture bug: transaction ${id} is out of balance by ${drift} minor units`);
86
+ }
87
+ const entryNo = state === 'draft' ? null : this.transactions.filter((t) => t.entryNo != null).length + 1;
88
+ const tx = {
89
+ id,
90
+ date: spec.date,
91
+ description: spec.description,
92
+ state,
93
+ postedAt: state === 'draft' ? null : stamp(this.seq),
94
+ postedBy: state === 'draft' ? null : 'https://fixture.book.pub#keeper',
95
+ reverses: spec.reverses ?? null,
96
+ entryNo,
97
+ kind: spec.kind ?? null,
98
+ evidence: spec.evidence ?? [],
99
+ postings,
100
+ createdAt: stamp(this.seq),
101
+ updatedAt: stamp(this.seq),
102
+ };
103
+ this.transactions.push(tx);
104
+ return tx;
105
+ }
106
+ /**
107
+ * Reverse a posted transaction the way the store does: the original flips to
108
+ * `void`, and a reversal posts with every leg negated and `reverses` set.
109
+ */
110
+ reverse(original, date) {
111
+ original.state = 'void';
112
+ return this.tx({
113
+ date,
114
+ description: `Reversal of ${original.description}`,
115
+ postings: original.postings.map((p) => ({ accountId: p.accountId, amountMinor: -p.amountMinor })),
116
+ reverses: original.id,
117
+ });
118
+ }
119
+ /**
120
+ * Close `[start, end]` the way the store does: sweep every income-statement
121
+ * account's reported balance as of `end` into retained earnings via a
122
+ * `closing` entry dated `end`, and record the period. Returns the entry
123
+ * (`null` when nothing needed sweeping).
124
+ */
125
+ closePeriod(start, end, retainedEarnings, incomeStatementAccounts) {
126
+ const legs = [];
127
+ for (const account of incomeStatementAccounts) {
128
+ const amounts = [];
129
+ for (const t of this.transactions) {
130
+ if (t.state === 'draft' || t.date > end)
131
+ continue;
132
+ for (const p of t.postings)
133
+ if (p.accountId === account.id)
134
+ amounts.push(p.amountMinor);
135
+ }
136
+ const balance = sumAmounts(amounts);
137
+ if (balance !== 0)
138
+ legs.push({ accountId: account.id, amountMinor: -balance });
139
+ }
140
+ const sweep = sumAmounts(legs.map((l) => l.amountMinor));
141
+ let closingEntry = null;
142
+ if (legs.length > 0) {
143
+ closingEntry = this.tx({
144
+ date: end,
145
+ description: `Closing entry ${start} .. ${end}`,
146
+ postings: [...legs, { accountId: retainedEarnings.id, amountMinor: -sweep }],
147
+ kind: 'closing',
148
+ });
149
+ }
150
+ this.seq += 1;
151
+ const period = {
152
+ id: `fx-per-${String(this.periods.length + 1).padStart(2, '0')}`,
153
+ start,
154
+ end,
155
+ status: 'closed',
156
+ closingEntryId: closingEntry?.id ?? null,
157
+ reopenEntryId: null,
158
+ closedAt: stamp(this.seq),
159
+ closedBy: 'https://fixture.book.pub#keeper',
160
+ reopenedAt: null,
161
+ reopenedBy: null,
162
+ };
163
+ this.periods.push(period);
164
+ return { period, closingEntry };
165
+ }
166
+ /** Reopen a closed period the way the store does: reversal voids the
167
+ * closing entry; the record flips to `reopened` (range unlocked, no
168
+ * balance assertions emitted for it). */
169
+ reopenPeriod(period, date) {
170
+ let reversal = null;
171
+ if (period.closingEntryId !== null) {
172
+ const original = this.transactions.find((t) => t.id === period.closingEntryId);
173
+ if (original)
174
+ reversal = this.reverse(original, date);
175
+ }
176
+ this.seq += 1;
177
+ period.status = 'reopened';
178
+ period.reopenEntryId = reversal?.id ?? null;
179
+ period.reopenedAt = stamp(this.seq);
180
+ period.reopenedBy = 'https://fixture.book.pub#keeper';
181
+ }
182
+ book() {
183
+ return { accounts: this.accounts, transactions: this.transactions, periods: this.periods };
184
+ }
185
+ }
186
+ /**
187
+ * The small hostile book: mangling edge cases, hostile text, a reversal pair,
188
+ * evidence, a closed period, and a draft that must not export.
189
+ */
190
+ export function buildBeancountMiniBook() {
191
+ const b = new BookBuilder();
192
+ const bank = b.account('Assets:Bank:Checking', 'asset');
193
+ const bare = b.account('Assets', 'asset'); // bare root → Assets:Assets
194
+ const card = b.account('Liabilities:CreditCard', 'liability');
195
+ const opening = b.account('Equity:OpeningBalances', 'equity');
196
+ const retained = b.account('Equity:RetainedEarnings', 'equity');
197
+ const sales = b.account('Revenue:Sales', 'revenue'); // root remap → Income:Revenue:Sales
198
+ const fees = b.account('Expenses:Bank Fees', 'expense'); // space → Expenses:Bank-Fees
199
+ const feesClash = b.account('Expenses:Bank-Fees', 'expense'); // collision → …-2
200
+ const cafe = b.account('Expenses:café & misc.', 'expense'); // unicode/punct mangle
201
+ const lower = b.account('misc:stuff', 'expense'); // lowercase components
202
+ b.tx({
203
+ date: '2026-01-01',
204
+ description: 'Opening balance',
205
+ postings: [
206
+ { accountId: bank.id, amountMinor: 500000, cleared: 'cleared' },
207
+ { accountId: opening.id, amountMinor: -500000 },
208
+ ],
209
+ });
210
+ b.tx({
211
+ date: '2026-01-05',
212
+ description: 'He said "hi" \\ and \n typed =SUM(A1)',
213
+ postings: [
214
+ { accountId: bank.id, amountMinor: 123456, memo: 'gross "wages" \\ note' },
215
+ { accountId: sales.id, amountMinor: -123456 },
216
+ ],
217
+ evidence: [{ filename: 'invoice-1.pdf', sha256: 'a'.repeat(64), size: 1234 }],
218
+ });
219
+ const fee = b.tx({
220
+ date: '2026-01-07',
221
+ description: 'Wire fee (typo)',
222
+ postings: [
223
+ { accountId: fees.id, amountMinor: 2500 },
224
+ { accountId: bank.id, amountMinor: -2500 },
225
+ ],
226
+ });
227
+ b.reverse(fee, '2026-01-08');
228
+ b.tx({
229
+ date: '2026-01-09',
230
+ description: 'Wire fee',
231
+ postings: [
232
+ { accountId: feesClash.id, amountMinor: 2500 },
233
+ { accountId: bank.id, amountMinor: -2500, cleared: 'cleared' },
234
+ ],
235
+ });
236
+ b.tx({
237
+ date: '2026-01-15',
238
+ description: 'Coffee + sundries on the card',
239
+ postings: [
240
+ { accountId: cafe.id, amountMinor: 1800, memo: 'espresso' },
241
+ { accountId: lower.id, amountMinor: 700 },
242
+ { accountId: card.id, amountMinor: -2500 },
243
+ ],
244
+ });
245
+ b.closePeriod('2026-01-01', '2026-01-31', retained, [sales, fees, feesClash, cafe, lower]);
246
+ b.tx({
247
+ date: '2026-02-02',
248
+ description: 'A lingering draft (must not export)',
249
+ postings: [
250
+ { accountId: bank.id, amountMinor: 1 },
251
+ { accountId: bare.id, amountMinor: 1 },
252
+ ],
253
+ state: 'draft',
254
+ });
255
+ return b.book();
256
+ }
257
+ /** How many reported (posted + void) transactions the parity book carries. */
258
+ export const BEANCOUNT_PARITY_TX_COUNT = 500;
259
+ /**
260
+ * The 500-transaction parity book: two currencies, reversal pairs, one closed
261
+ * period (with closing entry — its balance assertions gate the close in
262
+ * bean-check), one reopened period, evidence, drafts.
263
+ */
264
+ export function buildBeancountParityBook() {
265
+ const rnd = mulberry32(0x1ed6e12);
266
+ const b = new BookBuilder();
267
+ const bank = b.account('Assets:Bank:Checking', 'asset');
268
+ const cash = b.account('Assets:Cash', 'asset');
269
+ const savings = b.account('Assets:Savings', 'asset', 'EUR');
270
+ const card = b.account('Liabilities:CreditCard', 'liability');
271
+ const opening = b.account('Equity:OpeningBalances', 'equity');
272
+ const retained = b.account('Equity:RetainedEarnings', 'equity');
273
+ const revenue = b.account('Income:Revenue', 'revenue');
274
+ const sales = b.account('Revenue:Sales', 'revenue');
275
+ const interest = b.account('Income:Interest', 'revenue', 'EUR');
276
+ const fees = b.account('Expenses:Bank Fees', 'expense');
277
+ const hosting = b.account('Expenses:Hosting', 'expense');
278
+ const office = b.account('Expenses:Office', 'expense');
279
+ const dormant = b.account('Assets:Escrow', 'asset'); // zero postings — open only
280
+ void dormant;
281
+ const amount = () => 1 + Math.floor(rnd() * 99999);
282
+ const cleared = () => (rnd() < 0.3 ? 'cleared' : 'pending');
283
+ b.tx({
284
+ date: '2024-01-01',
285
+ description: 'Opening balance',
286
+ postings: [
287
+ { accountId: bank.id, amountMinor: 2000000, cleared: 'cleared' },
288
+ { accountId: opening.id, amountMinor: -2000000 },
289
+ ],
290
+ });
291
+ // 487 randomized entries. Everything up to 2025-01-31 stays USD-only so both
292
+ // period closes sweep a single currency (the store's closing entry is
293
+ // uniform-currency — a mixed book could not close these ranges at all); the
294
+ // EUR pair trades from February 2025 only.
295
+ const reversalCandidates = [];
296
+ for (let i = 0; i < 487; i += 1) {
297
+ const day = Math.floor(rnd() * 540); // 2024-01-01 .. 2025-06-23
298
+ const date = isoDay('2024-01-01', day);
299
+ const inEur = date >= '2025-02-01' && rnd() < 0.15;
300
+ let tx;
301
+ if (inEur) {
302
+ const a = amount();
303
+ tx = b.tx({
304
+ date,
305
+ description: `Interest accrual #${i + 1}`,
306
+ postings: [
307
+ { accountId: savings.id, amountMinor: a, cleared: cleared() },
308
+ { accountId: interest.id, amountMinor: -a },
309
+ ],
310
+ });
311
+ }
312
+ else if (rnd() < 0.45) {
313
+ const a = amount();
314
+ const into = rnd() < 0.7 ? bank : cash;
315
+ const from = rnd() < 0.6 ? revenue : sales;
316
+ tx = b.tx({
317
+ date,
318
+ description: `Sale #${i + 1} "receipt"`,
319
+ postings: [
320
+ { accountId: into.id, amountMinor: a, cleared: cleared() },
321
+ { accountId: from.id, amountMinor: -a, memo: rnd() < 0.2 ? `order \\ ${i}` : undefined },
322
+ ],
323
+ evidence: rnd() < 0.05 ? [{ filename: `receipt-${i}.pdf`, sha256: `${i.toString(16)}`.padStart(64, '0'), size: 100 + i }] : undefined,
324
+ });
325
+ }
326
+ else if (rnd() < 0.5) {
327
+ const a = amount();
328
+ const b1 = amount();
329
+ const spend = rnd() < 0.5 ? hosting : office;
330
+ tx = b.tx({
331
+ date,
332
+ description: `Vendor bill #${i + 1}`,
333
+ postings: [
334
+ { accountId: spend.id, amountMinor: a, memo: rnd() < 0.15 ? `line "one" #${i}` : undefined },
335
+ { accountId: fees.id, amountMinor: b1 },
336
+ { accountId: (rnd() < 0.5 ? bank : card).id, amountMinor: -(a + b1), cleared: cleared() },
337
+ ],
338
+ });
339
+ }
340
+ else {
341
+ const a = amount();
342
+ tx = b.tx({
343
+ date,
344
+ description: `Card payment #${i + 1}`,
345
+ postings: [
346
+ { accountId: card.id, amountMinor: a },
347
+ { accountId: bank.id, amountMinor: -a, cleared: cleared() },
348
+ ],
349
+ });
350
+ }
351
+ if (reversalCandidates.length < 5 && rnd() < 0.03)
352
+ reversalCandidates.push(tx);
353
+ }
354
+ // 5 reversal pairs (original flips void; reversal posts) — 10 of the 500.
355
+ for (const original of reversalCandidates) {
356
+ b.reverse(original, isoDay(original.date, 3) <= '2025-06-30' ? isoDay(original.date, 3) : original.date);
357
+ }
358
+ // A guaranteed January-2025 sale, so the Jan-2025 close below always has an
359
+ // income balance to sweep (its closing entry + reversal are counted on).
360
+ b.tx({
361
+ date: '2025-01-15',
362
+ description: 'January retainer',
363
+ postings: [
364
+ { accountId: bank.id, amountMinor: 150000, cleared: 'cleared' },
365
+ { accountId: revenue.id, amountMinor: -150000 },
366
+ ],
367
+ });
368
+ // Top up to exactly 497 reported: the 2024 closing entry (498), the Jan-2025
369
+ // closing entry (499) and the reopen's reversal (500) complete the book.
370
+ while (b.transactions.filter((t) => t.entryNo != null).length < BEANCOUNT_PARITY_TX_COUNT - 3) {
371
+ const a = amount();
372
+ b.tx({
373
+ date: isoDay('2025-06-24', b.transactions.length % 6),
374
+ description: `Top-up entry ${b.transactions.length}`,
375
+ postings: [
376
+ { accountId: office.id, amountMinor: a },
377
+ { accountId: bank.id, amountMinor: -a },
378
+ ],
379
+ });
380
+ }
381
+ // Close 2024 (USD-only activity ⇒ uniform-currency sweep).
382
+ const incomeStatement = [revenue, sales, interest, fees, hosting, office];
383
+ b.closePeriod('2024-01-01', '2024-12-31', retained, incomeStatement);
384
+ // Close and REOPEN January 2025: the closing entry flips void and its
385
+ // reversal posts — a reopened period must emit NO balance assertions while
386
+ // both halves of the voided close still export.
387
+ const jan = b.closePeriod('2025-01-01', '2025-01-31', retained, incomeStatement);
388
+ if (jan.closingEntry === null)
389
+ throw new Error('fixture bug: the Jan-2025 close swept nothing');
390
+ b.reopenPeriod(jan.period, '2025-02-03');
391
+ // Drafts never export (two of them, one empty-ish).
392
+ b.tx({ date: '2025-06-30', description: 'draft: pending vendor bill', postings: [{ accountId: office.id, amountMinor: 4200 }, { accountId: bank.id, amountMinor: -4200 }], state: 'draft' });
393
+ b.tx({ date: '2025-07-01', description: 'draft: notes only', postings: [], state: 'draft' });
394
+ const reported = b.transactions.filter((t) => t.entryNo != null).length;
395
+ if (reported !== BEANCOUNT_PARITY_TX_COUNT) {
396
+ throw new Error(`fixture bug: parity book carries ${reported} reported transactions, expected ${BEANCOUNT_PARITY_TX_COUNT}`);
397
+ }
398
+ return b.book();
399
+ }
400
+ /** Postings carried by {@link buildLedgerBenchBook} with default arguments. */
401
+ export const LEDGER_BENCH_POSTING_COUNT = 10000;
402
+ /**
403
+ * The LGR-15 benchmark book: `txCount` posted transactions of `legsPerTx`
404
+ * postings each (defaults: 1000 × 10 = {@link LEDGER_BENCH_POSTING_COUNT}
405
+ * postings). Seeded/deterministic like the parity book — the GENERATOR is the
406
+ * committed artifact, never the serialized data.
407
+ *
408
+ * Shape rationale: the trial-balance benchmark reads the book through
409
+ * `listTransactions`, whose hard cap is `LEDGER_MAX_TRANSACTION_LIMIT` (1000)
410
+ * — so the 10k postings ride on exactly 1000 transactions, which keeps the
411
+ * benchmarked read the SAME single-page read the report block performs on a
412
+ * real book. Single currency, no drafts/periods/evidence: the benchmark
413
+ * measures the arithmetic + read path, not fixture variety (the parity book
414
+ * covers variety).
415
+ */
416
+ export function buildLedgerBenchBook(txCount = 1000, legsPerTx = 10) {
417
+ if (legsPerTx < 2)
418
+ throw new Error('a journal entry needs at least 2 postings');
419
+ const rnd = mulberry32(0x1ed6e15);
420
+ const b = new BookBuilder();
421
+ const bank = b.account('Assets:Bank:Checking', 'asset');
422
+ const cash = b.account('Assets:Cash', 'asset');
423
+ const card = b.account('Liabilities:CreditCard', 'liability');
424
+ const opening = b.account('Equity:OpeningBalances', 'equity');
425
+ const retained = b.account('Equity:RetainedEarnings', 'equity');
426
+ const revenue = b.account('Income:Revenue', 'revenue');
427
+ const sales = b.account('Revenue:Sales', 'revenue');
428
+ const fees = b.account('Expenses:Bank Fees', 'expense');
429
+ const hosting = b.account('Expenses:Hosting', 'expense');
430
+ const office = b.account('Expenses:Office', 'expense');
431
+ void retained; // present so period closes stay possible on a seeded copy
432
+ const spread = [cash, card, opening, revenue, sales, fees, hosting, office];
433
+ for (let i = 0; i < txCount; i += 1) {
434
+ const date = isoDay('2025-01-01', Math.floor(rnd() * 180));
435
+ // legsPerTx - 1 random legs + one bank leg balancing the entry to zero.
436
+ const legs = [];
437
+ let sum = 0;
438
+ for (let leg = 0; leg < legsPerTx - 1; leg += 1) {
439
+ const amount = 1 + Math.floor(rnd() * 99999);
440
+ const signed = rnd() < 0.5 ? amount : -amount;
441
+ legs.push({ accountId: spread[Math.floor(rnd() * spread.length)].id, amountMinor: signed });
442
+ sum += signed;
443
+ }
444
+ if (sum === 0) {
445
+ // A zero balancing leg would be a degenerate posting — nudge the first leg
446
+ // by one minor unit so the bank leg is nonzero and the entry stays zero-sum.
447
+ legs[0] = { ...legs[0], amountMinor: legs[0].amountMinor + 1 };
448
+ sum += 1;
449
+ }
450
+ legs.push({ accountId: bank.id, amountMinor: -sum });
451
+ b.tx({ date, description: `Bench entry #${i + 1}`, postings: legs });
452
+ }
453
+ const postings = b.transactions.reduce((n, t) => n + t.postings.length, 0);
454
+ if (postings !== txCount * legsPerTx) {
455
+ throw new Error(`fixture bug: bench book carries ${postings} postings, expected ${txCount * legsPerTx}`);
456
+ }
457
+ return b.book();
458
+ }
459
+ //# sourceMappingURL=ledgerBeancountFixture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledgerBeancountFixture.js","sourceRoot":"","sources":["../src/ledgerBeancountFixture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Canonical postings CSV (LGR-7): the ledger's insurance export.
3
+ *
4
+ * A book must always be able to LEAVE the app in a canonical, tool-agnostic
5
+ * form. This module is a PURE function from ledger entities to CSV bytes —
6
+ * no I/O, no clock, no locale — with the hard guarantee that the SAME DATA
7
+ * always produces IDENTICAL BYTES:
8
+ *
9
+ * - one row per posting, long form (a transaction with N postings emits N rows);
10
+ * - deterministic ordering: transactions by (entry_no ascending, drafts —
11
+ * which have no entry number — after all numbered entries by createdAt then
12
+ * id), postings in creation order within their transaction;
13
+ * - RFC-4180 quoting (fields containing `"` `,` CR or LF are quoted, inner
14
+ * quotes doubled), LF line endings, UTF-8 with NO BOM, trailing newline;
15
+ * - amounts appear twice: `amount_minor` (the raw signed integer of minor
16
+ * units — the authoritative value) and `amount_formatted` (the locale-pinned
17
+ * {@link formatAmount} display string, e.g. `$1,234.56`);
18
+ * - output depends only on FIELD VALUES, never on object key order.
19
+ */
20
+ import type { LedgerAccount, LedgerTransaction } from './ledger';
21
+ /**
22
+ * The canonical column set, in order.
23
+ *
24
+ * | column | content |
25
+ * |-------------------|--------------------------------------------------------------------|
26
+ * | entry_no | server-assigned monotonic entry number; empty for drafts |
27
+ * | transaction_id | transaction row id (UUID) |
28
+ * | date | transaction ISO date (`YYYY-MM-DD`) |
29
+ * | description | transaction description (free text — see the apostrophe note) |
30
+ * | state | `draft` / `posted` / `void` |
31
+ * | posting_id | posting row id (UUID) |
32
+ * | account_name | hierarchical account name (`Assets:Bank:Checking`) |
33
+ * | account_type | `asset` / `liability` / `equity` / `revenue` / `expense` |
34
+ * | amount_minor | signed integer minor units (authoritative; ALWAYS verbatim) |
35
+ * | amount_formatted | {@link formatAmount} display string; EMPTY if the amount is corrupt |
36
+ * | currency | the account's ISO-4217-shaped code |
37
+ * | cleared | `pending` / `cleared` / `reconciled` |
38
+ * | reconciliation_id | reconciliation row id, empty when none |
39
+ * | reverses | id of the transaction this entry reverses, empty when none |
40
+ * | posted_at | ISO timestamp stamped at post time, empty for drafts |
41
+ * | posted_by | principal subject that posted, empty for drafts |
42
+ * | evidence_sha256s | semicolon-joined SHA-256 hashes of attached evidence |
43
+ * | memo | the POSTING's own free-text note (LGR-16); empty when none |
44
+ *
45
+ * **Adding a column.** `memo` was APPENDED (LGR-16), never inserted: this list
46
+ * is a documented contract, and a consumer that reads columns POSITIONALLY —
47
+ * the spreadsheet formula somebody wrote against `I2`, the awk one-liner in a
48
+ * runbook — keeps working across the change iff existing columns do not move.
49
+ * Every future column belongs at the end for the same reason.
50
+ *
51
+ * **Free-text columns and the leading apostrophe.** `description`, `memo`,
52
+ * `account_name` and `posted_by` carry text a ledger writer authored, so a
53
+ * value that would open as a spreadsheet FORMULA (leading `=` `+` `-` `@`, tab
54
+ * or CR) is emitted with a single leading apostrophe — the universal
55
+ * "treat this as text" marker. A value that ALREADY begins with `'` is prefixed
56
+ * too, which is what makes the escape INJECTIVE: a RE-IMPORTER strips ONE
57
+ * leading `'` from exactly these four columns and recovers the original value
58
+ * exactly, whether it was a formula lead-in or a genuine apostrophe. No other
59
+ * column is ever prefixed (notably `amount_minor`, whose negatives legitimately
60
+ * start with `-`), so machine columns round-trip byte-exactly.
61
+ */
62
+ export declare const LEDGER_CSV_COLUMNS: readonly ["entry_no", "transaction_id", "date", "description", "state", "posting_id", "account_name", "account_type", "amount_minor", "amount_formatted", "currency", "cleared", "reconciliation_id", "reverses", "posted_at", "posted_by", "evidence_sha256s", "memo"];
63
+ /**
64
+ * Build the canonical postings CSV from the full account list and the full
65
+ * transaction list (with postings). Pure and deterministic — see module doc.
66
+ *
67
+ * Ordering: numbered entries (posted/void) ascend by `entryNo`; drafts (no
68
+ * entry number) follow, ordered by (`createdAt`, `id`). Postings keep the
69
+ * creation order their transaction carries. A posting whose account does not
70
+ * resolve (should never happen — the verifier flags it) emits empty
71
+ * account_name/account_type/currency and a currency-less formatted amount
72
+ * rather than throwing, and an unformattable (raw-corrupted) amount emits an
73
+ * empty `amount_formatted` while `amount_minor` keeps the stored value
74
+ * verbatim — the insurance export always leaves the building.
75
+ */
76
+ export declare function buildLedgerPostingsCsv(accounts: readonly LedgerAccount[], transactions: readonly LedgerTransaction[]): string;