{"_id":"@adaskothebeast/hierarchical-convert-to-decimal","_rev":"4-9a902ece1162eb0251e6a710780c5ec1","name":"@adaskothebeast/hierarchical-convert-to-decimal","dist-tags":{"latest":"11.0.1"},"versions":{"10.0.0":{"name":"@adaskothebeast/hierarchical-convert-to-decimal","version":"10.0.0","_id":"@adaskothebeast/hierarchical-convert-to-decimal@10.0.0","maintainers":[{"name":"adasko","email":"adaskothebeast@gmail.com"}],"dist":{"shasum":"64c247bea90fe80403645b021ae0cec677d6e94b","tarball":"https://registry.npmjs.org/@adaskothebeast/hierarchical-convert-to-decimal/-/hierarchical-convert-to-decimal-10.0.0.tgz","fileCount":14,"integrity":"sha512-Ag280Cm9CEfhpyoaXeofaXo+/p4D2HUoR83PVBx6PFX9UM0vv+qC5Nd8i8KmY7Op6FAiBQ1WA3Fa3yG7WiB0ww==","signatures":[{"sig":"MEYCIQCjCfRp0u8YoAi+3sLBfUnJYccMBy0rGiCuin2JfQoxKwIhALujhNaXVk/cLoLLM6sNESuHmoxxkjfLCHVF0MQ+r6Du","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":22592},"main":"./index.esm.js","module":"./index.js","gitHead":"d939a562c5f5947e98df253b28d384399481063e","_npmUser":{"name":"adasko","email":"adaskothebeast@gmail.com"},"_npmVersion":"11.16.0","description":"**Deep, in-place conversion of numeric strings inside JSON payloads into arbitrary precision `Decimal` instances, part of [date-interceptors](https://github.com/AdaskoTheBeAsT/date-interceptors).**","directories":{},"_nodeVersion":"24.18.0","dependencies":{},"_hasShrinkwrap":false,"peerDependencies":{"decimal.js":"^10.6.0"},"_npmOperationalInternal":{"tmp":"tmp/hierarchical-convert-to-decimal_10.0.0_1785246910549_0.673180811795415","host":"s3://npm-registry-packages-npm-production"}},"10.0.2":{"name":"@adaskothebeast/hierarchical-convert-to-decimal","version":"10.0.2","_id":"@adaskothebeast/hierarchical-convert-to-decimal@10.0.2","maintainers":[{"name":"adasko","email":"adaskothebeast@gmail.com"}],"homepage":"https://github.com/AdaskoTheBeAsT/date-interceptors#readme","bugs":{"url":"https://github.com/AdaskoTheBeAsT/date-interceptors/issues"},"dist":{"shasum":"29a27912894f93861d59e309c8569804f353b186","tarball":"https://registry.npmjs.org/@adaskothebeast/hierarchical-convert-to-decimal/-/hierarchical-convert-to-decimal-10.0.2.tgz","fileCount":6,"integrity":"sha512-3WdYLxbaHNgrS1EDT1ryMsEiGrocNWsP26a/jagoUwYvy1HXQLnhmGXu3VpCnQ5MzpCbq4AbOkONnWUXx0Lnjw==","signatures":[{"sig":"MEUCIBGqHAmc8LOTyWnmRYhlJbA7+093q63Vpp269leLTvATAiEA2wPh6BZeTGtgwYLwLSBYbbaxQG67Qzq5cPksrjTjf8U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@adaskothebeast%2fhierarchical-convert-to-decimal@10.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":12641},"main":"./index.esm.js","type":"module","types":"./index.d.ts","module":"./index.esm.js","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:7727518f-0384-4986-b54a-906ad84346e0"}},"repository":{"url":"git+https://github.com/AdaskoTheBeAsT/date-interceptors.git","type":"git","directory":"libs/hierarchical-convert-to-decimal"},"_npmVersion":"11.16.0","description":"**Deep, in-place conversion of numeric strings inside JSON payloads into arbitrary precision `Decimal` instances, part of [date-interceptors](https://github.com/AdaskoTheBeAsT/date-interceptors).**","directories":{},"_nodeVersion":"24.18.0","dependencies":{},"_hasShrinkwrap":false,"peerDependencies":{"decimal.js":"^10.6.0"},"_npmOperationalInternal":{"tmp":"tmp/hierarchical-convert-to-decimal_10.0.2_1785252307732_0.1658000575030656","host":"s3://npm-registry-packages-npm-production"}},"11.0.0":{"name":"@adaskothebeast/hierarchical-convert-to-decimal","version":"11.0.0","keywords":["decimal","decimal.js","bignumber","converter","json","typescript"],"author":{"url":"https://github.com/adaskothebeast","name":"Adam Pluciński","email":"adaskothebeast@gmail.com"},"license":"MIT","_id":"@adaskothebeast/hierarchical-convert-to-decimal@11.0.0","maintainers":[{"name":"adasko","email":"adaskothebeast@gmail.com"}],"homepage":"https://github.com/AdaskoTheBeAsT/date-interceptors/tree/main/libs/hierarchical-convert-to-decimal#readme","bugs":{"url":"https://github.com/AdaskoTheBeAsT/date-interceptors/issues"},"dist":{"shasum":"7062d54be44346c085f21b1c2d5b99afa1be95fb","tarball":"https://registry.npmjs.org/@adaskothebeast/hierarchical-convert-to-decimal/-/hierarchical-convert-to-decimal-11.0.0.tgz","fileCount":11,"integrity":"sha512-eJxxccpr8FOoVdXV42+b1Lc131cdzT3Dqee1e1lyhf8lzHKgYaIQGfEo0+jdvjXcv2l/sRnpIsPwVXuKZYe5oQ==","signatures":[{"sig":"MEUCIQCrJ6J3ckpcBn+pxo1EpXfWyjfBywqI7c3mmHutf02EKwIgctlBYCKm46bl4BAGucQ0x4PlUV//L6H/hYQg82s+nPE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCICY+MXBhMoAgT7WCIipZvIxZ3iH8QxEmQpQNp4Bf3eYLAiAFq+4UC1M65pasR3tFEcZDXrDOcqD3bt4ao4Kd3mYpuA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@adaskothebeast%2fhierarchical-convert-to-decimal@11.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":15254},"main":"./index.cjs","type":"module","types":"./index.d.ts","module":"./index.esm.js","engines":{"node":">=22.12"},"exports":{".":{"import":{"types":"./index.d.ts","default":"./index.esm.js"},"require":{"types":"./index.d.cts","default":"./index.cjs"}},"./package.json":"./package.json"},"gitHead":"23dc193eb4d8d834b51de51e1900225a26c143ff","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:7727518f-0384-4986-b54a-906ad84346e0"}},"repository":{"url":"git+https://github.com/AdaskoTheBeAsT/date-interceptors.git","type":"git","directory":"libs/hierarchical-convert-to-decimal"},"_npmVersion":"11.19.0","description":"Deep, in-place conversion of numeric strings in JSON payloads into arbitrary-precision decimal.js Decimal instances.","directories":{},"sideEffects":false,"_nodeVersion":"26.10.0","dependencies":{"@adaskothebeast/hierarchical-convert-core":"^11.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"decimal.js":"^10.6.0"},"_npmOperationalInternal":{"tmp":"tmp/hierarchical-convert-to-decimal_11.0.0_1790551318972_0.5217821111281495","host":"s3://npm-registry-packages-npm-production"}},"11.0.1":{"_id":"@adaskothebeast/hierarchical-convert-to-decimal@11.0.1","bugs":{"url":"https://github.com/AdaskoTheBeAsT/date-interceptors/issues"},"dist":{"shasum":"10edeeb5518483fff2d65de992c9f5be02eed2b8","tarball":"https://registry.npmjs.org/@adaskothebeast/hierarchical-convert-to-decimal/-/hierarchical-convert-to-decimal-11.0.1.tgz","fileCount":11,"integrity":"sha512-/4XhZObabRC+NVXeTat/cWeMQSf/86jIPHHOxsmzkbMkenJk0QJBsHWOkI1laWlt2i8aLWkAXsmEYQSw0eMgwA==","signatures":[{"sig":"MEUCIH6W5RqjuqX/iIOOOxBKWTnPWiuf1oVoKGux2hoawF6sAiEA+djaeB5OCweSDKhDiMW/wgzDYDoU6hTOfkLrQkmd9j4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHzjEk+vqw5cD1SrF3KesDj5YF289+unIU/1dTVht62NAiBxRuTGhjdb1+XhkVzAWmSWJQQ775g5FOXajXuwKZ2urA=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@adaskothebeast%2fhierarchical-convert-to-decimal@11.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":15254},"main":"./index.cjs","name":"@adaskothebeast/hierarchical-convert-to-decimal","type":"module","types":"./index.d.ts","author":{"url":"https://github.com/adaskothebeast","name":"Adam Pluciński","email":"adaskothebeast@gmail.com"},"module":"./index.esm.js","engines":{"node":">=22.12"},"exports":{".":{"import":{"types":"./index.d.ts","default":"./index.esm.js"},"require":{"types":"./index.d.cts","default":"./index.cjs"}},"./package.json":"./package.json"},"gitHead":"f9edf319c5e23bf1a17f5ec4eb8dac0008b9fd26","license":"MIT","version":"11.0.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"7727518f-0384-4986-b54a-906ad84346e0"}},"homepage":"https://github.com/AdaskoTheBeAsT/date-interceptors/tree/main/libs/hierarchical-convert-to-decimal#readme","keywords":["decimal","decimal.js","bignumber","converter","json","typescript"],"repository":{"url":"git+https://github.com/AdaskoTheBeAsT/date-interceptors.git","type":"git","directory":"libs/hierarchical-convert-to-decimal"},"_npmVersion":"11.19.0","description":"Deep, in-place conversion of numeric strings in JSON payloads into arbitrary-precision decimal.js Decimal instances.","directories":{},"maintainers":[{"name":"adasko","email":"adaskothebeast@gmail.com"}],"sideEffects":false,"_nodeVersion":"26.10.0","dependencies":{"@adaskothebeast/hierarchical-convert-core":"^11.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"decimal.js":"^10.6.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/hierarchical-convert-to-decimal_11.0.1_1790749934601_0.39654743970355955"}}},"time":{"created":"2026-07-28T13:55:10.385Z","modified":"2026-09-30T06:32:15.086Z","10.0.0":"2026-07-28T13:55:10.673Z","10.0.2":"2026-07-28T15:25:07.920Z","11.0.0":"2026-09-27T23:21:59.047Z","11.0.1":"2026-09-30T06:32:14.695Z"},"bugs":{"url":"https://github.com/AdaskoTheBeAsT/date-interceptors/issues"},"author":{"url":"https://github.com/adaskothebeast","name":"Adam Pluciński","email":"adaskothebeast@gmail.com"},"license":"MIT","homepage":"https://github.com/AdaskoTheBeAsT/date-interceptors/tree/main/libs/hierarchical-convert-to-decimal#readme","keywords":["decimal","decimal.js","bignumber","converter","json","typescript"],"repository":{"url":"git+https://github.com/AdaskoTheBeAsT/date-interceptors.git","type":"git","directory":"libs/hierarchical-convert-to-decimal"},"description":"Deep, in-place conversion of numeric strings in JSON payloads into arbitrary-precision decimal.js Decimal instances.","maintainers":[{"name":"adasko","email":"adaskothebeast@gmail.com"}],"readme":"# 🧮 @adaskothebeast/hierarchical-convert-to-decimal\n\n**Deep, in-place conversion of numeric strings inside JSON payloads into arbitrary precision `Decimal` instances, part of [date-interceptors](https://github.com/AdaskoTheBeAsT/date-interceptors).**\n\n[![npm](https://img.shields.io/npm/v/%40adaskothebeast%2Fhierarchical-convert-to-decimal?color=cb3837&logo=npm)](https://www.npmjs.com/package/@adaskothebeast/hierarchical-convert-to-decimal)\n[![license](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)\n\nPeer dependency: `decimal.js` (types ship with it, no companion `@types` needed). Runtime dependency: the dependency-free `@adaskothebeast/hierarchical-convert-core`.\n\n---\n\n## 📦 Install\n\n```bash\nnpm i @adaskothebeast/hierarchical-convert-to-decimal decimal.js\n```\n\n---\n\n## 🎯 What it does\n\nMoney and quantities are transported as JSON strings precisely because `JSON.parse` would round them into a double. This package closes the last gap: it walks a parsed payload, finds strings that are plain numeric literals, and replaces them with `new Decimal(value)` so the digits survive into your domain model.\n\n```text\n{ \"total\": \"12345678901234567890.1234567890123456789\" }\n  -> total.toString() === '12345678901234567890.1234567890123456789'\n```\n\nThe traversal is the same hardened walk used by the sibling date converters (shared through `hierarchical-convert-core`): arrays and plain objects only, own enumerable properties only, `__proto__` / `constructor` / `prototype` skipped, the root plus 100 nested levels, cycles visited once, frozen or read-only properties left alone. Conversion happens in place and the function returns `void`. The full traversal rules are in the repository [conversion contract](https://github.com/AdaskoTheBeAsT/date-interceptors/blob/main/docs/conversion-contract.md#heuristic-date-conversion).\n\nRecognition is **content driven, not key driven**. There is no allow-list of field names and no path list, so any string that looks like a number becomes a `Decimal`. That is fast and zero-config, and it is also the reason the schema-driven [`@adaskothebeast/typewriter-runtime`](https://www.npmjs.com/package/@adaskothebeast/typewriter-runtime) path exists: only a schema can tell you that `\"42\"` is a quantity while `\"007\"` is a jersey number.\n\n---\n\n## 🧰 API\n\n| Export                         | Signature                | Notes                                   |\n| ------------------------------ | ------------------------ | --------------------------------------- |\n| `hierarchicalConvertToDecimal` | `(obj: unknown) => void` | Mutates `obj` in place, returns `void`. |\n\nNon-object inputs (`null`, numbers, strings, `undefined`) are accepted and ignored, so you can call it on any deserialized body without a guard. (Before 11.0.0 the signature also exposed internal `depth` and `visited` parameters.)\n\n---\n\n## ⚡ Usage\n\n```ts\nimport { hierarchicalConvertToDecimal } from '@adaskothebeast/hierarchical-convert-to-decimal';\nimport Decimal from 'decimal.js';\n\nconst payload = {\n  order: {\n    reference: 'ORD-2023-07',\n    total: '1999.99',\n    lines: [\n      { sku: 'A-1', unitPrice: '9.995', quantity: '3' },\n      { sku: 'A-2', unitPrice: '0.005', quantity: '1' },\n    ],\n  },\n};\n\nhierarchicalConvertToDecimal(payload);\n\nDecimal.isDecimal(payload.order.total); // true\npayload.order.lines[0].unitPrice.times(3).toString(); // '29.985'\npayload.order.reference; // 'ORD-2023-07' (unchanged)\n```\n\nAs a `fetch` post-processing step:\n\n```ts\nasync function getJson<T>(url: string): Promise<T> {\n  const response = await fetch(url);\n  const data = (await response.json()) as T;\n  hierarchicalConvertToDecimal(data);\n  return data;\n}\n```\n\nCombined with a date converter, order does not matter because the two recognisers cannot claim the same string:\n\n```ts\nimport { hierarchicalConvertToDate } from '@adaskothebeast/hierarchical-convert-to-date';\n\nhierarchicalConvertToDate(payload);\nhierarchicalConvertToDecimal(payload);\n```\n\n---\n\n## 🎛️ Options and configuration\n\nNone. The only knob is which strings match, and that is fixed:\n\n1. **Full match** against `^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?$`.\n2. **Construction** via `new Decimal(value)`, wrapped in a `try` / `catch` that silently leaves the string in place if construction throws.\n\n| Accepted        | Rejected     |\n| --------------- | ------------ |\n| `123.45`        | `''`         |\n| `-0.001`        | `' '`        |\n| `+42`           | `'1 000'`    |\n| `.5`            | `'1,000.00'` |\n| `1.25e+8`       | `'NaN'`      |\n| `42`            | `'Infinity'` |\n| `7.`            | `'0x10'`     |\n| `0000000000042` | `'1.2.3'`    |\n|                 | `'1e'`       |\n\nGlobal `decimal.js` configuration (`Decimal.set({ precision, rounding })`) is respected because the values are ordinary `Decimal` instances; it affects arithmetic, not the digits captured at construction time.\n\n---\n\n## 📤 Output examples\n\n```ts\nconst input = {\n  amount: '123.45',\n  negative: '-0.001',\n  signed: '+42',\n  bare: '.5',\n  exponential: '1.25e+8',\n  huge: '12345678901234567890.1234567890123456789',\n  thousands: '1,000.00',\n  text: 'adam',\n  alreadyNumber: 42,\n};\n\nhierarchicalConvertToDecimal(input);\n```\n\n```text\namount        -> Decimal 123.45\nnegative      -> Decimal -0.001\nsigned        -> Decimal 42\nbare          -> Decimal 0.5\nexponential   -> Decimal 125000000\nhuge          -> Decimal 12345678901234567890.1234567890123456789\nthousands     -> '1,000.00'  (unchanged)\ntext          -> 'adam'      (unchanged)\nalreadyNumber -> 42          (untouched, numbers are never wrapped)\n```\n\nTop-level arrays work as well: `['123.45', '-0.001']` becomes `[new Decimal('123.45'), new Decimal('-0.001')]`.\n\n---\n\n## ⚠️ Edge cases\n\n- **Plain integers are converted.** `\"42\"`, `\"007\"` and `\"+48123456789\"` all match the regex, so string-encoded identifiers, postal codes, phone numbers, version strings like `\"1.0\"` and ordering keys become `Decimal` instances. This is the main false-positive risk; if your payload carries such fields, hydrate through a schema with [`@adaskothebeast/typewriter-runtime`](https://www.npmjs.com/package/@adaskothebeast/typewriter-runtime) instead, or run this converter on a narrowed subtree.\n- **Leading zeros and trailing dots are normalised away.** `\"007\"` becomes `Decimal 7` and `\"7.\"` becomes `Decimal 7`. If the original literal has to round-trip byte for byte, keep the string.\n- **Numbers already parsed by `JSON.parse` are not touched.** Only strings are candidates, so precision lost before this function runs (an unquoted `1.005` in the JSON text) cannot be recovered here. Ask the server to quote the field.\n- **Construction is lossless, arithmetic is not.** `new Decimal(...)` keeps every digit you pass, but `decimal.js` operations round to `Decimal.precision` (default 20 significant digits). Call `Decimal.set({ precision: 40 })` once at startup when you compute on 39-digit values.\n- **Invalid input never throws out of the walk.** `new Decimal` throwing is caught silently and the string is preserved. In practice the regex makes this unreachable, since `decimal.js` accepts everything the regex accepts.\n- **`NaN`, `Infinity` and `-Infinity` strings are rejected on purpose.** `decimal.js` would happily construct them; converting them silently would turn a data error into a poisoned computation.\n- **Prototype pollution is blocked.** Own `__proto__`, `constructor` and `prototype` keys are skipped, so a hostile payload cannot reach `Object.prototype`. The side effect is that a nested object stored under a key literally named `constructor` or `prototype` is never traversed either.\n- **Only arrays and plain objects are entered (11.0.0).** Class instances, `Map`, `Set`, Buffers, and existing `Decimal` values are left untouched, so running the converter twice is a no-op. Before 11.0.0 every non-null object was entered.\n- **Frozen or read-only values stay strings.** Frozen objects, non-writable properties, and getter-only properties are skipped instead of throwing mid-walk.\n- **Depth is capped.** The root plus 100 nested levels are walked; deeper strings stay strings. This is DoS protection against adversarially nested JSON.\n- **Circular graphs are safe.** Visited objects are recorded, so `input.self = input` converts once and does not loop.\n- **A converted value is no longer JSON-serializable as a number.** `JSON.stringify` on a `Decimal` yields a quoted string (`decimal.js` defines `toJSON`), so a round trip produces `\"12.5\"`, not `12.5`. Serialize explicitly (`value.toString()`, `value.toFixed(2)`) when the wire format matters.\n- **Strings are terminal.** When a value is a string the walker does not recurse (a string has no children), so a non-matching string simply stays as it is.\n\n---\n\n## 🔗 Related packages\n\n- Other value kinds: [`hierarchical-convert-to-uuid`](https://www.npmjs.com/package/@adaskothebeast/hierarchical-convert-to-uuid), [`hierarchical-convert-to-date`](https://www.npmjs.com/package/@adaskothebeast/hierarchical-convert-to-date), [`-date-fns`](https://www.npmjs.com/package/@adaskothebeast/hierarchical-convert-to-date-fns), [`-dayjs`](https://www.npmjs.com/package/@adaskothebeast/hierarchical-convert-to-dayjs), [`-luxon`](https://www.npmjs.com/package/@adaskothebeast/hierarchical-convert-to-luxon), [`-moment`](https://www.npmjs.com/package/@adaskothebeast/hierarchical-convert-to-moment), [`-js-joda`](https://www.npmjs.com/package/@adaskothebeast/hierarchical-convert-to-js-joda), [`-temporal`](https://www.npmjs.com/package/@adaskothebeast/hierarchical-convert-to-temporal)\n- Transports: [`angular-date-http-interceptor`](https://www.npmjs.com/package/@adaskothebeast/angular-date-http-interceptor), [`axios-interceptor`](https://www.npmjs.com/package/@adaskothebeast/axios-interceptor), [`react-redux-toolkit-hierarchical-date-hook`](https://www.npmjs.com/package/@adaskothebeast/react-redux-toolkit-hierarchical-date-hook)\n- Schema stack, for payloads where guessing is not acceptable: [`typewriter-schema`](https://www.npmjs.com/package/@adaskothebeast/typewriter-schema), [`typewriter-runtime`](https://www.npmjs.com/package/@adaskothebeast/typewriter-runtime), [`typewriter-http-angular`](https://www.npmjs.com/package/@adaskothebeast/typewriter-http-angular), [`typewriter-http-axios`](https://www.npmjs.com/package/@adaskothebeast/typewriter-http-axios), [`typewriter-http-fetch`](https://www.npmjs.com/package/@adaskothebeast/typewriter-http-fetch)\n\nFull matrix and adapter recipes: [main README](https://github.com/AdaskoTheBeAsT/date-interceptors#readme).\n\n---\n\n## 📄 License\n\n[MIT](./LICENSE) © Adam Pluciński\n","readmeFilename":"README.md"}