@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,309 @@
1
+ /**
2
+ * Beancount export (LGR-13): the ledger serialized as a Beancount journal, so
3
+ * the whole book can be re-checked by an INDEPENDENT accounting implementation
4
+ * (`bean-check`, the beancount loader, Fava) — free QA for every invariant the
5
+ * ledger claims to enforce.
6
+ *
7
+ * This module is a PURE function from ledger entities to journal text — no
8
+ * I/O, no clock, no locale — with the same hard guarantee as the canonical CSV
9
+ * (`./ledgerCsv`): the SAME DATA always produces IDENTICAL BYTES. It reads the
10
+ * same entities the CSV export reads (one read model, two serializers).
11
+ *
12
+ * WHAT IS EMITTED (and why — the full contract lives in
13
+ * `docs/ledger/beancount-export.md`):
14
+ *
15
+ * - `open` for every account (earliest reported posting date, else the
16
+ * account's creation date), pinning the account's currency so bean-check
17
+ * enforces the ledger's own one-currency-per-account rule independently;
18
+ * - one `txn` per POSTED or VOID transaction — the exact set the report folds
19
+ * count (`REPORTED_STATES` in the ledger plugin's reports): a void original
20
+ * is offset by its posted reversal, and exporting only one half of the pair
21
+ * would misstate every balance. Drafts are not on the books and never
22
+ * export. Ledger-only facts (id, entry number, state, kind, reverses,
23
+ * evidence, memo, cleared state) ride as `lp-*` metadata;
24
+ * - `balance` assertions the day after each CLOSED period's end, for every
25
+ * account with a reported posting on or before the end — so bean-check
26
+ * re-verifies both the arithmetic and the closing sweep (income-statement
27
+ * accounts assert to zero) from the directives alone.
28
+ *
29
+ * SIGN MAPPING — the identity. The ledger stores signed DEBIT-POSITIVE minor
30
+ * units (LGR-2); Beancount amounts are signed decimals where an asset increase
31
+ * is positive and a credit-normal balance (Income/Liabilities/Equity) is
32
+ * negative — the same convention. No re-signing happens anywhere, including
33
+ * for contra/credit-normal accounts: a revenue balance exports negative, which
34
+ * is exactly how Beancount's own Income accounts carry it.
35
+ *
36
+ * MONEY DISCIPLINE: amounts pass through {@link formatBeancountAmount} (exact
37
+ * BigInt digit math, the `formatAmount` discipline without display grouping)
38
+ * and {@link sumAmounts} — never float arithmetic.
39
+ *
40
+ * CORRUPT BOOKS: unlike the insurance CSV (which must always leave the
41
+ * building), this export REFUSES a damaged book — an unresolvable account or a
42
+ * non-safe-integer amount throws a typed error instead of serializing a
43
+ * plausible-but-wrong journal. The reference implementation must never be
44
+ * handed data the ledger itself cannot vouch for; the LGR-7 verifier is the
45
+ * tool that names the damage.
46
+ */
47
+ import { MoneyRangeError, isValidMinor, sumAmounts } from './money';
48
+ import { LedgerError } from './ledger';
49
+ /**
50
+ * The Beancount ROOT for each ledger account type. Beancount requires every
51
+ * account to live under one of exactly five roots; the ledger's five types map
52
+ * onto them 1:1 (`revenue` → `Income` is the only rename).
53
+ */
54
+ export const BEANCOUNT_ROOT_BY_TYPE = {
55
+ asset: 'Assets',
56
+ liability: 'Liabilities',
57
+ equity: 'Equity',
58
+ revenue: 'Income',
59
+ expense: 'Expenses',
60
+ };
61
+ /**
62
+ * Serialize signed integer minor units as Beancount's plain decimal
63
+ * (`-1234.56`, `0.00`) — {@link formatAmount}'s exact BigInt digit math minus
64
+ * the display affordances (no thousands grouping, no symbols, no parens),
65
+ * because Beancount wants a machine number. Negative zero normalises to
66
+ * `0.00`. Throws {@link MoneyRangeError} on anything that is not a safe
67
+ * integer — see the module doc's corrupt-books stance.
68
+ */
69
+ export function formatBeancountAmount(minor) {
70
+ if (!isValidMinor(minor)) {
71
+ throw new MoneyRangeError(`formatBeancountAmount: amount must be a safe integer of minor units, got ${String(minor)}`);
72
+ }
73
+ const big = BigInt(minor);
74
+ const negative = big < 0n;
75
+ const abs = negative ? -big : big;
76
+ return `${negative ? '-' : ''}${abs / 100n}.${(abs % 100n).toString().padStart(2, '0')}`;
77
+ }
78
+ /**
79
+ * Quote a string for Beancount: backslash and double-quote are escaped (the
80
+ * two characters the lexer treats specially inside a string); everything else
81
+ * — including literal newlines and non-ASCII — is verbatim, which the lexer
82
+ * accepts (verified against beancount 3.1.0). Escaping only these two keeps
83
+ * the mapping injective: a re-importer that unescapes `\\` and `\"` recovers
84
+ * the original exactly.
85
+ */
86
+ export function quoteBeancountString(value) {
87
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
88
+ }
89
+ /** Beancount's currency grammar (shape only, like `isValidCurrencyCode`). */
90
+ const BEANCOUNT_CURRENCY_RE = /^[A-Z][A-Z0-9'._-]{0,22}[A-Z0-9]$|^[A-Z]$/;
91
+ /**
92
+ * Mangle ONE colon-separated component of a ledger account name into
93
+ * Beancount's component charset, deterministically:
94
+ *
95
+ * 1. every character outside `[A-Za-z0-9-]` becomes `-` (one `-` per
96
+ * character — no collapsing, so distinct inputs stay distinct more often);
97
+ * 2. a leading lowercase letter is uppercased; any other invalid lead
98
+ * (digit-ok, letter-ok; a `-` or nothing) is prefixed with `X`.
99
+ *
100
+ * The ledger's own name validator guarantees non-empty, non-whitespace-only
101
+ * components, but raw storage is not trusted: an empty component mangles to
102
+ * `X` instead of producing an invalid name.
103
+ */
104
+ export function mangleBeancountComponent(segment) {
105
+ let s = segment.replace(/[^A-Za-z0-9-]/g, '-');
106
+ if (s === '')
107
+ s = 'X';
108
+ if (!/^[A-Z0-9]/.test(s)) {
109
+ s = /^[a-z]/.test(s) ? s[0].toUpperCase() + s.slice(1) : `X${s}`;
110
+ }
111
+ return s;
112
+ }
113
+ /**
114
+ * Map ONE ledger account name onto a Beancount account name (before collision
115
+ * handling — see {@link buildBeancountAccountNames} for the full map):
116
+ *
117
+ * - the ROOT comes from the account TYPE ({@link BEANCOUNT_ROOT_BY_TYPE}),
118
+ * never from the name — `Revenue:Sales` typed `revenue` becomes
119
+ * `Income:Revenue:Sales`;
120
+ * - each component is mangled ({@link mangleBeancountComponent});
121
+ * - a first component that already equals the root is not repeated
122
+ * (`Assets:Bank` stays `Assets:Bank`, not `Assets:Assets:Bank`) — unless
123
+ * dropping it would leave the bare root, which Beancount rejects (an
124
+ * account named exactly `Assets` becomes `Assets:Assets`).
125
+ */
126
+ export function beancountAccountName(account) {
127
+ const root = BEANCOUNT_ROOT_BY_TYPE[account.type] ?? 'Equity';
128
+ const components = String(account.name).split(':').map(mangleBeancountComponent);
129
+ const rest = components[0] === root && components.length > 1 ? components.slice(1) : components;
130
+ return `${root}:${rest.join(':')}`;
131
+ }
132
+ /**
133
+ * The full account-id → Beancount-name map, with DETERMINISTIC collision
134
+ * handling: accounts are visited in (createdAt, id) order; the first claimant
135
+ * keeps the mapped name, later ones append `-2`, `-3`, … to the final
136
+ * component (bumping until free). Stable for a given book — the suffix order
137
+ * depends only on stored creation data, never on read order.
138
+ */
139
+ export function buildBeancountAccountNames(accounts) {
140
+ const ordered = [...accounts].sort((a, b) => byteCompare(a.createdAt, b.createdAt) || byteCompare(a.id, b.id));
141
+ const taken = new Set();
142
+ const names = new Map();
143
+ for (const account of ordered) {
144
+ const base = beancountAccountName(account);
145
+ let name = base;
146
+ for (let n = 2; taken.has(name); n += 1)
147
+ name = `${base}-${n}`;
148
+ taken.add(name);
149
+ names.set(account.id, name);
150
+ }
151
+ return names;
152
+ }
153
+ /** Locale-independent, byte-deterministic string compare (never `localeCompare`). */
154
+ function byteCompare(a, b) {
155
+ return a < b ? -1 : a > b ? 1 : 0;
156
+ }
157
+ /** The transaction states that are ON THE BOOKS — the report folds' rule
158
+ * (posted and void both count; a void original is offset by its posted
159
+ * reversal). Drafts never export. */
160
+ const EXPORTED_STATES = new Set(['posted', 'void']);
161
+ /** ISO `YYYY-MM-DD` + 1 day, in UTC (balance assertions bind at the START of
162
+ * their date, so "after period end" means the following day). */
163
+ function nextIsoDay(date) {
164
+ const parsed = new Date(`${date}T00:00:00Z`);
165
+ parsed.setUTCDate(parsed.getUTCDate() + 1);
166
+ return parsed.toISOString().slice(0, 10);
167
+ }
168
+ /** The account's currency, coerced onto Beancount's currency grammar. The
169
+ * ledger validates ISO-4217 shape at write time, so anything else is raw
170
+ * corruption — mapped to the ISO placeholder `XXX` rather than emitting a
171
+ * token bean-check cannot lex (the amount still exports verbatim, so the
172
+ * corruption stays visible instead of failing the whole journal on syntax). */
173
+ function beancountCurrency(currency) {
174
+ return typeof currency === 'string' && BEANCOUNT_CURRENCY_RE.test(currency) ? currency : 'XXX';
175
+ }
176
+ /**
177
+ * Canonical transaction order — the CSV export's: entry number ascending
178
+ * (posted and void entries always carry one), then (createdAt, id) for
179
+ * defensive totality over raw-corrupted rows.
180
+ */
181
+ function canonicalTxOrder(a, b) {
182
+ const an = a.entryNo ?? Number.POSITIVE_INFINITY;
183
+ const bn = b.entryNo ?? Number.POSITIVE_INFINITY;
184
+ if (an !== bn)
185
+ return an < bn ? -1 : 1;
186
+ return byteCompare(a.createdAt, b.createdAt) || byteCompare(a.id, b.id);
187
+ }
188
+ /**
189
+ * Build the Beancount journal from the full account list, the full
190
+ * transaction list (with postings) and the period records — the same read
191
+ * model the canonical CSV uses. Pure and byte-deterministic; see module doc.
192
+ *
193
+ * Throws {@link LedgerError} `account-not-found` when a posting references an
194
+ * account the list does not carry, and {@link MoneyRangeError} on a stored
195
+ * amount that is not a safe integer — the corrupt-books stance.
196
+ */
197
+ export function buildLedgerBeancount(accounts, transactions, periods) {
198
+ const names = buildBeancountAccountNames(accounts);
199
+ const accountById = new Map();
200
+ for (const account of accounts)
201
+ accountById.set(account.id, account);
202
+ const reported = transactions.filter((tx) => EXPORTED_STATES.has(tx.state)).sort(canonicalTxOrder);
203
+ // Earliest reported posting date per account — the `open` date (an account
204
+ // must be open on or before its first use; creation timestamps can postdate
205
+ // a backdated entry, so the postings win when they exist).
206
+ const firstUse = new Map();
207
+ for (const tx of reported) {
208
+ for (const posting of tx.postings) {
209
+ const seen = firstUse.get(posting.accountId);
210
+ if (seen === undefined || tx.date < seen)
211
+ firstUse.set(posting.accountId, tx.date);
212
+ }
213
+ }
214
+ const lines = [
215
+ '; OpenBook ledger — Beancount export (LGR-13).',
216
+ '; Deterministic: the same book always serializes to identical bytes.',
217
+ '; Sign mapping, account-name mangling, and round-trip limitations:',
218
+ '; docs/ledger/beancount-export.md',
219
+ 'option "title" "OpenBook ledger"',
220
+ '',
221
+ ];
222
+ // ── open directives (sorted by mapped name — unique by construction) ────────
223
+ const openOrder = [...accounts].sort((a, b) => byteCompare(names.get(a.id) ?? '', names.get(b.id) ?? ''));
224
+ for (const account of openOrder) {
225
+ const name = names.get(account.id) ?? beancountAccountName(account);
226
+ const date = firstUse.get(account.id) ?? String(account.createdAt).slice(0, 10);
227
+ lines.push(`${date} open ${name} ${beancountCurrency(account.currency)}`);
228
+ lines.push(` lp-id: ${quoteBeancountString(account.id)}`);
229
+ // The original ledger name, when mangling changed it — what makes the
230
+ // mapping reversible without consulting the ledger.
231
+ if (account.name !== name)
232
+ lines.push(` lp-name: ${quoteBeancountString(account.name)}`);
233
+ }
234
+ lines.push('');
235
+ // ── transactions ────────────────────────────────────────────────────────────
236
+ for (const tx of reported) {
237
+ lines.push(`${tx.date} * ${quoteBeancountString(tx.description)}`);
238
+ lines.push(` lp-id: ${quoteBeancountString(tx.id)}`);
239
+ if (tx.entryNo != null)
240
+ lines.push(` lp-entry-no: ${String(tx.entryNo)}`);
241
+ if (tx.state === 'void')
242
+ lines.push(' lp-state: "void"');
243
+ if (tx.kind === 'closing')
244
+ lines.push(' lp-kind: "closing"');
245
+ if (tx.reverses != null && tx.reverses !== '')
246
+ lines.push(` lp-reverses: ${quoteBeancountString(tx.reverses)}`);
247
+ // Evidence attachments are ids + hashes, not exportable files, so they ride
248
+ // as metadata — a `document` directive would make bean-check fail on the
249
+ // missing file (verified against beancount 3.1.0; see docs).
250
+ tx.evidence.forEach((item, i) => {
251
+ const e = (item ?? {});
252
+ const described = `${String(e.filename ?? '')} sha256=${String(e.sha256 ?? '')} size=${String(e.size ?? '')}`;
253
+ lines.push(` lp-evidence-${i + 1}: ${quoteBeancountString(described)}`);
254
+ });
255
+ for (const posting of tx.postings) {
256
+ const account = accountById.get(posting.accountId);
257
+ if (account === undefined) {
258
+ throw new LedgerError('account-not-found', `Beancount export: posting ${posting.id} references unknown account ${posting.accountId} — run the ledger verifier; the reference export refuses a damaged book`);
259
+ }
260
+ const name = names.get(posting.accountId) ?? beancountAccountName(account);
261
+ lines.push(` ${name} ${formatBeancountAmount(posting.amountMinor)} ${beancountCurrency(account.currency)}`);
262
+ if (typeof posting.memo === 'string' && posting.memo !== '') {
263
+ lines.push(` lp-memo: ${quoteBeancountString(posting.memo)}`);
264
+ }
265
+ if (posting.cleared === 'cleared' || posting.cleared === 'reconciled') {
266
+ lines.push(` lp-cleared: ${quoteBeancountString(posting.cleared)}`);
267
+ }
268
+ }
269
+ lines.push('');
270
+ }
271
+ // ── balance assertions after each CLOSED period ─────────────────────────────
272
+ // Only `closed` periods assert (a reopened period is history, not a live
273
+ // claim). Every account with a reported posting on or before the period end
274
+ // asserts its whole-book balance as of that end — income-statement accounts
275
+ // come out 0 after the closing sweep, so bean-check re-verifies the close.
276
+ const closed = periods
277
+ .filter((p) => p.status === 'closed')
278
+ .sort((a, b) => byteCompare(a.end, b.end) || byteCompare(a.start, b.start) || byteCompare(a.id, b.id));
279
+ for (const period of closed) {
280
+ lines.push(`; Balance assertions after closed period ${period.start} .. ${period.end}.`);
281
+ const assertDate = nextIsoDay(period.end);
282
+ for (const account of openOrder) {
283
+ const first = firstUse.get(account.id);
284
+ if (first === undefined || first > period.end)
285
+ continue;
286
+ const amounts = [];
287
+ for (const tx of reported) {
288
+ if (tx.date > period.end)
289
+ continue;
290
+ for (const posting of tx.postings) {
291
+ if (posting.accountId !== account.id)
292
+ continue;
293
+ if (!isValidMinor(posting.amountMinor)) {
294
+ throw new MoneyRangeError(`Beancount export: posting ${posting.id} carries a non-integer amount — run the ledger verifier`);
295
+ }
296
+ amounts.push(posting.amountMinor);
297
+ }
298
+ }
299
+ const name = names.get(account.id) ?? beancountAccountName(account);
300
+ lines.push(`${assertDate} balance ${name} ${formatBeancountAmount(sumAmounts(amounts))} ${beancountCurrency(account.currency)}`);
301
+ }
302
+ lines.push('');
303
+ }
304
+ // Exactly one trailing newline: drop the trailing blank separator, join LF.
305
+ while (lines.length > 0 && lines[lines.length - 1] === '')
306
+ lines.pop();
307
+ return `${lines.join('\n')}\n`;
308
+ }
309
+ //# sourceMappingURL=ledgerBeancount.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledgerBeancount.js","sourceRoot":"","sources":["../src/ledgerBeancount.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
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 type { LedgerAccount, LedgerPeriod, LedgerTransaction } from './ledger';
26
+ /** The entity slices {@link buildLedgerBeancount} consumes — one book. */
27
+ export interface LedgerBeancountFixtureBook {
28
+ accounts: LedgerAccount[];
29
+ transactions: LedgerTransaction[];
30
+ periods: LedgerPeriod[];
31
+ }
32
+ /**
33
+ * The small hostile book: mangling edge cases, hostile text, a reversal pair,
34
+ * evidence, a closed period, and a draft that must not export.
35
+ */
36
+ export declare function buildBeancountMiniBook(): LedgerBeancountFixtureBook;
37
+ /** How many reported (posted + void) transactions the parity book carries. */
38
+ export declare const BEANCOUNT_PARITY_TX_COUNT = 500;
39
+ /**
40
+ * The 500-transaction parity book: two currencies, reversal pairs, one closed
41
+ * period (with closing entry — its balance assertions gate the close in
42
+ * bean-check), one reopened period, evidence, drafts.
43
+ */
44
+ export declare function buildBeancountParityBook(): LedgerBeancountFixtureBook;
45
+ /** Postings carried by {@link buildLedgerBenchBook} with default arguments. */
46
+ export declare const LEDGER_BENCH_POSTING_COUNT = 10000;
47
+ /**
48
+ * The LGR-15 benchmark book: `txCount` posted transactions of `legsPerTx`
49
+ * postings each (defaults: 1000 × 10 = {@link LEDGER_BENCH_POSTING_COUNT}
50
+ * postings). Seeded/deterministic like the parity book — the GENERATOR is the
51
+ * committed artifact, never the serialized data.
52
+ *
53
+ * Shape rationale: the trial-balance benchmark reads the book through
54
+ * `listTransactions`, whose hard cap is `LEDGER_MAX_TRANSACTION_LIMIT` (1000)
55
+ * — so the 10k postings ride on exactly 1000 transactions, which keeps the
56
+ * benchmarked read the SAME single-page read the report block performs on a
57
+ * real book. Single currency, no drafts/periods/evidence: the benchmark
58
+ * measures the arithmetic + read path, not fixture variety (the parity book
59
+ * covers variety).
60
+ */
61
+ export declare function buildLedgerBenchBook(txCount?: number, legsPerTx?: number): LedgerBeancountFixtureBook;