@mulmoclaude/accounting-plugin 0.3.0 → 0.3.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/router.d.ts.map +1 -1
- package/dist/server/service.d.ts +2 -2
- package/dist/server/service.d.ts.map +1 -1
- package/dist/server.cjs +36 -26
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +36 -26
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -1485,27 +1485,45 @@ function unsupportedCountryError(received) {
|
|
|
1485
1485
|
function unsupportedFiscalYearEndError(received) {
|
|
1486
1486
|
return new AccountingError(400, `unsupported fiscalYearEnd ${JSON.stringify(received)} — must be a closing-month number ${FISCAL_YEAR_END_MONTHS.join(", ")} (1 = January … 12 = December)`);
|
|
1487
1487
|
}
|
|
1488
|
-
/**
|
|
1489
|
-
* (
|
|
1490
|
-
*
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
*
|
|
1498
|
-
*
|
|
1499
|
-
*
|
|
1488
|
+
/** Coerce + validate a free-form `fiscalYearEnd` from any ingress path
|
|
1489
|
+
* (REST body, MCP tool args, direct callers). The service is the
|
|
1490
|
+
* validation boundary, so this is deliberately tolerant of input SHAPE
|
|
1491
|
+
* but strict about the resulting value:
|
|
1492
|
+
* - absent / null / empty string → `undefined` (field omitted; the
|
|
1493
|
+
* caller decides default-vs-no-op);
|
|
1494
|
+
* - a number, or a numeric string ("8") from a hand-rolled client →
|
|
1495
|
+
* that month;
|
|
1496
|
+
* - a legacy calendar-quarter token ("Q1".."Q4") from a stale client
|
|
1497
|
+
* → its closing month (same Q1→3 mapping the read side applies);
|
|
1498
|
+
* - anything else non-empty (a typo, garbage, an out-of-range or
|
|
1499
|
+
* non-integer number) → 400, echoing the ORIGINAL value so the
|
|
1500
|
+
* bad payload can't be silently mistaken for the default. */
|
|
1501
|
+
function coerceFiscalYearEndInput(raw) {
|
|
1502
|
+
if (raw === void 0 || raw === null || raw === "") return void 0;
|
|
1503
|
+
let month = raw;
|
|
1504
|
+
if (typeof raw === "string") {
|
|
1505
|
+
const trimmed = raw.trim();
|
|
1506
|
+
if (trimmed === "") return void 0;
|
|
1507
|
+
if (/^Q[1-4]$/.test(trimmed)) return resolveFiscalYearEnd(trimmed);
|
|
1508
|
+
month = /^-?\d+$/.test(trimmed) ? Number(trimmed) : NaN;
|
|
1509
|
+
}
|
|
1510
|
+
if (!isFiscalYearEnd(month)) throw unsupportedFiscalYearEndError(raw);
|
|
1511
|
+
return month;
|
|
1512
|
+
}
|
|
1513
|
+
/** Boundary checks shared by updateBook (name / country only —
|
|
1514
|
+
* fiscalYearEnd is coerced + validated separately via
|
|
1515
|
+
* `coerceFiscalYearEndInput`). Throws on the first failure so the
|
|
1516
|
+
* surrounding function stays under the cognitive-complexity threshold;
|
|
1517
|
+
* each rule is also unit-testable independently via the service entry
|
|
1518
|
+
* point. */
|
|
1500
1519
|
function validateUpdateBookInput(input) {
|
|
1501
1520
|
if (input.name !== void 0 && (typeof input.name !== "string" || input.name.trim() === "")) throw new AccountingError(400, "name must be a non-empty string when supplied");
|
|
1502
1521
|
if (input.country !== void 0 && input.country !== "" && !isSupportedCountryCode(input.country)) throw unsupportedCountryError(input.country);
|
|
1503
|
-
if (input.fiscalYearEnd !== void 0 && !isFiscalYearEnd(input.fiscalYearEnd)) throw unsupportedFiscalYearEndError(input.fiscalYearEnd);
|
|
1504
1522
|
}
|
|
1505
1523
|
async function createBook(input, workspaceRoot) {
|
|
1506
1524
|
if (typeof input.name !== "string" || input.name.trim() === "") throw new AccountingError(400, "name is required");
|
|
1507
1525
|
if (input.country !== void 0 && !isSupportedCountryCode(input.country)) throw unsupportedCountryError(input.country);
|
|
1508
|
-
const fiscalYearEnd =
|
|
1526
|
+
const fiscalYearEnd = coerceFiscalYearEndInput(input.fiscalYearEnd) ?? 12;
|
|
1509
1527
|
const config = await loadOrInitConfig(workspaceRoot);
|
|
1510
1528
|
const bookId = input.id ?? await generateBookId(config, workspaceRoot);
|
|
1511
1529
|
if (!isSafeBookId(bookId)) throw new AccountingError(400, `invalid book id ${JSON.stringify(bookId)} — allowed characters are A-Z a-z 0-9 _ - (1-64 chars; cannot start with _ or -)`);
|
|
@@ -1530,11 +1548,12 @@ async function updateBook(input, workspaceRoot) {
|
|
|
1530
1548
|
const target = findBook(config, input.bookId);
|
|
1531
1549
|
if (!target) throw new AccountingError(404, `book ${JSON.stringify(input.bookId)} not found`);
|
|
1532
1550
|
validateUpdateBookInput(input);
|
|
1551
|
+
const fiscalYearEnd = coerceFiscalYearEndInput(input.fiscalYearEnd);
|
|
1533
1552
|
const next = {
|
|
1534
1553
|
...target,
|
|
1535
1554
|
...input.name !== void 0 ? { name: input.name } : {},
|
|
1536
1555
|
...input.country !== void 0 && input.country !== "" ? { country: input.country } : {},
|
|
1537
|
-
...
|
|
1556
|
+
...fiscalYearEnd !== void 0 ? { fiscalYearEnd } : {}
|
|
1538
1557
|
};
|
|
1539
1558
|
if (input.country === "") delete next.country;
|
|
1540
1559
|
await writeConfig({ books: config.books.map((book) => book.id === input.bookId ? next : book) }, workspaceRoot);
|
|
@@ -1882,15 +1901,6 @@ function asyncHandler(namespace, fallbackMessage, handler) {
|
|
|
1882
1901
|
}
|
|
1883
1902
|
//#endregion
|
|
1884
1903
|
//#region src/server/router.ts
|
|
1885
|
-
/** Coerce an inbound `fiscalYearEnd` to a number. The UI select and
|
|
1886
|
-
* the LLM tool (JSON-schema `number` enum) both send a number, but a
|
|
1887
|
-
* numeric string ("8") from a hand-rolled client is accepted too so
|
|
1888
|
-
* it isn't silently dropped to `undefined` (→ the December default).
|
|
1889
|
-
* Range validation stays authoritative in the service layer. */
|
|
1890
|
-
function coerceFiscalYearEnd(raw) {
|
|
1891
|
-
if (typeof raw === "number") return raw;
|
|
1892
|
-
if (typeof raw === "string" && raw.trim() !== "" && Number.isInteger(Number(raw))) return Number(raw);
|
|
1893
|
-
}
|
|
1894
1904
|
async function handleOpenBook(rest) {
|
|
1895
1905
|
if (typeof rest.bookId !== "string" || rest.bookId === "") throw new AccountingError(400, "openBook: bookId is required. Call 'getBooks' to enumerate, or 'createBook' first on a fresh workspace.");
|
|
1896
1906
|
const list = await listBooks();
|
|
@@ -1939,7 +1949,7 @@ var ACTION_HANDLERS = {
|
|
|
1939
1949
|
name: String(rest.name ?? ""),
|
|
1940
1950
|
currency: typeof rest.currency === "string" ? rest.currency : void 0,
|
|
1941
1951
|
country: typeof rest.country === "string" ? rest.country : void 0,
|
|
1942
|
-
fiscalYearEnd:
|
|
1952
|
+
fiscalYearEnd: rest.fiscalYearEnd
|
|
1943
1953
|
});
|
|
1944
1954
|
return {
|
|
1945
1955
|
bookId: result.book.id,
|
|
@@ -1951,7 +1961,7 @@ var ACTION_HANDLERS = {
|
|
|
1951
1961
|
bookId: String(rest.bookId ?? ""),
|
|
1952
1962
|
name: typeof rest.name === "string" ? rest.name : void 0,
|
|
1953
1963
|
country: typeof rest.country === "string" ? rest.country : void 0,
|
|
1954
|
-
fiscalYearEnd:
|
|
1964
|
+
fiscalYearEnd: rest.fiscalYearEnd
|
|
1955
1965
|
});
|
|
1956
1966
|
return {
|
|
1957
1967
|
bookId: result.book.id,
|