@fatdevcon/precisekit

PreciseKit

Exact, Safe & Reliable TypeScript Utilities

Zero-dependency TypeScript utilities for exact math, financial calculations, safe data handling, locale parsing, dates, security and async workflows.

npm install @fatdevcon/precisekit

One calculation, two answers

Both lines run live in your browser.

JavaScript0.1 + 0.2
0.30000000000004

The highlighted digits are floating-point error.

@fatdevcon/precisekitsummary(0.1, 0.2)
0.3

Exact: no digits are lost.

0
runtime dependencies. Library code, runtime and build tooling still require maintenance.
30.2 kB
Full-root browser bundle, minified + gzip. Named imports are measured separately. Compare releases.
ESM + CJS
with bundled type declarations and an exact, typed error model.
Node 18+
Bun and modern browsers, using the runtime's own Intl data.

Bundle size, measured

Version 0.3.5 replaced decimal.js and date-fns with an in-package BigInt decimal engine and an ISO 8601 parser and formatter, which removed both runtime dependencies. Supported operations are checked against those libraries as development-only test oracles; this is not a claim of full API or behavioral parity with either. Later releases added features, so the full-package import has grown again while importing a single feature stays small.

Full-package import, minified + gzip. Every bar uses one scale; the longest bar is the largest size.
  • Historical fixture, recorded at release time
  • Current measurement, regenerated from the build
  1. 0.3.4historicalwith two runtime dependencies
    32.3 kBgzip

    90.9 kB minifiedBrotli not measuredrounded to 0.1 kB in the source

  2. 0.4.2historicalno runtime dependencies
    24.8 kBgzip

    70.8 kB minified21.7 kB Brotli

  3. 0.5.0historicalno runtime dependencies
    29.8 kBgzip

    86.5 kB minified26.2 kB Brotli

  4. 0.5.2currentno runtime dependencies
    30.2 kBgzip

    87.7 kB minified26.5 kB Brotli

0.5.2: 87.7 kB minified / 30.2 kB gzip / 26.5 kB Brotli. Measured with esbuild 0.28.2; historical figures retain their original fixtures.

How each row was measured

Sizes are decimal kilobytes (1 kB = 1,000 bytes). Fixtures and tooling differ between rows, so read the trend rather than small differences.

0.3.4, historical
Full package with decimal.js and date-fns bundled, minified. The CHANGELOG records the sizes rounded to 0.1 kB; the bundler, target and compression level were not recorded and no Brotli figure exists. Source: CHANGELOG.md, 0.3.5 entry (90.9 kB minified, 32.3 kB gzipped).
0.4.2, historical
Full package, published as @fatdevcon/utilities. esbuild 0.27.2, browser platform, ES2020 IIFE, minify and tree-shaking, gzip level 9, Brotli with Node zlib defaults, measured from the packed 0.4.2 tarball on Node v22.22.3. Source: plans/v0.4.2/precisekit-v0.4.2-baseline.json (minified, gzip) and plans/v0.5.0/precisekit-v0.5.0-measurements.json (Brotli).
0.5.0, historical
Full package, published as @fatdevcon/precisekit. esbuild 0.27.2, browser platform, ES2020 IIFE, minify and tree-shaking, gzip level 9, Brotli with Node zlib defaults, measured from dist/index.mjs on Node v22.22.3. Source: plans/v0.5.0/precisekit-v0.5.0-measurements.json.
0.5.2, current
Full package, @fatdevcon/precisekit. esbuild 0.28.2, browser platform, ES2020 IIFE, minify and tree-shaking, gzip level 9, Brotli with Node zlib defaults, measured from dist/index.mjs (SHA-256 fe707acd0742) on Node v22.22.3. Source: docs/bundle-data.json, generated by scripts/measure-bundle.mjs.

Importing only what you use

Named imports from the current release, minified + gzip with the same toolchain. A feature row imports every function in that area; a function row imports one function.

ImportgzipBrotli
Money6.1 kB5.3 kB
Locale parsing6.1 kB5.2 kB
Security1.7 kB1.5 kB
Business calendar1.5 kB1.3 kB
Normalization1.8 kB1.6 kB
Concurrency1.2 kB1.0 kB
compactNullish0.8 kB0.7 kB
hasByPath0.8 kB0.7 kB
pickBy1.0 kB0.8 kB

What is inside

Exact money, locale parsing, safe data normalization, bounded async work, business calendars and security helpers. Import what you need.

Exact decimal and big-number math

Add, subtract, multiply, divide, round and compare numbers, bigints and numeric strings without drift. Magnitudes reach about 1e300000, and failures use typed errors with stable codes.

Formatting and currencies

Numbers, currencies, percentages, units, compact values and byte sizes through the runtime's Intl data, with ISO 4217 country and currency metadata. Parse sizes and durations back to exact values.

Dates and time zones

Token-based formatting in any IANA zone, ISO 8601 parsing, calendar arithmetic that respects daylight saving, relative time, and DST-aware offsets in seconds, minutes or hours.

Unicode-safe text

Truncate, mask, slugify and change case without splitting emoji, flags or combining accents. Vietnamese and other diacritics are handled.

Collections and objects

Group, chunk, partition and range arrays; drop null and undefined entries; deep clone, merge and compare objects; keep fields by predicate; read and check nested own properties, and write nested paths with prototype-pollution guards.

Async control and algorithms

Debounce, throttle, memoize, retry with backoff, abort-aware waits and timeouts, plus classic sorting and searching with documented limits.

Quick start

Every utility is a named export, so imports stay focused. Results below are what the code returns.

Exact decimal math

import { divide, summary, round } from "@fatdevcon/precisekit";

summary(0.1, 0.2);                 // 0.3
round(1.005, 2);                   // 1.01
divide("1", "3", { precision: 5 }); // "0.33333"
summary("100000000000000000000000", "1");
// "100000000000000000000001"

Format for a real user

import { formatCurrency, formatBytes } from "@fatdevcon/precisekit";

formatCurrency("12345678901234567890.129", "USD");
// "$12,345,678,901,234,567,890.13"
formatCurrency(1234.5, "VND", "vi-VN"); // "1.235 ₫"
formatBytes(1536, { base: 1024 });      // "1.5 KiB"

Dates in any time zone

import { formatDate, addMonths } from "@fatdevcon/precisekit";

formatDate(new Date("2026-01-15T17:30:00Z"), "yyyy-MM-dd HH:mm xxx", {
  timeZone: "Asia/Ho_Chi_Minh",
}); // "2026-01-16 00:30 +07:00"
formatDate(addMonths("2026-01-31", 1), "yyyy-MM-dd"); // "2026-02-28"

Retry flaky work

import { withRetry } from "@fatdevcon/precisekit";

const config = await withRetry(() => fetch("/config.json").then((r) => r.json()), {
  retryCount: 3,
  delay: ({ count }) => 200 * 2 ** count,
});

Playground

Run one supported function call per line against the real package. Values can be strings, numbers, bigints, arrays and objects. Lines are parsed as data and calls, never executed as code.

ResultRuns locally in your browser

API reference

219 documented functions with parameters, return values, errors and examples. Executable examples are checked against the built package. Illustrative and async examples are marked separately; some environment-dependent expectations are skipped by the docs runner.

The full reference needs JavaScript. See the README overview for workflows; type declarations also ship with the package.

Common tasks

Short answers to questions developers search for, using only exported APIs.

How to group data in JavaScript

import { groupBy } from "@fatdevcon/precisekit";

const byRole = groupBy([
  { name: "An", role: "dev" }, { name: "Binh", role: "qa" },
], (user) => user.role);
// { dev: [{ name: "An", ... }], qa: [{ name: "Binh", ... }] }

How to avoid floating-point errors when adding money

import { sumValueInArray, multiply } from "@fatdevcon/precisekit";

sumValueInArray([19.99, 5.01, 0.1]); // 25.1
multiply("19.99", 3);                // "59.97"

How to format a time zone-aware date

import { getTimeZoneOffset, formatInTimeZone } from "@fatdevcon/precisekit";

const instant = new Date("2026-01-15T12:00:00Z");
getTimeZoneOffset(instant, "Asia/Ho_Chi_Minh", { unit: "hours" }); // 7
formatInTimeZone(instant, "Asia/Ho_Chi_Minh", "en-GB", {
  dateStyle: "short", timeStyle: "short", hourCycle: "h23",
}); // "15/01/2026, 19:00"

How to parse a file size or duration string

import { parseBytes, parseDuration } from "@fatdevcon/precisekit";

parseBytes("1.5 GB");        // 1500000000
parseBytes("512 KiB");       // 524288
parseDuration("1h 2m 3s");   // 3723000
parseDuration("2 days, 4 hours"); // 187200000

How to update nested state without mutating it

import { setByPath } from "@fatdevcon/precisekit";

const state = { user: { name: "An", tags: ["a"] } };
const next = setByPath(state, "user.tags[1]", "b");
// next.user.tags is ["a", "b"]; state is unchanged

How to format very large currency values

import { formatCurrency } from "@fatdevcon/precisekit";

formatCurrency("12345678901234567890.129", "USD");
// "$12,345,678,901,234,567,890.13"

How to drop empty values from arrays and objects

import { compactNullish, pickBy } from "@fatdevcon/precisekit";

compactNullish([0, null, "", undefined, false, 1]); // [0, "", false, 1]
pickBy({ id: 7, note: null, flag: false }, (value) => value !== null);
// { id: 7, flag: false }

How to check a nested key without reading it

import { getByPath, hasByPath } from "@fatdevcon/precisekit";

const data = { a: { b: undefined, list: [{ id: 1 }] } };
hasByPath(data, "a.b");          // true
getByPath(data, "a.b");          // undefined
hasByPath(data, "a.list[0].id"); // true
hasByPath(data, "a.missing");    // false

Release 0.5.2

  • Replace date-format tokenization, timezone suffix matching and byte-size parsing with linear-time processing to address the three reported ReDoS locations, preserving public formatting contracts.
  • Replace single-pass FAQ tag stripping in documentation generation and verification with a shared linear extractor that handles nested and incomplete delimiters. Keep entity decoding separate from JSON-LD script encoding.
  • Add adversarial date, number and documentation-text regression tests, including large inputs.
  • Audit CI and npm OIDC publishing, preserve provenance and document external configuration and verification requirements in the v0.5.2 release report.
  • Synchronize package metadata and the documentation playground for 0.5.2. No public APIs, error codes, export paths or runtime dependencies are added.

The full history is in the changelog.

Installed-package verification

Clean consumer fixtures verify Node, Bun, ESM, CommonJS and strict TypeScript declarations from the packed tarball.

Historical accuracy

Older changelog and migration records retain the package name under which those versions were released.

Compatible and measured

Strict conversions, readable ESM/CJS output, exact numeric behavior and zero runtime dependencies remain intact.

Migrate from @fatdevcon/utilities

The migration from utilities 0.4.2 to PreciseKit 0.5.0 keeps the existing function and type contracts and adds six groups of helpers. Update installation and imports to @fatdevcon/precisekit. See the migration guide.

Replace the package

npm uninstall @fatdevcon/utilities
npm install @fatdevcon/precisekit

Update imports

import { summary, formatCurrency } from "@fatdevcon/precisekit";

The old package is not aliased or deprecated automatically. No function rename or behavioral compatibility break is introduced by the package migration.

Frequently asked questions

How do I avoid floating-point errors like 0.1 + 0.2 in JavaScript?

Use summary, subtract, multiply, divide and round. They calculate in exact decimal arithmetic, so summary(0.1, 0.2) is 0.3 and round(1.005, 2) is 1.01. Pass bigints or numeric strings when values exceed 2^53.

Does it have dependencies?

No. Since 0.3.5 the package has zero runtime dependencies. decimal.js and date-fns are development-only test oracles.

Does it support TypeScript, ESM and CommonJS?

Yes. It ships ESM, CommonJS and type declarations, and public option and result types are exported alongside the functions.

Which runtimes are supported?

Node.js 18 and newer, Bun, and modern browsers. Formatting and time zone helpers use the standard Intl APIs, so output follows the runtime's locale data.

Can it replace decimal.js, big.js, date-fns or lodash?

For many everyday cases, yes: exact arithmetic with rounding modes, ISO parsing, token formatting and common collection helpers are covered. It is a focused toolkit, not a full replacement. Libraries with broader scope, such as complex date arithmetic or arbitrary-base numbers, still fit those jobs better.

Does formatDate use the same tokens as date-fns?

It supports a documented subset, including yyyy-MM-dd HH:mm:ss, EEEE, do, xxx and PPpp, and accepts a date-fns locale object as options.locale. It is not a complete date-fns replacement; locale-sensitive output depends on runtime ICU data and the selected locale.

Is it tree-shakeable?

Yes. The package is side-effect free and every utility is a named export, so bundlers drop what you do not import.