@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.
- package/dist/backup.d.ts +75 -1
- package/dist/backup.js +8 -1
- package/dist/backup.js.map +1 -1
- package/dist/client.d.ts +134 -2
- package/dist/client.js +171 -0
- package/dist/client.js.map +1 -1
- package/dist/csv.d.ts +49 -0
- package/dist/csv.js +116 -0
- package/dist/csv.js.map +1 -0
- package/dist/importAssets.d.ts +10 -0
- package/dist/importAssets.js +17 -0
- package/dist/importAssets.js.map +1 -1
- package/dist/index.d.ts +10 -4
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/ledger.d.ts +772 -0
- package/dist/ledger.js +493 -0
- package/dist/ledger.js.map +1 -0
- package/dist/ledgerBeancount.d.ts +117 -0
- package/dist/ledgerBeancount.js +309 -0
- package/dist/ledgerBeancount.js.map +1 -0
- package/dist/ledgerBeancountFixture.d.ts +61 -0
- package/dist/ledgerBeancountFixture.js +459 -0
- package/dist/ledgerBeancountFixture.js.map +1 -0
- package/dist/ledgerCsv.d.ts +76 -0
- package/dist/ledgerCsv.js +197 -0
- package/dist/ledgerCsv.js.map +1 -0
- package/dist/money.d.ts +179 -0
- package/dist/money.js +283 -0
- package/dist/money.js.map +1 -0
- package/dist/notionImport.d.ts +5 -3
- package/dist/notionImport.js +6 -56
- package/dist/notionImport.js.map +1 -1
- package/dist/plugins.d.ts +24 -0
- package/dist/plugins.js +25 -0
- package/dist/plugins.js.map +1 -1
- package/dist/provenance.d.ts +17 -0
- package/dist/provenance.js.map +1 -1
- package/dist/routes.d.ts +121 -0
- package/dist/routes.js +122 -0
- package/dist/routes.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/backup.d.ts
CHANGED
|
@@ -11,7 +11,60 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { StoredPage } from './types';
|
|
13
13
|
import type { StoredDatabase } from './database';
|
|
14
|
-
|
|
14
|
+
import type { LedgerAuditEvent } from './ledger';
|
|
15
|
+
/**
|
|
16
|
+
* Version 2 (LGR-15): the bundle MAY carry a {@link LedgerBackupSection} — the
|
|
17
|
+
* ledger's durability surface (audit stream, settings rows, evidence assets)
|
|
18
|
+
* that pages/databases alone cannot express. Additive: a v1 bundle (no
|
|
19
|
+
* `ledger` key) still imports; a v2 bundle read by an old server imports its
|
|
20
|
+
* pages/databases and ignores the extra key.
|
|
21
|
+
*/
|
|
22
|
+
export declare const BACKUP_VERSION = 2;
|
|
23
|
+
/**
|
|
24
|
+
* One content-addressed asset carried by a backup (LGR-15): the evidence bytes
|
|
25
|
+
* behind a ledger transaction's manifest. `id` IS the SHA-256 of the bytes —
|
|
26
|
+
* the importer re-derives it and refuses a mismatch, so a bundle can never
|
|
27
|
+
* plant bytes under a hash they do not answer to.
|
|
28
|
+
*/
|
|
29
|
+
export interface LedgerBackupAsset {
|
|
30
|
+
/** Asset-store id: 64 lowercase hex chars, the SHA-256 of the bytes. */
|
|
31
|
+
id: string;
|
|
32
|
+
mime: string;
|
|
33
|
+
/** Byte count of the decoded content. */
|
|
34
|
+
size: number;
|
|
35
|
+
/** The raw bytes, base64-encoded (JSON cannot carry binary). */
|
|
36
|
+
bytesBase64: string;
|
|
37
|
+
/** Page ids holding an `asset_refs` edge to this asset (restored where the page exists). */
|
|
38
|
+
refs: string[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The ledger durability surface of a backup (LGR-15). The ledger's ENTITIES
|
|
42
|
+
* (accounts/transactions/postings/reconciliations and their host pages) travel
|
|
43
|
+
* as ordinary pages/databases in the bundle; this section carries what those
|
|
44
|
+
* rows alone cannot restore:
|
|
45
|
+
*
|
|
46
|
+
* - `settings` — the seeded ids (`ledgerDb`), the period records
|
|
47
|
+
* (`ledgerPeriods`), and the entry-number sequence (`ledgerEntrySeq`),
|
|
48
|
+
* verbatim as stored;
|
|
49
|
+
* - `audit` — the FULL append-only audit stream, seq order, hashes included.
|
|
50
|
+
* Restored verbatim so the tamper-evidence chain survives the round trip
|
|
51
|
+
* (the LGR-7 verifier re-checks it against the restored rows);
|
|
52
|
+
* - `assets` — the evidence bytes referenced by transaction manifests, so the
|
|
53
|
+
* verifier's receipt re-hash check still has bytes to answer with.
|
|
54
|
+
*
|
|
55
|
+
* Restore semantics are deliberately narrow (see the server's `importBundle`):
|
|
56
|
+
* overwrite mode only, and ONLY into a library with no seeded ledger — a
|
|
57
|
+
* library that already has one keeps its LGR-3 protections and the section is
|
|
58
|
+
* skipped, reported via `ImportResult.ledger`.
|
|
59
|
+
*/
|
|
60
|
+
export interface LedgerBackupSection {
|
|
61
|
+
/** Raw `settings` rows by key: `ledgerDb`, `ledgerPeriods`, `ledgerEntrySeq` (when present). */
|
|
62
|
+
settings: Record<string, unknown>;
|
|
63
|
+
/** The full audit stream, ascending `seq`, verbatim (hashes included). */
|
|
64
|
+
audit: LedgerAuditEvent[];
|
|
65
|
+
/** Evidence assets referenced by ledger transaction manifests. */
|
|
66
|
+
assets: LedgerBackupAsset[];
|
|
67
|
+
}
|
|
15
68
|
export interface LibraryBackup {
|
|
16
69
|
version: number;
|
|
17
70
|
exportedAt: string;
|
|
@@ -19,6 +72,8 @@ export interface LibraryBackup {
|
|
|
19
72
|
databases: StoredDatabase[];
|
|
20
73
|
/** pageId → emoji icon (added client-side; ignored by the server). */
|
|
21
74
|
icons?: Record<string, string>;
|
|
75
|
+
/** LGR-15: the ledger durability surface; absent when no ledger is seeded. */
|
|
76
|
+
ledger?: LedgerBackupSection;
|
|
22
77
|
}
|
|
23
78
|
export type ImportMode = 'copy' | 'overwrite';
|
|
24
79
|
/** What the client sends to restore: the (already-selected) pages/databases + mode. */
|
|
@@ -26,7 +81,24 @@ export interface ImportRequest {
|
|
|
26
81
|
pages: StoredPage[];
|
|
27
82
|
databases: StoredDatabase[];
|
|
28
83
|
mode: ImportMode;
|
|
84
|
+
/**
|
|
85
|
+
* LGR-15: the bundle's ledger section, forwarded on a full overwrite restore.
|
|
86
|
+
* Applied only when the target has no seeded ledger AND the selection carried
|
|
87
|
+
* the ledger's own pages/databases; otherwise skipped and reported.
|
|
88
|
+
*/
|
|
89
|
+
ledger?: LedgerBackupSection;
|
|
29
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* What became of a bundle's {@link LedgerBackupSection} on import (LGR-15).
|
|
93
|
+
* - `restored` — settings + audit stream + evidence assets applied;
|
|
94
|
+
* - `skipped-existing-ledger` — the target already has a seeded ledger, whose
|
|
95
|
+
* LGR-3 protections stand (restore ledger bundles into a FRESH library);
|
|
96
|
+
* - `skipped-copy-mode` — copy mode re-ids every page, which would sever the
|
|
97
|
+
* audit stream's entity references; the section only applies in overwrite;
|
|
98
|
+
* - `skipped-incomplete` — the page selection did not carry the ledger's own
|
|
99
|
+
* host pages/databases, so the section had nothing sound to attach to.
|
|
100
|
+
*/
|
|
101
|
+
export type LedgerRestoreOutcome = 'restored' | 'skipped-existing-ledger' | 'skipped-copy-mode' | 'skipped-incomplete';
|
|
30
102
|
export interface ImportResult {
|
|
31
103
|
/** New pages created (copy mode, or overwrite of a not-yet-existing id). */
|
|
32
104
|
created: number;
|
|
@@ -36,6 +108,8 @@ export interface ImportResult {
|
|
|
36
108
|
renamed: number;
|
|
37
109
|
/** old page id → new page id (copy mode; identity in overwrite). */
|
|
38
110
|
idMap: Record<string, string>;
|
|
111
|
+
/** LGR-15: outcome of the bundle's ledger section; absent when none was sent. */
|
|
112
|
+
ledger?: LedgerRestoreOutcome;
|
|
39
113
|
/**
|
|
40
114
|
* True when this apply was a **replay** of an already-imported bundle (ER-6):
|
|
41
115
|
* the bundle's content hash matched a prior import, so nothing was written and
|
package/dist/backup.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Version 2 (LGR-15): the bundle MAY carry a {@link LedgerBackupSection} — the
|
|
3
|
+
* ledger's durability surface (audit stream, settings rows, evidence assets)
|
|
4
|
+
* that pages/databases alone cannot express. Additive: a v1 bundle (no
|
|
5
|
+
* `ledger` key) still imports; a v2 bundle read by an old server imports its
|
|
6
|
+
* pages/databases and ignores the extra key.
|
|
7
|
+
*/
|
|
8
|
+
export const BACKUP_VERSION = 2;
|
|
2
9
|
export const BACKUP_CADENCES = ['daily', 'weekly', 'monthly', 'yearly'];
|
|
3
10
|
/** Interval of each cadence, in milliseconds. */
|
|
4
11
|
export const BACKUP_CADENCE_MS = {
|
package/dist/backup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../src/backup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../src/backup.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
CHANGED
|
@@ -4,9 +4,10 @@ import type { AgentChatEvent, AgentChatMessage, AgentChatOptions, AiConfig, AiPr
|
|
|
4
4
|
import type { AclLevel, AgentEditsMode, AgentEditsPolicy, Member, MemberRole, MemberStatus, PageAcl, PageGraph, PageInput, PageMeta, PageVersionMeta, PageVisibility, StoredPage, StoredPageVersion } from './types';
|
|
5
5
|
import type { InstanceConfig, InstanceInfo, StoredEdit } from './provenance';
|
|
6
6
|
import type { AgentTokenMeta, AgentTokenScope } from './identity';
|
|
7
|
-
import type { BackupCadence, BackupConfig, BackupStatus, ImportRequest, ImportResult } from './backup';
|
|
7
|
+
import type { BackupCadence, BackupConfig, BackupStatus, ImportRequest, ImportResult, LedgerBackupSection } from './backup';
|
|
8
8
|
import type { DatabaseInput, DatabaseRow, DatabaseUpdate, RowInput, RowUpdate, StoredDatabase } from './database';
|
|
9
9
|
import type { CommentInput, StoredComment, StoredSuggestion, SuggestionInput, SuggestionStatus, SuggestionUpdate } from './suggestions';
|
|
10
|
+
import type { LedgerAccount, LedgerAccountInput, LedgerAccountPatch, LedgerAuditEvent, LedgerClearedState, LedgerDraftInput, LedgerDraftPatch, LedgerInfo, LedgerPeriod, LedgerPeriodCloseInput, LedgerPeriodCloseResult, LedgerPeriodReopenResult, LedgerPosting, LedgerReconciliation, LedgerReconciliationInput, LedgerReconciliationPatch, LedgerReconciliationStatus, LedgerReconciliationSummary, LedgerReverseOptions, LedgerTransaction, LedgerTransactionState, LedgerVerifyReport } from './ledger';
|
|
10
11
|
/** Handlers for a single page's live update stream. */
|
|
11
12
|
export interface PageSubscription {
|
|
12
13
|
/** A newer version of the page was saved (by anyone). */
|
|
@@ -170,10 +171,12 @@ export interface DataClient {
|
|
|
170
171
|
* it. Resolves `true` if a live page was trashed.
|
|
171
172
|
*/
|
|
172
173
|
deletePage(id: string): Promise<boolean>;
|
|
173
|
-
/** Export the whole space: every live page (full data) + every database
|
|
174
|
+
/** Export the whole space: every live page (full data) + every database —
|
|
175
|
+
* plus the ledger durability section when a ledger is seeded (LGR-15). */
|
|
174
176
|
exportLibrary(): Promise<{
|
|
175
177
|
pages: StoredPage[];
|
|
176
178
|
databases: StoredDatabase[];
|
|
179
|
+
ledger?: LedgerBackupSection;
|
|
177
180
|
}>;
|
|
178
181
|
/** Restore a (client-selected) set of pages/databases; see {@link ImportRequest}. */
|
|
179
182
|
importLibrary(req: ImportRequest): Promise<ImportResult>;
|
|
@@ -289,6 +292,85 @@ export interface DataClient {
|
|
|
289
292
|
reorderRows(databaseId: string, orderedIds: string[]): Promise<void>;
|
|
290
293
|
/** Subscribe to a database's live row-list updates. Returns an unsubscribe fn. */
|
|
291
294
|
subscribeRows(databaseId: string, onRows: (rows: DatabaseRow[]) => void): () => void;
|
|
295
|
+
/** Whether the ledger is initialized, and the seeded database/host ids. */
|
|
296
|
+
ledgerInfo(): Promise<LedgerInfo>;
|
|
297
|
+
/** Seed the four managed ledger databases + restricted host page (idempotent). */
|
|
298
|
+
ledgerInit(): Promise<LedgerInfo>;
|
|
299
|
+
/** List accounts (hierarchy is encoded in the colon-delimited names). */
|
|
300
|
+
ledgerListAccounts(): Promise<LedgerAccount[]>;
|
|
301
|
+
/** Create an account. `currency` defaults to `USD`. */
|
|
302
|
+
ledgerCreateAccount(input: LedgerAccountInput): Promise<LedgerAccount>;
|
|
303
|
+
/** Fetch one account, or `null` when it does not exist. */
|
|
304
|
+
ledgerGetAccount(id: string): Promise<LedgerAccount | null>;
|
|
305
|
+
/** Rename / close / reopen an account. Closing rejects at nonzero posted balance. */
|
|
306
|
+
ledgerUpdateAccount(id: string, patch: LedgerAccountPatch): Promise<LedgerAccount>;
|
|
307
|
+
/** List transactions with their postings (`state` filters; `limit` caps). */
|
|
308
|
+
ledgerListTransactions(opts?: {
|
|
309
|
+
state?: LedgerTransactionState;
|
|
310
|
+
limit?: number;
|
|
311
|
+
}): Promise<LedgerTransaction[]>;
|
|
312
|
+
/** Fetch one transaction (with postings), or `null` when it does not exist. */
|
|
313
|
+
ledgerGetTransaction(id: string): Promise<LedgerTransaction | null>;
|
|
314
|
+
/** Create a DRAFT transaction (with postings). Drafts are freely mutable. */
|
|
315
|
+
ledgerCreateDraft(input: LedgerDraftInput): Promise<LedgerTransaction>;
|
|
316
|
+
/** Update a DRAFT (posted/void transactions are immutable — typed rejection). */
|
|
317
|
+
ledgerUpdateDraft(id: string, patch: LedgerDraftPatch): Promise<LedgerTransaction>;
|
|
318
|
+
/** Delete a DRAFT and its postings (permanent, audited). Posted/void reject. */
|
|
319
|
+
ledgerDeleteDraft(id: string): Promise<boolean>;
|
|
320
|
+
/** Post a draft atomically (validates all invariants; assigns the entry number). */
|
|
321
|
+
ledgerPostTransaction(id: string): Promise<LedgerTransaction>;
|
|
322
|
+
/** Atomically create + post the reversing entry and void the original. */
|
|
323
|
+
ledgerReverseTransaction(id: string, opts?: LedgerReverseOptions): Promise<LedgerTransaction>;
|
|
324
|
+
/** Flip a posting between `pending`/`cleared` (`reconciled` is locked, LGR-11). */
|
|
325
|
+
ledgerSetPostingCleared(postingId: string, cleared: LedgerClearedState): Promise<LedgerPosting>;
|
|
326
|
+
/** List reconciliations, newest statement first. Filters are ANDed. */
|
|
327
|
+
ledgerListReconciliations(opts?: {
|
|
328
|
+
accountId?: string;
|
|
329
|
+
status?: LedgerReconciliationStatus;
|
|
330
|
+
}): Promise<LedgerReconciliation[]>;
|
|
331
|
+
/** One reconciliation with its live cleared balance + difference, or `null`. */
|
|
332
|
+
ledgerGetReconciliation(id: string): Promise<LedgerReconciliationSummary | null>;
|
|
333
|
+
/** START a reconciliation. Rejects `reconciliation-exists` if one is open. */
|
|
334
|
+
ledgerStartReconciliation(input: LedgerReconciliationInput): Promise<LedgerReconciliation>;
|
|
335
|
+
/** AMEND an OPEN reconciliation's statement date/balance (LGR-22) — the fix
|
|
336
|
+
* for a mistyped target, which no amount of ticking can reach zero. Touches
|
|
337
|
+
* no posting; returns the summary with the difference recomputed. */
|
|
338
|
+
ledgerAmendReconciliation(id: string, patch: LedgerReconciliationPatch): Promise<LedgerReconciliationSummary>;
|
|
339
|
+
/** ABANDON an OPEN reconciliation (LGR-22): end it without balancing it and
|
|
340
|
+
* without posting anything. Terminal, audited, posting-neutral — every tick
|
|
341
|
+
* keeps its cleared state, and the account is free to start a new one. */
|
|
342
|
+
ledgerAbandonReconciliation(id: string): Promise<LedgerReconciliation>;
|
|
343
|
+
/** Match (`cleared`) or unmatch (`pending`) one posting inside an OPEN one. */
|
|
344
|
+
ledgerToggleReconciliationPosting(id: string, postingId: string, cleared: 'pending' | 'cleared'): Promise<LedgerReconciliationSummary>;
|
|
345
|
+
/** FINISH — only at a difference of exactly 0; freezes the matched postings. */
|
|
346
|
+
ledgerFinishReconciliation(id: string): Promise<LedgerReconciliationSummary>;
|
|
347
|
+
/** REOPEN a finished reconciliation (explicit, audited); unfreezes its postings. */
|
|
348
|
+
ledgerReopenReconciliation(id: string): Promise<LedgerReconciliationSummary>;
|
|
349
|
+
/** Every period record — closed AND reopened history (LGR-12). */
|
|
350
|
+
ledgerListPeriods(): Promise<LedgerPeriod[]>;
|
|
351
|
+
/** CLOSE a period: closing entry + date-range lock; warns (never blocks) on
|
|
352
|
+
* open reconciliations. Store-enforced — `period-closed` rejections for any
|
|
353
|
+
* posting/reversal dated inside the range hold over both transports. */
|
|
354
|
+
ledgerClosePeriod(input: LedgerPeriodCloseInput): Promise<LedgerPeriodCloseResult>;
|
|
355
|
+
/** REOPEN a closed period (explicit, audited): voids the closing entry via a
|
|
356
|
+
* reversal and restores postability for the range. */
|
|
357
|
+
ledgerReopenPeriod(id: string): Promise<LedgerPeriodReopenResult>;
|
|
358
|
+
/** Read the append-only audit log, newest first (`before` = seq cursor). */
|
|
359
|
+
ledgerListAudit(opts?: {
|
|
360
|
+
limit?: number;
|
|
361
|
+
before?: number;
|
|
362
|
+
}): Promise<LedgerAuditEvent[]>;
|
|
363
|
+
/** The whole ledger as the canonical postings CSV (LGR-7) — byte-stable:
|
|
364
|
+
* same data ⇒ identical bytes over BOTH transports. */
|
|
365
|
+
ledgerExportCsv(): Promise<string>;
|
|
366
|
+
/** The whole ledger as a Beancount journal (LGR-13) — byte-stable like the
|
|
367
|
+
* CSV, built from the same read model; `bean-check`/Fava re-verify it with
|
|
368
|
+
* an independent implementation. */
|
|
369
|
+
ledgerExportBeancount(): Promise<string>;
|
|
370
|
+
/** The independent invariant verifier's report (LGR-7). Admin-gated over
|
|
371
|
+
* HTTP (the report names entity ids across the whole book); the local
|
|
372
|
+
* single-user store answers directly. */
|
|
373
|
+
ledgerVerify(): Promise<LedgerVerifyReport>;
|
|
292
374
|
/** List a page's suggestions, newest first. `status` filters (e.g. only open). */
|
|
293
375
|
listSuggestions(pageId: string, status?: SuggestionStatus): Promise<StoredSuggestion[]>;
|
|
294
376
|
/** Persist a new suggestion (status defaults to `open`). */
|
|
@@ -554,6 +636,7 @@ export declare class HttpDataClient implements DataClient {
|
|
|
554
636
|
exportLibrary(): Promise<{
|
|
555
637
|
pages: StoredPage[];
|
|
556
638
|
databases: StoredDatabase[];
|
|
639
|
+
ledger?: LedgerBackupSection;
|
|
557
640
|
}>;
|
|
558
641
|
importLibrary(req: ImportRequest): Promise<ImportResult>;
|
|
559
642
|
listTrash(): Promise<PageMeta[]>;
|
|
@@ -619,6 +702,55 @@ export declare class HttpDataClient implements DataClient {
|
|
|
619
702
|
updateRow(databaseId: string, rowId: string, patch: RowUpdate): Promise<DatabaseRow>;
|
|
620
703
|
reorderRows(databaseId: string, orderedIds: string[]): Promise<void>;
|
|
621
704
|
subscribeRows(databaseId: string, onRows: (rows: DatabaseRow[]) => void): () => void;
|
|
705
|
+
/**
|
|
706
|
+
* Like {@link request}, but re-materializes the server's `{error, code}` body
|
|
707
|
+
* into a typed {@link LedgerError} — so a caller catches the SAME error class
|
|
708
|
+
* over HTTP as it does against the in-process {@link PageStore} (local mode).
|
|
709
|
+
*/
|
|
710
|
+
private ledgerRequest;
|
|
711
|
+
/** Re-materialize a non-2xx ledger response into a typed {@link LedgerError}. */
|
|
712
|
+
private throwLedgerError;
|
|
713
|
+
ledgerInfo(): Promise<LedgerInfo>;
|
|
714
|
+
ledgerInit(): Promise<LedgerInfo>;
|
|
715
|
+
ledgerListAccounts(): Promise<LedgerAccount[]>;
|
|
716
|
+
ledgerCreateAccount(input: LedgerAccountInput): Promise<LedgerAccount>;
|
|
717
|
+
ledgerGetAccount(id: string): Promise<LedgerAccount | null>;
|
|
718
|
+
ledgerUpdateAccount(id: string, patch: LedgerAccountPatch): Promise<LedgerAccount>;
|
|
719
|
+
ledgerListTransactions(opts?: {
|
|
720
|
+
state?: LedgerTransactionState;
|
|
721
|
+
limit?: number;
|
|
722
|
+
}): Promise<LedgerTransaction[]>;
|
|
723
|
+
ledgerGetTransaction(id: string): Promise<LedgerTransaction | null>;
|
|
724
|
+
ledgerCreateDraft(input: LedgerDraftInput): Promise<LedgerTransaction>;
|
|
725
|
+
ledgerUpdateDraft(id: string, patch: LedgerDraftPatch): Promise<LedgerTransaction>;
|
|
726
|
+
ledgerDeleteDraft(id: string): Promise<boolean>;
|
|
727
|
+
ledgerPostTransaction(id: string): Promise<LedgerTransaction>;
|
|
728
|
+
ledgerReverseTransaction(id: string, opts?: LedgerReverseOptions): Promise<LedgerTransaction>;
|
|
729
|
+
ledgerSetPostingCleared(postingId: string, cleared: LedgerClearedState): Promise<LedgerPosting>;
|
|
730
|
+
ledgerListReconciliations(opts?: {
|
|
731
|
+
accountId?: string;
|
|
732
|
+
status?: LedgerReconciliationStatus;
|
|
733
|
+
}): Promise<LedgerReconciliation[]>;
|
|
734
|
+
ledgerGetReconciliation(id: string): Promise<LedgerReconciliationSummary | null>;
|
|
735
|
+
ledgerStartReconciliation(input: LedgerReconciliationInput): Promise<LedgerReconciliation>;
|
|
736
|
+
ledgerAmendReconciliation(id: string, patch: LedgerReconciliationPatch): Promise<LedgerReconciliationSummary>;
|
|
737
|
+
ledgerAbandonReconciliation(id: string): Promise<LedgerReconciliation>;
|
|
738
|
+
ledgerToggleReconciliationPosting(id: string, postingId: string, cleared: 'pending' | 'cleared'): Promise<LedgerReconciliationSummary>;
|
|
739
|
+
ledgerFinishReconciliation(id: string): Promise<LedgerReconciliationSummary>;
|
|
740
|
+
ledgerReopenReconciliation(id: string): Promise<LedgerReconciliationSummary>;
|
|
741
|
+
ledgerListPeriods(): Promise<LedgerPeriod[]>;
|
|
742
|
+
ledgerClosePeriod(input: LedgerPeriodCloseInput): Promise<LedgerPeriodCloseResult>;
|
|
743
|
+
ledgerReopenPeriod(id: string): Promise<LedgerPeriodReopenResult>;
|
|
744
|
+
ledgerListAudit(opts?: {
|
|
745
|
+
limit?: number;
|
|
746
|
+
before?: number;
|
|
747
|
+
}): Promise<LedgerAuditEvent[]>;
|
|
748
|
+
/** Canonical postings CSV (LGR-7) — a ledger read that is text, not JSON. */
|
|
749
|
+
ledgerExportCsv(): Promise<string>;
|
|
750
|
+
/** Beancount journal (LGR-13) — text like the CSV, same error mapping. */
|
|
751
|
+
ledgerExportBeancount(): Promise<string>;
|
|
752
|
+
/** The independent verifier's report (admin-gated server-side). */
|
|
753
|
+
ledgerVerify(): Promise<LedgerVerifyReport>;
|
|
622
754
|
listSuggestions(pageId: string, status?: SuggestionStatus): Promise<StoredSuggestion[]>;
|
|
623
755
|
createSuggestion(input: SuggestionInput): Promise<StoredSuggestion>;
|
|
624
756
|
updateSuggestion(id: string, patch: SuggestionUpdate): Promise<StoredSuggestion>;
|
package/dist/client.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { API } from './routes';
|
|
2
|
+
import { LEDGER_ERROR_CODES, LedgerError } from './ledger';
|
|
2
3
|
/**
|
|
3
4
|
* The global `fetch`, wrapped so it's safe to store on an object and call as a
|
|
4
5
|
* property/method. WebKit (the desktop WKWebView) throws "Can only call
|
|
@@ -779,6 +780,176 @@ export class HttpDataClient {
|
|
|
779
780
|
subscribeRows(databaseId, onRows) {
|
|
780
781
|
return this.liveStream().onRows(databaseId, onRows);
|
|
781
782
|
}
|
|
783
|
+
// ── Ledger: server-enforced double-entry accounting (LGR-3) ──────────────────
|
|
784
|
+
/**
|
|
785
|
+
* Like {@link request}, but re-materializes the server's `{error, code}` body
|
|
786
|
+
* into a typed {@link LedgerError} — so a caller catches the SAME error class
|
|
787
|
+
* over HTTP as it does against the in-process {@link PageStore} (local mode).
|
|
788
|
+
*/
|
|
789
|
+
async ledgerRequest(method, path, body) {
|
|
790
|
+
const res = await this.authFetch(`${this.baseUrl}${path}`, {
|
|
791
|
+
method,
|
|
792
|
+
headers: body === undefined ? undefined : { 'Content-Type': 'application/json' },
|
|
793
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
794
|
+
cache: 'no-store',
|
|
795
|
+
});
|
|
796
|
+
if (!res.ok)
|
|
797
|
+
return this.throwLedgerError(res);
|
|
798
|
+
if (res.status === 204)
|
|
799
|
+
return undefined;
|
|
800
|
+
return (await res.json());
|
|
801
|
+
}
|
|
802
|
+
/** Re-materialize a non-2xx ledger response into a typed {@link LedgerError}. */
|
|
803
|
+
async throwLedgerError(res) {
|
|
804
|
+
let data = null;
|
|
805
|
+
try {
|
|
806
|
+
data = (await res.json());
|
|
807
|
+
}
|
|
808
|
+
catch {
|
|
809
|
+
data = null;
|
|
810
|
+
}
|
|
811
|
+
if (data?.code && LEDGER_ERROR_CODES.includes(data.code)) {
|
|
812
|
+
throw new LedgerError(data.code, data.error ?? `ledger request failed (${res.status})`);
|
|
813
|
+
}
|
|
814
|
+
throw new Error(`OpenBook request failed (${res.status} ${res.statusText})${data?.error ? `: ${data.error}` : ''}`);
|
|
815
|
+
}
|
|
816
|
+
ledgerInfo() {
|
|
817
|
+
return this.ledgerRequest('GET', API.ledger);
|
|
818
|
+
}
|
|
819
|
+
ledgerInit() {
|
|
820
|
+
return this.ledgerRequest('POST', API.ledger);
|
|
821
|
+
}
|
|
822
|
+
ledgerListAccounts() {
|
|
823
|
+
return this.ledgerRequest('GET', API.ledgerAccounts);
|
|
824
|
+
}
|
|
825
|
+
ledgerCreateAccount(input) {
|
|
826
|
+
return this.ledgerRequest('POST', API.ledgerAccounts, input);
|
|
827
|
+
}
|
|
828
|
+
async ledgerGetAccount(id) {
|
|
829
|
+
try {
|
|
830
|
+
return await this.ledgerRequest('GET', API.ledgerAccount(id));
|
|
831
|
+
}
|
|
832
|
+
catch (err) {
|
|
833
|
+
if (err instanceof LedgerError && err.code === 'not-found')
|
|
834
|
+
return null;
|
|
835
|
+
throw err;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
ledgerUpdateAccount(id, patch) {
|
|
839
|
+
return this.ledgerRequest('PATCH', API.ledgerAccount(id), patch);
|
|
840
|
+
}
|
|
841
|
+
ledgerListTransactions(opts) {
|
|
842
|
+
const params = new URLSearchParams();
|
|
843
|
+
if (opts?.state)
|
|
844
|
+
params.set('state', opts.state);
|
|
845
|
+
if (opts?.limit != null)
|
|
846
|
+
params.set('limit', String(opts.limit));
|
|
847
|
+
const query = params.toString();
|
|
848
|
+
return this.ledgerRequest('GET', `${API.ledgerTransactions}${query ? `?${query}` : ''}`);
|
|
849
|
+
}
|
|
850
|
+
async ledgerGetTransaction(id) {
|
|
851
|
+
try {
|
|
852
|
+
return await this.ledgerRequest('GET', API.ledgerTransaction(id));
|
|
853
|
+
}
|
|
854
|
+
catch (err) {
|
|
855
|
+
if (err instanceof LedgerError && err.code === 'not-found')
|
|
856
|
+
return null;
|
|
857
|
+
throw err;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
ledgerCreateDraft(input) {
|
|
861
|
+
return this.ledgerRequest('POST', API.ledgerTransactions, input);
|
|
862
|
+
}
|
|
863
|
+
ledgerUpdateDraft(id, patch) {
|
|
864
|
+
return this.ledgerRequest('PATCH', API.ledgerTransaction(id), patch);
|
|
865
|
+
}
|
|
866
|
+
async ledgerDeleteDraft(id) {
|
|
867
|
+
await this.ledgerRequest('DELETE', API.ledgerTransaction(id));
|
|
868
|
+
return true;
|
|
869
|
+
}
|
|
870
|
+
ledgerPostTransaction(id) {
|
|
871
|
+
return this.ledgerRequest('POST', API.ledgerTransactionPost(id));
|
|
872
|
+
}
|
|
873
|
+
ledgerReverseTransaction(id, opts) {
|
|
874
|
+
return this.ledgerRequest('POST', API.ledgerTransactionReverse(id), opts ?? {});
|
|
875
|
+
}
|
|
876
|
+
ledgerSetPostingCleared(postingId, cleared) {
|
|
877
|
+
return this.ledgerRequest('PUT', API.ledgerPostingCleared(postingId), { cleared });
|
|
878
|
+
}
|
|
879
|
+
// ── Statement reconciliation (LGR-11) ───────────────────────────────────────
|
|
880
|
+
ledgerListReconciliations(opts) {
|
|
881
|
+
const params = new URLSearchParams();
|
|
882
|
+
if (opts?.accountId != null)
|
|
883
|
+
params.set('accountId', opts.accountId);
|
|
884
|
+
if (opts?.status != null)
|
|
885
|
+
params.set('status', opts.status);
|
|
886
|
+
const query = params.toString();
|
|
887
|
+
return this.ledgerRequest('GET', `${API.ledgerReconciliations}${query ? `?${query}` : ''}`);
|
|
888
|
+
}
|
|
889
|
+
async ledgerGetReconciliation(id) {
|
|
890
|
+
try {
|
|
891
|
+
return await this.ledgerRequest('GET', API.ledgerReconciliation(id));
|
|
892
|
+
}
|
|
893
|
+
catch (err) {
|
|
894
|
+
if (err instanceof LedgerError && err.code === 'not-found')
|
|
895
|
+
return null;
|
|
896
|
+
throw err;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
ledgerStartReconciliation(input) {
|
|
900
|
+
return this.ledgerRequest('POST', API.ledgerReconciliations, input);
|
|
901
|
+
}
|
|
902
|
+
ledgerAmendReconciliation(id, patch) {
|
|
903
|
+
return this.ledgerRequest('PATCH', API.ledgerReconciliation(id), patch);
|
|
904
|
+
}
|
|
905
|
+
ledgerAbandonReconciliation(id) {
|
|
906
|
+
return this.ledgerRequest('POST', API.ledgerReconciliationAbandon(id));
|
|
907
|
+
}
|
|
908
|
+
ledgerToggleReconciliationPosting(id, postingId, cleared) {
|
|
909
|
+
return this.ledgerRequest('PUT', API.ledgerReconciliationPosting(id, postingId), { cleared });
|
|
910
|
+
}
|
|
911
|
+
ledgerFinishReconciliation(id) {
|
|
912
|
+
return this.ledgerRequest('POST', API.ledgerReconciliationFinish(id));
|
|
913
|
+
}
|
|
914
|
+
ledgerReopenReconciliation(id) {
|
|
915
|
+
return this.ledgerRequest('POST', API.ledgerReconciliationReopen(id));
|
|
916
|
+
}
|
|
917
|
+
ledgerListPeriods() {
|
|
918
|
+
return this.ledgerRequest('GET', API.ledgerPeriods);
|
|
919
|
+
}
|
|
920
|
+
ledgerClosePeriod(input) {
|
|
921
|
+
return this.ledgerRequest('POST', API.ledgerPeriods, input);
|
|
922
|
+
}
|
|
923
|
+
ledgerReopenPeriod(id) {
|
|
924
|
+
return this.ledgerRequest('POST', API.ledgerPeriodReopen(id));
|
|
925
|
+
}
|
|
926
|
+
ledgerListAudit(opts) {
|
|
927
|
+
const params = new URLSearchParams();
|
|
928
|
+
if (opts?.limit != null)
|
|
929
|
+
params.set('limit', String(opts.limit));
|
|
930
|
+
if (opts?.before != null)
|
|
931
|
+
params.set('before', String(opts.before));
|
|
932
|
+
const query = params.toString();
|
|
933
|
+
return this.ledgerRequest('GET', `${API.ledgerAudit}${query ? `?${query}` : ''}`);
|
|
934
|
+
}
|
|
935
|
+
/** Canonical postings CSV (LGR-7) — a ledger read that is text, not JSON. */
|
|
936
|
+
async ledgerExportCsv() {
|
|
937
|
+
const res = await this.authFetch(`${this.baseUrl}${API.ledgerExportCsv}`, { cache: 'no-store' });
|
|
938
|
+
if (!res.ok)
|
|
939
|
+
return this.throwLedgerError(res);
|
|
940
|
+
return res.text();
|
|
941
|
+
}
|
|
942
|
+
/** Beancount journal (LGR-13) — text like the CSV, same error mapping. */
|
|
943
|
+
async ledgerExportBeancount() {
|
|
944
|
+
const res = await this.authFetch(`${this.baseUrl}${API.ledgerExportBeancount}`, { cache: 'no-store' });
|
|
945
|
+
if (!res.ok)
|
|
946
|
+
return this.throwLedgerError(res);
|
|
947
|
+
return res.text();
|
|
948
|
+
}
|
|
949
|
+
/** The independent verifier's report (admin-gated server-side). */
|
|
950
|
+
async ledgerVerify() {
|
|
951
|
+
return this.ledgerRequest('GET', API.ledgerVerify);
|
|
952
|
+
}
|
|
782
953
|
// ── Suggestions + comments (the review layer) ────────────────────────────────
|
|
783
954
|
async listSuggestions(pageId, status) {
|
|
784
955
|
const path = status ? `${API.suggestions(pageId)}?status=${encodeURIComponent(status)}` : API.suggestions(pageId);
|