/** * Nested-path support for `moneyFields` declarations. * * A descriptor map key is a PATH, not just a top-level field name: * * - `'total'` — top-level scalar (the original form) * - `'billing.monthlyServiceFee'` — nested object member * - `'lineItems[].amount'` — member of every element of an array * - `'summary.*'` — every value of a record/map object * - `'income[].taxWithheld'`, `'byMonth.*.amount'` — segments compose * * Paths are parsed ONCE, at collection registration, and a syntactically * invalid declaration throws there — loudly, at setup time. (The * historical failure mode this kills: a declared-but-unreachable path * that was silently ignored, leaving the field un-quantized — a latent * 100× bug.) A path that doesn't match a given record at write time is * NOT an error — optional fields and empty arrays are legitimate — but * a path segment that hits a value of the WRONG SHAPE (`[]` on a * non-array, `.*` on a non-object) throws, because that means the * declaration and the data model disagree. */ export type MoneyPathSegment = { readonly kind: 'key'; readonly key: string; readonly array: boolean; } | { readonly kind: 'wildcard'; readonly array: boolean; }; /** * Parse a moneyFields path into segments. Throws `ValidationError` on * bad syntax. Results are memoized — the same declared paths are walked * on every write/read. */ export declare function parseMoneyPath(path: string): readonly MoneyPathSegment[]; /** True when the path is a plain top-level field name (the fast path). */ export declare function isSimpleMoneyPath(path: string): boolean; /** * Validate every declared path's syntax. Call at collection * registration so typos throw at setup, not silently no-op per write. */ export declare function validateMoneyFieldPaths(moneyFields: Record): void; /** * The leaf visitor: receives the (already-cloned) container holding the * money value and the key/index of that value. Mutating the container * is safe — every container on a matched path is a fresh clone. */ export type MoneyLeafVisitor = (container: Record | unknown[], key: string | number) => void; /** * Walk `node` along `segments`, copy-on-write-cloning every container on * a matched path, and call `visit` at each leaf position. Returns the * (possibly new) node; unmatched paths return the input untouched. * * Shape mismatches (`[]` on a non-array, `*` on a non-object) THROW on * the write path — the declaration and the data model disagree, and * writing through would store an un-quantized amount. On the READ path * pass `lenient: true` instead: stored data predating a declaration * change must stay readable, so a mismatched node is returned untouched * (mirrors the defensive `continue` the flat decoder always had). */ export declare function transformAtMoneyPath(node: unknown, path: string, segments: readonly MoneyPathSegment[], index: number, visit: MoneyLeafVisitor, lenient: boolean): unknown;