@flickfyi/flkr 0.0.140 → 0.0.146
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/node_modules/@flickfyi/attestation/.turbo/turbo-build.log +7 -7
- package/node_modules/@flickfyi/biscuit/.turbo/turbo-build.log +7 -7
- package/node_modules/@flickfyi/core/.oxlintrc.json +2 -1
- package/node_modules/@flickfyi/core/.turbo/turbo-build.log +502 -95
- package/node_modules/@flickfyi/core/README.md +58 -4
- package/node_modules/@flickfyi/core/dist/index.cjs +1 -1
- package/node_modules/@flickfyi/core/dist/index.js +35 -24
- package/node_modules/@flickfyi/core/dist/kowalski/calculations.d.ts +17 -0
- package/node_modules/@flickfyi/core/dist/kowalski/calculator.d.ts +13 -0
- package/node_modules/@flickfyi/core/dist/kowalski/catalog.cjs +1 -0
- package/node_modules/@flickfyi/core/dist/kowalski/catalog.d.cts +962 -0
- package/node_modules/@flickfyi/core/dist/kowalski/catalog.d.ts +962 -0
- package/node_modules/@flickfyi/core/dist/kowalski/catalog.js +639 -0
- package/node_modules/@flickfyi/core/dist/kowalski/clocks.d.ts +4 -0
- package/node_modules/@flickfyi/core/dist/kowalski/date-time.d.ts +22 -0
- package/node_modules/@flickfyi/core/dist/kowalski/details.d.ts +7 -0
- package/node_modules/@flickfyi/core/dist/kowalski/everyday.d.ts +8 -0
- package/node_modules/@flickfyi/core/dist/kowalski/format.d.ts +4 -0
- package/node_modules/@flickfyi/core/dist/kowalski/frame.d.ts +161 -0
- package/node_modules/@flickfyi/core/dist/kowalski/index.cjs +21 -0
- package/node_modules/@flickfyi/core/dist/kowalski/index.d.cts +8 -0
- package/node_modules/@flickfyi/core/dist/kowalski/index.d.ts +8 -0
- package/node_modules/@flickfyi/core/dist/kowalski/index.js +7496 -0
- package/node_modules/@flickfyi/core/dist/kowalski/productivity.d.ts +5 -0
- package/node_modules/@flickfyi/core/dist/kowalski/recipe.d.ts +8 -0
- package/node_modules/@flickfyi/core/dist/kowalski/tools.d.ts +7 -0
- package/node_modules/@flickfyi/core/dist/kowalski/use-clock.d.ts +6 -0
- package/node_modules/@flickfyi/core/dist/kowalski/use-configured-feature.d.ts +19 -0
- package/node_modules/@flickfyi/core/dist/kowalski/use-feature-store.d.ts +15 -0
- package/node_modules/@flickfyi/core/dist/kowalski.css +506 -0
- package/node_modules/@flickfyi/core/dist/molecules/field/field.d.ts +8 -0
- package/node_modules/@flickfyi/core/dist/molecules/index.cjs +1 -1
- package/node_modules/@flickfyi/core/dist/molecules/index.d.cts +3 -0
- package/node_modules/@flickfyi/core/dist/molecules/index.d.ts +3 -0
- package/node_modules/@flickfyi/core/dist/molecules/index.js +88 -27
- package/node_modules/@flickfyi/core/dist/molecules/segmented/segmented.d.ts +10 -0
- package/node_modules/@flickfyi/core/dist/molecules/stepper/stepper.d.ts +10 -0
- package/node_modules/@flickfyi/core/dist/quantum/use-store.d.ts +7 -1
- package/node_modules/@flickfyi/core/dist/themes.css +2 -0
- package/node_modules/@flickfyi/core/examples/kowalski/bill-split/bill-split.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/bill-split/bill-split.tsx +17 -0
- package/node_modules/@flickfyi/core/examples/kowalski/bookmark/bookmark.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/bookmark/bookmark.tsx +23 -0
- package/node_modules/@flickfyi/core/examples/kowalski/budget/budget.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/budget/budget.tsx +26 -0
- package/node_modules/@flickfyi/core/examples/kowalski/calculator/calculator.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/calculator/calculator.tsx +24 -0
- package/node_modules/@flickfyi/core/examples/kowalski/checklist/checklist.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/checklist/checklist.tsx +26 -0
- package/node_modules/@flickfyi/core/examples/kowalski/color-palette/color-palette.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/color-palette/color-palette.tsx +22 -0
- package/node_modules/@flickfyi/core/examples/kowalski/contact/contact.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/contact/contact.tsx +23 -0
- package/node_modules/@flickfyi/core/examples/kowalski/countdown/countdown.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/countdown/countdown.tsx +23 -0
- package/node_modules/@flickfyi/core/examples/kowalski/event/event.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/event/event.tsx +24 -0
- package/node_modules/@flickfyi/core/examples/kowalski/expense/expense.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/expense/expense.tsx +23 -0
- package/node_modules/@flickfyi/core/examples/kowalski/goal/goal.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/goal/goal.tsx +24 -0
- package/node_modules/@flickfyi/core/examples/kowalski/habit/habit.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/habit/habit.tsx +23 -0
- package/node_modules/@flickfyi/core/examples/kowalski/ledger/ledger.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/ledger/ledger.tsx +25 -0
- package/node_modules/@flickfyi/core/examples/kowalski/note/note.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/note/note.tsx +22 -0
- package/node_modules/@flickfyi/core/examples/kowalski/poll/poll.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/poll/poll.tsx +26 -0
- package/node_modules/@flickfyi/core/examples/kowalski/random-picker/random-picker.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/random-picker/random-picker.tsx +23 -0
- package/node_modules/@flickfyi/core/examples/kowalski/rating/rating.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/rating/rating.tsx +19 -0
- package/node_modules/@flickfyi/core/examples/kowalski/recipe/recipe.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/recipe/recipe.tsx +120 -0
- package/node_modules/@flickfyi/core/examples/kowalski/reminder/reminder.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/reminder/reminder.tsx +23 -0
- package/node_modules/@flickfyi/core/examples/kowalski/rotation/rotation.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/rotation/rotation.tsx +27 -0
- package/node_modules/@flickfyi/core/examples/kowalski/scoreboard/scoreboard.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/scoreboard/scoreboard.tsx +26 -0
- package/node_modules/@flickfyi/core/examples/kowalski/shopping-list/shopping-list.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/shopping-list/shopping-list.tsx +27 -0
- package/node_modules/@flickfyi/core/examples/kowalski/task-board/task-board.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/task-board/task-board.tsx +27 -0
- package/node_modules/@flickfyi/core/examples/kowalski/theme.ts +3 -0
- package/node_modules/@flickfyi/core/examples/kowalski/time-zone/time-zone.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/time-zone/time-zone.tsx +22 -0
- package/node_modules/@flickfyi/core/examples/kowalski/timer/timer.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/timer/timer.tsx +23 -0
- package/node_modules/@flickfyi/core/examples/kowalski/trip/trip.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/trip/trip.tsx +26 -0
- package/node_modules/@flickfyi/core/examples/kowalski/unit-converter/unit-converter.stories.tsx +15 -0
- package/node_modules/@flickfyi/core/examples/kowalski/unit-converter/unit-converter.tsx +24 -0
- package/node_modules/@flickfyi/core/examples/parrott/calculator/calculator.stories.tsx +3 -1
- package/node_modules/@flickfyi/core/examples/parrott/checklist/checklist.stories.tsx +3 -1
- package/node_modules/@flickfyi/core/examples/parrott/countdown/countdown.stories.tsx +3 -1
- package/node_modules/@flickfyi/core/examples/parrott/counter/counter.stories.tsx +1 -0
- package/node_modules/@flickfyi/core/examples/parrott/fortune/fortune.stories.tsx +3 -1
- package/node_modules/@flickfyi/core/examples/parrott/grid/grid.stories.tsx +3 -1
- package/node_modules/@flickfyi/core/examples/parrott/kale-smoothie/kale-smoothie.stories.tsx +1 -0
- package/node_modules/@flickfyi/core/examples/parrott/list/list.stories.tsx +3 -1
- package/node_modules/@flickfyi/core/examples/parrott/log/log.stories.tsx +3 -1
- package/node_modules/@flickfyi/core/examples/parrott/timed-task/timed-task.stories.tsx +3 -1
- package/node_modules/@flickfyi/core/examples/parrott/timer/timer.stories.tsx +3 -1
- package/node_modules/@flickfyi/core/fixtures/native/host/project.yml +3 -0
- package/node_modules/@flickfyi/core/fixtures/native/src/catalog-features.tsx +88 -0
- package/node_modules/@flickfyi/core/package.json +35 -5
- package/node_modules/@flickfyi/core/package.tsgo.json +19 -1
- package/node_modules/@flickfyi/core/scripts/vite-globals.d.ts +2 -0
- package/node_modules/@flickfyi/core/src/generated-types.d.ts +1 -0
- package/node_modules/@flickfyi/core/src/kowalski/README.md +9 -0
- package/node_modules/@flickfyi/core/src/kowalski/calculations.ts +106 -0
- package/node_modules/@flickfyi/core/src/kowalski/calculator.ts +243 -0
- package/node_modules/@flickfyi/core/src/kowalski/catalog.ts +787 -0
- package/node_modules/@flickfyi/core/src/kowalski/clocks.tsx +464 -0
- package/node_modules/@flickfyi/core/src/kowalski/date-time.tsx +469 -0
- package/node_modules/@flickfyi/core/src/kowalski/details.tsx +1387 -0
- package/node_modules/@flickfyi/core/src/kowalski/everyday.tsx +652 -0
- package/node_modules/@flickfyi/core/src/kowalski/format.ts +48 -0
- package/node_modules/@flickfyi/core/src/kowalski/frame.tsx +351 -0
- package/node_modules/@flickfyi/core/src/kowalski/index.ts +23 -0
- package/node_modules/@flickfyi/core/src/kowalski/productivity.tsx +575 -0
- package/node_modules/@flickfyi/core/src/kowalski/recipe.tsx +555 -0
- package/node_modules/@flickfyi/core/src/kowalski/stories/composition.stories.tsx +47 -0
- package/node_modules/@flickfyi/core/src/kowalski/stories/composition.tsx +100 -0
- package/node_modules/@flickfyi/core/src/kowalski/tools.tsx +1213 -0
- package/node_modules/@flickfyi/core/src/kowalski/use-clock.ts +19 -0
- package/node_modules/@flickfyi/core/src/kowalski/use-configured-feature.ts +85 -0
- package/node_modules/@flickfyi/core/src/kowalski/use-feature-store.ts +119 -0
- package/node_modules/@flickfyi/core/src/molecules/field/field.tsx +34 -0
- package/node_modules/@flickfyi/core/src/molecules/index.ts +3 -0
- package/node_modules/@flickfyi/core/src/molecules/segmented/segmented.tsx +48 -0
- package/node_modules/@flickfyi/core/src/molecules/stepper/stepper.tsx +54 -0
- package/node_modules/@flickfyi/core/src/quantum/use-store.ts +41 -16
- package/node_modules/@flickfyi/core/tests/fly.test.ts +34 -7
- package/node_modules/@flickfyi/core/tests/kowalski-calculations.test.ts +60 -0
- package/node_modules/@flickfyi/core/tests/kowalski-calculator.test.ts +142 -0
- package/node_modules/@flickfyi/core/tests/kowalski-contracts.test.ts +223 -0
- package/node_modules/@flickfyi/core/tests/kowalski-date-time.test.tsx +462 -0
- package/node_modules/@flickfyi/core/tests/kowalski-features.test.tsx +2158 -0
- package/node_modules/@flickfyi/core/tests/kowalski-store.test.tsx +514 -0
- package/node_modules/@flickfyi/core/tests/preview-build.test.ts +1 -1
- package/node_modules/@flickfyi/core/tests/quantum-use-store.test.ts +53 -1
- package/node_modules/@flickfyi/core/tests/verify-tsgo-assets.test.ts +1 -1
- package/node_modules/@flickfyi/core/tsconfig.vite-dts.json +2 -1
- package/node_modules/@flickfyi/core/tsgo.bun.lock +111 -9
- package/node_modules/@flickfyi/core/tsgo.package.json +21 -3
- package/node_modules/@flickfyi/core/vite.config.ts +9 -5
- package/node_modules/@flickfyi/core/vitest.config.ts +1 -1
- package/node_modules/@flickfyi/http-message-sig/.turbo/turbo-build.log +7 -7
- package/node_modules/@flickfyi/identity/.turbo/turbo-build.log +8 -9
- package/node_modules/@flickfyi/lens/.turbo/turbo-build.log +7 -7
- package/node_modules/@flickfyi/photon/.turbo/turbo-build.log +9 -9
- package/node_modules/@flickfyi/photon/Sources/Photon/Components/Button.swift +29 -2
- package/node_modules/@flickfyi/photon/Sources/PhotonVersion/Resources/photon-version.json +1 -1
- package/node_modules/@flickfyi/photon/Tests/PhotonTests/Components/ButtonHostTests.swift +26 -0
- package/node_modules/@flickfyi/photon/Tests/PhotonTests/Fixtures/StyleX/catalog-features.styles.json +1 -0
- package/node_modules/@flickfyi/photon/Tests/PhotonTests/Fixtures/StyleX/catalog-native.styles.json +1 -1
- package/node_modules/@flickfyi/photon/Tests/PhotonTests/Fixtures/StyleX/catalog-scale.styles.json +1 -1
- package/node_modules/@flickfyi/photon/Tests/PhotonTests/Fixtures/StyleX/catalog-theme.styles.json +1 -1
- package/node_modules/@flickfyi/photon/Tests/PhotonTests/Fixtures/StyleX/catalog-typography.styles.json +1 -1
- package/node_modules/@flickfyi/photon/Tests/PhotonTests/Fixtures/StyleX/stylex-calculator.styles.json +1 -1
- package/node_modules/@flickfyi/photon/dist/globals.d.ts +5 -0
- package/node_modules/@flickfyi/photon/dist/version.cjs +1 -1
- package/node_modules/@flickfyi/photon/dist/version.js +1 -1
- package/node_modules/@flickfyi/photon/generated/react-swiftui.d.ts +1 -0
- package/node_modules/@flickfyi/photon/package.json +1 -1
- package/node_modules/@flickfyi/photon/src/globals.d.ts +5 -0
- package/node_modules/@flickfyi/rwsdk-extensions/.turbo/turbo-build.log +7 -7
- package/node_modules/@formatjs/fast-memoize/LICENSE.md +9 -0
- package/node_modules/@formatjs/fast-memoize/README.md +3 -0
- package/node_modules/@formatjs/fast-memoize/index.d.ts +35 -0
- package/node_modules/@formatjs/fast-memoize/index.js +69 -0
- package/node_modules/@formatjs/fast-memoize/index.js.map +1 -0
- package/node_modules/@formatjs/fast-memoize/package.json +25 -0
- package/node_modules/@formatjs/icu-messageformat-parser/LICENSE.md +9 -0
- package/node_modules/@formatjs/icu-messageformat-parser/README.md +38 -0
- package/node_modules/@formatjs/icu-messageformat-parser/index.d.ts +393 -0
- package/node_modules/@formatjs/icu-messageformat-parser/index.js +2187 -0
- package/node_modules/@formatjs/icu-messageformat-parser/index.js.map +1 -0
- package/node_modules/@formatjs/icu-messageformat-parser/manipulator.d.ts +175 -0
- package/node_modules/@formatjs/icu-messageformat-parser/manipulator.js +209 -0
- package/node_modules/@formatjs/icu-messageformat-parser/manipulator.js.map +1 -0
- package/node_modules/@formatjs/icu-messageformat-parser/no-parser.d.ts +183 -0
- package/node_modules/@formatjs/icu-messageformat-parser/no-parser.js +162 -0
- package/node_modules/@formatjs/icu-messageformat-parser/no-parser.js.map +1 -0
- package/node_modules/@formatjs/icu-messageformat-parser/package.json +21 -0
- package/node_modules/@formatjs/icu-messageformat-parser/printer.d.ts +154 -0
- package/node_modules/@formatjs/icu-messageformat-parser/printer.js +224 -0
- package/node_modules/@formatjs/icu-messageformat-parser/printer.js.map +1 -0
- package/node_modules/@formatjs/icu-skeleton-parser/LICENSE.md +9 -0
- package/node_modules/@formatjs/icu-skeleton-parser/README.md +1 -0
- package/node_modules/@formatjs/icu-skeleton-parser/index.d.ts +67 -0
- package/node_modules/@formatjs/icu-skeleton-parser/index.js +380 -0
- package/node_modules/@formatjs/icu-skeleton-parser/index.js.map +1 -0
- package/node_modules/@formatjs/icu-skeleton-parser/package.json +15 -0
- package/node_modules/@formatjs/intl/LICENSE.md +9 -0
- package/node_modules/@formatjs/intl/README.md +3 -0
- package/node_modules/@formatjs/intl/index.d.ts +499 -0
- package/node_modules/@formatjs/intl/index.js +595 -0
- package/node_modules/@formatjs/intl/index.js.map +1 -0
- package/node_modules/@formatjs/intl/package.json +37 -0
- package/node_modules/decimal.js/LICENCE.md +23 -0
- package/node_modules/decimal.js/README.md +246 -0
- package/node_modules/decimal.js/decimal.d.ts +301 -0
- package/node_modules/decimal.js/decimal.js +4951 -0
- package/node_modules/decimal.js/decimal.mjs +4914 -0
- package/node_modules/decimal.js/package.json +55 -0
- package/node_modules/format-quantity/LICENSE +21 -0
- package/node_modules/format-quantity/README.md +181 -0
- package/node_modules/format-quantity/dist/cjs/format-quantity.cjs.development.d.ts +131 -0
- package/node_modules/format-quantity/dist/cjs/format-quantity.cjs.development.js +214 -0
- package/node_modules/format-quantity/dist/cjs/format-quantity.cjs.development.js.map +1 -0
- package/node_modules/format-quantity/dist/cjs/format-quantity.cjs.production.d.ts +131 -0
- package/node_modules/format-quantity/dist/cjs/format-quantity.cjs.production.js +2 -0
- package/node_modules/format-quantity/dist/cjs/format-quantity.cjs.production.js.map +1 -0
- package/node_modules/format-quantity/dist/cjs/index.d.ts +1 -0
- package/node_modules/format-quantity/dist/cjs/index.js +6 -0
- package/node_modules/format-quantity/dist/format-quantity.d.mts +131 -0
- package/node_modules/format-quantity/dist/format-quantity.legacy-esm.d.ts +131 -0
- package/node_modules/format-quantity/dist/format-quantity.legacy-esm.js +268 -0
- package/node_modules/format-quantity/dist/format-quantity.legacy-esm.js.map +1 -0
- package/node_modules/format-quantity/dist/format-quantity.mjs +208 -0
- package/node_modules/format-quantity/dist/format-quantity.mjs.map +1 -0
- package/node_modules/format-quantity/dist/format-quantity.production.d.mts +131 -0
- package/node_modules/format-quantity/dist/format-quantity.production.mjs +2 -0
- package/node_modules/format-quantity/dist/format-quantity.production.mjs.map +1 -0
- package/node_modules/format-quantity/dist/format-quantity.umd.min.js +3 -0
- package/node_modules/format-quantity/dist/format-quantity.umd.min.js.map +1 -0
- package/node_modules/format-quantity/package.json +81 -0
- package/node_modules/intl-messageformat/LICENSE.md +33 -0
- package/node_modules/intl-messageformat/README.md +3 -0
- package/node_modules/intl-messageformat/index.d.ts +177 -0
- package/node_modules/intl-messageformat/index.js +327 -0
- package/node_modules/intl-messageformat/index.js.map +1 -0
- package/node_modules/intl-messageformat/intl-messageformat.iife.js +2819 -0
- package/node_modules/intl-messageformat/package.json +40 -0
- package/node_modules/numeric-quantity/LICENSE +21 -0
- package/node_modules/numeric-quantity/README.md +306 -0
- package/node_modules/numeric-quantity/dist/cjs/index.d.ts +1 -0
- package/node_modules/numeric-quantity/dist/cjs/index.js +6 -0
- package/node_modules/numeric-quantity/dist/cjs/numeric-quantity.cjs.development.d.ts +293 -0
- package/node_modules/numeric-quantity/dist/cjs/numeric-quantity.cjs.development.js +579 -0
- package/node_modules/numeric-quantity/dist/cjs/numeric-quantity.cjs.development.js.map +1 -0
- package/node_modules/numeric-quantity/dist/cjs/numeric-quantity.cjs.production.d.ts +293 -0
- package/node_modules/numeric-quantity/dist/cjs/numeric-quantity.cjs.production.js +2 -0
- package/node_modules/numeric-quantity/dist/cjs/numeric-quantity.cjs.production.js.map +1 -0
- package/node_modules/numeric-quantity/dist/numeric-quantity.d.mts +293 -0
- package/node_modules/numeric-quantity/dist/numeric-quantity.iife.umd.min.js +2 -0
- package/node_modules/numeric-quantity/dist/numeric-quantity.iife.umd.min.js.map +1 -0
- package/node_modules/numeric-quantity/dist/numeric-quantity.legacy-esm.d.ts +293 -0
- package/node_modules/numeric-quantity/dist/numeric-quantity.legacy-esm.js +622 -0
- package/node_modules/numeric-quantity/dist/numeric-quantity.legacy-esm.js.map +1 -0
- package/node_modules/numeric-quantity/dist/numeric-quantity.mjs +564 -0
- package/node_modules/numeric-quantity/dist/numeric-quantity.mjs.map +1 -0
- package/node_modules/numeric-quantity/dist/numeric-quantity.production.d.mts +293 -0
- package/node_modules/numeric-quantity/dist/numeric-quantity.production.mjs +2 -0
- package/node_modules/numeric-quantity/dist/numeric-quantity.production.mjs.map +1 -0
- package/node_modules/numeric-quantity/package.json +72 -0
- package/node_modules/validator/LICENSE +20 -0
- package/node_modules/validator/README.md +269 -0
- package/node_modules/validator/es/index.js +218 -0
- package/node_modules/validator/es/lib/alpha.js +151 -0
- package/node_modules/validator/es/lib/blacklist.js +5 -0
- package/node_modules/validator/es/lib/contains.js +15 -0
- package/node_modules/validator/es/lib/equals.js +5 -0
- package/node_modules/validator/es/lib/escape.js +5 -0
- package/node_modules/validator/es/lib/isAbaRouting.js +15 -0
- package/node_modules/validator/es/lib/isAfter.js +10 -0
- package/node_modules/validator/es/lib/isAlpha.js +23 -0
- package/node_modules/validator/es/lib/isAlphanumeric.js +23 -0
- package/node_modules/validator/es/lib/isAscii.js +10 -0
- package/node_modules/validator/es/lib/isBIC.js +16 -0
- package/node_modules/validator/es/lib/isBase32.js +15 -0
- package/node_modules/validator/es/lib/isBase58.js +8 -0
- package/node_modules/validator/es/lib/isBase64.js +23 -0
- package/node_modules/validator/es/lib/isBefore.js +10 -0
- package/node_modules/validator/es/lib/isBoolean.js +15 -0
- package/node_modules/validator/es/lib/isBtcAddress.js +7 -0
- package/node_modules/validator/es/lib/isByteLength.js +19 -0
- package/node_modules/validator/es/lib/isCreditCard.js +43 -0
- package/node_modules/validator/es/lib/isCurrency.js +74 -0
- package/node_modules/validator/es/lib/isDataURI.js +31 -0
- package/node_modules/validator/es/lib/isDate.js +93 -0
- package/node_modules/validator/es/lib/isDecimal.js +22 -0
- package/node_modules/validator/es/lib/isDivisibleBy.js +6 -0
- package/node_modules/validator/es/lib/isEAN.js +70 -0
- package/node_modules/validator/es/lib/isEmail.js +165 -0
- package/node_modules/validator/es/lib/isEmpty.js +10 -0
- package/node_modules/validator/es/lib/isEthereumAddress.js +6 -0
- package/node_modules/validator/es/lib/isFQDN.js +67 -0
- package/node_modules/validator/es/lib/isFloat.js +14 -0
- package/node_modules/validator/es/lib/isFullWidth.js +6 -0
- package/node_modules/validator/es/lib/isHSL.js +13 -0
- package/node_modules/validator/es/lib/isHalfWidth.js +6 -0
- package/node_modules/validator/es/lib/isHash.js +21 -0
- package/node_modules/validator/es/lib/isHexColor.js +13 -0
- package/node_modules/validator/es/lib/isHexadecimal.js +6 -0
- package/node_modules/validator/es/lib/isIBAN.js +171 -0
- package/node_modules/validator/es/lib/isIMEI.js +40 -0
- package/node_modules/validator/es/lib/isIP.js +58 -0
- package/node_modules/validator/es/lib/isIPRange.js +41 -0
- package/node_modules/validator/es/lib/isISBN.js +47 -0
- package/node_modules/validator/es/lib/isISIN.js +55 -0
- package/node_modules/validator/es/lib/isISO15924.js +9 -0
- package/node_modules/validator/es/lib/isISO31661Alpha2.js +21 -0
- package/node_modules/validator/es/lib/isISO31661Alpha3.js +20 -0
- package/node_modules/validator/es/lib/isISO31661Numeric.js +8 -0
- package/node_modules/validator/es/lib/isISO4217.js +9 -0
- package/node_modules/validator/es/lib/isISO6346.js +28 -0
- package/node_modules/validator/es/lib/isISO6391.js +6 -0
- package/node_modules/validator/es/lib/isISO8601.js +42 -0
- package/node_modules/validator/es/lib/isISRC.js +8 -0
- package/node_modules/validator/es/lib/isISSN.js +19 -0
- package/node_modules/validator/es/lib/isIdentityCard.js +407 -0
- package/node_modules/validator/es/lib/isIn.js +23 -0
- package/node_modules/validator/es/lib/isInt.js +19 -0
- package/node_modules/validator/es/lib/isJSON.js +26 -0
- package/node_modules/validator/es/lib/isJWT.js +15 -0
- package/node_modules/validator/es/lib/isLatLong.js +21 -0
- package/node_modules/validator/es/lib/isLength.js +27 -0
- package/node_modules/validator/es/lib/isLicensePlate.js +58 -0
- package/node_modules/validator/es/lib/isLocale.js +107 -0
- package/node_modules/validator/es/lib/isLowercase.js +5 -0
- package/node_modules/validator/es/lib/isLuhnNumber.js +25 -0
- package/node_modules/validator/es/lib/isMACAddress.js +36 -0
- package/node_modules/validator/es/lib/isMD5.js +6 -0
- package/node_modules/validator/es/lib/isMagnetURI.js +9 -0
- package/node_modules/validator/es/lib/isMailtoURI.js +75 -0
- package/node_modules/validator/es/lib/isMimeType.js +40 -0
- package/node_modules/validator/es/lib/isMobilePhone.js +212 -0
- package/node_modules/validator/es/lib/isMongoId.js +6 -0
- package/node_modules/validator/es/lib/isMultibyte.js +10 -0
- package/node_modules/validator/es/lib/isNumeric.js +10 -0
- package/node_modules/validator/es/lib/isOctal.js +6 -0
- package/node_modules/validator/es/lib/isPassportNumber.js +145 -0
- package/node_modules/validator/es/lib/isPort.js +8 -0
- package/node_modules/validator/es/lib/isPostalCode.js +100 -0
- package/node_modules/validator/es/lib/isRFC3339.js +21 -0
- package/node_modules/validator/es/lib/isRgbColor.js +34 -0
- package/node_modules/validator/es/lib/isSemVer.js +14 -0
- package/node_modules/validator/es/lib/isSlug.js +6 -0
- package/node_modules/validator/es/lib/isStrongPassword.js +90 -0
- package/node_modules/validator/es/lib/isSurrogatePair.js +6 -0
- package/node_modules/validator/es/lib/isTaxID.js +1407 -0
- package/node_modules/validator/es/lib/isTime.js +22 -0
- package/node_modules/validator/es/lib/isULID.js +5 -0
- package/node_modules/validator/es/lib/isURL.js +241 -0
- package/node_modules/validator/es/lib/isUUID.js +23 -0
- package/node_modules/validator/es/lib/isUppercase.js +5 -0
- package/node_modules/validator/es/lib/isVAT.js +264 -0
- package/node_modules/validator/es/lib/isVariableWidth.js +7 -0
- package/node_modules/validator/es/lib/isWhitelisted.js +10 -0
- package/node_modules/validator/es/lib/ltrim.js +7 -0
- package/node_modules/validator/es/lib/matches.js +8 -0
- package/node_modules/validator/es/lib/normalizeEmail.js +131 -0
- package/node_modules/validator/es/lib/rtrim.js +15 -0
- package/node_modules/validator/es/lib/stripLow.js +7 -0
- package/node_modules/validator/es/lib/toBoolean.js +8 -0
- package/node_modules/validator/es/lib/toDate.js +6 -0
- package/node_modules/validator/es/lib/toFloat.js +5 -0
- package/node_modules/validator/es/lib/toInt.js +5 -0
- package/node_modules/validator/es/lib/trim.js +5 -0
- package/node_modules/validator/es/lib/unescape.js +8 -0
- package/node_modules/validator/es/lib/util/algorithms.js +79 -0
- package/node_modules/validator/es/lib/util/assertString.js +4 -0
- package/node_modules/validator/es/lib/util/checkHost.js +12 -0
- package/node_modules/validator/es/lib/util/includesArray.js +6 -0
- package/node_modules/validator/es/lib/util/includesString.js +4 -0
- package/node_modules/validator/es/lib/util/merge.js +14 -0
- package/node_modules/validator/es/lib/util/multilineRegex.js +12 -0
- package/node_modules/validator/es/lib/util/nullUndefinedCheck.js +3 -0
- package/node_modules/validator/es/lib/util/toString.js +13 -0
- package/node_modules/validator/es/lib/util/typeOf.js +10 -0
- package/node_modules/validator/es/lib/whitelist.js +5 -0
- package/node_modules/validator/index.js +229 -0
- package/node_modules/validator/lib/alpha.js +157 -0
- package/node_modules/validator/lib/blacklist.js +14 -0
- package/node_modules/validator/lib/contains.js +24 -0
- package/node_modules/validator/lib/equals.js +14 -0
- package/node_modules/validator/lib/escape.js +14 -0
- package/node_modules/validator/lib/isAbaRouting.js +23 -0
- package/node_modules/validator/lib/isAfter.js +19 -0
- package/node_modules/validator/lib/isAlpha.js +31 -0
- package/node_modules/validator/lib/isAlphanumeric.js +31 -0
- package/node_modules/validator/lib/isAscii.js +18 -0
- package/node_modules/validator/lib/isBIC.js +24 -0
- package/node_modules/validator/lib/isBase32.js +24 -0
- package/node_modules/validator/lib/isBase58.js +16 -0
- package/node_modules/validator/lib/isBase64.js +32 -0
- package/node_modules/validator/lib/isBefore.js +19 -0
- package/node_modules/validator/lib/isBoolean.js +24 -0
- package/node_modules/validator/lib/isBtcAddress.js +16 -0
- package/node_modules/validator/lib/isByteLength.js +27 -0
- package/node_modules/validator/lib/isCreditCard.js +52 -0
- package/node_modules/validator/lib/isCurrency.js +83 -0
- package/node_modules/validator/lib/isDataURI.js +40 -0
- package/node_modules/validator/lib/isDate.js +102 -0
- package/node_modules/validator/lib/isDecimal.js +31 -0
- package/node_modules/validator/lib/isDivisibleBy.js +15 -0
- package/node_modules/validator/lib/isEAN.js +78 -0
- package/node_modules/validator/lib/isEmail.js +174 -0
- package/node_modules/validator/lib/isEmpty.js +19 -0
- package/node_modules/validator/lib/isEthereumAddress.js +15 -0
- package/node_modules/validator/lib/isFQDN.js +76 -0
- package/node_modules/validator/lib/isFloat.js +22 -0
- package/node_modules/validator/lib/isFullWidth.js +14 -0
- package/node_modules/validator/lib/isHSL.js +22 -0
- package/node_modules/validator/lib/isHalfWidth.js +14 -0
- package/node_modules/validator/lib/isHash.js +30 -0
- package/node_modules/validator/lib/isHexColor.js +22 -0
- package/node_modules/validator/lib/isHexadecimal.js +15 -0
- package/node_modules/validator/lib/isIBAN.js +178 -0
- package/node_modules/validator/lib/isIMEI.js +49 -0
- package/node_modules/validator/lib/isIP.js +67 -0
- package/node_modules/validator/lib/isIPRange.js +50 -0
- package/node_modules/validator/lib/isISBN.js +56 -0
- package/node_modules/validator/lib/isISIN.js +64 -0
- package/node_modules/validator/lib/isISO15924.js +16 -0
- package/node_modules/validator/lib/isISO31661Alpha2.js +28 -0
- package/node_modules/validator/lib/isISO31661Alpha3.js +28 -0
- package/node_modules/validator/lib/isISO31661Numeric.js +16 -0
- package/node_modules/validator/lib/isISO4217.js +16 -0
- package/node_modules/validator/lib/isISO6346.js +35 -0
- package/node_modules/validator/lib/isISO6391.js +15 -0
- package/node_modules/validator/lib/isISO8601.js +50 -0
- package/node_modules/validator/lib/isISRC.js +16 -0
- package/node_modules/validator/lib/isISSN.js +28 -0
- package/node_modules/validator/lib/isIdentityCard.js +416 -0
- package/node_modules/validator/lib/isIn.js +32 -0
- package/node_modules/validator/lib/isInt.js +28 -0
- package/node_modules/validator/lib/isJSON.js +35 -0
- package/node_modules/validator/lib/isJWT.js +24 -0
- package/node_modules/validator/lib/isLatLong.js +30 -0
- package/node_modules/validator/lib/isLength.js +35 -0
- package/node_modules/validator/lib/isLicensePlate.js +67 -0
- package/node_modules/validator/lib/isLocale.js +115 -0
- package/node_modules/validator/lib/isLowercase.js +14 -0
- package/node_modules/validator/lib/isLuhnNumber.js +34 -0
- package/node_modules/validator/lib/isMACAddress.js +45 -0
- package/node_modules/validator/lib/isMD5.js +15 -0
- package/node_modules/validator/lib/isMagnetURI.js +18 -0
- package/node_modules/validator/lib/isMailtoURI.js +84 -0
- package/node_modules/validator/lib/isMimeType.js +48 -0
- package/node_modules/validator/lib/isMobilePhone.js +219 -0
- package/node_modules/validator/lib/isMongoId.js +15 -0
- package/node_modules/validator/lib/isMultibyte.js +18 -0
- package/node_modules/validator/lib/isNumeric.js +19 -0
- package/node_modules/validator/lib/isOctal.js +15 -0
- package/node_modules/validator/lib/isPassportNumber.js +152 -0
- package/node_modules/validator/lib/isPort.js +17 -0
- package/node_modules/validator/lib/isPostalCode.js +107 -0
- package/node_modules/validator/lib/isRFC3339.js +29 -0
- package/node_modules/validator/lib/isRgbColor.js +42 -0
- package/node_modules/validator/lib/isSemVer.js +22 -0
- package/node_modules/validator/lib/isSlug.js +15 -0
- package/node_modules/validator/lib/isStrongPassword.js +99 -0
- package/node_modules/validator/lib/isSurrogatePair.js +15 -0
- package/node_modules/validator/lib/isTaxID.js +1417 -0
- package/node_modules/validator/lib/isTime.js +31 -0
- package/node_modules/validator/lib/isULID.js +14 -0
- package/node_modules/validator/lib/isURL.js +249 -0
- package/node_modules/validator/lib/isUUID.js +32 -0
- package/node_modules/validator/lib/isUppercase.js +14 -0
- package/node_modules/validator/lib/isVAT.js +274 -0
- package/node_modules/validator/lib/isVariableWidth.js +16 -0
- package/node_modules/validator/lib/isWhitelisted.js +19 -0
- package/node_modules/validator/lib/ltrim.js +16 -0
- package/node_modules/validator/lib/matches.js +17 -0
- package/node_modules/validator/lib/normalizeEmail.js +140 -0
- package/node_modules/validator/lib/rtrim.js +24 -0
- package/node_modules/validator/lib/stripLow.js +16 -0
- package/node_modules/validator/lib/toBoolean.js +17 -0
- package/node_modules/validator/lib/toDate.js +15 -0
- package/node_modules/validator/lib/toFloat.js +14 -0
- package/node_modules/validator/lib/toInt.js +14 -0
- package/node_modules/validator/lib/trim.js +14 -0
- package/node_modules/validator/lib/unescape.js +17 -0
- package/node_modules/validator/lib/util/algorithms.js +88 -0
- package/node_modules/validator/lib/util/assertString.js +12 -0
- package/node_modules/validator/lib/util/checkHost.js +20 -0
- package/node_modules/validator/lib/util/includesArray.js +14 -0
- package/node_modules/validator/lib/util/includesString.js +12 -0
- package/node_modules/validator/lib/util/merge.js +22 -0
- package/node_modules/validator/lib/util/multilineRegex.js +20 -0
- package/node_modules/validator/lib/util/nullUndefinedCheck.js +11 -0
- package/node_modules/validator/lib/util/toString.js +21 -0
- package/node_modules/validator/lib/util/typeOf.js +18 -0
- package/node_modules/validator/lib/whitelist.js +14 -0
- package/node_modules/validator/package.json +76 -0
- package/node_modules/validator/validator.js +5808 -0
- package/node_modules/validator/validator.min.js +23 -0
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"numeric-quantity.legacy-esm.js","names":[],"sources":["../src/constants.ts","../src/parseRomanNumerals.ts","../src/numericQuantity.ts","../src/isNumericQuantity.ts"],"sourcesContent":["import type {\n NumericQuantityOptions,\n RomanNumeralAscii,\n RomanNumeralUnicode,\n SubscriptDigit,\n SuperscriptDigit,\n VulgarFraction,\n} from './types';\n\n// #region Decimal_Number Unicode category\n\n/**\n * Unicode decimal digit block start code points.\n * Each block contains 10 contiguous digits (0-9).\n * This list covers all \\p{Nd} (Decimal_Number) blocks through Unicode 17.0.\n * The drift test in index.test.ts validates completeness against the JS engine.\n */\nconst decimalDigitBlockStarts = [\n 0x0030, // ASCII (0-9)\n 0x0660, // Arabic-Indic\n 0x06f0, // Extended Arabic-Indic (Persian/Urdu)\n 0x07c0, // NKo\n 0x0966, // Devanagari\n 0x09e6, // Bengali\n 0x0a66, // Gurmukhi\n 0x0ae6, // Gujarati\n 0x0b66, // Oriya\n 0x0be6, // Tamil\n 0x0c66, // Telugu\n 0x0ce6, // Kannada\n 0x0d66, // Malayalam\n 0x0de6, // Sinhala Lith\n 0x0e50, // Thai\n 0x0ed0, // Lao\n 0x0f20, // Tibetan\n 0x1040, // Myanmar\n 0x1090, // Myanmar Shan\n 0x17e0, // Khmer\n 0x1810, // Mongolian\n 0x1946, // Limbu\n 0x19d0, // New Tai Lue\n 0x1a80, // Tai Tham Hora\n 0x1a90, // Tai Tham Tham\n 0x1b50, // Balinese\n 0x1bb0, // Sundanese\n 0x1c40, // Lepcha\n 0x1c50, // Ol Chiki\n 0xa620, // Vai\n 0xa8d0, // Saurashtra\n 0xa900, // Kayah Li\n 0xa9d0, // Javanese\n 0xa9f0, // Myanmar Tai Laing\n 0xaa50, // Cham\n 0xabf0, // Meetei Mayek\n 0xff10, // Fullwidth\n 0x104a0, // Osmanya\n 0x10d30, // Hanifi Rohingya\n 0x10d40, // Garay\n 0x11066, // Brahmi\n 0x110f0, // Sora Sompeng\n 0x11136, // Chakma\n 0x111d0, // Sharada\n 0x112f0, // Khudawadi\n 0x11450, // Newa\n 0x114d0, // Tirhuta\n 0x11650, // Modi\n 0x116c0, // Takri\n 0x116d0, // Myanmar Pao\n 0x116da, // Myanmar Eastern Pwo Karen\n 0x11730, // Ahom\n 0x118e0, // Warang Citi\n 0x11950, // Dives Akuru\n 0x11bf0, // Sunuwar\n 0x11c50, // Bhaiksuki\n 0x11d50, // Masaram Gondi\n 0x11da0, // Gunjala Gondi\n 0x11de0, // Tolong Siki\n 0x11f50, // Kawi\n 0x16130, // Gurung Khema\n 0x16a60, // Mro\n 0x16ac0, // Tangsa\n 0x16b50, // Pahawh Hmong\n 0x16d70, // Kirat Rai\n 0x1ccf0, // Outlined Digits\n 0x1d7ce, // Mathematical Bold\n 0x1d7d8, // Mathematical Double-Struck\n 0x1d7e2, // Mathematical Sans-Serif\n 0x1d7ec, // Mathematical Sans-Serif Bold\n 0x1d7f6, // Mathematical Monospace\n 0x1e140, // Nyiakeng Puachue Hmong\n 0x1e2f0, // Wancho\n 0x1e4f0, // Nag Mundari\n 0x1e5f1, // Ol Onal\n 0x1e950, // Adlam\n 0x1fbf0, // Segmented Digits\n] as const;\n\n// RegExp ctor (not literal) so old Babel regex transforms (e.g. Expo Snackager) skip `\\p{}`\nconst unicodeDecimalDigitRegex = new RegExp('\\\\p{Nd}', 'gu');\n\n/**\n * Normalizes non-ASCII decimal digits to ASCII digits.\n * Converts characters from Unicode decimal digit blocks (e.g., Arabic-Indic,\n * Devanagari, Bengali) to their ASCII equivalents (0-9).\n *\n * All current Unicode \\p{Nd} blocks are included in decimalDigitBlockStarts.\n */\nexport const normalizeDigits = (str: string): string =>\n str.replace(unicodeDecimalDigitRegex, ch => {\n const cp = ch.codePointAt(0)!;\n // ASCII digits (0x0030-0x0039) don't need conversion\n if (cp <= 0x39) return ch;\n // Binary search for the largest block start ≤ cp\n let lo = 0;\n let hi = decimalDigitBlockStarts.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (decimalDigitBlockStarts[mid] <= cp) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n return String(cp - decimalDigitBlockStarts[lo]!);\n });\n\n/**\n * Map of Unicode superscript and subscript digit code points to ASCII digits.\n */\nexport const superSubDigitToAsciiMap: Record<SuperscriptDigit | SubscriptDigit, `${number}`> = {\n '⁰': '0',\n '¹': '1',\n '²': '2',\n '³': '3',\n '⁴': '4',\n '⁵': '5',\n '⁶': '6',\n '⁷': '7',\n '⁸': '8',\n '⁹': '9',\n '₀': '0',\n '₁': '1',\n '₂': '2',\n '₃': '3',\n '₄': '4',\n '₅': '5',\n '₆': '6',\n '₇': '7',\n '₈': '8',\n '₉': '9',\n} as const;\n\n/**\n * Captures Unicode superscript and subscript digits.\n */\nexport const superSubDigitsRegex: RegExp = /[⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉]/g;\n\n/**\n * Map of Unicode fraction code points to their ASCII equivalents.\n */\nexport const vulgarFractionToAsciiMap: Record<VulgarFraction, `${number}/${number | ''}`> = {\n '¼': '1/4',\n '½': '1/2',\n '¾': '3/4',\n '⅐': '1/7',\n '⅑': '1/9',\n '⅒': '1/10',\n '⅓': '1/3',\n '⅔': '2/3',\n '⅕': '1/5',\n '⅖': '2/5',\n '⅗': '3/5',\n '⅘': '4/5',\n '⅙': '1/6',\n '⅚': '5/6',\n '⅛': '1/8',\n '⅜': '3/8',\n '⅝': '5/8',\n '⅞': '7/8',\n '⅟': '1/',\n} as const;\n\n/**\n * Captures the individual elements of a numeric string. Commas and underscores are allowed\n * as separators, as long as they appear between digits and are not consecutive.\n *\n * Capture groups:\n *\n * | # | Description | Example(s) |\n * | --- | -------------------------------------------------- | ----------------------------------------------------------------------- |\n * | `0` | entire match | `\"2 1/3\"` from `\"2 1/3\"` |\n * | `1` | sign (`-` or `+`) | `\"-\"` from `\"-2 1/3\"` |\n * | `2` | whole number or numerator | `\"2\"` from `\"2 1/3\"`; `\"1\"` from `\"1/3\"` |\n * | `3` | entire tail: fraction, decimal, and/or exponent | `\" 1/3\"` from `\"2 1/3\"`; `\".23\"` from `\"1.23\"`; `\".5e3\"` from `\"1.5e3\"` |\n * | `4` | exponent, when there is no decimal portion | `\"e3\"` from `\"1e3\"` |\n * | `5` | exponent, when there is a decimal portion | `\"e-3\"` from `\"1.5e-3\"` |\n * | `6` | numerator of a mixed number (incl. leading space) | `\" 2\"` from `\"1 2/3\"` |\n *\n * Groups 4–6 are subgroups of group 3 and are not used directly by\n * {@link numericQuantity}; only groups 1–3 are consumed.\n *\n * _Capture groups 2 and 3 may include comma/underscore separators._\n *\n * @example\n *\n * ```ts\n * numericRegex.exec(\"1\") // [ \"1\", null, \"1\", null, null, null, null ]\n * numericRegex.exec(\"1.23\") // [ \"1.23\", null, \"1\", \".23\", null, null, null ]\n * numericRegex.exec(\"1 2/3\") // [ \"1 2/3\", null, \"1\", \" 2/3\", null, null, \" 2\" ]\n * numericRegex.exec(\"2/3\") // [ \"2/3\", null, \"2\", \"/3\", null, null, null ]\n * numericRegex.exec(\"2 / 3\") // [ \"2 / 3\", null, \"2\", \" / 3\", null, null, null ]\n * numericRegex.exec(\"-2 1/3\") // [ \"-2 1/3\", \"-\", \"2\", \" 1/3\", null, null, \" 1\" ]\n * numericRegex.exec(\"1e3\") // [ \"1e3\", null, \"1\", \"e3\", \"e3\", null, null ]\n * numericRegex.exec(\"1.5e-3\") // [ \"1.5e-3\", null, \"1\", \".5e-3\", null, \"e-3\", null ]\n * numericRegex.exec(\"1,000\") // [ \"1,000\", null, \"1,000\", null, null, null, null ]\n * ```\n *\n * @remarks\n *\n * This pattern and {@link numericRegexWithTrailingInvalid} are maintained as two separate\n * literals and must be kept in sync. They differ _only_ in the tail: `$` here versus\n * `(\\s*[^.\\d/].*)?` there. `src/index.test.ts` asserts that equality.\n *\n * Group 2 is `((?:\\d(?:[,_]\\d|\\d)*)?)`, deliberately `?` and not `*`. Every string the\n * inner group matches begins and ends with a digit, so concatenating two of them yields\n * another string of the same shape: an outer `*` would add no strings to the language,\n * only exponentially many ways to split one digit run across iterations. That made a\n * failing match catastrophically backtrack — `'1'.repeat(30) + '!'` took ~15s on V8.\n */\nexport const numericRegex: RegExp =\n /^(?=[-+]?\\s*\\.\\d|[-+]?\\s*\\d)([-+])?\\s*((?:\\d(?:[,_]\\d|\\d)*)?)(([eE][+-]?\\d(?:[,_]\\d|\\d)*)?|\\.\\d(?:[,_]\\d|\\d)*([eE][+-]?\\d(?:[,_]\\d|\\d)*)?|(\\s+\\d(?:[,_]\\d|\\d)*\\s*)?\\s*\\/\\s*\\d(?:[,_]\\d|\\d)*)?$/;\n/**\n * Same as {@link numericRegex}, but allows (and ignores) trailing invalid characters.\n * Capture groups 1–6 are identical to {@link numericRegex}'s; capture group 7 contains the\n * trailing invalid portion.\n *\n * @example\n *\n * ```ts\n * numericRegexWithTrailingInvalid.exec(\"1abc\")\n * // [ \"1abc\", null, \"1\", null, null, null, null, \"abc\" ]\n * numericRegexWithTrailingInvalid.exec(\"1 2/3 xyz\")\n * // [ \"1 2/3 xyz\", null, \"1\", \" 2/3\", null, null, \" 2\", \" xyz\" ]\n * ```\n *\n * @remarks\n *\n * Must be kept in sync with {@link numericRegex} — see that pattern's remarks.\n */\nexport const numericRegexWithTrailingInvalid: RegExp =\n /^(?=[-+]?\\s*\\.\\d|[-+]?\\s*\\d)([-+])?\\s*((?:\\d(?:[,_]\\d|\\d)*)?)(([eE][+-]?\\d(?:[,_]\\d|\\d)*)?|\\.\\d(?:[,_]\\d|\\d)*([eE][+-]?\\d(?:[,_]\\d|\\d)*)?|(\\s+\\d(?:[,_]\\d|\\d)*\\s*)?\\s*\\/\\s*\\d(?:[,_]\\d|\\d)*)?(\\s*[^.\\d/].*)?/;\n\n/**\n * Captures any Unicode vulgar fractions.\n */\nexport const vulgarFractionsRegex: RegExp = /([¼½¾⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟])/g;\n\n// #endregion\n\n// #region Roman numerals\n\n/**\n * Map of Roman numeral sequences to their decimal equivalents.\n */\nexport const romanNumeralValues: {\n [\n k in\n | `${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`\n | `${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}${RomanNumeralAscii}`\n ]?: number;\n} = {\n MMM: 3000,\n MM: 2000,\n M: 1000,\n CM: 900,\n DCCC: 800,\n DCC: 700,\n DC: 600,\n D: 500,\n CD: 400,\n CCC: 300,\n CC: 200,\n C: 100,\n XC: 90,\n LXXX: 80,\n LXX: 70,\n LX: 60,\n L: 50,\n XL: 40,\n XXX: 30,\n XX: 20,\n // XII/XI are redundant for ASCII input (X + II / X + I), but `romanNumeralUnicodeToAsciiMap`\n // maps Ⅻ/ⅻ and Ⅺ/ⅺ onto these keys, so they are used in practice.\n XII: 12,\n XI: 11,\n X: 10,\n IX: 9,\n VIII: 8,\n VII: 7,\n VI: 6,\n V: 5,\n IV: 4,\n III: 3,\n II: 2,\n I: 1,\n} as const;\n\n/**\n * Map of Unicode Roman numeral code points to their ASCII equivalents.\n */\nexport const romanNumeralUnicodeToAsciiMap: Record<\n RomanNumeralUnicode,\n keyof typeof romanNumeralValues\n> = {\n // Roman Numeral One (U+2160)\n Ⅰ: 'I',\n // Roman Numeral Two (U+2161)\n Ⅱ: 'II',\n // Roman Numeral Three (U+2162)\n Ⅲ: 'III',\n // Roman Numeral Four (U+2163)\n Ⅳ: 'IV',\n // Roman Numeral Five (U+2164)\n Ⅴ: 'V',\n // Roman Numeral Six (U+2165)\n Ⅵ: 'VI',\n // Roman Numeral Seven (U+2166)\n Ⅶ: 'VII',\n // Roman Numeral Eight (U+2167)\n Ⅷ: 'VIII',\n // Roman Numeral Nine (U+2168)\n Ⅸ: 'IX',\n // Roman Numeral Ten (U+2169)\n Ⅹ: 'X',\n // Roman Numeral Eleven (U+216A)\n Ⅺ: 'XI',\n // Roman Numeral Twelve (U+216B)\n Ⅻ: 'XII',\n // Roman Numeral Fifty (U+216C)\n Ⅼ: 'L',\n // Roman Numeral One Hundred (U+216D)\n Ⅽ: 'C',\n // Roman Numeral Five Hundred (U+216E)\n Ⅾ: 'D',\n // Roman Numeral One Thousand (U+216F)\n Ⅿ: 'M',\n // Small Roman Numeral One (U+2170)\n ⅰ: 'I',\n // Small Roman Numeral Two (U+2171)\n ⅱ: 'II',\n // Small Roman Numeral Three (U+2172)\n ⅲ: 'III',\n // Small Roman Numeral Four (U+2173)\n ⅳ: 'IV',\n // Small Roman Numeral Five (U+2174)\n ⅴ: 'V',\n // Small Roman Numeral Six (U+2175)\n ⅵ: 'VI',\n // Small Roman Numeral Seven (U+2176)\n ⅶ: 'VII',\n // Small Roman Numeral Eight (U+2177)\n ⅷ: 'VIII',\n // Small Roman Numeral Nine (U+2178)\n ⅸ: 'IX',\n // Small Roman Numeral Ten (U+2179)\n ⅹ: 'X',\n // Small Roman Numeral Eleven (U+217A)\n ⅺ: 'XI',\n // Small Roman Numeral Twelve (U+217B)\n ⅻ: 'XII',\n // Small Roman Numeral Fifty (U+217C)\n ⅼ: 'L',\n // Small Roman Numeral One Hundred (U+217D)\n ⅽ: 'C',\n // Small Roman Numeral Five Hundred (U+217E)\n ⅾ: 'D',\n // Small Roman Numeral One Thousand (U+217F)\n ⅿ: 'M',\n} as const;\n\n/**\n * Captures all Unicode Roman numeral code points.\n */\nexport const romanNumeralUnicodeRegex: RegExp = /([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/gi;\n\n/**\n * Captures a valid Roman numeral sequence.\n *\n * Capture groups:\n *\n * | # | Description | Example |\n * | --- | --------------- | ------------------------ |\n * | `0` | Entire string | \"MCCXIV\" from \"MCCXIV\" |\n * | `1` | Thousands | \"M\" from \"MCCXIV\" |\n * | `2` | Hundreds | \"CC\" from \"MCCXIV\" |\n * | `3` | Tens | \"X\" from \"MCCXIV\" |\n * | `4` | Ones | \"IV\" from \"MCCXIV\" |\n *\n * @example\n *\n * ```ts\n * romanNumeralRegex.exec(\"M\") // [ \"M\", \"M\", \"\", \"\", \"\" ]\n * romanNumeralRegex.exec(\"XII\") // [ \"XII\", \"\", \"\", \"X\", \"II\" ]\n * romanNumeralRegex.exec(\"MCCXIV\") // [ \"MCCXIV\", \"M\", \"CC\", \"X\", \"IV\" ]\n * ```\n */\nexport const romanNumeralRegex: RegExp =\n /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;\n\n// #endregion\n\n/**\n * Default options for {@link numericQuantity}.\n */\nexport const defaultOptions: Required<NumericQuantityOptions> = {\n round: 3,\n allowTrailingInvalid: false,\n romanNumerals: false,\n bigIntOnOverflow: false,\n decimalSeparator: '.',\n allowCurrency: false,\n percentage: false,\n verbose: false,\n} as const;\n","import {\n romanNumeralRegex,\n romanNumeralUnicodeRegex,\n romanNumeralUnicodeToAsciiMap,\n romanNumeralValues,\n} from './constants';\n\n// Just a shorthand type alias\ntype RomanNumeralKey = keyof typeof romanNumeralValues;\n\n/**\n * Converts a string of Roman numerals to a number, like `parseInt`\n * for Roman numerals. Uses modern, strict rules (only 1 to 3999).\n *\n * The string can include ASCII representations of Roman numerals\n * or Unicode Roman numeral code points (`U+2160` through `U+217F`).\n */\nexport const parseRomanNumerals = (romanNumerals: string): number => {\n const normalized = romanNumerals\n // Convert Unicode Roman numerals to ASCII\n .replace(\n romanNumeralUnicodeRegex,\n (_m, rn: keyof typeof romanNumeralUnicodeToAsciiMap) => romanNumeralUnicodeToAsciiMap[rn]\n )\n // Normalize to uppercase (more common for Roman numerals)\n .toUpperCase();\n\n const regexResult = romanNumeralRegex.exec(normalized);\n\n if (!regexResult) {\n return NaN;\n }\n\n const [, thousands, hundreds, tens, ones] = regexResult;\n\n return (\n (romanNumeralValues[thousands as RomanNumeralKey] ?? 0) +\n (romanNumeralValues[hundreds as RomanNumeralKey] ?? 0) +\n (romanNumeralValues[tens as RomanNumeralKey] ?? 0) +\n (romanNumeralValues[ones as RomanNumeralKey] ?? 0)\n );\n};\n","import {\n defaultOptions,\n normalizeDigits,\n numericRegexWithTrailingInvalid,\n superSubDigitToAsciiMap,\n superSubDigitsRegex,\n vulgarFractionToAsciiMap,\n vulgarFractionsRegex,\n} from './constants';\nimport { parseRomanNumerals } from './parseRomanNumerals';\nimport type {\n NumericQuantityOptions,\n NumericQuantityReturnType,\n NumericQuantityVerboseResult,\n} from './types';\n\nconst leadingSlashRegex = /^\\s*\\//;\n// RegExp ctor (not literals) so old Babel regex transforms (e.g. Expo Snackager) skip `\\p{}`\nconst currencyPrefixRegex = new RegExp('^([-+]?)\\\\s*(\\\\p{Sc}+)\\\\s*', 'u');\nconst currencySuffixRegex = new RegExp('\\\\s*(\\\\p{Sc}+)\\\\s*$', 'u');\nconst percentageSuffixRegex = /\\s*%$/;\n\nconst maxSafeBigInt = BigInt(Number.MAX_SAFE_INTEGER);\n/**\n * Upper bound on `10 ** exp` magnitudes evaluated exactly. Anything beyond this\n * overflows/underflows the `number` path to `Infinity`/`0` anyway, so bounding it\n * avoids allocating absurdly large `bigint`s (and `BigInt(Infinity)` throwing).\n */\nconst maxExactExponent = 10_000;\nconst pow10 = (exp: number) => 10n ** BigInt(exp);\n\n/**\n * Evaluates the parsed numeric components as an exact `bigint` rational (magnitude only,\n * sign is applied by the caller) and rounds the result half-up.\n *\n * Returns the rounded `bigint` only if its magnitude exceeds `Number.MAX_SAFE_INTEGER`;\n * otherwise returns `undefined`, meaning \"no overflow, use the `number` path\".\n *\n * @param group1 - Whole number or numerator (separators already stripped).\n * @param group2 - Decimal/exponent/fraction tail, if any.\n * @param divideBy100 - Whether a percentage suffix was stripped.\n */\nconst toRoundedBigInt = (\n group1: string,\n group2: string | undefined,\n divideBy100: boolean\n): bigint | undefined => {\n let numerator: bigint;\n let denominator = 1n;\n\n if (!group2) {\n // Plain integer\n numerator = BigInt(group1);\n } else if (group2.startsWith('.') || group2.startsWith('e') || group2.startsWith('E')) {\n // Decimal and/or scientific notation\n const expIndex = group2.search(/[eE]/);\n const exponent = expIndex === -1 ? 0 : parseInt(group2.slice(expIndex + 1));\n // Defer to the `number` path for non-finite/absurd exponents, before allocating any\n // `bigint`s. Fractional digit counts are bounded by the input length, but the exponent\n // is not, so validate it first.\n if (!Number.isFinite(exponent) || Math.abs(exponent) > maxExactExponent) return undefined;\n const fractionDigits = (expIndex === -1 ? group2 : group2.slice(0, expIndex)).slice(1);\n numerator = BigInt(`${group1}${fractionDigits}`);\n if (fractionDigits) denominator = pow10(fractionDigits.length);\n if (exponent > 0) numerator *= pow10(exponent);\n else if (exponent < 0) denominator *= pow10(-exponent);\n } else if (leadingSlashRegex.test(group2)) {\n // Pure fraction, e.g. \"1/2\"\n numerator = BigInt(group1);\n denominator = BigInt(group2.replace('/', '').trim());\n } else {\n // Mixed number, e.g. \"1 2/3\"\n const [n, d] = group2.split('/');\n denominator = BigInt(d.trim());\n numerator = BigInt(group1) * denominator + BigInt(n.trim());\n }\n\n if (divideBy100) denominator *= 100n;\n\n // Let the `number` path produce `Infinity` for zero denominators\n if (denominator === 0n) return undefined;\n\n // Round half-up (BigInt division truncates, i.e. floors for non-negative operands)\n const rounded = (2n * numerator + denominator) / (2n * denominator);\n\n return rounded > maxSafeBigInt ? rounded : undefined;\n};\n\n/**\n * Converts a string to a number, like an enhanced version of `parseFloat`.\n *\n * The string can include mixed numbers, vulgar fractions, or Roman numerals.\n * Input is expected to be a `string`, but will be coerced to `string` if necessary.\n */\nfunction numericQuantity(quantity: unknown): number;\nfunction numericQuantity<const T extends NumericQuantityOptions | undefined | null>(\n quantity: unknown,\n options: T\n): NumericQuantityReturnType<T>;\nfunction numericQuantity(\n quantity: unknown,\n options: NumericQuantityOptions | null = defaultOptions\n): number | bigint | NumericQuantityVerboseResult {\n const opts: Required<NumericQuantityOptions> = {\n ...defaultOptions,\n ...options,\n };\n\n // `String` (unlike a template literal) does not throw for `symbol` input, but it does\n // throw for objects with a null prototype or throwing `toString`/`valueOf` methods.\n // Coercion failures fall through as an empty string, which parses to `NaN`.\n let originalInput: string;\n if (typeof quantity === 'string') {\n originalInput = quantity;\n } else {\n try {\n originalInput = String(quantity);\n } catch {\n originalInput = '';\n }\n }\n\n // Metadata for verbose output\n let currencyPrefix: string | undefined;\n let currencySuffix: string | undefined;\n let percentageSuffix: boolean | undefined;\n let trailingInvalid: string | undefined;\n let parsedSign: '-' | '+' | undefined;\n let parsedWhole: number | undefined;\n let parsedNumerator: number | undefined;\n let parsedDenominator: number | undefined;\n\n const buildVerboseResult = (value: number | bigint): NumericQuantityVerboseResult => {\n const result: NumericQuantityVerboseResult = {\n value,\n input: originalInput,\n };\n if (currencyPrefix) result.currencyPrefix = currencyPrefix;\n if (currencySuffix) result.currencySuffix = currencySuffix;\n if (percentageSuffix) result.percentageSuffix = percentageSuffix;\n if (trailingInvalid) result.trailingInvalid = trailingInvalid;\n if (parsedSign) result.sign = parsedSign;\n if (parsedWhole !== undefined) result.whole = parsedWhole;\n if (parsedNumerator !== undefined) result.numerator = parsedNumerator;\n if (parsedDenominator !== undefined) result.denominator = parsedDenominator;\n return result;\n };\n\n const returnValue = (value: number | bigint) =>\n opts.verbose ? buildVerboseResult(value) : value;\n\n /**\n * Divides by 100 if a percentage suffix was stripped. Exact scaling, no rounding:\n * `round` applies to the quantity as written, before this division.\n */\n const applyPercentage = (value: number) =>\n percentageSuffix && opts.percentage !== 'number' ? value / 100 : value;\n\n if (typeof quantity === 'symbol') {\n // `String(sym)` is e.g. `'Symbol(1)'`; never a valid quantity\n return returnValue(NaN);\n }\n\n if (typeof quantity === 'number' || typeof quantity === 'bigint') {\n return returnValue(quantity);\n }\n\n let finalResult = NaN;\n let workingString = originalInput;\n\n // Strip currency/percentage affixes until none match, so the two are order-independent.\n // At most one `%` is stripped per parse, so `'50%%'` remains invalid.\n let affixStripped = true;\n while (affixStripped) {\n affixStripped = false;\n\n if (opts.allowCurrency) {\n const prefixMatch = currencyPrefixRegex.exec(workingString);\n if (prefixMatch?.[2]) {\n currencyPrefix = (currencyPrefix ?? '') + prefixMatch[2];\n // Keep the sign if present, remove currency symbol\n workingString = (prefixMatch[1] || '') + workingString.slice(prefixMatch[0].length);\n affixStripped = true;\n }\n\n const suffixMatch = currencySuffixRegex.exec(workingString);\n if (suffixMatch) {\n currencySuffix = suffixMatch[1] + (currencySuffix ?? '');\n workingString = workingString.slice(0, -suffixMatch[0].length);\n affixStripped = true;\n }\n }\n\n if (!percentageSuffix && opts.percentage) {\n const pctMatch = percentageSuffixRegex.exec(workingString);\n if (pctMatch) {\n percentageSuffix = true;\n workingString = workingString.slice(0, -pctMatch[0].length);\n affixStripped = true;\n }\n }\n }\n\n // Coerce to string and normalize\n const quantityAsString = normalizeDigits(\n workingString\n // Convert vulgar fractions to ASCII, with a leading space\n // to keep the whole number and the fraction separate\n .replace(\n vulgarFractionsRegex,\n (_m, vf: keyof typeof vulgarFractionToAsciiMap) => ` ${vulgarFractionToAsciiMap[vf]}`\n )\n // Convert superscript/subscript digits to ASCII\n .replace(\n superSubDigitsRegex,\n ch => superSubDigitToAsciiMap[ch as keyof typeof superSubDigitToAsciiMap]\n )\n // Convert fraction slash to standard slash\n .replace('⁄', '/')\n .trim()\n );\n\n // Bail out if the string was only white space\n if (quantityAsString.length === 0) {\n return returnValue(NaN);\n }\n\n let normalizedString = quantityAsString;\n\n if (opts.decimalSeparator === ',') {\n const commaCount = (quantityAsString.match(/,/g) || []).length;\n if (commaCount === 1) {\n // Treat lone comma as decimal separator; remove all \".\" since they represent\n // thousands/whatever separators\n normalizedString = quantityAsString.replaceAll('.', '_').replace(',', '.');\n } else if (commaCount > 1) {\n // The second comma and everything after is \"trailing invalid\"\n const firstCommaIndex = quantityAsString.indexOf(',');\n const secondCommaIndex = quantityAsString.indexOf(',', firstCommaIndex + 1);\n normalizedString = quantityAsString\n .substring(0, secondCommaIndex)\n .replaceAll('.', '_')\n .replace(',', '.');\n } else {\n // No comma as decimal separator, so remove all \".\" since they represent\n // thousands/whatever separators\n normalizedString = quantityAsString.replaceAll('.', '_');\n }\n }\n\n const regexResult = numericRegexWithTrailingInvalid.exec(normalizedString);\n\n // If the Arabic numeral regex fails, try Roman numerals\n if (!regexResult) {\n if (!opts.romanNumerals) return returnValue(NaN);\n return returnValue(applyPercentage(parseRomanNumerals(quantityAsString)));\n }\n\n // Capture trailing invalid characters. Group 7 catches chars starting with [^.\\d/], but\n // the regex (which lacks a $ anchor) may also leave unconsumed input starting with \".\",\n // \"/\", or digits (e.g. \"0.1.2\" or \"1/\"), and the comma-decimal path above may have\n // truncated the string. All normalization steps are length-preserving, so the number of\n // consumed characters maps 1:1 onto `quantityAsString`; slice the remainder from the\n // original (normalized-digit) input so nothing is dropped and no internal placeholder\n // (\"_\" for a stripped \".\") leaks into the metadata.\n const consumedLength = regexResult[0].length - (regexResult[7]?.length ?? 0);\n const rawTrailing = quantityAsString.slice(consumedLength).trim();\n if (rawTrailing) {\n trailingInvalid = rawTrailing;\n if (!opts.allowTrailingInvalid) {\n return returnValue(NaN);\n }\n }\n\n const [, sign, rawGroup1, rawGroup2] = regexResult;\n if (sign === '-' || sign === '+') parsedSign = sign;\n const numberGroup1 = rawGroup1.replaceAll(',', '').replaceAll('_', '');\n const numberGroup2 = rawGroup2?.replaceAll(',', '').replaceAll('_', '');\n\n // Exact overflow evaluation must precede the empty-whole shortcut below, otherwise\n // leading-decimal scientific values (e.g. \".1e17\") skip the `bigint` path entirely.\n if (opts.bigIntOnOverflow) {\n const asBigInt = toRoundedBigInt(\n numberGroup1,\n numberGroup2,\n !!percentageSuffix && opts.percentage !== 'number'\n );\n if (asBigInt !== undefined) {\n return returnValue(sign === '-' ? -asBigInt : asBigInt);\n }\n }\n\n // Numerify capture group 1\n finalResult =\n !numberGroup1 && numberGroup2 && numberGroup2.startsWith('.') ? 0 : parseInt(numberGroup1);\n\n // If capture group 2 is null, then we're dealing with an integer\n // and there is nothing left to process\n if (!numberGroup2) {\n finalResult = sign === '-' ? finalResult * -1 : finalResult;\n return returnValue(applyPercentage(finalResult));\n }\n\n // Non-finite `round` values are treated as `false` (no rounding);\n // negative values clamp to 0 (round to whole number)\n const roundTo =\n typeof opts.round === 'number' && Number.isFinite(opts.round)\n ? Math.floor(Math.max(0, opts.round))\n : false;\n // `10 ** roundTo` (not `parseFloat('1e' + roundTo)`, which mangles exponential notation,\n // e.g. `1e21` => \"1e1e+21\" => 10). Factors beyond `Number.MAX_VALUE` are `Infinity`, which\n // would poison the arithmetic below; rounding to >308 decimals is a no-op for a double\n // anyway, so fall back to no rounding. `NaN` means \"no rounding\".\n const roundingFactorRaw = roundTo === false ? NaN : 10 ** roundTo;\n const roundingFactor = Number.isFinite(roundingFactorRaw) ? roundingFactorRaw : NaN;\n\n /** Rounds to `roundTo` decimal places, falling back to `value` if the scaling overflows. */\n const round = (value: number, scaled: number) => {\n const result = Math.round(scaled) / roundingFactor;\n return Number.isFinite(result) || !Number.isFinite(value) ? result : value;\n };\n\n if (\n numberGroup2.startsWith('.') ||\n numberGroup2.startsWith('e') ||\n numberGroup2.startsWith('E')\n ) {\n // If first char of `numberGroup2` is \".\" or \"e\"/\"E\", it's a decimal\n const decimalValue = parseFloat(`${finalResult}${numberGroup2}`);\n finalResult = isNaN(roundingFactor)\n ? decimalValue\n : round(decimalValue, decimalValue * roundingFactor);\n } else if (leadingSlashRegex.test(numberGroup2)) {\n // If the first non-space char is \"/\" it's a pure fraction (e.g. \"1/2\")\n const numerator = parseInt(numberGroup1);\n const denominator = parseInt(numberGroup2.replace('/', ''));\n parsedNumerator = numerator;\n parsedDenominator = denominator;\n finalResult = isNaN(roundingFactor)\n ? numerator / denominator\n : round(numerator / denominator, (numerator * roundingFactor) / denominator);\n } else {\n // Otherwise it's a mixed fraction (e.g. \"1 2/3\")\n const fractionArray = numberGroup2.split('/');\n const [numerator, denominator] = fractionArray.map(v => parseInt(v));\n parsedWhole = finalResult;\n parsedNumerator = numerator;\n parsedDenominator = denominator;\n finalResult += isNaN(roundingFactor)\n ? numerator / denominator\n : round(numerator / denominator, (numerator * roundingFactor) / denominator);\n }\n\n finalResult = sign === '-' ? finalResult * -1 : finalResult;\n\n return returnValue(applyPercentage(finalResult));\n}\n\nexport { numericQuantity };\n","import { numericQuantity } from './numericQuantity';\nimport type { NumericQuantityOptions } from './types';\n\n/**\n * Checks if a value represents a valid numeric quantity.\n *\n * Returns `true` if the value can be parsed as a number, `false` otherwise.\n * Accepts the same options as `numericQuantity`.\n */\nexport const isNumericQuantity = (quantity: unknown, options?: NumericQuantityOptions): boolean => {\n const result = numericQuantity(quantity, { ...options, verbose: false });\n return typeof result === 'bigint' || !isNaN(result);\n};\n"],"mappings":";;;;;;;AAiBA,MAAM,0BAA0B;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAGA,MAAM,2BAA2B,IAAI,OAAO,WAAW,IAAI;;;;;;;;AAS3D,MAAa,mBAAmB,QAC9B,IAAI,QAAQ,2BAA0B,OAAM;CAC1C,MAAM,KAAK,GAAG,YAAY,CAAC;CAE3B,IAAI,MAAM,IAAM,OAAO;CAEvB,IAAI,KAAK;CACT,IAAI,KAAK,wBAAwB,SAAS;CAC1C,OAAO,KAAK,IAAI;EACd,MAAM,MAAO,KAAK,KAAK,MAAO;EAC9B,IAAI,wBAAwB,QAAQ,IAClC,KAAK;OAEL,KAAK,MAAM;CAEf;CACA,OAAO,OAAO,KAAK,wBAAwB,GAAI;AACjD,CAAC;;;;AAKH,MAAa,0BAAkF;CAC7F,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;AACP;;;;AAKA,MAAa,sBAA8B;;;;AAK3C,MAAa,2BAA+E;CAC1F,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;AACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,MAAa,eACX;;;;;;;;;;;;;;;;;;;AAmBF,MAAa,kCACX;;;;AAKF,MAAa,uBAA+B;;;;AAS5C,MAAa,qBAQT;CACF,KAAK;CACL,IAAI;CACJ,GAAG;CACH,IAAI;CACJ,MAAM;CACN,KAAK;CACL,IAAI;CACJ,GAAG;CACH,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,GAAG;CACH,IAAI;CACJ,MAAM;CACN,KAAK;CACL,IAAI;CACJ,GAAG;CACH,IAAI;CACJ,KAAK;CACL,IAAI;CAGJ,KAAK;CACL,IAAI;CACJ,GAAG;CACH,IAAI;CACJ,MAAM;CACN,KAAK;CACL,IAAI;CACJ,GAAG;CACH,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,GAAG;AACL;;;;AAKA,MAAa,gCAGT;CAEF,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;CAEH,GAAG;AACL;;;;AAKA,MAAa,2BAAmC;;;;;;;;;;;;;;;;;;;;;;AAuBhD,MAAa,oBACX;;;;AAOF,MAAa,iBAAmD;CAC9D,OAAO;CACP,sBAAsB;CACtB,eAAe;CACf,kBAAkB;CAClB,kBAAkB;CAClB,eAAe;CACf,YAAY;CACZ,SAAS;AACX;;;;;;;;;;ACxZA,MAAa,sBAAsB,kBAAkC;;CACnE,MAAM,aAAa,cAEhB,QACC,2BACC,IAAI,OAAmD,8BAA8B,GACxF,CAAC,CAEA,YAAY;CAEf,MAAM,cAAc,kBAAkB,KAAK,UAAU;CAErD,IAAI,CAAC,aACH,OAAO;CAGT,MAAM,GAAG,WAAW,UAAU,MAAM,QAAQ;CAE5C,SAAA,sBACG,mBAAmB,gBAAA,QAAA,wBAAA,KAAA,IAAA,sBAAiC,OAAA,uBACpD,mBAAmB,eAAA,QAAA,yBAAA,KAAA,IAAA,uBAAgC,OAAA,uBACnD,mBAAmB,WAAA,QAAA,yBAAA,KAAA,IAAA,uBAA4B,OAAA,uBAC/C,mBAAmB,WAAA,QAAA,yBAAA,KAAA,IAAA,uBAA4B;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzBA,MAAM,oBAAoB;AAE1B,MAAM,sBAAsB,IAAI,OAAO,8BAA8B,GAAG;AACxE,MAAM,sBAAsB,IAAI,OAAO,uBAAuB,GAAG;AACjE,MAAM,wBAAwB;AAE9B,MAAM,gBAAgB,OAAO,OAAO,gBAAgB;;;;;;AAMpD,MAAM,mBAAmB;AACzB,MAAM,SAAS,QAAgB,OAAO,OAAO,GAAG;;;;;;;;;;;;AAahD,MAAM,mBACJ,QACA,QACA,gBACuB;CACvB,IAAI;CACJ,IAAI,cAAc;CAElB,IAAI,CAAC,QAEH,YAAY,OAAO,MAAM;MACpB,IAAI,OAAO,WAAW,GAAG,KAAK,OAAO,WAAW,GAAG,KAAK,OAAO,WAAW,GAAG,GAAG;EAErF,MAAM,WAAW,OAAO,OAAO,MAAM;EACrC,MAAM,WAAW,aAAa,KAAK,IAAI,SAAS,OAAO,MAAM,WAAW,CAAC,CAAC;EAI1E,IAAI,CAAC,OAAO,SAAS,QAAQ,KAAK,KAAK,IAAI,QAAQ,IAAI,kBAAkB,OAAO,KAAA;EAChF,MAAM,kBAAkB,aAAa,KAAK,SAAS,OAAO,MAAM,GAAG,QAAQ,EAAA,CAAG,MAAM,CAAC;EACrF,YAAY,OAAO,GAAG,SAAS,gBAAgB;EAC/C,IAAI,gBAAgB,cAAc,MAAM,eAAe,MAAM;EAC7D,IAAI,WAAW,GAAG,aAAa,MAAM,QAAQ;OACxC,IAAI,WAAW,GAAG,eAAe,MAAM,CAAC,QAAQ;CACvD,OAAO,IAAI,kBAAkB,KAAK,MAAM,GAAG;EAEzC,YAAY,OAAO,MAAM;EACzB,cAAc,OAAO,OAAO,QAAQ,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC;CACrD,OAAO;EAEL,MAAM,CAAC,GAAG,KAAK,OAAO,MAAM,GAAG;EAC/B,cAAc,OAAO,EAAE,KAAK,CAAC;EAC7B,YAAY,OAAO,MAAM,IAAI,cAAc,OAAO,EAAE,KAAK,CAAC;CAC5D;CAEA,IAAI,aAAa,eAAe;CAGhC,IAAI,gBAAgB,IAAI,OAAO,KAAA;CAG/B,MAAM,WAAW,KAAK,YAAY,gBAAgB,KAAK;CAEvD,OAAO,UAAU,gBAAgB,UAAU,KAAA;AAC7C;AAaA,SAAS,gBACP,UACA,UAAyC,gBACO;;CAChD,MAAM,OAAA,eAAA,eAAA,CAAA,GACD,cAAA,GACA,OACL;CAKA,IAAI;CACJ,IAAI,OAAO,aAAa,UACtB,gBAAgB;MAEhB,IAAI;EACF,gBAAgB,OAAO,QAAQ;CACjC,SAAA,SAAQ;EACN,gBAAgB;CAClB;CAIF,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,MAAM,sBAAsB,UAAyD;EACnF,MAAM,SAAuC;GAC3C;GACA,OAAO;EACT;EACA,IAAI,gBAAgB,OAAO,iBAAiB;EAC5C,IAAI,gBAAgB,OAAO,iBAAiB;EAC5C,IAAI,kBAAkB,OAAO,mBAAmB;EAChD,IAAI,iBAAiB,OAAO,kBAAkB;EAC9C,IAAI,YAAY,OAAO,OAAO;EAC9B,IAAI,gBAAgB,KAAA,GAAW,OAAO,QAAQ;EAC9C,IAAI,oBAAoB,KAAA,GAAW,OAAO,YAAY;EACtD,IAAI,sBAAsB,KAAA,GAAW,OAAO,cAAc;EAC1D,OAAO;CACT;CAEA,MAAM,eAAe,UACnB,KAAK,UAAU,mBAAmB,KAAK,IAAI;;;;;CAM7C,MAAM,mBAAmB,UACvB,oBAAoB,KAAK,eAAe,WAAW,QAAQ,MAAM;CAEnE,IAAI,OAAO,aAAa,UAEtB,OAAO,YAAY,GAAG;CAGxB,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,UACtD,OAAO,YAAY,QAAQ;CAG7B,IAAI,cAAc;CAClB,IAAI,gBAAgB;CAIpB,IAAI,gBAAgB;CACpB,OAAO,eAAe;EACpB,gBAAgB;EAEhB,IAAI,KAAK,eAAe;GACtB,MAAM,cAAc,oBAAoB,KAAK,aAAa;GAC1D,IAAA,gBAAA,QAAA,gBAAA,KAAA,IAAA,KAAA,IAAI,YAAc,IAAI;;IACpB,mBAAA,kBAAkB,oBAAA,QAAA,oBAAA,KAAA,IAAA,kBAAkB,MAAM,YAAY;IAEtD,iBAAiB,YAAY,MAAM,MAAM,cAAc,MAAM,YAAY,EAAE,CAAC,MAAM;IAClF,gBAAgB;GAClB;GAEA,MAAM,cAAc,oBAAoB,KAAK,aAAa;GAC1D,IAAI,aAAa;;IACf,iBAAiB,YAAY,OAAA,kBAAM,oBAAA,QAAA,oBAAA,KAAA,IAAA,kBAAkB;IACrD,gBAAgB,cAAc,MAAM,GAAG,CAAC,YAAY,EAAE,CAAC,MAAM;IAC7D,gBAAgB;GAClB;EACF;EAEA,IAAI,CAAC,oBAAoB,KAAK,YAAY;GACxC,MAAM,WAAW,sBAAsB,KAAK,aAAa;GACzD,IAAI,UAAU;IACZ,mBAAmB;IACnB,gBAAgB,cAAc,MAAM,GAAG,CAAC,SAAS,EAAE,CAAC,MAAM;IAC1D,gBAAgB;GAClB;EACF;CACF;CAGA,MAAM,mBAAmB,gBACvB,cAGG,QACC,uBACC,IAAI,OAA8C,IAAI,yBAAyB,KAClF,CAAC,CAEA,QACC,sBACA,OAAM,wBAAwB,GAChC,CAAC,CAEA,QAAQ,KAAK,GAAG,CAAC,CACjB,KAAK,CACV;CAGA,IAAI,iBAAiB,WAAW,GAC9B,OAAO,YAAY,GAAG;CAGxB,IAAI,mBAAmB;CAEvB,IAAI,KAAK,qBAAqB,KAAK;EACjC,MAAM,cAAc,iBAAiB,MAAM,IAAI,KAAK,CAAC,EAAA,CAAG;EACxD,IAAI,eAAe,GAGjB,mBAAmB,iBAAiB,WAAW,KAAK,GAAG,CAAC,CAAC,QAAQ,KAAK,GAAG;OACpE,IAAI,aAAa,GAAG;GAEzB,MAAM,kBAAkB,iBAAiB,QAAQ,GAAG;GACpD,MAAM,mBAAmB,iBAAiB,QAAQ,KAAK,kBAAkB,CAAC;GAC1E,mBAAmB,iBAChB,UAAU,GAAG,gBAAgB,CAAC,CAC9B,WAAW,KAAK,GAAG,CAAC,CACpB,QAAQ,KAAK,GAAG;EACrB,OAGE,mBAAmB,iBAAiB,WAAW,KAAK,GAAG;CAE3D;CAEA,MAAM,cAAc,gCAAgC,KAAK,gBAAgB;CAGzE,IAAI,CAAC,aAAa;EAChB,IAAI,CAAC,KAAK,eAAe,OAAO,YAAY,GAAG;EAC/C,OAAO,YAAY,gBAAgB,mBAAmB,gBAAgB,CAAC,CAAC;CAC1E;CASA,MAAM,iBAAiB,YAAY,EAAE,CAAC,WAAA,yBAAA,gBAAU,YAAY,QAAA,QAAA,kBAAA,KAAA,IAAA,KAAA,IAAA,cAAI,YAAA,QAAA,0BAAA,KAAA,IAAA,wBAAU;CAC1E,MAAM,cAAc,iBAAiB,MAAM,cAAc,CAAC,CAAC,KAAK;CAChE,IAAI,aAAa;EACf,kBAAkB;EAClB,IAAI,CAAC,KAAK,sBACR,OAAO,YAAY,GAAG;CAE1B;CAEA,MAAM,GAAG,MAAM,WAAW,aAAa;CACvC,IAAI,SAAS,OAAO,SAAS,KAAK,aAAa;CAC/C,MAAM,eAAe,UAAU,WAAW,KAAK,EAAE,CAAC,CAAC,WAAW,KAAK,EAAE;CACrE,MAAM,eAAA,cAAA,QAAA,cAAA,KAAA,IAAA,KAAA,IAAe,UAAW,WAAW,KAAK,EAAE,CAAC,CAAC,WAAW,KAAK,EAAE;CAItE,IAAI,KAAK,kBAAkB;EACzB,MAAM,WAAW,gBACf,cACA,cACA,CAAC,CAAC,oBAAoB,KAAK,eAAe,QAC5C;EACA,IAAI,aAAa,KAAA,GACf,OAAO,YAAY,SAAS,MAAM,CAAC,WAAW,QAAQ;CAE1D;CAGA,cACE,CAAC,gBAAgB,gBAAgB,aAAa,WAAW,GAAG,IAAI,IAAI,SAAS,YAAY;CAI3F,IAAI,CAAC,cAAc;EACjB,cAAc,SAAS,MAAM,cAAc,KAAK;EAChD,OAAO,YAAY,gBAAgB,WAAW,CAAC;CACjD;CAIA,MAAM,UACJ,OAAO,KAAK,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,IACxD,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,KAAK,CAAC,IAClC;CAKN,MAAM,oBAAoB,YAAY,QAAQ,MAAM,MAAM;CAC1D,MAAM,iBAAiB,OAAO,SAAS,iBAAiB,IAAI,oBAAoB;;CAGhF,MAAM,SAAS,OAAe,WAAmB;EAC/C,MAAM,SAAS,KAAK,MAAM,MAAM,IAAI;EACpC,OAAO,OAAO,SAAS,MAAM,KAAK,CAAC,OAAO,SAAS,KAAK,IAAI,SAAS;CACvE;CAEA,IACE,aAAa,WAAW,GAAG,KAC3B,aAAa,WAAW,GAAG,KAC3B,aAAa,WAAW,GAAG,GAC3B;EAEA,MAAM,eAAe,WAAW,GAAG,cAAc,cAAc;EAC/D,cAAc,MAAM,cAAc,IAC9B,eACA,MAAM,cAAc,eAAe,cAAc;CACvD,OAAO,IAAI,kBAAkB,KAAK,YAAY,GAAG;EAE/C,MAAM,YAAY,SAAS,YAAY;EACvC,MAAM,cAAc,SAAS,aAAa,QAAQ,KAAK,EAAE,CAAC;EAC1D,kBAAkB;EAClB,oBAAoB;EACpB,cAAc,MAAM,cAAc,IAC9B,YAAY,cACZ,MAAM,YAAY,aAAc,YAAY,iBAAkB,WAAW;CAC/E,OAAO;EAGL,MAAM,CAAC,WAAW,eADI,aAAa,MAAM,GACI,CAAC,CAAC,KAAI,MAAK,SAAS,CAAC,CAAC;EACnE,cAAc;EACd,kBAAkB;EAClB,oBAAoB;EACpB,eAAe,MAAM,cAAc,IAC/B,YAAY,cACZ,MAAM,YAAY,aAAc,YAAY,iBAAkB,WAAW;CAC/E;CAEA,cAAc,SAAS,MAAM,cAAc,KAAK;CAEhD,OAAO,YAAY,gBAAgB,WAAW,CAAC;AACjD;;;;;;;;;AC3VA,MAAa,qBAAqB,UAAmB,YAA8C;CACjG,MAAM,SAAS,gBAAgB,UAAA,eAAA,eAAA,CAAA,GAAe,OAAA,GAAA,CAAA,GAAA,EAAS,SAAS,MAAA,CAAM,CAAC;CACvE,OAAO,OAAO,WAAW,YAAY,CAAC,MAAM,MAAM;AACpD"}
|
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
//#region src/constants.ts
|
|
2
|
+
/**
|
|
3
|
+
* Unicode decimal digit block start code points.
|
|
4
|
+
* Each block contains 10 contiguous digits (0-9).
|
|
5
|
+
* This list covers all \p{Nd} (Decimal_Number) blocks through Unicode 17.0.
|
|
6
|
+
* The drift test in index.test.ts validates completeness against the JS engine.
|
|
7
|
+
*/
|
|
8
|
+
const decimalDigitBlockStarts = [
|
|
9
|
+
48,
|
|
10
|
+
1632,
|
|
11
|
+
1776,
|
|
12
|
+
1984,
|
|
13
|
+
2406,
|
|
14
|
+
2534,
|
|
15
|
+
2662,
|
|
16
|
+
2790,
|
|
17
|
+
2918,
|
|
18
|
+
3046,
|
|
19
|
+
3174,
|
|
20
|
+
3302,
|
|
21
|
+
3430,
|
|
22
|
+
3558,
|
|
23
|
+
3664,
|
|
24
|
+
3792,
|
|
25
|
+
3872,
|
|
26
|
+
4160,
|
|
27
|
+
4240,
|
|
28
|
+
6112,
|
|
29
|
+
6160,
|
|
30
|
+
6470,
|
|
31
|
+
6608,
|
|
32
|
+
6784,
|
|
33
|
+
6800,
|
|
34
|
+
6992,
|
|
35
|
+
7088,
|
|
36
|
+
7232,
|
|
37
|
+
7248,
|
|
38
|
+
42528,
|
|
39
|
+
43216,
|
|
40
|
+
43264,
|
|
41
|
+
43472,
|
|
42
|
+
43504,
|
|
43
|
+
43600,
|
|
44
|
+
44016,
|
|
45
|
+
65296,
|
|
46
|
+
66720,
|
|
47
|
+
68912,
|
|
48
|
+
68928,
|
|
49
|
+
69734,
|
|
50
|
+
69872,
|
|
51
|
+
69942,
|
|
52
|
+
70096,
|
|
53
|
+
70384,
|
|
54
|
+
70736,
|
|
55
|
+
70864,
|
|
56
|
+
71248,
|
|
57
|
+
71360,
|
|
58
|
+
71376,
|
|
59
|
+
71386,
|
|
60
|
+
71472,
|
|
61
|
+
71904,
|
|
62
|
+
72016,
|
|
63
|
+
72688,
|
|
64
|
+
72784,
|
|
65
|
+
73040,
|
|
66
|
+
73120,
|
|
67
|
+
73184,
|
|
68
|
+
73552,
|
|
69
|
+
90416,
|
|
70
|
+
92768,
|
|
71
|
+
92864,
|
|
72
|
+
93008,
|
|
73
|
+
93552,
|
|
74
|
+
118e3,
|
|
75
|
+
120782,
|
|
76
|
+
120792,
|
|
77
|
+
120802,
|
|
78
|
+
120812,
|
|
79
|
+
120822,
|
|
80
|
+
123200,
|
|
81
|
+
123632,
|
|
82
|
+
124144,
|
|
83
|
+
124401,
|
|
84
|
+
125264,
|
|
85
|
+
130032
|
|
86
|
+
];
|
|
87
|
+
const unicodeDecimalDigitRegex = /* @__PURE__ */ new RegExp("\\p{Nd}", "gu");
|
|
88
|
+
/**
|
|
89
|
+
* Normalizes non-ASCII decimal digits to ASCII digits.
|
|
90
|
+
* Converts characters from Unicode decimal digit blocks (e.g., Arabic-Indic,
|
|
91
|
+
* Devanagari, Bengali) to their ASCII equivalents (0-9).
|
|
92
|
+
*
|
|
93
|
+
* All current Unicode \p{Nd} blocks are included in decimalDigitBlockStarts.
|
|
94
|
+
*/
|
|
95
|
+
const normalizeDigits = (str) => str.replace(unicodeDecimalDigitRegex, (ch) => {
|
|
96
|
+
const cp = ch.codePointAt(0);
|
|
97
|
+
if (cp <= 57) return ch;
|
|
98
|
+
let lo = 0;
|
|
99
|
+
let hi = decimalDigitBlockStarts.length - 1;
|
|
100
|
+
while (lo < hi) {
|
|
101
|
+
const mid = lo + hi + 1 >>> 1;
|
|
102
|
+
if (decimalDigitBlockStarts[mid] <= cp) lo = mid;
|
|
103
|
+
else hi = mid - 1;
|
|
104
|
+
}
|
|
105
|
+
return String(cp - decimalDigitBlockStarts[lo]);
|
|
106
|
+
});
|
|
107
|
+
/**
|
|
108
|
+
* Map of Unicode superscript and subscript digit code points to ASCII digits.
|
|
109
|
+
*/
|
|
110
|
+
const superSubDigitToAsciiMap = {
|
|
111
|
+
"⁰": "0",
|
|
112
|
+
"¹": "1",
|
|
113
|
+
"²": "2",
|
|
114
|
+
"³": "3",
|
|
115
|
+
"⁴": "4",
|
|
116
|
+
"⁵": "5",
|
|
117
|
+
"⁶": "6",
|
|
118
|
+
"⁷": "7",
|
|
119
|
+
"⁸": "8",
|
|
120
|
+
"⁹": "9",
|
|
121
|
+
"₀": "0",
|
|
122
|
+
"₁": "1",
|
|
123
|
+
"₂": "2",
|
|
124
|
+
"₃": "3",
|
|
125
|
+
"₄": "4",
|
|
126
|
+
"₅": "5",
|
|
127
|
+
"₆": "6",
|
|
128
|
+
"₇": "7",
|
|
129
|
+
"₈": "8",
|
|
130
|
+
"₉": "9"
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Captures Unicode superscript and subscript digits.
|
|
134
|
+
*/
|
|
135
|
+
const superSubDigitsRegex = /[⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉]/g;
|
|
136
|
+
/**
|
|
137
|
+
* Map of Unicode fraction code points to their ASCII equivalents.
|
|
138
|
+
*/
|
|
139
|
+
const vulgarFractionToAsciiMap = {
|
|
140
|
+
"¼": "1/4",
|
|
141
|
+
"½": "1/2",
|
|
142
|
+
"¾": "3/4",
|
|
143
|
+
"⅐": "1/7",
|
|
144
|
+
"⅑": "1/9",
|
|
145
|
+
"⅒": "1/10",
|
|
146
|
+
"⅓": "1/3",
|
|
147
|
+
"⅔": "2/3",
|
|
148
|
+
"⅕": "1/5",
|
|
149
|
+
"⅖": "2/5",
|
|
150
|
+
"⅗": "3/5",
|
|
151
|
+
"⅘": "4/5",
|
|
152
|
+
"⅙": "1/6",
|
|
153
|
+
"⅚": "5/6",
|
|
154
|
+
"⅛": "1/8",
|
|
155
|
+
"⅜": "3/8",
|
|
156
|
+
"⅝": "5/8",
|
|
157
|
+
"⅞": "7/8",
|
|
158
|
+
"⅟": "1/"
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Captures the individual elements of a numeric string. Commas and underscores are allowed
|
|
162
|
+
* as separators, as long as they appear between digits and are not consecutive.
|
|
163
|
+
*
|
|
164
|
+
* Capture groups:
|
|
165
|
+
*
|
|
166
|
+
* | # | Description | Example(s) |
|
|
167
|
+
* | --- | -------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
168
|
+
* | `0` | entire match | `"2 1/3"` from `"2 1/3"` |
|
|
169
|
+
* | `1` | sign (`-` or `+`) | `"-"` from `"-2 1/3"` |
|
|
170
|
+
* | `2` | whole number or numerator | `"2"` from `"2 1/3"`; `"1"` from `"1/3"` |
|
|
171
|
+
* | `3` | entire tail: fraction, decimal, and/or exponent | `" 1/3"` from `"2 1/3"`; `".23"` from `"1.23"`; `".5e3"` from `"1.5e3"` |
|
|
172
|
+
* | `4` | exponent, when there is no decimal portion | `"e3"` from `"1e3"` |
|
|
173
|
+
* | `5` | exponent, when there is a decimal portion | `"e-3"` from `"1.5e-3"` |
|
|
174
|
+
* | `6` | numerator of a mixed number (incl. leading space) | `" 2"` from `"1 2/3"` |
|
|
175
|
+
*
|
|
176
|
+
* Groups 4–6 are subgroups of group 3 and are not used directly by
|
|
177
|
+
* {@link numericQuantity}; only groups 1–3 are consumed.
|
|
178
|
+
*
|
|
179
|
+
* _Capture groups 2 and 3 may include comma/underscore separators._
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
*
|
|
183
|
+
* ```ts
|
|
184
|
+
* numericRegex.exec("1") // [ "1", null, "1", null, null, null, null ]
|
|
185
|
+
* numericRegex.exec("1.23") // [ "1.23", null, "1", ".23", null, null, null ]
|
|
186
|
+
* numericRegex.exec("1 2/3") // [ "1 2/3", null, "1", " 2/3", null, null, " 2" ]
|
|
187
|
+
* numericRegex.exec("2/3") // [ "2/3", null, "2", "/3", null, null, null ]
|
|
188
|
+
* numericRegex.exec("2 / 3") // [ "2 / 3", null, "2", " / 3", null, null, null ]
|
|
189
|
+
* numericRegex.exec("-2 1/3") // [ "-2 1/3", "-", "2", " 1/3", null, null, " 1" ]
|
|
190
|
+
* numericRegex.exec("1e3") // [ "1e3", null, "1", "e3", "e3", null, null ]
|
|
191
|
+
* numericRegex.exec("1.5e-3") // [ "1.5e-3", null, "1", ".5e-3", null, "e-3", null ]
|
|
192
|
+
* numericRegex.exec("1,000") // [ "1,000", null, "1,000", null, null, null, null ]
|
|
193
|
+
* ```
|
|
194
|
+
*
|
|
195
|
+
* @remarks
|
|
196
|
+
*
|
|
197
|
+
* This pattern and {@link numericRegexWithTrailingInvalid} are maintained as two separate
|
|
198
|
+
* literals and must be kept in sync. They differ _only_ in the tail: `$` here versus
|
|
199
|
+
* `(\s*[^.\d/].*)?` there. `src/index.test.ts` asserts that equality.
|
|
200
|
+
*
|
|
201
|
+
* Group 2 is `((?:\d(?:[,_]\d|\d)*)?)`, deliberately `?` and not `*`. Every string the
|
|
202
|
+
* inner group matches begins and ends with a digit, so concatenating two of them yields
|
|
203
|
+
* another string of the same shape: an outer `*` would add no strings to the language,
|
|
204
|
+
* only exponentially many ways to split one digit run across iterations. That made a
|
|
205
|
+
* failing match catastrophically backtrack — `'1'.repeat(30) + '!'` took ~15s on V8.
|
|
206
|
+
*/
|
|
207
|
+
const numericRegex = /^(?=[-+]?\s*\.\d|[-+]?\s*\d)([-+])?\s*((?:\d(?:[,_]\d|\d)*)?)(([eE][+-]?\d(?:[,_]\d|\d)*)?|\.\d(?:[,_]\d|\d)*([eE][+-]?\d(?:[,_]\d|\d)*)?|(\s+\d(?:[,_]\d|\d)*\s*)?\s*\/\s*\d(?:[,_]\d|\d)*)?$/;
|
|
208
|
+
/**
|
|
209
|
+
* Same as {@link numericRegex}, but allows (and ignores) trailing invalid characters.
|
|
210
|
+
* Capture groups 1–6 are identical to {@link numericRegex}'s; capture group 7 contains the
|
|
211
|
+
* trailing invalid portion.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
*
|
|
215
|
+
* ```ts
|
|
216
|
+
* numericRegexWithTrailingInvalid.exec("1abc")
|
|
217
|
+
* // [ "1abc", null, "1", null, null, null, null, "abc" ]
|
|
218
|
+
* numericRegexWithTrailingInvalid.exec("1 2/3 xyz")
|
|
219
|
+
* // [ "1 2/3 xyz", null, "1", " 2/3", null, null, " 2", " xyz" ]
|
|
220
|
+
* ```
|
|
221
|
+
*
|
|
222
|
+
* @remarks
|
|
223
|
+
*
|
|
224
|
+
* Must be kept in sync with {@link numericRegex} — see that pattern's remarks.
|
|
225
|
+
*/
|
|
226
|
+
const numericRegexWithTrailingInvalid = /^(?=[-+]?\s*\.\d|[-+]?\s*\d)([-+])?\s*((?:\d(?:[,_]\d|\d)*)?)(([eE][+-]?\d(?:[,_]\d|\d)*)?|\.\d(?:[,_]\d|\d)*([eE][+-]?\d(?:[,_]\d|\d)*)?|(\s+\d(?:[,_]\d|\d)*\s*)?\s*\/\s*\d(?:[,_]\d|\d)*)?(\s*[^.\d/].*)?/;
|
|
227
|
+
/**
|
|
228
|
+
* Captures any Unicode vulgar fractions.
|
|
229
|
+
*/
|
|
230
|
+
const vulgarFractionsRegex = /([¼½¾⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟])/g;
|
|
231
|
+
/**
|
|
232
|
+
* Map of Roman numeral sequences to their decimal equivalents.
|
|
233
|
+
*/
|
|
234
|
+
const romanNumeralValues = {
|
|
235
|
+
MMM: 3e3,
|
|
236
|
+
MM: 2e3,
|
|
237
|
+
M: 1e3,
|
|
238
|
+
CM: 900,
|
|
239
|
+
DCCC: 800,
|
|
240
|
+
DCC: 700,
|
|
241
|
+
DC: 600,
|
|
242
|
+
D: 500,
|
|
243
|
+
CD: 400,
|
|
244
|
+
CCC: 300,
|
|
245
|
+
CC: 200,
|
|
246
|
+
C: 100,
|
|
247
|
+
XC: 90,
|
|
248
|
+
LXXX: 80,
|
|
249
|
+
LXX: 70,
|
|
250
|
+
LX: 60,
|
|
251
|
+
L: 50,
|
|
252
|
+
XL: 40,
|
|
253
|
+
XXX: 30,
|
|
254
|
+
XX: 20,
|
|
255
|
+
XII: 12,
|
|
256
|
+
XI: 11,
|
|
257
|
+
X: 10,
|
|
258
|
+
IX: 9,
|
|
259
|
+
VIII: 8,
|
|
260
|
+
VII: 7,
|
|
261
|
+
VI: 6,
|
|
262
|
+
V: 5,
|
|
263
|
+
IV: 4,
|
|
264
|
+
III: 3,
|
|
265
|
+
II: 2,
|
|
266
|
+
I: 1
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* Map of Unicode Roman numeral code points to their ASCII equivalents.
|
|
270
|
+
*/
|
|
271
|
+
const romanNumeralUnicodeToAsciiMap = {
|
|
272
|
+
Ⅰ: "I",
|
|
273
|
+
Ⅱ: "II",
|
|
274
|
+
Ⅲ: "III",
|
|
275
|
+
Ⅳ: "IV",
|
|
276
|
+
Ⅴ: "V",
|
|
277
|
+
Ⅵ: "VI",
|
|
278
|
+
Ⅶ: "VII",
|
|
279
|
+
Ⅷ: "VIII",
|
|
280
|
+
Ⅸ: "IX",
|
|
281
|
+
Ⅹ: "X",
|
|
282
|
+
Ⅺ: "XI",
|
|
283
|
+
Ⅻ: "XII",
|
|
284
|
+
Ⅼ: "L",
|
|
285
|
+
Ⅽ: "C",
|
|
286
|
+
Ⅾ: "D",
|
|
287
|
+
Ⅿ: "M",
|
|
288
|
+
ⅰ: "I",
|
|
289
|
+
ⅱ: "II",
|
|
290
|
+
ⅲ: "III",
|
|
291
|
+
ⅳ: "IV",
|
|
292
|
+
ⅴ: "V",
|
|
293
|
+
ⅵ: "VI",
|
|
294
|
+
ⅶ: "VII",
|
|
295
|
+
ⅷ: "VIII",
|
|
296
|
+
ⅸ: "IX",
|
|
297
|
+
ⅹ: "X",
|
|
298
|
+
ⅺ: "XI",
|
|
299
|
+
ⅻ: "XII",
|
|
300
|
+
ⅼ: "L",
|
|
301
|
+
ⅽ: "C",
|
|
302
|
+
ⅾ: "D",
|
|
303
|
+
ⅿ: "M"
|
|
304
|
+
};
|
|
305
|
+
/**
|
|
306
|
+
* Captures all Unicode Roman numeral code points.
|
|
307
|
+
*/
|
|
308
|
+
const romanNumeralUnicodeRegex = /([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ])/gi;
|
|
309
|
+
/**
|
|
310
|
+
* Captures a valid Roman numeral sequence.
|
|
311
|
+
*
|
|
312
|
+
* Capture groups:
|
|
313
|
+
*
|
|
314
|
+
* | # | Description | Example |
|
|
315
|
+
* | --- | --------------- | ------------------------ |
|
|
316
|
+
* | `0` | Entire string | "MCCXIV" from "MCCXIV" |
|
|
317
|
+
* | `1` | Thousands | "M" from "MCCXIV" |
|
|
318
|
+
* | `2` | Hundreds | "CC" from "MCCXIV" |
|
|
319
|
+
* | `3` | Tens | "X" from "MCCXIV" |
|
|
320
|
+
* | `4` | Ones | "IV" from "MCCXIV" |
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
*
|
|
324
|
+
* ```ts
|
|
325
|
+
* romanNumeralRegex.exec("M") // [ "M", "M", "", "", "" ]
|
|
326
|
+
* romanNumeralRegex.exec("XII") // [ "XII", "", "", "X", "II" ]
|
|
327
|
+
* romanNumeralRegex.exec("MCCXIV") // [ "MCCXIV", "M", "CC", "X", "IV" ]
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
const romanNumeralRegex = /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/i;
|
|
331
|
+
/**
|
|
332
|
+
* Default options for {@link numericQuantity}.
|
|
333
|
+
*/
|
|
334
|
+
const defaultOptions = {
|
|
335
|
+
round: 3,
|
|
336
|
+
allowTrailingInvalid: false,
|
|
337
|
+
romanNumerals: false,
|
|
338
|
+
bigIntOnOverflow: false,
|
|
339
|
+
decimalSeparator: ".",
|
|
340
|
+
allowCurrency: false,
|
|
341
|
+
percentage: false,
|
|
342
|
+
verbose: false
|
|
343
|
+
};
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region src/parseRomanNumerals.ts
|
|
346
|
+
/**
|
|
347
|
+
* Converts a string of Roman numerals to a number, like `parseInt`
|
|
348
|
+
* for Roman numerals. Uses modern, strict rules (only 1 to 3999).
|
|
349
|
+
*
|
|
350
|
+
* The string can include ASCII representations of Roman numerals
|
|
351
|
+
* or Unicode Roman numeral code points (`U+2160` through `U+217F`).
|
|
352
|
+
*/
|
|
353
|
+
const parseRomanNumerals = (romanNumerals) => {
|
|
354
|
+
const normalized = romanNumerals.replace(romanNumeralUnicodeRegex, (_m, rn) => romanNumeralUnicodeToAsciiMap[rn]).toUpperCase();
|
|
355
|
+
const regexResult = romanNumeralRegex.exec(normalized);
|
|
356
|
+
if (!regexResult) return NaN;
|
|
357
|
+
const [, thousands, hundreds, tens, ones] = regexResult;
|
|
358
|
+
return (romanNumeralValues[thousands] ?? 0) + (romanNumeralValues[hundreds] ?? 0) + (romanNumeralValues[tens] ?? 0) + (romanNumeralValues[ones] ?? 0);
|
|
359
|
+
};
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/numericQuantity.ts
|
|
362
|
+
const leadingSlashRegex = /^\s*\//;
|
|
363
|
+
const currencyPrefixRegex = /* @__PURE__ */ new RegExp("^([-+]?)\\s*(\\p{Sc}+)\\s*", "u");
|
|
364
|
+
const currencySuffixRegex = /* @__PURE__ */ new RegExp("\\s*(\\p{Sc}+)\\s*$", "u");
|
|
365
|
+
const percentageSuffixRegex = /\s*%$/;
|
|
366
|
+
const maxSafeBigInt = BigInt(Number.MAX_SAFE_INTEGER);
|
|
367
|
+
/**
|
|
368
|
+
* Upper bound on `10 ** exp` magnitudes evaluated exactly. Anything beyond this
|
|
369
|
+
* overflows/underflows the `number` path to `Infinity`/`0` anyway, so bounding it
|
|
370
|
+
* avoids allocating absurdly large `bigint`s (and `BigInt(Infinity)` throwing).
|
|
371
|
+
*/
|
|
372
|
+
const maxExactExponent = 1e4;
|
|
373
|
+
const pow10 = (exp) => 10n ** BigInt(exp);
|
|
374
|
+
/**
|
|
375
|
+
* Evaluates the parsed numeric components as an exact `bigint` rational (magnitude only,
|
|
376
|
+
* sign is applied by the caller) and rounds the result half-up.
|
|
377
|
+
*
|
|
378
|
+
* Returns the rounded `bigint` only if its magnitude exceeds `Number.MAX_SAFE_INTEGER`;
|
|
379
|
+
* otherwise returns `undefined`, meaning "no overflow, use the `number` path".
|
|
380
|
+
*
|
|
381
|
+
* @param group1 - Whole number or numerator (separators already stripped).
|
|
382
|
+
* @param group2 - Decimal/exponent/fraction tail, if any.
|
|
383
|
+
* @param divideBy100 - Whether a percentage suffix was stripped.
|
|
384
|
+
*/
|
|
385
|
+
const toRoundedBigInt = (group1, group2, divideBy100) => {
|
|
386
|
+
let numerator;
|
|
387
|
+
let denominator = 1n;
|
|
388
|
+
if (!group2) numerator = BigInt(group1);
|
|
389
|
+
else if (group2.startsWith(".") || group2.startsWith("e") || group2.startsWith("E")) {
|
|
390
|
+
const expIndex = group2.search(/[eE]/);
|
|
391
|
+
const exponent = expIndex === -1 ? 0 : parseInt(group2.slice(expIndex + 1));
|
|
392
|
+
if (!Number.isFinite(exponent) || Math.abs(exponent) > maxExactExponent) return void 0;
|
|
393
|
+
const fractionDigits = (expIndex === -1 ? group2 : group2.slice(0, expIndex)).slice(1);
|
|
394
|
+
numerator = BigInt(`${group1}${fractionDigits}`);
|
|
395
|
+
if (fractionDigits) denominator = pow10(fractionDigits.length);
|
|
396
|
+
if (exponent > 0) numerator *= pow10(exponent);
|
|
397
|
+
else if (exponent < 0) denominator *= pow10(-exponent);
|
|
398
|
+
} else if (leadingSlashRegex.test(group2)) {
|
|
399
|
+
numerator = BigInt(group1);
|
|
400
|
+
denominator = BigInt(group2.replace("/", "").trim());
|
|
401
|
+
} else {
|
|
402
|
+
const [n, d] = group2.split("/");
|
|
403
|
+
denominator = BigInt(d.trim());
|
|
404
|
+
numerator = BigInt(group1) * denominator + BigInt(n.trim());
|
|
405
|
+
}
|
|
406
|
+
if (divideBy100) denominator *= 100n;
|
|
407
|
+
if (denominator === 0n) return void 0;
|
|
408
|
+
const rounded = (2n * numerator + denominator) / (2n * denominator);
|
|
409
|
+
return rounded > maxSafeBigInt ? rounded : void 0;
|
|
410
|
+
};
|
|
411
|
+
function numericQuantity(quantity, options = defaultOptions) {
|
|
412
|
+
var _regexResult$;
|
|
413
|
+
const opts = {
|
|
414
|
+
...defaultOptions,
|
|
415
|
+
...options
|
|
416
|
+
};
|
|
417
|
+
let originalInput;
|
|
418
|
+
if (typeof quantity === "string") originalInput = quantity;
|
|
419
|
+
else try {
|
|
420
|
+
originalInput = String(quantity);
|
|
421
|
+
} catch {
|
|
422
|
+
originalInput = "";
|
|
423
|
+
}
|
|
424
|
+
let currencyPrefix;
|
|
425
|
+
let currencySuffix;
|
|
426
|
+
let percentageSuffix;
|
|
427
|
+
let trailingInvalid;
|
|
428
|
+
let parsedSign;
|
|
429
|
+
let parsedWhole;
|
|
430
|
+
let parsedNumerator;
|
|
431
|
+
let parsedDenominator;
|
|
432
|
+
const buildVerboseResult = (value) => {
|
|
433
|
+
const result = {
|
|
434
|
+
value,
|
|
435
|
+
input: originalInput
|
|
436
|
+
};
|
|
437
|
+
if (currencyPrefix) result.currencyPrefix = currencyPrefix;
|
|
438
|
+
if (currencySuffix) result.currencySuffix = currencySuffix;
|
|
439
|
+
if (percentageSuffix) result.percentageSuffix = percentageSuffix;
|
|
440
|
+
if (trailingInvalid) result.trailingInvalid = trailingInvalid;
|
|
441
|
+
if (parsedSign) result.sign = parsedSign;
|
|
442
|
+
if (parsedWhole !== void 0) result.whole = parsedWhole;
|
|
443
|
+
if (parsedNumerator !== void 0) result.numerator = parsedNumerator;
|
|
444
|
+
if (parsedDenominator !== void 0) result.denominator = parsedDenominator;
|
|
445
|
+
return result;
|
|
446
|
+
};
|
|
447
|
+
const returnValue = (value) => opts.verbose ? buildVerboseResult(value) : value;
|
|
448
|
+
/**
|
|
449
|
+
* Divides by 100 if a percentage suffix was stripped. Exact scaling, no rounding:
|
|
450
|
+
* `round` applies to the quantity as written, before this division.
|
|
451
|
+
*/
|
|
452
|
+
const applyPercentage = (value) => percentageSuffix && opts.percentage !== "number" ? value / 100 : value;
|
|
453
|
+
if (typeof quantity === "symbol") return returnValue(NaN);
|
|
454
|
+
if (typeof quantity === "number" || typeof quantity === "bigint") return returnValue(quantity);
|
|
455
|
+
let finalResult = NaN;
|
|
456
|
+
let workingString = originalInput;
|
|
457
|
+
let affixStripped = true;
|
|
458
|
+
while (affixStripped) {
|
|
459
|
+
affixStripped = false;
|
|
460
|
+
if (opts.allowCurrency) {
|
|
461
|
+
const prefixMatch = currencyPrefixRegex.exec(workingString);
|
|
462
|
+
if (prefixMatch === null || prefixMatch === void 0 ? void 0 : prefixMatch[2]) {
|
|
463
|
+
currencyPrefix = (currencyPrefix ?? "") + prefixMatch[2];
|
|
464
|
+
workingString = (prefixMatch[1] || "") + workingString.slice(prefixMatch[0].length);
|
|
465
|
+
affixStripped = true;
|
|
466
|
+
}
|
|
467
|
+
const suffixMatch = currencySuffixRegex.exec(workingString);
|
|
468
|
+
if (suffixMatch) {
|
|
469
|
+
currencySuffix = suffixMatch[1] + (currencySuffix ?? "");
|
|
470
|
+
workingString = workingString.slice(0, -suffixMatch[0].length);
|
|
471
|
+
affixStripped = true;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (!percentageSuffix && opts.percentage) {
|
|
475
|
+
const pctMatch = percentageSuffixRegex.exec(workingString);
|
|
476
|
+
if (pctMatch) {
|
|
477
|
+
percentageSuffix = true;
|
|
478
|
+
workingString = workingString.slice(0, -pctMatch[0].length);
|
|
479
|
+
affixStripped = true;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const quantityAsString = normalizeDigits(workingString.replace(vulgarFractionsRegex, (_m, vf) => ` ${vulgarFractionToAsciiMap[vf]}`).replace(superSubDigitsRegex, (ch) => superSubDigitToAsciiMap[ch]).replace("⁄", "/").trim());
|
|
484
|
+
if (quantityAsString.length === 0) return returnValue(NaN);
|
|
485
|
+
let normalizedString = quantityAsString;
|
|
486
|
+
if (opts.decimalSeparator === ",") {
|
|
487
|
+
const commaCount = (quantityAsString.match(/,/g) || []).length;
|
|
488
|
+
if (commaCount === 1) normalizedString = quantityAsString.replaceAll(".", "_").replace(",", ".");
|
|
489
|
+
else if (commaCount > 1) {
|
|
490
|
+
const firstCommaIndex = quantityAsString.indexOf(",");
|
|
491
|
+
const secondCommaIndex = quantityAsString.indexOf(",", firstCommaIndex + 1);
|
|
492
|
+
normalizedString = quantityAsString.substring(0, secondCommaIndex).replaceAll(".", "_").replace(",", ".");
|
|
493
|
+
} else normalizedString = quantityAsString.replaceAll(".", "_");
|
|
494
|
+
}
|
|
495
|
+
const regexResult = numericRegexWithTrailingInvalid.exec(normalizedString);
|
|
496
|
+
if (!regexResult) {
|
|
497
|
+
if (!opts.romanNumerals) return returnValue(NaN);
|
|
498
|
+
return returnValue(applyPercentage(parseRomanNumerals(quantityAsString)));
|
|
499
|
+
}
|
|
500
|
+
const consumedLength = regexResult[0].length - (((_regexResult$ = regexResult[7]) === null || _regexResult$ === void 0 ? void 0 : _regexResult$.length) ?? 0);
|
|
501
|
+
const rawTrailing = quantityAsString.slice(consumedLength).trim();
|
|
502
|
+
if (rawTrailing) {
|
|
503
|
+
trailingInvalid = rawTrailing;
|
|
504
|
+
if (!opts.allowTrailingInvalid) return returnValue(NaN);
|
|
505
|
+
}
|
|
506
|
+
const [, sign, rawGroup1, rawGroup2] = regexResult;
|
|
507
|
+
if (sign === "-" || sign === "+") parsedSign = sign;
|
|
508
|
+
const numberGroup1 = rawGroup1.replaceAll(",", "").replaceAll("_", "");
|
|
509
|
+
const numberGroup2 = rawGroup2 === null || rawGroup2 === void 0 ? void 0 : rawGroup2.replaceAll(",", "").replaceAll("_", "");
|
|
510
|
+
if (opts.bigIntOnOverflow) {
|
|
511
|
+
const asBigInt = toRoundedBigInt(numberGroup1, numberGroup2, !!percentageSuffix && opts.percentage !== "number");
|
|
512
|
+
if (asBigInt !== void 0) return returnValue(sign === "-" ? -asBigInt : asBigInt);
|
|
513
|
+
}
|
|
514
|
+
finalResult = !numberGroup1 && numberGroup2 && numberGroup2.startsWith(".") ? 0 : parseInt(numberGroup1);
|
|
515
|
+
if (!numberGroup2) {
|
|
516
|
+
finalResult = sign === "-" ? finalResult * -1 : finalResult;
|
|
517
|
+
return returnValue(applyPercentage(finalResult));
|
|
518
|
+
}
|
|
519
|
+
const roundTo = typeof opts.round === "number" && Number.isFinite(opts.round) ? Math.floor(Math.max(0, opts.round)) : false;
|
|
520
|
+
const roundingFactorRaw = roundTo === false ? NaN : 10 ** roundTo;
|
|
521
|
+
const roundingFactor = Number.isFinite(roundingFactorRaw) ? roundingFactorRaw : NaN;
|
|
522
|
+
/** Rounds to `roundTo` decimal places, falling back to `value` if the scaling overflows. */
|
|
523
|
+
const round = (value, scaled) => {
|
|
524
|
+
const result = Math.round(scaled) / roundingFactor;
|
|
525
|
+
return Number.isFinite(result) || !Number.isFinite(value) ? result : value;
|
|
526
|
+
};
|
|
527
|
+
if (numberGroup2.startsWith(".") || numberGroup2.startsWith("e") || numberGroup2.startsWith("E")) {
|
|
528
|
+
const decimalValue = parseFloat(`${finalResult}${numberGroup2}`);
|
|
529
|
+
finalResult = isNaN(roundingFactor) ? decimalValue : round(decimalValue, decimalValue * roundingFactor);
|
|
530
|
+
} else if (leadingSlashRegex.test(numberGroup2)) {
|
|
531
|
+
const numerator = parseInt(numberGroup1);
|
|
532
|
+
const denominator = parseInt(numberGroup2.replace("/", ""));
|
|
533
|
+
parsedNumerator = numerator;
|
|
534
|
+
parsedDenominator = denominator;
|
|
535
|
+
finalResult = isNaN(roundingFactor) ? numerator / denominator : round(numerator / denominator, numerator * roundingFactor / denominator);
|
|
536
|
+
} else {
|
|
537
|
+
const [numerator, denominator] = numberGroup2.split("/").map((v) => parseInt(v));
|
|
538
|
+
parsedWhole = finalResult;
|
|
539
|
+
parsedNumerator = numerator;
|
|
540
|
+
parsedDenominator = denominator;
|
|
541
|
+
finalResult += isNaN(roundingFactor) ? numerator / denominator : round(numerator / denominator, numerator * roundingFactor / denominator);
|
|
542
|
+
}
|
|
543
|
+
finalResult = sign === "-" ? finalResult * -1 : finalResult;
|
|
544
|
+
return returnValue(applyPercentage(finalResult));
|
|
545
|
+
}
|
|
546
|
+
//#endregion
|
|
547
|
+
//#region src/isNumericQuantity.ts
|
|
548
|
+
/**
|
|
549
|
+
* Checks if a value represents a valid numeric quantity.
|
|
550
|
+
*
|
|
551
|
+
* Returns `true` if the value can be parsed as a number, `false` otherwise.
|
|
552
|
+
* Accepts the same options as `numericQuantity`.
|
|
553
|
+
*/
|
|
554
|
+
const isNumericQuantity = (quantity, options) => {
|
|
555
|
+
const result = numericQuantity(quantity, {
|
|
556
|
+
...options,
|
|
557
|
+
verbose: false
|
|
558
|
+
});
|
|
559
|
+
return typeof result === "bigint" || !isNaN(result);
|
|
560
|
+
};
|
|
561
|
+
//#endregion
|
|
562
|
+
export { defaultOptions, isNumericQuantity, normalizeDigits, numericQuantity, numericRegex, numericRegexWithTrailingInvalid, parseRomanNumerals, romanNumeralRegex, romanNumeralUnicodeRegex, romanNumeralUnicodeToAsciiMap, romanNumeralValues, superSubDigitToAsciiMap, superSubDigitsRegex, vulgarFractionToAsciiMap, vulgarFractionsRegex };
|
|
563
|
+
|
|
564
|
+
//# sourceMappingURL=numeric-quantity.mjs.map
|