@mulmoclaude/accounting-plugin 0.3.3 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/accountNormalize.d.ts +1 -1
- package/dist/server/accountNormalize.d.ts.map +1 -1
- package/dist/server/context.d.ts +3 -10
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/defaultAccounts.d.ts +1 -1
- package/dist/server/defaultAccounts.d.ts.map +1 -1
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/io.d.ts +2 -1
- package/dist/server/io.d.ts.map +1 -1
- package/dist/server/journal.d.ts +1 -1
- package/dist/server/journal.d.ts.map +1 -1
- package/dist/server/openingBalances.d.ts +1 -1
- package/dist/server/openingBalances.d.ts.map +1 -1
- package/dist/server/report.d.ts +6 -64
- package/dist/server/report.d.ts.map +1 -1
- package/dist/server/service.d.ts +1 -1
- package/dist/server/service.d.ts.map +1 -1
- package/dist/server/snapshotCache.d.ts +2 -1
- package/dist/server/snapshotCache.d.ts.map +1 -1
- package/dist/server/timeSeries.d.ts +1 -1
- package/dist/server/timeSeries.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -123
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server.cjs +22 -73
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +14 -65
- package/dist/server.js.map +1 -1
- package/dist/shared/errors.d.ts +1 -1
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/types.d.ts +188 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared-CcU3_qAa.cjs +749 -0
- package/dist/shared-CcU3_qAa.cjs.map +1 -0
- package/dist/shared-MOpJ1kUa.js +516 -0
- package/dist/shared-MOpJ1kUa.js.map +1 -0
- package/dist/shared.cjs +40 -519
- package/dist/shared.js +2 -483
- package/dist/vue/api.d.ts +2 -121
- package/dist/vue/api.d.ts.map +1 -1
- package/dist/vue/lang/index.d.ts +204 -215
- package/dist/vue/lang/index.d.ts.map +1 -1
- package/dist/vue.cjs +1651 -1678
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +1652 -1679
- package/dist/vue.js.map +1 -1
- package/package.json +11 -6
- package/dist/server/atomic.d.ts +0 -15
- package/dist/server/atomic.d.ts.map +0 -1
- package/dist/shared.cjs.map +0 -1
- package/dist/shared.js.map +0 -1
package/dist/server.cjs
CHANGED
|
@@ -21,12 +21,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
enumerable: true
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
|
-
const require_shared = require("./shared.cjs");
|
|
24
|
+
const require_shared = require("./shared-CcU3_qAa.cjs");
|
|
25
25
|
let express = require("express");
|
|
26
26
|
let node_crypto = require("node:crypto");
|
|
27
27
|
let node_fs = require("node:fs");
|
|
28
28
|
let node_path = require("node:path");
|
|
29
29
|
node_path = __toESM(node_path, 1);
|
|
30
|
+
let _mulmoclaude_core_files = require("@mulmoclaude/core/files");
|
|
30
31
|
//#region src/server/context.ts
|
|
31
32
|
var deps = null;
|
|
32
33
|
/** Called once by the host before the accounting router is mounted. */
|
|
@@ -55,52 +56,6 @@ var log = {
|
|
|
55
56
|
debug: (namespace, msg, data) => (deps?.logger ?? consoleLogger).debug(namespace, msg, data)
|
|
56
57
|
};
|
|
57
58
|
//#endregion
|
|
58
|
-
//#region src/server/atomic.ts
|
|
59
|
-
/** True for a `not found` filesystem error. */
|
|
60
|
-
function isEnoent(err) {
|
|
61
|
-
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
62
|
-
}
|
|
63
|
-
var IS_WINDOWS = process.platform === "win32";
|
|
64
|
-
var RENAME_RETRY_DELAYS_MS = [
|
|
65
|
-
30,
|
|
66
|
-
100,
|
|
67
|
-
300
|
|
68
|
-
];
|
|
69
|
-
function hasErrnoCode(err) {
|
|
70
|
-
return typeof err === "object" && err !== null && "code" in err && typeof err.code === "string";
|
|
71
|
-
}
|
|
72
|
-
function isTransientRenameError(err) {
|
|
73
|
-
if (!IS_WINDOWS || !hasErrnoCode(err)) return false;
|
|
74
|
-
return err.code === "EPERM" || err.code === "EBUSY" || err.code === "EACCES";
|
|
75
|
-
}
|
|
76
|
-
async function renameWithWindowsRetry(fromPath, toPath) {
|
|
77
|
-
for (const delayMs of RENAME_RETRY_DELAYS_MS) try {
|
|
78
|
-
await node_fs.promises.rename(fromPath, toPath);
|
|
79
|
-
return;
|
|
80
|
-
} catch (err) {
|
|
81
|
-
if (!isTransientRenameError(err)) throw err;
|
|
82
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
83
|
-
}
|
|
84
|
-
await node_fs.promises.rename(fromPath, toPath);
|
|
85
|
-
}
|
|
86
|
-
/** Atomic write: unique tmp alongside destination, then rename. */
|
|
87
|
-
async function writeFileAtomic(filePath, content, opts = {}) {
|
|
88
|
-
const tmp = opts.uniqueTmp ?? true ? `${filePath}.${(0, node_crypto.randomUUID)()}.tmp` : `${filePath}.tmp`;
|
|
89
|
-
await node_fs.promises.mkdir(node_path.default.dirname(filePath), { recursive: true });
|
|
90
|
-
try {
|
|
91
|
-
await node_fs.promises.writeFile(tmp, content, { encoding: "utf-8" });
|
|
92
|
-
await renameWithWindowsRetry(tmp, filePath);
|
|
93
|
-
} catch (err) {
|
|
94
|
-
await node_fs.promises.unlink(tmp).catch(() => {});
|
|
95
|
-
throw err;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
/** Atomic JSON write (2-space indent), the only serialization shape the
|
|
99
|
-
* accounting io layer needs. */
|
|
100
|
-
async function writeJsonAtomic(filePath, data, opts = {}) {
|
|
101
|
-
await writeFileAtomic(filePath, JSON.stringify(data, null, 2), opts);
|
|
102
|
-
}
|
|
103
|
-
//#endregion
|
|
104
59
|
//#region src/server/io.ts
|
|
105
60
|
var root = (workspaceRoot) => workspaceRoot ?? defaultWorkspaceRoot();
|
|
106
61
|
function accountingRoot(workspaceRoot) {
|
|
@@ -162,7 +117,7 @@ async function readJsonStrict(filePath) {
|
|
|
162
117
|
const raw = await node_fs.promises.readFile(filePath, "utf-8");
|
|
163
118
|
return JSON.parse(raw);
|
|
164
119
|
} catch (err) {
|
|
165
|
-
if (isEnoent(err)) return null;
|
|
120
|
+
if ((0, _mulmoclaude_core_files.isEnoent)(err)) return null;
|
|
166
121
|
throw err;
|
|
167
122
|
}
|
|
168
123
|
}
|
|
@@ -189,13 +144,13 @@ async function readConfig(workspaceRoot) {
|
|
|
189
144
|
};
|
|
190
145
|
}
|
|
191
146
|
async function writeConfig(config, workspaceRoot) {
|
|
192
|
-
await writeJsonAtomic(configPath(workspaceRoot), config);
|
|
147
|
+
await (0, _mulmoclaude_core_files.writeJsonAtomic)(configPath(workspaceRoot), config);
|
|
193
148
|
}
|
|
194
149
|
async function readAccounts(bookId, workspaceRoot) {
|
|
195
150
|
return await readJsonStrict(accountsPath(bookId, workspaceRoot)) ?? [];
|
|
196
151
|
}
|
|
197
152
|
async function writeAccounts(bookId, accounts, workspaceRoot) {
|
|
198
|
-
await writeJsonAtomic(accountsPath(bookId, workspaceRoot), accounts);
|
|
153
|
+
await (0, _mulmoclaude_core_files.writeJsonAtomic)(accountsPath(bookId, workspaceRoot), accounts);
|
|
199
154
|
}
|
|
200
155
|
/** Convert a YYYY-MM-DD date string to its YYYY-MM month bucket. The
|
|
201
156
|
* month bucket dictates which JSONL file the entry lives in. */
|
|
@@ -261,7 +216,7 @@ async function readJournalMonth(bookId, period, workspaceRoot) {
|
|
|
261
216
|
try {
|
|
262
217
|
raw = await node_fs.promises.readFile(file, "utf-8");
|
|
263
218
|
} catch (err) {
|
|
264
|
-
if (isEnoent(err)) return {
|
|
219
|
+
if ((0, _mulmoclaude_core_files.isEnoent)(err)) return {
|
|
265
220
|
entries: [],
|
|
266
221
|
skipped: 0
|
|
267
222
|
};
|
|
@@ -290,7 +245,7 @@ async function listJournalPeriods(bookId, workspaceRoot) {
|
|
|
290
245
|
try {
|
|
291
246
|
names = await node_fs.promises.readdir(journalDir(bookId, workspaceRoot));
|
|
292
247
|
} catch (err) {
|
|
293
|
-
if (isEnoent(err)) return [];
|
|
248
|
+
if ((0, _mulmoclaude_core_files.isEnoent)(err)) return [];
|
|
294
249
|
throw err;
|
|
295
250
|
}
|
|
296
251
|
return names.filter((name) => /^\d{4}-\d{2}\.jsonl$/.test(name)).map((name) => name.slice(0, 7)).sort();
|
|
@@ -301,7 +256,7 @@ async function readSnapshot(bookId, period, workspaceRoot) {
|
|
|
301
256
|
async function writeSnapshot(bookId, snapshot, workspaceRoot) {
|
|
302
257
|
const file = snapshotFileFor(bookId, snapshot.period, workspaceRoot);
|
|
303
258
|
await node_fs.promises.mkdir(node_path.default.dirname(file), { recursive: true });
|
|
304
|
-
await writeJsonAtomic(file, snapshot, { uniqueTmp: true });
|
|
259
|
+
await (0, _mulmoclaude_core_files.writeJsonAtomic)(file, snapshot, { uniqueTmp: true });
|
|
305
260
|
}
|
|
306
261
|
/** Drop snapshot files for all periods >= `fromPeriod`. The next
|
|
307
262
|
* read regenerates them. Idempotent: missing files are silently
|
|
@@ -311,7 +266,7 @@ async function invalidateSnapshotsFrom(bookId, fromPeriod, workspaceRoot) {
|
|
|
311
266
|
try {
|
|
312
267
|
names = await node_fs.promises.readdir(snapshotsDir(bookId, workspaceRoot));
|
|
313
268
|
} catch (err) {
|
|
314
|
-
if (isEnoent(err)) return { removed: [] };
|
|
269
|
+
if ((0, _mulmoclaude_core_files.isEnoent)(err)) return { removed: [] };
|
|
315
270
|
throw err;
|
|
316
271
|
}
|
|
317
272
|
const removed = [];
|
|
@@ -349,16 +304,6 @@ async function removeBookDir(bookId, workspaceRoot) {
|
|
|
349
304
|
});
|
|
350
305
|
}
|
|
351
306
|
//#endregion
|
|
352
|
-
//#region src/server/types.ts
|
|
353
|
-
/** B/S accounts (assets / liabilities / equity). Used by opening
|
|
354
|
-
* balance validation: opening entries reference balance-sheet
|
|
355
|
-
* accounts only. */
|
|
356
|
-
var BALANCE_SHEET_ACCOUNT_TYPES = [
|
|
357
|
-
"asset",
|
|
358
|
-
"liability",
|
|
359
|
-
"equity"
|
|
360
|
-
];
|
|
361
|
-
//#endregion
|
|
362
307
|
//#region src/server/journal.ts
|
|
363
308
|
/** Floating-point tolerance for the debit = credit check. Currency
|
|
364
309
|
* amounts arrive as JavaScript numbers (the on-wire format is JSON,
|
|
@@ -589,7 +534,7 @@ function validateLineAccountTypes(input, errors) {
|
|
|
589
534
|
});
|
|
590
535
|
return;
|
|
591
536
|
}
|
|
592
|
-
if (!BALANCE_SHEET_ACCOUNT_TYPES.includes(acct.type)) errors.push({
|
|
537
|
+
if (!require_shared.BALANCE_SHEET_ACCOUNT_TYPES.includes(acct.type)) errors.push({
|
|
593
538
|
field: `lines[${idx}].accountCode`,
|
|
594
539
|
message: `account ${acct.code} is type ${acct.type}; opening balances may only reference balance-sheet accounts (asset / liability / equity)`
|
|
595
540
|
});
|
|
@@ -661,6 +606,16 @@ function normalizeStoredAccount(input, existing) {
|
|
|
661
606
|
//#endregion
|
|
662
607
|
//#region src/server/report.ts
|
|
663
608
|
var ZERO_TOLERANCE = .0049;
|
|
609
|
+
/** Collapse an accountCode → netDebit map into the sorted
|
|
610
|
+
* `AccountBalance[]` wire shape. Shared by `aggregateBalances` and
|
|
611
|
+
* the snapshot cache's `mergeBalances` so the two produce identical,
|
|
612
|
+
* deterministically ordered output. */
|
|
613
|
+
function sortedBalancesFromMap(balanceByCode) {
|
|
614
|
+
return Array.from(balanceByCode.entries()).map(([accountCode, netDebit]) => ({
|
|
615
|
+
accountCode,
|
|
616
|
+
netDebit
|
|
617
|
+
})).sort((lhs, rhs) => lhs.accountCode.localeCompare(rhs.accountCode));
|
|
618
|
+
}
|
|
664
619
|
/** Returns net (debit − credit) per account across the supplied
|
|
665
620
|
* entries. Voids work by having an original + reverse pair that
|
|
666
621
|
* cancel mathematically — both are included in aggregation (their
|
|
@@ -682,10 +637,7 @@ function aggregateBalances(entries) {
|
|
|
682
637
|
map.set(line.accountCode, cur + debit - credit);
|
|
683
638
|
}
|
|
684
639
|
}
|
|
685
|
-
return
|
|
686
|
-
accountCode,
|
|
687
|
-
netDebit
|
|
688
|
-
})).sort((lhs, rhs) => lhs.accountCode.localeCompare(rhs.accountCode));
|
|
640
|
+
return sortedBalancesFromMap(map);
|
|
689
641
|
}
|
|
690
642
|
function naturalSign$1(type, netDebit) {
|
|
691
643
|
if (type === "asset" || type === "expense") return netDebit;
|
|
@@ -1036,10 +988,7 @@ function mergeBalances(base, delta) {
|
|
|
1036
988
|
const map = /* @__PURE__ */ new Map();
|
|
1037
989
|
for (const row of base) map.set(row.accountCode, row.netDebit);
|
|
1038
990
|
for (const row of delta) map.set(row.accountCode, (map.get(row.accountCode) ?? 0) + row.netDebit);
|
|
1039
|
-
return
|
|
1040
|
-
accountCode,
|
|
1041
|
-
netDebit
|
|
1042
|
-
})).sort((lhs, rhs) => lhs.accountCode.localeCompare(rhs.accountCode));
|
|
991
|
+
return sortedBalancesFromMap(map);
|
|
1043
992
|
}
|
|
1044
993
|
async function buildEmptySnapshot(bookId, period, workspaceRoot) {
|
|
1045
994
|
const empty = {
|