@whocomply/ledger 0.1.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/LICENSE +21 -0
- package/README.md +64 -0
- package/dist/index.cjs +559 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +590 -0
- package/dist/index.d.ts +590 -0
- package/dist/index.js +530 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var LedgerApiError = class extends Error {
|
|
3
|
+
/** HTTP status code */
|
|
4
|
+
status;
|
|
5
|
+
/** Alias for status, per the Alphaex integration contract. */
|
|
6
|
+
httpStatus;
|
|
7
|
+
/** Stable machine-readable code (absent on transport-level failures). */
|
|
8
|
+
code;
|
|
9
|
+
/** Structured error context (e.g. InsufficientBalanceDetails). */
|
|
10
|
+
details;
|
|
11
|
+
/** Raw response body, when one could be read */
|
|
12
|
+
body;
|
|
13
|
+
constructor(status, message, options = {}) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "LedgerApiError";
|
|
16
|
+
this.status = status;
|
|
17
|
+
this.httpStatus = status;
|
|
18
|
+
this.code = options.code;
|
|
19
|
+
this.details = options.details;
|
|
20
|
+
this.body = options.body;
|
|
21
|
+
}
|
|
22
|
+
/** True for 409s: double reversals, closed periods, existing resources. */
|
|
23
|
+
get isConflict() {
|
|
24
|
+
return this.status === 409;
|
|
25
|
+
}
|
|
26
|
+
/** Typed accessor for INSUFFICIENT_BALANCE detail. */
|
|
27
|
+
get insufficientBalance() {
|
|
28
|
+
if (this.code !== "INSUFFICIENT_BALANCE") {
|
|
29
|
+
return void 0;
|
|
30
|
+
}
|
|
31
|
+
return this.details;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var MissingIdempotencyKeyError = class extends Error {
|
|
35
|
+
constructor(method) {
|
|
36
|
+
super(`${method} requires an explicit idempotencyKey (client has requireIdempotencyKey: true)`);
|
|
37
|
+
this.name = "MissingIdempotencyKeyError";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/http.ts
|
|
42
|
+
var HttpClient = class {
|
|
43
|
+
baseUrl;
|
|
44
|
+
headers;
|
|
45
|
+
fetchImpl;
|
|
46
|
+
timeoutMs;
|
|
47
|
+
tenant;
|
|
48
|
+
requireIdempotencyKey;
|
|
49
|
+
constructor(options) {
|
|
50
|
+
if (!options.baseUrl) {
|
|
51
|
+
throw new Error("baseUrl is required");
|
|
52
|
+
}
|
|
53
|
+
if (!options.tenant) {
|
|
54
|
+
throw new Error("tenant is required");
|
|
55
|
+
}
|
|
56
|
+
if (!options.apiKey && !options.token) {
|
|
57
|
+
throw new Error("either apiKey or token is required");
|
|
58
|
+
}
|
|
59
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
60
|
+
this.tenant = options.tenant;
|
|
61
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
62
|
+
this.timeoutMs = options.timeoutMs;
|
|
63
|
+
this.requireIdempotencyKey = options.requireIdempotencyKey ?? false;
|
|
64
|
+
this.headers = { "Content-Type": "application/json" };
|
|
65
|
+
if (options.apiKey) {
|
|
66
|
+
this.headers["X-API-Key"] = options.apiKey;
|
|
67
|
+
} else if (options.token) {
|
|
68
|
+
this.headers["Authorization"] = `Bearer ${options.token}`;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Path inside the tenant scope, e.g. tenantPath('/accounts') */
|
|
72
|
+
tenantPath(path) {
|
|
73
|
+
return `/tenants/${encodeURIComponent(this.tenant)}${path}`;
|
|
74
|
+
}
|
|
75
|
+
effectiveSignal(callerSignal) {
|
|
76
|
+
const timeoutSignal = this.timeoutMs ? AbortSignal.timeout(this.timeoutMs) : void 0;
|
|
77
|
+
if (callerSignal && timeoutSignal) {
|
|
78
|
+
return typeof AbortSignal.any === "function" ? AbortSignal.any([callerSignal, timeoutSignal]) : callerSignal;
|
|
79
|
+
}
|
|
80
|
+
return callerSignal ?? timeoutSignal;
|
|
81
|
+
}
|
|
82
|
+
async request(method, path, options = {}) {
|
|
83
|
+
let url = `${this.baseUrl}/api/v1${path}`;
|
|
84
|
+
if (options.query) {
|
|
85
|
+
const params = new URLSearchParams();
|
|
86
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
87
|
+
if (value !== void 0 && value !== "") {
|
|
88
|
+
params.set(key, String(value));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const qs = params.toString();
|
|
92
|
+
if (qs) {
|
|
93
|
+
url += `?${qs}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const response = await this.fetchImpl(url, {
|
|
97
|
+
method,
|
|
98
|
+
headers: this.headers,
|
|
99
|
+
body: options.body === void 0 ? void 0 : JSON.stringify(options.body),
|
|
100
|
+
signal: this.effectiveSignal(options.signal)
|
|
101
|
+
});
|
|
102
|
+
if (options.raw) {
|
|
103
|
+
const text = await response.text();
|
|
104
|
+
if (!response.ok) {
|
|
105
|
+
throw new LedgerApiError(response.status, text || response.statusText, { body: text });
|
|
106
|
+
}
|
|
107
|
+
return text;
|
|
108
|
+
}
|
|
109
|
+
let envelope;
|
|
110
|
+
try {
|
|
111
|
+
envelope = await response.json();
|
|
112
|
+
} catch {
|
|
113
|
+
envelope = void 0;
|
|
114
|
+
}
|
|
115
|
+
if (!response.ok || !envelope || envelope.success !== true) {
|
|
116
|
+
const message = envelope?.error ?? `request failed with status ${response.status}`;
|
|
117
|
+
throw new LedgerApiError(response.status, message, {
|
|
118
|
+
code: envelope?.code,
|
|
119
|
+
details: envelope?.details,
|
|
120
|
+
body: envelope
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return envelope.data;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var generateIdempotencyKey = () => {
|
|
127
|
+
const cryptoApi = globalThis.crypto;
|
|
128
|
+
if (cryptoApi && "randomUUID" in cryptoApi) {
|
|
129
|
+
return cryptoApi.randomUUID();
|
|
130
|
+
}
|
|
131
|
+
return `idem-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// src/resources/accounts.ts
|
|
135
|
+
var AccountsResource = class {
|
|
136
|
+
constructor(http) {
|
|
137
|
+
this.http = http;
|
|
138
|
+
}
|
|
139
|
+
http;
|
|
140
|
+
create(input, options = {}) {
|
|
141
|
+
return this.http.request("POST", this.http.tenantPath("/accounts"), { body: input, signal: options.signal });
|
|
142
|
+
}
|
|
143
|
+
list(params = {}, options = {}) {
|
|
144
|
+
return this.http.request("GET", this.http.tenantPath("/accounts"), {
|
|
145
|
+
signal: options.signal,
|
|
146
|
+
query: {
|
|
147
|
+
account_type: params.accountType,
|
|
148
|
+
parent_code: params.parentCode,
|
|
149
|
+
currency: params.currency,
|
|
150
|
+
search: params.search,
|
|
151
|
+
page: params.page,
|
|
152
|
+
page_size: params.pageSize
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
get(accountId, options = {}) {
|
|
157
|
+
return this.http.request("GET", this.http.tenantPath(`/accounts/${accountId}`), { signal: options.signal });
|
|
158
|
+
}
|
|
159
|
+
getByCode(code, options = {}) {
|
|
160
|
+
return this.http.request("GET", this.http.tenantPath(`/accounts/code/${encodeURIComponent(code)}`), { signal: options.signal });
|
|
161
|
+
}
|
|
162
|
+
update(accountId, input, options = {}) {
|
|
163
|
+
return this.http.request("PUT", this.http.tenantPath(`/accounts/${accountId}`), { body: input, signal: options.signal });
|
|
164
|
+
}
|
|
165
|
+
deactivate(accountId, options = {}) {
|
|
166
|
+
return this.http.request("DELETE", this.http.tenantPath(`/accounts/${accountId}`), { signal: options.signal });
|
|
167
|
+
}
|
|
168
|
+
/** Current materialized balance. Currency defaults to the tenant base currency. */
|
|
169
|
+
balance(accountId, options = {}) {
|
|
170
|
+
return this.http.request("GET", this.http.tenantPath(`/accounts/${accountId}/balance`), {
|
|
171
|
+
signal: options.signal,
|
|
172
|
+
query: { currency: options.currency }
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/** Journal-derived balance through the end of a past day (YYYY-MM-DD, UTC). */
|
|
176
|
+
balanceAsOf(accountId, asOf, options = {}) {
|
|
177
|
+
return this.http.request("GET", this.http.tenantPath(`/accounts/${accountId}/balance`), {
|
|
178
|
+
signal: options.signal,
|
|
179
|
+
query: { as_of: asOf, currency: options.currency }
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
hierarchy(options = {}) {
|
|
183
|
+
return this.http.request("GET", this.http.tenantPath("/accounts/hierarchy"), { signal: options.signal });
|
|
184
|
+
}
|
|
185
|
+
stats(options = {}) {
|
|
186
|
+
return this.http.request("GET", this.http.tenantPath("/accounts/stats"), { signal: options.signal });
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// src/resources/balances.ts
|
|
191
|
+
var BalancesResource = class {
|
|
192
|
+
constructor(http) {
|
|
193
|
+
this.http = http;
|
|
194
|
+
}
|
|
195
|
+
http;
|
|
196
|
+
/**
|
|
197
|
+
* Bulk balances: every (account, currency) row with its monotonic
|
|
198
|
+
* version. Filter by codePrefix/codeSuffix, currency, or account
|
|
199
|
+
* metadata containment (e.g. { org_id: '...' }).
|
|
200
|
+
*/
|
|
201
|
+
list(params = {}, options = {}) {
|
|
202
|
+
const metadata = params.metadata && Object.keys(params.metadata).length > 0 ? Object.entries(params.metadata).map(([key, value]) => `${key}:${value}`).join(",") : void 0;
|
|
203
|
+
return this.http.request("GET", this.http.tenantPath("/balances"), {
|
|
204
|
+
query: {
|
|
205
|
+
limit: params.limit,
|
|
206
|
+
offset: params.offset,
|
|
207
|
+
code_prefix: params.codePrefix,
|
|
208
|
+
code_suffix: params.codeSuffix,
|
|
209
|
+
currency: params.currency,
|
|
210
|
+
metadata
|
|
211
|
+
},
|
|
212
|
+
signal: options.signal
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// src/resources/events.ts
|
|
218
|
+
var EventsResource = class {
|
|
219
|
+
constructor(http) {
|
|
220
|
+
this.http = http;
|
|
221
|
+
}
|
|
222
|
+
http;
|
|
223
|
+
/**
|
|
224
|
+
* Durable event feed, strictly ordered by sequence. Poll with
|
|
225
|
+
* afterId = the last sequence you processed; store next_cursor.
|
|
226
|
+
*/
|
|
227
|
+
list(params = {}, options = {}) {
|
|
228
|
+
return this.http.request("GET", this.http.tenantPath("/events"), {
|
|
229
|
+
query: {
|
|
230
|
+
after_id: params.afterId,
|
|
231
|
+
limit: params.limit
|
|
232
|
+
},
|
|
233
|
+
signal: options.signal
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// src/resources/currencies.ts
|
|
239
|
+
var CurrenciesResource = class {
|
|
240
|
+
constructor(http) {
|
|
241
|
+
this.http = http;
|
|
242
|
+
}
|
|
243
|
+
http;
|
|
244
|
+
/**
|
|
245
|
+
* Register a currency with its precision policy. Postings beyond the
|
|
246
|
+
* exponent's decimal places are rejected. Unregistered currencies fall
|
|
247
|
+
* back to the storage maximum of 18 decimal places.
|
|
248
|
+
*/
|
|
249
|
+
register(input, options = {}) {
|
|
250
|
+
return this.http.request("POST", this.http.tenantPath("/currencies"), { body: input, signal: options.signal });
|
|
251
|
+
}
|
|
252
|
+
list(options = {}) {
|
|
253
|
+
return this.http.request("GET", this.http.tenantPath("/currencies"), { signal: options.signal });
|
|
254
|
+
}
|
|
255
|
+
update(code, input, options = {}) {
|
|
256
|
+
return this.http.request("PUT", this.http.tenantPath(`/currencies/${encodeURIComponent(code)}`), {
|
|
257
|
+
signal: options.signal,
|
|
258
|
+
body: input
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// src/resources/periods.ts
|
|
264
|
+
var PeriodsResource = class {
|
|
265
|
+
constructor(http) {
|
|
266
|
+
this.http = http;
|
|
267
|
+
}
|
|
268
|
+
http;
|
|
269
|
+
/** Periods of one tenant may not overlap; violations raise a 409. */
|
|
270
|
+
create(input, options = {}) {
|
|
271
|
+
return this.http.request("POST", this.http.tenantPath("/periods"), { body: input, signal: options.signal });
|
|
272
|
+
}
|
|
273
|
+
list(options = {}) {
|
|
274
|
+
return this.http.request("GET", this.http.tenantPath("/periods"), { signal: options.signal });
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Close a period. Enforced by the database: nothing can post into a
|
|
278
|
+
* closed period, and posting attempts raise a 409.
|
|
279
|
+
*/
|
|
280
|
+
close(periodId, options = {}) {
|
|
281
|
+
return this.http.request("POST", this.http.tenantPath(`/periods/${periodId}/close`), { signal: options.signal });
|
|
282
|
+
}
|
|
283
|
+
reopen(periodId, options = {}) {
|
|
284
|
+
return this.http.request("POST", this.http.tenantPath(`/periods/${periodId}/reopen`), { signal: options.signal });
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// src/resources/recurring.ts
|
|
289
|
+
var RecurringJournalsResource = class {
|
|
290
|
+
constructor(http) {
|
|
291
|
+
this.http = http;
|
|
292
|
+
}
|
|
293
|
+
http;
|
|
294
|
+
/**
|
|
295
|
+
* Register a repeating journal. Each occurrence posts exactly once: the
|
|
296
|
+
* worker derives the idempotency key from (schedule id, run date).
|
|
297
|
+
*/
|
|
298
|
+
create(input, options = {}) {
|
|
299
|
+
return this.http.request("POST", this.http.tenantPath("/recurring-journals"), { body: input, signal: options.signal });
|
|
300
|
+
}
|
|
301
|
+
list(options = {}) {
|
|
302
|
+
return this.http.request("GET", this.http.tenantPath("/recurring-journals"), { signal: options.signal });
|
|
303
|
+
}
|
|
304
|
+
pause(scheduleId, options = {}) {
|
|
305
|
+
return this.http.request("POST", this.http.tenantPath(`/recurring-journals/${scheduleId}/pause`), { signal: options.signal });
|
|
306
|
+
}
|
|
307
|
+
resume(scheduleId, options = {}) {
|
|
308
|
+
return this.http.request("POST", this.http.tenantPath(`/recurring-journals/${scheduleId}/resume`), { signal: options.signal });
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// src/resources/reports.ts
|
|
313
|
+
var dimensionsParam = (dimensions) => {
|
|
314
|
+
if (!dimensions || Object.keys(dimensions).length === 0) {
|
|
315
|
+
return void 0;
|
|
316
|
+
}
|
|
317
|
+
return Object.entries(dimensions).map(([key, value]) => `${key}:${value}`).join(",");
|
|
318
|
+
};
|
|
319
|
+
var ReportsResource = class {
|
|
320
|
+
constructor(http) {
|
|
321
|
+
this.http = http;
|
|
322
|
+
}
|
|
323
|
+
http;
|
|
324
|
+
/**
|
|
325
|
+
* Trial balance derived from posted journal lines, grouped by currency.
|
|
326
|
+
* asOf (YYYY-MM-DD) means "through the end of that day, UTC"; defaults
|
|
327
|
+
* to now.
|
|
328
|
+
*/
|
|
329
|
+
trialBalance(options = {}) {
|
|
330
|
+
return this.http.request("GET", this.http.tenantPath("/reports/trial-balance"), {
|
|
331
|
+
signal: options.signal,
|
|
332
|
+
query: { as_of: options.asOf }
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
/** Revenue and expenses over an inclusive date range, optionally filtered by dimensions. */
|
|
336
|
+
profitAndLoss(options) {
|
|
337
|
+
return this.http.request("GET", this.http.tenantPath("/reports/profit-and-loss"), {
|
|
338
|
+
signal: options.signal,
|
|
339
|
+
query: {
|
|
340
|
+
start_date: options.startDate,
|
|
341
|
+
end_date: options.endDate,
|
|
342
|
+
dimensions: dimensionsParam(options.dimensions)
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
/** Assets, liabilities, and equity (with computed retained earnings) as of a date. */
|
|
347
|
+
balanceSheet(options = {}) {
|
|
348
|
+
return this.http.request("GET", this.http.tenantPath("/reports/balance-sheet"), {
|
|
349
|
+
signal: options.signal,
|
|
350
|
+
query: { as_of: options.asOf }
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
/** One account's activity with opening, running, and closing balances. */
|
|
354
|
+
generalLedger(options) {
|
|
355
|
+
return this.http.request("GET", this.http.tenantPath("/reports/general-ledger"), {
|
|
356
|
+
signal: options.signal,
|
|
357
|
+
query: {
|
|
358
|
+
account_code: options.accountCode,
|
|
359
|
+
start_date: options.startDate,
|
|
360
|
+
end_date: options.endDate,
|
|
361
|
+
currency: options.currency
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
/** Per-currency net positions with base equivalents at the provided rates. */
|
|
366
|
+
fxExposure(options = {}) {
|
|
367
|
+
const rates = options.rates ? Object.entries(options.rates).map(([code, rate]) => `${code}:${rate}`).join(",") : void 0;
|
|
368
|
+
return this.http.request("GET", this.http.tenantPath("/reports/fx-exposure"), {
|
|
369
|
+
signal: options.signal,
|
|
370
|
+
query: { rates, as_of: options.asOf }
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Posted journals as a Xero manual-journal import CSV (raw string).
|
|
375
|
+
* Dates are inclusive. Currency defaults to the tenant base currency.
|
|
376
|
+
*/
|
|
377
|
+
xeroJournalsCsv(options) {
|
|
378
|
+
return this.http.request("GET", this.http.tenantPath("/reports/xero-journals"), {
|
|
379
|
+
signal: options.signal,
|
|
380
|
+
query: {
|
|
381
|
+
start_date: options.startDate,
|
|
382
|
+
end_date: options.endDate,
|
|
383
|
+
currency: options.currency
|
|
384
|
+
},
|
|
385
|
+
raw: true
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// src/resources/transactions.ts
|
|
391
|
+
var TransactionsResource = class {
|
|
392
|
+
constructor(http) {
|
|
393
|
+
this.http = http;
|
|
394
|
+
}
|
|
395
|
+
http;
|
|
396
|
+
idempotencyKey(method, provided) {
|
|
397
|
+
if (provided) {
|
|
398
|
+
return provided;
|
|
399
|
+
}
|
|
400
|
+
if (this.http.requireIdempotencyKey) {
|
|
401
|
+
throw new MissingIdempotencyKeyError(method);
|
|
402
|
+
}
|
|
403
|
+
return generateIdempotencyKey();
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Post a balanced double-entry transaction. Debits must equal credits per
|
|
407
|
+
* currency or the API rejects with UNBALANCED_TRANSACTION. posted_at
|
|
408
|
+
* backdates the posting and requires the transactions:backdate scope.
|
|
409
|
+
*/
|
|
410
|
+
post(input, options = {}) {
|
|
411
|
+
const { idempotencyKey, postedAt, ...rest } = input;
|
|
412
|
+
return this.http.request("POST", this.http.tenantPath("/transactions/double-entry"), {
|
|
413
|
+
body: {
|
|
414
|
+
...rest,
|
|
415
|
+
idempotency_key: this.idempotencyKey("transactions.post", idempotencyKey),
|
|
416
|
+
...postedAt ? { posted_at: postedAt } : {}
|
|
417
|
+
},
|
|
418
|
+
signal: options.signal
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
get(transactionId, options = {}) {
|
|
422
|
+
return this.http.request("GET", this.http.tenantPath(`/transactions/${transactionId}`), {
|
|
423
|
+
query: { include: options.includeLines ? "lines" : void 0 },
|
|
424
|
+
signal: options.signal
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
lines(transactionId, options = {}) {
|
|
428
|
+
return this.http.request("GET", this.http.tenantPath(`/transactions/${transactionId}/lines`), {
|
|
429
|
+
signal: options.signal
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
list(params = {}, options = {}) {
|
|
433
|
+
return this.http.request("GET", this.http.tenantPath("/transactions"), {
|
|
434
|
+
query: {
|
|
435
|
+
limit: params.limit,
|
|
436
|
+
offset: params.offset,
|
|
437
|
+
account_code: params.accountCode,
|
|
438
|
+
reference: params.reference,
|
|
439
|
+
status: params.status,
|
|
440
|
+
start_date: params.startDate,
|
|
441
|
+
end_date: params.endDate,
|
|
442
|
+
include: params.includeLines ? "lines" : void 0
|
|
443
|
+
},
|
|
444
|
+
signal: options.signal
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Maker-checker: store a fully validated entry with zero balance impact.
|
|
449
|
+
* Balances apply only when a DIFFERENT actor approves.
|
|
450
|
+
*/
|
|
451
|
+
draft(input, options = {}) {
|
|
452
|
+
const { idempotencyKey, postedAt, ...rest } = input;
|
|
453
|
+
void postedAt;
|
|
454
|
+
return this.http.request("POST", this.http.tenantPath("/transactions/draft"), {
|
|
455
|
+
body: {
|
|
456
|
+
...rest,
|
|
457
|
+
idempotency_key: this.idempotencyKey("transactions.draft", idempotencyKey)
|
|
458
|
+
},
|
|
459
|
+
signal: options.signal
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
/** Approve a draft (requires the transactions:approve scope and a different actor). */
|
|
463
|
+
approve(transactionId, options = {}) {
|
|
464
|
+
return this.http.request("POST", this.http.tenantPath(`/transactions/${transactionId}/approve`), {
|
|
465
|
+
signal: options.signal
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
/** Reject a draft; it never had balance impact. */
|
|
469
|
+
reject(transactionId, options = {}) {
|
|
470
|
+
return this.http.request("POST", this.http.tenantPath(`/transactions/${transactionId}/reject`), {
|
|
471
|
+
signal: options.signal
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
/** Append an immutable note (audit support). */
|
|
475
|
+
addNote(transactionId, note, options = {}) {
|
|
476
|
+
return this.http.request("POST", this.http.tenantPath(`/transactions/${transactionId}/notes`), {
|
|
477
|
+
body: { note },
|
|
478
|
+
signal: options.signal
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
listNotes(transactionId, options = {}) {
|
|
482
|
+
return this.http.request("GET", this.http.tenantPath(`/transactions/${transactionId}/notes`), {
|
|
483
|
+
signal: options.signal
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Post a reversal: a new transaction mirroring the original with debits
|
|
488
|
+
* and credits swapped, linked via reverses_transaction_id. A transaction
|
|
489
|
+
* can be reversed at most once; a second attempt raises ALREADY_REVERSED.
|
|
490
|
+
*/
|
|
491
|
+
reverse(transactionId, reverseOptions = {}, options = {}) {
|
|
492
|
+
return this.http.request("POST", this.http.tenantPath(`/transactions/${transactionId}/reverse`), {
|
|
493
|
+
body: {
|
|
494
|
+
idempotency_key: this.idempotencyKey("transactions.reverse", reverseOptions.idempotencyKey),
|
|
495
|
+
description: reverseOptions.description,
|
|
496
|
+
metadata: reverseOptions.metadata
|
|
497
|
+
},
|
|
498
|
+
signal: options.signal
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
// src/index.ts
|
|
504
|
+
var LedgerClient = class {
|
|
505
|
+
accounts;
|
|
506
|
+
balances;
|
|
507
|
+
events;
|
|
508
|
+
transactions;
|
|
509
|
+
currencies;
|
|
510
|
+
periods;
|
|
511
|
+
recurringJournals;
|
|
512
|
+
reports;
|
|
513
|
+
constructor(options) {
|
|
514
|
+
const http = new HttpClient(options);
|
|
515
|
+
this.accounts = new AccountsResource(http);
|
|
516
|
+
this.balances = new BalancesResource(http);
|
|
517
|
+
this.events = new EventsResource(http);
|
|
518
|
+
this.transactions = new TransactionsResource(http);
|
|
519
|
+
this.currencies = new CurrenciesResource(http);
|
|
520
|
+
this.periods = new PeriodsResource(http);
|
|
521
|
+
this.recurringJournals = new RecurringJournalsResource(http);
|
|
522
|
+
this.reports = new ReportsResource(http);
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
export {
|
|
526
|
+
LedgerApiError,
|
|
527
|
+
LedgerClient,
|
|
528
|
+
MissingIdempotencyKeyError
|
|
529
|
+
};
|
|
530
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/accounts.ts","../src/resources/balances.ts","../src/resources/events.ts","../src/resources/currencies.ts","../src/resources/periods.ts","../src/resources/recurring.ts","../src/resources/reports.ts","../src/resources/transactions.ts","../src/index.ts"],"sourcesContent":["/** Stable machine-readable error codes; branch on these, never on message text. */\nexport type LedgerErrorCode =\n | 'INSUFFICIENT_BALANCE'\n | 'UNBALANCED_TRANSACTION'\n | 'AMOUNT_PRECISION'\n | 'AMOUNT_TOO_LARGE'\n | 'CURRENCY_DEACTIVATED'\n | 'CURRENCY_PRECISION'\n | 'PERIOD_CLOSED'\n | 'ALREADY_REVERSED'\n | 'CANNOT_REVERSE_UNPOSTED'\n | 'ACCOUNT_NOT_FOUND'\n | 'ACCOUNT_EXISTS'\n | 'IDEMPOTENCY_CONFLICT'\n | 'VALIDATION_ERROR'\n | 'NOT_DRAFT'\n | 'SELF_APPROVAL'\n | 'TRANSACTION_NOT_FOUND'\n | (string & {});\n\n/** Structured detail attached to INSUFFICIENT_BALANCE errors. */\nexport interface InsufficientBalanceDetails {\n account_code: string;\n currency: string;\n /** Decimal strings; render customer copy from these, never parse prose. */\n attempted: string;\n available: string;\n}\n\n/** Error raised for any non-success response from the ledger API. */\nexport class LedgerApiError extends Error {\n /** HTTP status code */\n readonly status: number;\n /** Alias for status, per the Alphaex integration contract. */\n readonly httpStatus: number;\n /** Stable machine-readable code (absent on transport-level failures). */\n readonly code?: LedgerErrorCode;\n /** Structured error context (e.g. InsufficientBalanceDetails). */\n readonly details?: unknown;\n /** Raw response body, when one could be read */\n readonly body?: unknown;\n\n constructor(status: number, message: string, options: { code?: string; details?: unknown; body?: unknown } = {}) {\n super(message);\n this.name = 'LedgerApiError';\n this.status = status;\n this.httpStatus = status;\n this.code = options.code;\n this.details = options.details;\n this.body = options.body;\n }\n\n /** True for 409s: double reversals, closed periods, existing resources. */\n get isConflict(): boolean {\n return this.status === 409;\n }\n\n /** Typed accessor for INSUFFICIENT_BALANCE detail. */\n get insufficientBalance(): InsufficientBalanceDetails | undefined {\n if (this.code !== 'INSUFFICIENT_BALANCE') {\n return undefined;\n }\n return this.details as InsufficientBalanceDetails;\n }\n}\n\n/** Thrown in strict idempotency mode when a mutating call omits the key. */\nexport class MissingIdempotencyKeyError extends Error {\n constructor(method: string) {\n super(`${method} requires an explicit idempotencyKey (client has requireIdempotencyKey: true)`);\n this.name = 'MissingIdempotencyKeyError';\n }\n}\n","import { LedgerApiError } from './errors';\n\nexport interface LedgerClientOptions {\n /** Ledger API origin, e.g. https://ledger.internal.example.com or http://localhost:8080 */\n baseUrl: string;\n /** Tenant slug all requests are scoped to */\n tenant: string;\n /** Machine access: sent as X-API-Key. Create keys in the dashboard under Settings. */\n apiKey?: string;\n /** User access: sent as Authorization: Bearer. Mostly for dashboards. */\n token?: string;\n /** Override fetch (testing, custom agents). Defaults to globalThis.fetch. */\n fetch?: typeof fetch;\n /** Per-request timeout in milliseconds. No timeout when omitted. */\n timeoutMs?: number;\n /**\n * Strict idempotency: posting/reversal/draft methods throw when called\n * without an explicit idempotencyKey instead of auto-generating one.\n */\n requireIdempotencyKey?: boolean;\n}\n\n/** Per-call options accepted by every SDK method. */\nexport interface RequestOptions {\n /** Abort the request (combined with the client timeout when both are set). */\n signal?: AbortSignal;\n}\n\ntype Query = Record<string, string | number | undefined>;\n\ninterface InternalRequestOptions {\n query?: Query;\n body?: unknown;\n /** Return the raw response text instead of unwrapping the JSON envelope (CSV exports). */\n raw?: boolean;\n signal?: AbortSignal;\n}\n\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly headers: Record<string, string>;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs?: number;\n readonly tenant: string;\n readonly requireIdempotencyKey: boolean;\n\n constructor(options: LedgerClientOptions) {\n if (!options.baseUrl) {\n throw new Error('baseUrl is required');\n }\n if (!options.tenant) {\n throw new Error('tenant is required');\n }\n if (!options.apiKey && !options.token) {\n throw new Error('either apiKey or token is required');\n }\n\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '');\n this.tenant = options.tenant;\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n this.timeoutMs = options.timeoutMs;\n this.requireIdempotencyKey = options.requireIdempotencyKey ?? false;\n this.headers = { 'Content-Type': 'application/json' };\n if (options.apiKey) {\n this.headers['X-API-Key'] = options.apiKey;\n } else if (options.token) {\n this.headers['Authorization'] = `Bearer ${options.token}`;\n }\n }\n\n /** Path inside the tenant scope, e.g. tenantPath('/accounts') */\n tenantPath(path: string): string {\n return `/tenants/${encodeURIComponent(this.tenant)}${path}`;\n }\n\n private effectiveSignal(callerSignal?: AbortSignal): AbortSignal | undefined {\n const timeoutSignal = this.timeoutMs ? AbortSignal.timeout(this.timeoutMs) : undefined;\n if (callerSignal && timeoutSignal) {\n return typeof AbortSignal.any === 'function'\n ? AbortSignal.any([callerSignal, timeoutSignal])\n : callerSignal;\n }\n return callerSignal ?? timeoutSignal;\n }\n\n async request<T>(method: string, path: string, options: InternalRequestOptions = {}): Promise<T> {\n let url = `${this.baseUrl}/api/v1${path}`;\n if (options.query) {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(options.query)) {\n if (value !== undefined && value !== '') {\n params.set(key, String(value));\n }\n }\n const qs = params.toString();\n if (qs) {\n url += `?${qs}`;\n }\n }\n\n const response = await this.fetchImpl(url, {\n method,\n headers: this.headers,\n body: options.body === undefined ? undefined : JSON.stringify(options.body),\n signal: this.effectiveSignal(options.signal),\n });\n\n if (options.raw) {\n const text = await response.text();\n if (!response.ok) {\n throw new LedgerApiError(response.status, text || response.statusText, { body: text });\n }\n return text as T;\n }\n\n let envelope: { success?: boolean; data?: T; error?: string; code?: string; details?: unknown } | undefined;\n try {\n envelope = (await response.json()) as typeof envelope;\n } catch {\n envelope = undefined;\n }\n\n if (!response.ok || !envelope || envelope.success !== true) {\n const message = envelope?.error ?? `request failed with status ${response.status}`;\n throw new LedgerApiError(response.status, message, {\n code: envelope?.code,\n details: envelope?.details,\n body: envelope,\n });\n }\n\n return envelope.data as T;\n }\n}\n\n/** Idempotency key generator used when the caller does not supply one. */\nexport const generateIdempotencyKey = (): string => {\n const cryptoApi = globalThis.crypto as Crypto | undefined;\n if (cryptoApi && 'randomUUID' in cryptoApi) {\n return cryptoApi.randomUUID();\n }\n return `idem-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;\n};\n","import type { HttpClient, RequestOptions } from '../http';\nimport type {\n Account,\n AccountBalance,\n AccountStats,\n AccountsPage,\n AsOfBalance,\n CreateAccountInput,\n ListAccountsParams,\n UpdateAccountInput,\n} from '../types';\n\nexport class AccountsResource {\n constructor(private readonly http: HttpClient) {}\n\n create(input: CreateAccountInput, options: RequestOptions = {}): Promise<Account> {\n return this.http.request('POST', this.http.tenantPath('/accounts'), { body: input, signal: options.signal });\n }\n\n list(params: ListAccountsParams = {}, options: RequestOptions = {}): Promise<AccountsPage> {\n return this.http.request('GET', this.http.tenantPath('/accounts'), {\n signal: options.signal,\n query: {\n account_type: params.accountType,\n parent_code: params.parentCode,\n currency: params.currency,\n search: params.search,\n page: params.page,\n page_size: params.pageSize,\n },\n });\n }\n\n get(accountId: string, options: RequestOptions = {}): Promise<Account> {\n return this.http.request('GET', this.http.tenantPath(`/accounts/${accountId}`), { signal: options.signal });\n }\n\n getByCode(code: string, options: RequestOptions = {}): Promise<Account> {\n return this.http.request('GET', this.http.tenantPath(`/accounts/code/${encodeURIComponent(code)}`), { signal: options.signal });\n }\n\n update(accountId: string, input: UpdateAccountInput, options: RequestOptions = {}): Promise<Account> {\n return this.http.request('PUT', this.http.tenantPath(`/accounts/${accountId}`), { body: input, signal: options.signal });\n }\n\n deactivate(accountId: string, options: RequestOptions = {}): Promise<{ message: string }> {\n return this.http.request('DELETE', this.http.tenantPath(`/accounts/${accountId}`), { signal: options.signal });\n }\n\n /** Current materialized balance. Currency defaults to the tenant base currency. */\n balance(accountId: string, options: RequestOptions & { currency?: string } = {}): Promise<AccountBalance> {\n return this.http.request('GET', this.http.tenantPath(`/accounts/${accountId}/balance`), {\n signal: options.signal,\n query: { currency: options.currency },\n });\n }\n\n /** Journal-derived balance through the end of a past day (YYYY-MM-DD, UTC). */\n balanceAsOf(accountId: string, asOf: string, options: RequestOptions & { currency?: string } = {}): Promise<AsOfBalance> {\n return this.http.request('GET', this.http.tenantPath(`/accounts/${accountId}/balance`), {\n signal: options.signal,\n query: { as_of: asOf, currency: options.currency },\n });\n }\n\n hierarchy(options: RequestOptions = {}): Promise<Account[]> {\n return this.http.request('GET', this.http.tenantPath('/accounts/hierarchy'), { signal: options.signal });\n }\n\n stats(options: RequestOptions = {}): Promise<AccountStats> {\n return this.http.request('GET', this.http.tenantPath('/accounts/stats'), { signal: options.signal });\n }\n}\n","import type { HttpClient, RequestOptions } from '../http';\nimport type { BalancesPage, ListBalancesParams } from '../types';\n\nexport class BalancesResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Bulk balances: every (account, currency) row with its monotonic\n * version. Filter by codePrefix/codeSuffix, currency, or account\n * metadata containment (e.g. { org_id: '...' }).\n */\n list(params: ListBalancesParams = {}, options: RequestOptions = {}): Promise<BalancesPage> {\n const metadata = params.metadata && Object.keys(params.metadata).length > 0\n ? Object.entries(params.metadata).map(([key, value]) => `${key}:${value}`).join(',')\n : undefined;\n return this.http.request('GET', this.http.tenantPath('/balances'), {\n query: {\n limit: params.limit,\n offset: params.offset,\n code_prefix: params.codePrefix,\n code_suffix: params.codeSuffix,\n currency: params.currency,\n metadata,\n },\n signal: options.signal,\n });\n }\n}\n","import type { HttpClient, RequestOptions } from '../http';\nimport type { EventsPage } from '../types';\n\nexport class EventsResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Durable event feed, strictly ordered by sequence. Poll with\n * afterId = the last sequence you processed; store next_cursor.\n */\n list(params: { afterId?: number; limit?: number } = {}, options: RequestOptions = {}): Promise<EventsPage> {\n return this.http.request('GET', this.http.tenantPath('/events'), {\n query: {\n after_id: params.afterId,\n limit: params.limit,\n },\n signal: options.signal,\n });\n }\n}\n","import type { HttpClient, RequestOptions } from '../http';\nimport type { Currency, RegisterCurrencyInput, UpdateCurrencyInput } from '../types';\n\nexport class CurrenciesResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Register a currency with its precision policy. Postings beyond the\n * exponent's decimal places are rejected. Unregistered currencies fall\n * back to the storage maximum of 18 decimal places.\n */\n register(input: RegisterCurrencyInput, options: RequestOptions = {}): Promise<Currency> {\n return this.http.request('POST', this.http.tenantPath('/currencies'), { body: input, signal: options.signal });\n }\n\n list(options: RequestOptions = {}): Promise<Currency[]> {\n return this.http.request('GET', this.http.tenantPath('/currencies'), { signal: options.signal });\n }\n\n update(code: string, input: UpdateCurrencyInput, options: RequestOptions = {}): Promise<Currency> {\n return this.http.request('PUT', this.http.tenantPath(`/currencies/${encodeURIComponent(code)}`), {\n signal: options.signal,\n body: input,\n });\n }\n}\n","import type { HttpClient, RequestOptions } from '../http';\nimport type { AccountingPeriod, CreatePeriodInput } from '../types';\n\nexport class PeriodsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Periods of one tenant may not overlap; violations raise a 409. */\n create(input: CreatePeriodInput, options: RequestOptions = {}): Promise<AccountingPeriod> {\n return this.http.request('POST', this.http.tenantPath('/periods'), { body: input, signal: options.signal });\n }\n\n list(options: RequestOptions = {}): Promise<AccountingPeriod[]> {\n return this.http.request('GET', this.http.tenantPath('/periods'), { signal: options.signal });\n }\n\n /**\n * Close a period. Enforced by the database: nothing can post into a\n * closed period, and posting attempts raise a 409.\n */\n close(periodId: string, options: RequestOptions = {}): Promise<AccountingPeriod> {\n return this.http.request('POST', this.http.tenantPath(`/periods/${periodId}/close`), { signal: options.signal });\n }\n\n reopen(periodId: string, options: RequestOptions = {}): Promise<AccountingPeriod> {\n return this.http.request('POST', this.http.tenantPath(`/periods/${periodId}/reopen`), { signal: options.signal });\n }\n}\n","import type { HttpClient, RequestOptions } from '../http';\nimport type { CreateRecurringJournalInput, RecurringJournal } from '../types';\n\nexport class RecurringJournalsResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Register a repeating journal. Each occurrence posts exactly once: the\n * worker derives the idempotency key from (schedule id, run date).\n */\n create(input: CreateRecurringJournalInput, options: RequestOptions = {}): Promise<RecurringJournal> {\n return this.http.request('POST', this.http.tenantPath('/recurring-journals'), { body: input, signal: options.signal });\n }\n\n list(options: RequestOptions = {}): Promise<RecurringJournal[]> {\n return this.http.request('GET', this.http.tenantPath('/recurring-journals'), { signal: options.signal });\n }\n\n pause(scheduleId: string, options: RequestOptions = {}): Promise<RecurringJournal> {\n return this.http.request('POST', this.http.tenantPath(`/recurring-journals/${scheduleId}/pause`), { signal: options.signal });\n }\n\n resume(scheduleId: string, options: RequestOptions = {}): Promise<RecurringJournal> {\n return this.http.request('POST', this.http.tenantPath(`/recurring-journals/${scheduleId}/resume`), { signal: options.signal });\n }\n}\n","import type { HttpClient, RequestOptions } from '../http';\nimport type { BalanceSheet, FxExposure, GeneralLedger, ProfitAndLoss, TrialBalance } from '../types';\n\nconst dimensionsParam = (dimensions?: Record<string, string>): string | undefined => {\n if (!dimensions || Object.keys(dimensions).length === 0) {\n return undefined;\n }\n return Object.entries(dimensions)\n .map(([key, value]) => `${key}:${value}`)\n .join(',');\n};\n\nexport class ReportsResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Trial balance derived from posted journal lines, grouped by currency.\n * asOf (YYYY-MM-DD) means \"through the end of that day, UTC\"; defaults\n * to now.\n */\n trialBalance(options: RequestOptions & { asOf?: string } = {}): Promise<TrialBalance> {\n return this.http.request('GET', this.http.tenantPath('/reports/trial-balance'), {\n signal: options.signal,\n query: { as_of: options.asOf },\n });\n }\n\n /** Revenue and expenses over an inclusive date range, optionally filtered by dimensions. */\n profitAndLoss(options: RequestOptions & { startDate: string; endDate: string; dimensions?: Record<string, string> }): Promise<ProfitAndLoss> {\n return this.http.request('GET', this.http.tenantPath('/reports/profit-and-loss'), {\n signal: options.signal,\n query: {\n start_date: options.startDate,\n end_date: options.endDate,\n dimensions: dimensionsParam(options.dimensions),\n },\n });\n }\n\n /** Assets, liabilities, and equity (with computed retained earnings) as of a date. */\n balanceSheet(options: RequestOptions & { asOf?: string } = {}): Promise<BalanceSheet> {\n return this.http.request('GET', this.http.tenantPath('/reports/balance-sheet'), {\n signal: options.signal,\n query: { as_of: options.asOf },\n });\n }\n\n /** One account's activity with opening, running, and closing balances. */\n generalLedger(options: RequestOptions & { accountCode: string; startDate: string; endDate: string; currency?: string }): Promise<GeneralLedger> {\n return this.http.request('GET', this.http.tenantPath('/reports/general-ledger'), {\n signal: options.signal,\n query: {\n account_code: options.accountCode,\n start_date: options.startDate,\n end_date: options.endDate,\n currency: options.currency,\n },\n });\n }\n\n /** Per-currency net positions with base equivalents at the provided rates. */\n fxExposure(options: RequestOptions & { rates?: Record<string, string>; asOf?: string } = {}): Promise<FxExposure> {\n const rates = options.rates\n ? Object.entries(options.rates).map(([code, rate]) => `${code}:${rate}`).join(',')\n : undefined;\n return this.http.request('GET', this.http.tenantPath('/reports/fx-exposure'), {\n signal: options.signal,\n query: { rates, as_of: options.asOf },\n });\n }\n\n /**\n * Posted journals as a Xero manual-journal import CSV (raw string).\n * Dates are inclusive. Currency defaults to the tenant base currency.\n */\n xeroJournalsCsv(options: RequestOptions & { startDate: string; endDate: string; currency?: string }): Promise<string> {\n return this.http.request('GET', this.http.tenantPath('/reports/xero-journals'), {\n signal: options.signal,\n query: {\n start_date: options.startDate,\n end_date: options.endDate,\n currency: options.currency,\n },\n raw: true,\n });\n }\n}\n","import { generateIdempotencyKey } from '../http';\nimport type { HttpClient, RequestOptions } from '../http';\nimport { MissingIdempotencyKeyError } from '../errors';\nimport type {\n ListTransactionsParams,\n PostEntryInput,\n Transaction,\n TransactionLine,\n TransactionNote,\n TransactionsPage,\n} from '../types';\n\nexport class TransactionsResource {\n constructor(private readonly http: HttpClient) {}\n\n private idempotencyKey(method: string, provided?: string): string {\n if (provided) {\n return provided;\n }\n if (this.http.requireIdempotencyKey) {\n throw new MissingIdempotencyKeyError(method);\n }\n return generateIdempotencyKey();\n }\n\n /**\n * Post a balanced double-entry transaction. Debits must equal credits per\n * currency or the API rejects with UNBALANCED_TRANSACTION. posted_at\n * backdates the posting and requires the transactions:backdate scope.\n */\n post(input: PostEntryInput, options: RequestOptions = {}): Promise<Transaction> {\n const { idempotencyKey, postedAt, ...rest } = input;\n return this.http.request('POST', this.http.tenantPath('/transactions/double-entry'), {\n body: {\n ...rest,\n idempotency_key: this.idempotencyKey('transactions.post', idempotencyKey),\n ...(postedAt ? { posted_at: postedAt } : {}),\n },\n signal: options.signal,\n });\n }\n\n get(transactionId: string, options: RequestOptions & { includeLines?: boolean } = {}): Promise<Transaction> {\n return this.http.request('GET', this.http.tenantPath(`/transactions/${transactionId}`), {\n query: { include: options.includeLines ? 'lines' : undefined },\n signal: options.signal,\n });\n }\n\n lines(transactionId: string, options: RequestOptions = {}): Promise<TransactionLine[]> {\n return this.http.request('GET', this.http.tenantPath(`/transactions/${transactionId}/lines`), {\n signal: options.signal,\n });\n }\n\n list(params: ListTransactionsParams = {}, options: RequestOptions = {}): Promise<TransactionsPage> {\n return this.http.request('GET', this.http.tenantPath('/transactions'), {\n query: {\n limit: params.limit,\n offset: params.offset,\n account_code: params.accountCode,\n reference: params.reference,\n status: params.status,\n start_date: params.startDate,\n end_date: params.endDate,\n include: params.includeLines ? 'lines' : undefined,\n },\n signal: options.signal,\n });\n }\n\n /**\n * Maker-checker: store a fully validated entry with zero balance impact.\n * Balances apply only when a DIFFERENT actor approves.\n */\n draft(input: PostEntryInput, options: RequestOptions = {}): Promise<Transaction> {\n const { idempotencyKey, postedAt, ...rest } = input;\n void postedAt;\n return this.http.request('POST', this.http.tenantPath('/transactions/draft'), {\n body: {\n ...rest,\n idempotency_key: this.idempotencyKey('transactions.draft', idempotencyKey),\n },\n signal: options.signal,\n });\n }\n\n /** Approve a draft (requires the transactions:approve scope and a different actor). */\n approve(transactionId: string, options: RequestOptions = {}): Promise<Transaction> {\n return this.http.request('POST', this.http.tenantPath(`/transactions/${transactionId}/approve`), {\n signal: options.signal,\n });\n }\n\n /** Reject a draft; it never had balance impact. */\n reject(transactionId: string, options: RequestOptions = {}): Promise<Transaction> {\n return this.http.request('POST', this.http.tenantPath(`/transactions/${transactionId}/reject`), {\n signal: options.signal,\n });\n }\n\n /** Append an immutable note (audit support). */\n addNote(transactionId: string, note: string, options: RequestOptions = {}): Promise<TransactionNote> {\n return this.http.request('POST', this.http.tenantPath(`/transactions/${transactionId}/notes`), {\n body: { note },\n signal: options.signal,\n });\n }\n\n listNotes(transactionId: string, options: RequestOptions = {}): Promise<TransactionNote[]> {\n return this.http.request('GET', this.http.tenantPath(`/transactions/${transactionId}/notes`), {\n signal: options.signal,\n });\n }\n\n /**\n * Post a reversal: a new transaction mirroring the original with debits\n * and credits swapped, linked via reverses_transaction_id. A transaction\n * can be reversed at most once; a second attempt raises ALREADY_REVERSED.\n */\n reverse(\n transactionId: string,\n reverseOptions: { idempotencyKey?: string; description?: string; metadata?: Record<string, unknown> } = {},\n options: RequestOptions = {}\n ): Promise<Transaction> {\n return this.http.request('POST', this.http.tenantPath(`/transactions/${transactionId}/reverse`), {\n body: {\n idempotency_key: this.idempotencyKey('transactions.reverse', reverseOptions.idempotencyKey),\n description: reverseOptions.description,\n metadata: reverseOptions.metadata,\n },\n signal: options.signal,\n });\n }\n}\n","import { HttpClient } from './http';\nimport type { LedgerClientOptions } from './http';\nimport { AccountsResource } from './resources/accounts';\nimport { BalancesResource } from './resources/balances';\nimport { EventsResource } from './resources/events';\nimport { CurrenciesResource } from './resources/currencies';\nimport { PeriodsResource } from './resources/periods';\nimport { RecurringJournalsResource } from './resources/recurring';\nimport { ReportsResource } from './resources/reports';\nimport { TransactionsResource } from './resources/transactions';\n\nexport class LedgerClient {\n readonly accounts: AccountsResource;\n readonly balances: BalancesResource;\n readonly events: EventsResource;\n readonly transactions: TransactionsResource;\n readonly currencies: CurrenciesResource;\n readonly periods: PeriodsResource;\n readonly recurringJournals: RecurringJournalsResource;\n readonly reports: ReportsResource;\n\n constructor(options: LedgerClientOptions) {\n const http = new HttpClient(options);\n this.accounts = new AccountsResource(http);\n this.balances = new BalancesResource(http);\n this.events = new EventsResource(http);\n this.transactions = new TransactionsResource(http);\n this.currencies = new CurrenciesResource(http);\n this.periods = new PeriodsResource(http);\n this.recurringJournals = new RecurringJournalsResource(http);\n this.reports = new ReportsResource(http);\n }\n}\n\nexport { LedgerApiError, MissingIdempotencyKeyError } from './errors';\nexport type { InsufficientBalanceDetails, LedgerErrorCode } from './errors';\nexport type { LedgerClientOptions, RequestOptions } from './http';\nexport * from './types';\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@whocomply/ledger",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript SDK for the WhoComply Ledger API: multi-tenant double-entry ledger for fintechs",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"LICENSE",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"main": "./dist/index.cjs",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"require": "./dist/index.cjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"smoke": "tsx examples/quickstart.ts",
|
|
27
|
+
"prepublishOnly": "npm run build && npm test"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^25.9.3",
|
|
34
|
+
"tsup": "^8.3.5",
|
|
35
|
+
"tsx": "^4.19.2",
|
|
36
|
+
"typescript": "~5.7.2",
|
|
37
|
+
"vitest": "^2.1.8"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/whocomply/liftoff.git",
|
|
42
|
+
"directory": "sdks/typescript"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/whocomply/liftoff/tree/main/sdks/typescript#readme",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/whocomply/liftoff/issues"
|
|
47
|
+
},
|
|
48
|
+
"keywords": [
|
|
49
|
+
"ledger",
|
|
50
|
+
"double-entry",
|
|
51
|
+
"fintech",
|
|
52
|
+
"accounting",
|
|
53
|
+
"whocomply"
|
|
54
|
+
],
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
}
|
|
58
|
+
}
|