@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.
Files changed (54) hide show
  1. package/dist/server/accountNormalize.d.ts +1 -1
  2. package/dist/server/accountNormalize.d.ts.map +1 -1
  3. package/dist/server/context.d.ts +3 -10
  4. package/dist/server/context.d.ts.map +1 -1
  5. package/dist/server/defaultAccounts.d.ts +1 -1
  6. package/dist/server/defaultAccounts.d.ts.map +1 -1
  7. package/dist/server/http.d.ts.map +1 -1
  8. package/dist/server/index.d.ts +1 -1
  9. package/dist/server/index.d.ts.map +1 -1
  10. package/dist/server/io.d.ts +2 -1
  11. package/dist/server/io.d.ts.map +1 -1
  12. package/dist/server/journal.d.ts +1 -1
  13. package/dist/server/journal.d.ts.map +1 -1
  14. package/dist/server/openingBalances.d.ts +1 -1
  15. package/dist/server/openingBalances.d.ts.map +1 -1
  16. package/dist/server/report.d.ts +6 -64
  17. package/dist/server/report.d.ts.map +1 -1
  18. package/dist/server/service.d.ts +1 -1
  19. package/dist/server/service.d.ts.map +1 -1
  20. package/dist/server/snapshotCache.d.ts +2 -1
  21. package/dist/server/snapshotCache.d.ts.map +1 -1
  22. package/dist/server/timeSeries.d.ts +1 -1
  23. package/dist/server/timeSeries.d.ts.map +1 -1
  24. package/dist/server/types.d.ts +1 -123
  25. package/dist/server/types.d.ts.map +1 -1
  26. package/dist/server.cjs +22 -73
  27. package/dist/server.cjs.map +1 -1
  28. package/dist/server.js +14 -65
  29. package/dist/server.js.map +1 -1
  30. package/dist/shared/errors.d.ts +1 -1
  31. package/dist/shared/errors.d.ts.map +1 -1
  32. package/dist/shared/index.d.ts +1 -0
  33. package/dist/shared/index.d.ts.map +1 -1
  34. package/dist/shared/types.d.ts +188 -0
  35. package/dist/shared/types.d.ts.map +1 -0
  36. package/dist/shared-CcU3_qAa.cjs +749 -0
  37. package/dist/shared-CcU3_qAa.cjs.map +1 -0
  38. package/dist/shared-MOpJ1kUa.js +516 -0
  39. package/dist/shared-MOpJ1kUa.js.map +1 -0
  40. package/dist/shared.cjs +40 -519
  41. package/dist/shared.js +2 -483
  42. package/dist/vue/api.d.ts +2 -121
  43. package/dist/vue/api.d.ts.map +1 -1
  44. package/dist/vue/lang/index.d.ts +204 -215
  45. package/dist/vue/lang/index.d.ts.map +1 -1
  46. package/dist/vue.cjs +1651 -1678
  47. package/dist/vue.cjs.map +1 -1
  48. package/dist/vue.js +1652 -1679
  49. package/dist/vue.js.map +1 -1
  50. package/package.json +11 -6
  51. package/dist/server/atomic.d.ts +0 -15
  52. package/dist/server/atomic.d.ts.map +0 -1
  53. package/dist/shared.cjs.map +0 -1
  54. 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 Array.from(map.entries()).map(([accountCode, netDebit]) => ({
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 Array.from(map.entries()).map(([accountCode, netDebit]) => ({
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 = {