From e0540468c3090ceecfc4aaa01ccf960ca20cc003 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 1 Jun 2026 15:16:12 -0500 Subject: [PATCH] feat(pure): parseTarget with comma and magnitude suffix support --- package.json | 2 +- src/pure.js | 28 ++++++++++++++++++++++++++++ tests/pure.test.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/pure.js create mode 100644 tests/pure.test.js diff --git a/package.json b/package.json index ea16505..601a4d4 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,6 @@ "private": true, "type": "module", "scripts": { - "test": "node --test tests/" + "test": "node --test" } } diff --git a/src/pure.js b/src/pure.js new file mode 100644 index 0000000..25971ab --- /dev/null +++ b/src/pure.js @@ -0,0 +1,28 @@ +const SUFFIXES = { k: 1e3, m: 1e6, b: 1e9, t: 1e12 }; + +export function parseTarget(input) { + if (input === null || input === undefined || input === '') return null; + + if (typeof input === 'number') { + if (!Number.isFinite(input) || input <= 0) return null; + return Math.floor(input); + } + + if (typeof input !== 'string') return null; + + const cleaned = input.replace(/,/g, '').trim().toLowerCase(); + if (cleaned === '') return null; + + // Reject anything other than digits, optional decimal, and optional single trailing suffix letter + if (!/^\d+(\.\d+)?[kmbt]?$/.test(cleaned)) return null; + + const match = cleaned.match(/^(\d+(?:\.\d+)?)([kmbt])?$/); + if (!match) return null; + + const num = parseFloat(match[1]); + if (!Number.isFinite(num) || num <= 0) return null; + + const suffix = match[2]; + const multiplier = suffix ? SUFFIXES[suffix] : 1; + return Math.floor(num * multiplier); +} diff --git a/tests/pure.test.js b/tests/pure.test.js new file mode 100644 index 0000000..957d1be --- /dev/null +++ b/tests/pure.test.js @@ -0,0 +1,33 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { parseTarget } from '../src/pure.js'; + +test('parseTarget: integer numbers', () => { + assert.equal(parseTarget(25), 25); + assert.equal(parseTarget(25000000), 25_000_000); + assert.equal(parseTarget(2.5e7), 25_000_000); +}); + +test('parseTarget: string numbers with commas', () => { + assert.equal(parseTarget('25'), 25); + assert.equal(parseTarget('25,000,000'), 25_000_000); + assert.equal(parseTarget('25,000,000.5'), 25_000_000); +}); + +test('parseTarget: magnitude suffixes (case-insensitive)', () => { + assert.equal(parseTarget('25K'), 25_000); + assert.equal(parseTarget('25m'), 25_000_000); + assert.equal(parseTarget('1.5B'), 1_500_000_000); + assert.equal(parseTarget('100t'), 100_000_000_000_000); +}); + +test('parseTarget: rejects invalid input', () => { + assert.equal(parseTarget(0), null); + assert.equal(parseTarget(-1), null); + assert.equal(parseTarget('abc'), null); + assert.equal(parseTarget(''), null); + assert.equal(parseTarget(null), null); + assert.equal(parseTarget(undefined), null); + assert.equal(parseTarget('25M.5'), null); + assert.equal(parseTarget('1.5.5M'), null); +});