@rtorcato/js-common 2.8.1 → 3.0.0

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.
Files changed (141) hide show
  1. package/AGENTS.md +116 -0
  2. package/CHANGELOG.md +30 -0
  3. package/CONTRIBUTORS.md +5 -0
  4. package/README.md +71 -22
  5. package/dist/abortController/index.d.ts +30 -0
  6. package/dist/abortController/index.d.ts.map +1 -1
  7. package/dist/arrays/arrays.test-d.d.ts +2 -0
  8. package/dist/arrays/arrays.test-d.d.ts.map +1 -0
  9. package/dist/arrays/arrays.test-d.js +1 -0
  10. package/dist/arrays/index.js +1 -1
  11. package/dist/boolean/index.d.ts +18 -6
  12. package/dist/boolean/index.d.ts.map +1 -1
  13. package/dist/boolean/index.js +1 -1
  14. package/dist/chunks/chunk-5IA77ERW.js +1 -0
  15. package/dist/chunks/chunk-7JQSIREC.js +1 -0
  16. package/dist/chunks/chunk-ARW46OJS.js +1 -0
  17. package/dist/chunks/chunk-MFJ5XPUX.js +1 -0
  18. package/dist/chunks/chunk-RBXTRLWB.js +1 -0
  19. package/dist/cli/catalog.js +131 -0
  20. package/dist/cli/cli.mjs +16 -109
  21. package/dist/colors/index.d.ts +23 -3
  22. package/dist/colors/index.d.ts.map +1 -1
  23. package/dist/console/index.d.ts +12 -0
  24. package/dist/console/index.d.ts.map +1 -1
  25. package/dist/crypto/index.d.ts +23 -0
  26. package/dist/crypto/index.d.ts.map +1 -1
  27. package/dist/currency/index.d.ts +0 -14
  28. package/dist/currency/index.d.ts.map +1 -1
  29. package/dist/currency/index.js +1 -1
  30. package/dist/date/index.d.ts +16 -0
  31. package/dist/date/index.d.ts.map +1 -1
  32. package/dist/datetime/index.d.ts +21 -9
  33. package/dist/datetime/index.d.ts.map +1 -1
  34. package/dist/datetime/index.js +1 -1
  35. package/dist/emails/index.d.ts +22 -0
  36. package/dist/emails/index.d.ts.map +1 -1
  37. package/dist/emails/index.js +1 -1
  38. package/dist/env/index.d.ts +24 -0
  39. package/dist/env/index.d.ts.map +1 -1
  40. package/dist/errors/index.d.ts +30 -2
  41. package/dist/errors/index.d.ts.map +1 -1
  42. package/dist/events/index.d.ts +25 -1
  43. package/dist/events/index.d.ts.map +1 -1
  44. package/dist/events/index.js +1 -1
  45. package/dist/fetch/index.d.ts +25 -0
  46. package/dist/fetch/index.d.ts.map +1 -1
  47. package/dist/file/index.d.ts +20 -0
  48. package/dist/file/index.d.ts.map +1 -1
  49. package/dist/functions/functions.test-d.d.ts +2 -0
  50. package/dist/functions/functions.test-d.d.ts.map +1 -0
  51. package/dist/functions/functions.test-d.js +1 -0
  52. package/dist/functions/index.d.ts +32 -2
  53. package/dist/functions/index.d.ts.map +1 -1
  54. package/dist/functions/index.js +1 -1
  55. package/dist/geometry/index.d.ts +21 -0
  56. package/dist/geometry/index.d.ts.map +1 -1
  57. package/dist/html/index.d.ts +38 -0
  58. package/dist/html/index.d.ts.map +1 -1
  59. package/dist/html/index.js +1 -1
  60. package/dist/i18n/index.d.ts +23 -7
  61. package/dist/i18n/index.d.ts.map +1 -1
  62. package/dist/i18n/index.js +1 -1
  63. package/dist/interval/index.d.ts +15 -0
  64. package/dist/interval/index.d.ts.map +1 -1
  65. package/dist/json/index.d.ts +27 -0
  66. package/dist/json/index.d.ts.map +1 -1
  67. package/dist/logger/index.js +1 -1
  68. package/dist/logger/logger.d.ts +12 -2
  69. package/dist/logger/logger.d.ts.map +1 -1
  70. package/dist/logger/logger.js +1 -1
  71. package/dist/logging/index.d.ts +19 -0
  72. package/dist/logging/index.d.ts.map +1 -1
  73. package/dist/maps/index.d.ts +21 -0
  74. package/dist/maps/index.d.ts.map +1 -1
  75. package/dist/mime-types/index.d.ts +20 -0
  76. package/dist/mime-types/index.d.ts.map +1 -1
  77. package/dist/node/index.d.ts +0 -9
  78. package/dist/node/index.d.ts.map +1 -1
  79. package/dist/node/index.js +1 -1
  80. package/dist/numbers/index.d.ts +28 -12
  81. package/dist/numbers/index.d.ts.map +1 -1
  82. package/dist/numbers/index.js +1 -1
  83. package/dist/objects/index.d.ts +23 -0
  84. package/dist/objects/index.d.ts.map +1 -1
  85. package/dist/objects/index.js +1 -1
  86. package/dist/objects/objects.test-d.d.ts +2 -0
  87. package/dist/objects/objects.test-d.d.ts.map +1 -0
  88. package/dist/objects/objects.test-d.js +1 -0
  89. package/dist/os/index.d.ts +18 -0
  90. package/dist/os/index.d.ts.map +1 -1
  91. package/dist/process/index.d.ts +18 -0
  92. package/dist/process/index.d.ts.map +1 -1
  93. package/dist/promises/index.d.ts +29 -2
  94. package/dist/promises/index.d.ts.map +1 -1
  95. package/dist/promises/index.js +1 -1
  96. package/dist/promises/promises.test-d.d.ts +2 -0
  97. package/dist/promises/promises.test-d.d.ts.map +1 -0
  98. package/dist/promises/promises.test-d.js +1 -0
  99. package/dist/random/index.d.ts +21 -0
  100. package/dist/random/index.d.ts.map +1 -1
  101. package/dist/regex/index.d.ts +23 -0
  102. package/dist/regex/index.d.ts.map +1 -1
  103. package/dist/security/index.d.ts +36 -4
  104. package/dist/security/index.d.ts.map +1 -1
  105. package/dist/security/index.js +1 -1
  106. package/dist/sets/index.d.ts +19 -0
  107. package/dist/sets/index.d.ts.map +1 -1
  108. package/dist/sleep/index.d.ts +20 -0
  109. package/dist/sleep/index.d.ts.map +1 -1
  110. package/dist/strings/index.d.ts +0 -52
  111. package/dist/strings/index.d.ts.map +1 -1
  112. package/dist/strings/index.js +1 -1
  113. package/dist/system/index.d.ts +18 -0
  114. package/dist/system/index.d.ts.map +1 -1
  115. package/dist/time/index.d.ts +23 -4
  116. package/dist/time/index.d.ts.map +1 -1
  117. package/dist/try/trycatch.d.ts +24 -0
  118. package/dist/try/trycatch.d.ts.map +1 -1
  119. package/dist/try/trycatch.test-d.d.ts +2 -0
  120. package/dist/try/trycatch.test-d.d.ts.map +1 -0
  121. package/dist/try/trycatch.test-d.js +1 -0
  122. package/dist/types/index.d.ts +0 -1
  123. package/dist/url/index.d.ts +23 -0
  124. package/dist/url/index.d.ts.map +1 -1
  125. package/dist/url/index.js +1 -1
  126. package/dist/uuid/index.d.ts +10 -0
  127. package/dist/uuid/index.d.ts.map +1 -1
  128. package/dist/validation/index.d.ts +25 -0
  129. package/dist/validation/index.d.ts.map +1 -1
  130. package/dist/validation/index.js +1 -1
  131. package/package.json +25 -23
  132. package/dist/chunks/chunk-6J7HIWG6.js +0 -1
  133. package/dist/formatting/index.d.ts +0 -40
  134. package/dist/formatting/index.d.ts.map +0 -1
  135. package/dist/formatting/index.js +0 -1
  136. package/dist/math/index.d.ts +0 -37
  137. package/dist/math/index.d.ts.map +0 -1
  138. package/dist/math/index.js +0 -1
  139. package/dist/try/attempt.d.ts +0 -8
  140. package/dist/try/attempt.d.ts.map +0 -1
  141. package/dist/try/attempt.js +0 -1
package/AGENTS.md ADDED
@@ -0,0 +1,116 @@
1
+ <!-- Generated from skills/js-common/SKILL.md by scripts/sync-agents.mjs — do not edit by hand; run `pnpm sync:agents`. -->
2
+
3
+ # Using @rtorcato/js-common
4
+
5
+ `@rtorcato/js-common` is a tree-shakeable, ESM-only utility library for Node.js >= 22 (TypeScript-first, JSDoc on every public API). One subpath export per concern — 44 of them. The package root is intentionally empty.
6
+
7
+ ## Rules
8
+
9
+ 1. **Import from the subpath, never the package root.** The root export has no runtime code at all, so a root import gets you nothing.
10
+
11
+ ```ts
12
+ // ✅ do
13
+ import { slugify } from '@rtorcato/js-common/strings'
14
+ // ❌ don't — the root export is empty by design
15
+ import { slugify } from '@rtorcato/js-common'
16
+ ```
17
+
18
+ 2. **Every export name has exactly one home.** No name is exported from two modules, so the module → exports table below is the only lookup you need. These pairs still read alike — check which one you want:
19
+
20
+ | Looks like | Actually |
21
+ |---|---|
22
+ | `functions.once` vs `events.onceEvent` | `once(fn)` memoises a call; `onceEvent(target, type)` awaits a DOM event |
23
+ | `logger` vs `logging` | `logger` is one configured pino instance; `logging` is `console` helpers with no runtime deps |
24
+ | `try.tryCatch` vs `errors.tryWithFallback` | Result object vs swallow-and-fall-back — see rule 3 |
25
+ | `date.formatDate` vs `datetime.formatDateTimeLocal` | a UTC calendar day vs a local instant |
26
+
27
+ Where two modules used to ship the same name, the loser was deleted rather than aliased — [MODULE-BOUNDARIES.md](https://github.com/rtorcato/js-common/blob/main/MODULE-BOUNDARIES.md) records which side won and why.
28
+
29
+ 3. **Three different error idioms — don't mix them up.**
30
+
31
+ ```ts
32
+ // Result object — preferred for new code
33
+ import { tryCatch } from '@rtorcato/js-common/try'
34
+ const { data, error } = await tryCatch(() => fetchData())
35
+
36
+ // Go-style tuple: [error, data] — error FIRST
37
+ import { to } from '@rtorcato/js-common/promises'
38
+ const [err, value] = await to(fetchData())
39
+
40
+ // Swallows the error and returns a fallback — only when the fallback is genuinely safe
41
+ import { tryWithFallback } from '@rtorcato/js-common/errors'
42
+ const rows = await tryWithFallback(() => fetchRows(), [])
43
+ ```
44
+
45
+ `tryCatch` in `errors` was renamed to `tryWithFallback` in 2.0; `tryCatch` now means the Result helper in `try`.
46
+
47
+ 4. **Node-only modules.** `crypto`, `security`, and `file` import `node:crypto` / `node:fs`; `logger` uses pino. Don't ship these to a browser bundle. `events` is the opposite — it wraps DOM `EventTarget` and is browser-only. `os`, `process`, `node`, and `system` probe for `process` / `window` and degrade rather than throw.
48
+
49
+ 5. **`./types` is types-only.** It has a `types` field and no `import` field on purpose — use it for `import type { Prettify, Merge } from '@rtorcato/js-common/types'` only.
50
+
51
+ 6. **Some helpers need a peer input you must supply.** `convertCurrency(amount, rate)` takes the rate — the library does no FX lookup. `checkEnv(schema)` needs a Zod object schema and throws with the list of missing keys.
52
+
53
+ ## Module → exports
54
+
55
+ Import from `@rtorcato/js-common/<module>`.
56
+
57
+ | Module | Exports |
58
+ |---|---|
59
+ | abortController | createAbortController, abortPromise, withAbort, abortAfter |
60
+ | arrays | first, last, unique, flatten, chunk, compact, shuffle, groupBy |
61
+ | boolean | toBoolean, xor, and, or, not |
62
+ | colors | randomColor, matchingTextColor, hexToRgb, rgbToHex, isValidHex, darken, lighten |
63
+ | console | disableConsole, clearConsole |
64
+ | crypto | hashString, randomHex, hmacHash, base64Encode, base64Decode |
65
+ | currency | getCurrencySymbol, getCurrencyName, getCurrencyLocale, formatPrice, formatPriceCompact, parsePrice, parseCurrencyString, convertCurrency, isValidCurrencyCode, isValidCurrency |
66
+ | date | today, parseDate, formatDate, daysBetween, diffInHours, diffInMinutes, isLeapYear, addDays, subDays, addMonths, getDayOfWeek, startOfDay, endOfDay, isWeekend, isSameDay, isToday, formatRelative |
67
+ | datetime | nowIso, parseIsoDateTime, formatDateTimeLocal, getTimezoneOffset, toUtcDate, unixTimestamp, unixMillis, getIsoWeek, getIsoWeekInfo |
68
+ | emails | isValidEmail, normalizeEmail, maskEmail, getEmailDomain, isFreeEmailProvider |
69
+ | env | getENV, isDev, isProd, isTest, getNodeEnv, checkEnv, RootApiEnvSchema |
70
+ | errors | createCustomError, isErrorName, getErrorMessage, assert, tryWithFallback |
71
+ | events | on, emit, onceEvent, preventDefault, stopPropagation |
72
+ | fetch | fetchWithTimeout, postJson, getJson, fetchText, handleApiError |
73
+ | file | readFileAsString, writeFileAsString, fileExists, deleteFile, getFileExtension |
74
+ | functions | once, debounce, throttle, compose, pipe |
75
+ | geometry | distance2D, midpoint2D, angle2D, pointInRect, rectsOverlap, polygonArea |
76
+ | html | escapeHtml, unescapeHtml, stripHtmlTags, textToHtml, containsHtml |
77
+ | i18n | detectLanguage, formatNumber, formatDateI18n, t |
78
+ | interval | runInterval, clearIntervalById |
79
+ | json | safeJsonParse, safeJsonStringify, isValidJson, deepCloneJson |
80
+ | logger | logger (pino instance — pretty in dev, JSON in prod) |
81
+ | logging | logWithTimestamp, info, warn, error, captureConsole, ConsoleLevel |
82
+ | maps | invertMap, mapValues, mergeMaps, objectToMap, mapToObject |
83
+ | mime-types | lookup, types, extensions, mimeTypes |
84
+ | node | nodeVersionCheck, getNodeMajorVersion, isNode, requireOptional |
85
+ | numbers | clamp, roundTo, formatPercent, isFiniteNumber, isInteger, between, sum, average, mod, min, max |
86
+ | objects | isPlainObject, deepMerge, omit, pick, deepClone |
87
+ | os | getOsPlatform, getOsRelease, getOsArch, getHomeDir, getTmpDir |
88
+ | process | getProcessId, getProcessUptime, getCwd, getProcessPlatform, exitProcess, isCI |
89
+ | promises | delay, to, withTimeout, all, allSettled, race |
90
+ | random | randomInt, randomFloat, randomBool, randomElement, randomString |
91
+ | regex | escapeRegExp, testRegex, matchAll, replaceAllRegex, splitByRegex |
92
+ | security | stripScriptish, isStrongPassword, generateSecureToken |
93
+ | sets | union, intersection, difference, isSubset, isSuperset, setToArray, arrayToSet |
94
+ | sleep | sleep, sleepSync, sleepRandom, sleepWithAbort |
95
+ | strings | titleCase, capitalize, camelCase, kebabCase, snakeCase, truncate, padStart, padEnd, replaceString, reverse, slugify, words, wordCount, pluralize, ordinalize, isBlank, mask, template |
96
+ | system | isMacOs, isWindows, isLinux, isIOS, isAndroid, getPlatform, isTouchDevice |
97
+ | time | nowTime, nowTimeShort, parseTime, formatTime, secondsBetween, pad2 |
98
+ | try | tryCatch, isSuccess, Result, Success, Failure |
99
+ | types | Prettify, Merge (types only) |
100
+ | url | isValidUrl, getQueryParams, setQueryParam, removeQueryParam, joinUrl, getHostname |
101
+ | uuid | getUUID, getUUIDv7, getShortUUID, toShortUUID, fromShortUUID, isUUID, isUUIDv4, isNilUUID, getUUIDVersion, getNilUUID, uuidToBytes, bytesToUUID |
102
+ | validation | isDefined, isString, isNumber, isBoolean, isArray, isObject, isEmail, isUrl |
103
+
104
+ ## CLI
105
+
106
+ The package also ships a binary for shell use:
107
+
108
+ ```bash
109
+ npx @rtorcato/js-common@latest date today
110
+ npx @rtorcato/js-common@latest math sum 1 2 3
111
+ npx @rtorcato/js-common@latest text capitalize "hello world"
112
+ ```
113
+
114
+ See [CLI.md](https://github.com/rtorcato/js-common/blob/main/CLI.md) for the full command list.
115
+
116
+ For full signatures and examples, see <https://rtorcato.github.io/js-common/>.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,33 @@
1
+ # Changelog — frozen at 2.8.2
2
+
3
+ **This file stops at 2.8.2 and will not be updated again. It is not the place to
4
+ look for what shipped.**
5
+
6
+ [**GitHub Releases**](https://github.com/rtorcato/js-common/releases) is the
7
+ canonical record, and the [changelog page on the docs
8
+ site](https://rtorcato.github.io/js-common/docs/changelog) is built from it.
9
+
10
+ Why: `@semantic-release/git` is dropped from this repo's plugin list because the
11
+ `code-scanning-main` ruleset rejects the release commit with `GH013` — a commit
12
+ created seconds earlier can never have CodeQL results (see
13
+ [#153](https://github.com/rtorcato/js-common/issues/153) and
14
+ [#211](https://github.com/rtorcato/js-common/issues/211)). Nothing commits this
15
+ file back to `main`, so it holds whatever the last release with that plugin
16
+ wrote. The same applies to the `version` field in `package.json`.
17
+
18
+ Kept in the repo rather than deleted because GitHub Releases only go back to
19
+ v1.2.0 — the hand-written `1.0.0` section at the bottom is the only record of
20
+ that release, which is also why this file carries two formats.
21
+
22
+ ---
23
+
24
+ ## [2.8.2](https://github.com/rtorcato/js-common/compare/v2.8.1...v2.8.2) (2026-07-23)
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * **release:** upgrade npm for OIDC trusted publishing ([#110](https://github.com/rtorcato/js-common/issues/110)) ([93db8b5](https://github.com/rtorcato/js-common/commit/93db8b56041e916c63c1e250312e8527df931cc4))
30
+
1
31
  ## [2.8.1](https://github.com/rtorcato/js-common/compare/v2.8.0...v2.8.1) (2026-06-12)
2
32
 
3
33
 
package/CONTRIBUTORS.md CHANGED
@@ -31,6 +31,11 @@ We welcome contributions from everyone! Here's how you can help:
31
31
  8. Push to your branch (`git push origin feature/amazing-feature`)
32
32
  9. Open a Pull Request
33
33
 
34
+ Adding a helper? Check [MODULE-BOUNDARIES.md](MODULE-BOUNDARIES.md) first. It
35
+ records which module owns what, and the rule that no name is exported from two
36
+ modules — module paths are frozen, so a helper in the wrong place is expensive
37
+ to move later.
38
+
34
39
  ### 📝 Documentation
35
40
  - Improve README.md, code comments, or JSDoc
36
41
  - Add examples for existing functions
package/README.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # js-common
2
2
 
3
- ![js-common banner](./banner.png)
4
-
3
+ <p align="center">
4
+ <picture>
5
+ <source media="(max-width: 600px)" srcset="./brand/banner-mobile.png">
6
+ <img src="./brand/banner.png" alt="js-common — Tree-shakeable TypeScript utilities: tiny bundles, full type safety, CLI included." width="100%">
7
+ </picture>
8
+ </p>
9
+
10
+ [![CI](https://github.com/rtorcato/js-common/actions/workflows/ci.yml/badge.svg)](https://github.com/rtorcato/js-common/actions/workflows/ci.yml)
5
11
  [![npm version](https://badge.fury.io/js/%40rtorcato%2Fjs-common.svg)](https://badge.fury.io/js/%40rtorcato%2Fjs-common)
12
+ [![npm downloads](https://img.shields.io/npm/dm/@rtorcato/js-common.svg)](https://www.npmjs.com/package/@rtorcato/js-common)
13
+ [![Bundle size](https://img.shields.io/bundlephobia/minzip/@rtorcato/js-common)](https://bundlephobia.com/package/@rtorcato/js-common)
6
14
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
15
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
8
16
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.0.0-brightgreen.svg)](https://nodejs.org/)
@@ -18,7 +26,7 @@ A comprehensive set of common JavaScript and TypeScript utilities for Node.js pr
18
26
  - **Tree-shakeable** — import only what you need via subpath exports
19
27
  - **TypeScript** — full type definitions, JSDoc on every public API
20
28
  - **CLI included** — optional binary for use in scripts and terminals
21
- - **Modular** — one module per concern, 46 subpaths
29
+ - **Modular** — one module per concern, 44 subpaths
22
30
  - **Minimal runtime deps** — only `date-fns`, `date-fns-tz`, `luxon`, `pino`, `uuid`, `short-uuid`, `zod`. CLI packages (`chalk`, `commander`, `figlet`, …) are `optionalDependencies` and only needed for the CLI.
23
31
 
24
32
  ## Installation
@@ -27,11 +35,41 @@ A comprehensive set of common JavaScript and TypeScript utilities for Node.js pr
27
35
  npm install @rtorcato/js-common
28
36
  ```
29
37
 
30
- ## Migrating from 1.x to 2.x
38
+ Pre-release builds are published to the `beta` dist-tag, so they never reach a plain `npm install`:
39
+
40
+ ```bash
41
+ npm install @rtorcato/js-common@beta
42
+ ```
43
+
44
+ ## Use with AI
45
+
46
+ An agent skill lives in [`skills/js-common`](./skills/js-common/SKILL.md) — it teaches an AI coding agent the subpath-import rules, which same-named helper belongs to which module, the three error idioms, which modules are Node-only, and the full module → exports map.
47
+
48
+ **Claude Code** — this repo is its own plugin marketplace:
49
+
50
+ ```
51
+ /plugin marketplace add rtorcato/js-common
52
+ /plugin install js-common@js-common
53
+ ```
54
+
55
+ **Cursor, Copilot, Codex and other tools** — read [`AGENTS.md`](./AGENTS.md), the cross-tool convention file. It carries the same guidance and also ships in the npm tarball, so tools that scan `node_modules` find it at `node_modules/@rtorcato/js-common/AGENTS.md`.
31
56
 
32
- The only breaking change in 2.0 is a rename in the `errors` module:
57
+ **Any tool the [`skills`](https://www.npmjs.com/package/skills) CLI supports:**
58
+
59
+ ```bash
60
+ npx skills add https://github.com/rtorcato/js-common --skill js-common
61
+ ```
62
+
63
+ `AGENTS.md` is generated from `SKILL.md` by `pnpm sync:agents`; CI fails if they drift. Edit `SKILL.md`, never `AGENTS.md`.
64
+
65
+ ## Migrating
66
+
67
+ **2.x → 3.x** — every helper now has exactly one home. `./formatting` and `./math` are gone, and where two modules shipped the same name the loser was deleted rather than aliased, so you get a build error naming the fix. One helper was also renamed rather than moved: `sanitizeString` is now `stripScriptish`, because the old name promised sanitising it never did — it removes `<script>` blocks and inline `on*` handlers and nothing else. The full before/after map is in the [migration guide](https://rtorcato.github.io/js-common/docs/guides/migration); the reasoning is in [MODULE-BOUNDARIES.md](MODULE-BOUNDARIES.md).
68
+
69
+ **1.x → 2.x** — the only breaking change in 2.0 is a rename in the `errors` module:
33
70
 
34
71
  ```ts
72
+ // boundary-check: ignore — quotes the pre-2.0 API on purpose
35
73
  // 1.x — swallow errors and fall back to a default value
36
74
  import { tryCatch } from '@rtorcato/js-common/errors'
37
75
  const data = await tryCatch(() => fetchData(), [])
@@ -76,16 +114,16 @@ See [CLI.md](./CLI.md) for complete CLI documentation.
76
114
  ```typescript
77
115
  // Import specific modules (recommended for tree-shaking)
78
116
  import { formatDate, today, daysBetween } from '@rtorcato/js-common/date'
79
- import { validateEmail, isValidEmail } from '@rtorcato/js-common/emails'
80
- import { generateUUID, isValidUUID } from '@rtorcato/js-common/uuid'
117
+ import { isValidEmail, normalizeEmail } from '@rtorcato/js-common/emails'
118
+ import { getUUID, isUUID } from '@rtorcato/js-common/uuid'
81
119
  import { sum, average, roundTo } from '@rtorcato/js-common/numbers'
82
- import { capitalize, toTitleCase } from '@rtorcato/js-common/formatting'
120
+ import { capitalize, titleCase } from '@rtorcato/js-common/strings'
83
121
 
84
122
  // Example usage
85
123
  console.log(today()) // "2026-05-29"
86
124
  console.log(sum([1, 2, 3, 4, 5])) // 15
87
125
  console.log(capitalize('hello world')) // "Hello world"
88
- console.log(generateUUID()) // "f47ac10b-58cc-4372-a567-0e02b2c3d479"
126
+ console.log(getUUID()) // "f47ac10b-58cc-4372-a567-0e02b2c3d479"
89
127
  ```
90
128
 
91
129
  ### Bundle Size Impact
@@ -94,6 +132,10 @@ Each module is shipped as its own subpath export so bundlers only include what y
94
132
 
95
133
  ## Available Modules
96
134
 
135
+ Which module a helper belongs to — and why no helper is exported from two of
136
+ them — is recorded in [MODULE-BOUNDARIES.md](MODULE-BOUNDARIES.md). These paths
137
+ are frozen as of that record.
138
+
97
139
  ### Date & Time
98
140
  ```typescript
99
141
  import { today, formatDate, daysBetween, isLeapYear } from '@rtorcato/js-common/date'
@@ -102,29 +144,27 @@ import { nowTime, parseTime, secondsBetween } from '@rtorcato/js-common/time'
102
144
  import { runInterval, clearIntervalById } from '@rtorcato/js-common/interval'
103
145
  ```
104
146
 
105
- ### Numbers & Math
147
+ ### Numbers
106
148
  ```typescript
107
- import { sum, average, roundTo, clamp, getRandomInt } from '@rtorcato/js-common/numbers'
108
- import { add, subtract, multiply, divide } from '@rtorcato/js-common/math'
149
+ import { sum, average, roundTo, clamp, formatPercent } from '@rtorcato/js-common/numbers'
109
150
  import { randomInt, randomFloat, randomBool, randomElement } from '@rtorcato/js-common/random'
110
151
  ```
111
152
 
112
153
  ### Text & Strings
113
154
  ```typescript
114
- import { capitalize, toTitleCase, padZero } from '@rtorcato/js-common/formatting'
115
- import { slugify, truncate, removeEmojis } from '@rtorcato/js-common/strings'
155
+ import { slugify, truncate, capitalize, titleCase, pluralize } from '@rtorcato/js-common/strings'
116
156
  import { escapeHtml, unescapeHtml, stripHtmlTags } from '@rtorcato/js-common/html'
117
157
  import { escapeRegExp, matchAll, replaceAllRegex } from '@rtorcato/js-common/regex'
118
- import { detectLanguage, formatNumber, t, pluralize } from '@rtorcato/js-common/i18n'
158
+ import { detectLanguage, formatNumber, formatDateI18n, t } from '@rtorcato/js-common/i18n'
119
159
  ```
120
160
 
121
161
  ### Security & Validation
122
162
  ```typescript
123
163
  import { isStrongPassword, generateSecureToken } from '@rtorcato/js-common/security'
124
- import { isValidEmail, validateEmail } from '@rtorcato/js-common/emails'
164
+ import { isValidEmail, maskEmail } from '@rtorcato/js-common/emails'
125
165
  import { isValidUrl } from '@rtorcato/js-common/url'
126
- import { isString, isNumber, isArray, isObject } from '@rtorcato/js-common/validation'
127
- import { toBoolean, isBoolean, xor } from '@rtorcato/js-common/boolean'
166
+ import { isString, isNumber, isBoolean, isArray, isObject } from '@rtorcato/js-common/validation'
167
+ import { toBoolean, and, or, not, xor } from '@rtorcato/js-common/boolean'
128
168
  ```
129
169
 
130
170
  ### Data Structures
@@ -138,7 +178,7 @@ import { union, intersection, difference, isSubset } from '@rtorcato/js-common/s
138
178
 
139
179
  ### Async & Control Flow
140
180
  ```typescript
141
- import { delay, timeout, retry } from '@rtorcato/js-common/promises'
181
+ import { delay, withTimeout, allSettled } from '@rtorcato/js-common/promises'
142
182
  import { debounce, throttle, once } from '@rtorcato/js-common/functions'
143
183
  import { sleep } from '@rtorcato/js-common/sleep'
144
184
  import { tryCatch } from '@rtorcato/js-common/try'
@@ -166,7 +206,7 @@ import { info, warn, error, captureConsole } from '@rtorcato/js-common/logging'
166
206
  - `colors` — color manipulation and conversion
167
207
  - `crypto` — cryptographic helpers
168
208
  - `currency` — currency formatting and conversion
169
- - `events` — event emitter utilities
209
+ - `events` — DOM event helpers (`on`, `emit`, `onceEvent`)
170
210
  - `fetch` — HTTP request helpers
171
211
  - `file` — file system helpers
172
212
  - `geometry` — 2D geometry calculations
@@ -209,10 +249,10 @@ pnpm run benchmark
209
249
 
210
250
  ## Documentation site
211
251
 
212
- The full documentation site lives in [`apps/doc`](./apps/doc) and is built with [Astro Starlight](https://starlight.astro.build/). It auto-deploys to GitHub Pages on every push to `main` that touches `apps/doc/**`.
252
+ The full documentation site lives in [`apps/docs`](./apps/docs) and is built with [Docusaurus](https://docusaurus.io/). It auto-deploys to GitHub Pages on every push to `main` that touches `apps/docs/**`.
213
253
 
214
254
  ```bash
215
- # Run the docs locally (http://localhost:4321/js-common)
255
+ # Run the docs locally (http://localhost:3000/js-common/)
216
256
  pnpm --filter @rtorcato/js-common-docs dev
217
257
 
218
258
  # Build the static site
@@ -221,6 +261,10 @@ pnpm --filter @rtorcato/js-common-docs build
221
261
 
222
262
  Live site: <https://rtorcato.github.io/js-common/>
223
263
 
264
+ ## Roadmap
265
+
266
+ Direction and progress are tracked entirely on GitHub — see the [milestones](https://github.com/rtorcato/js-common/milestones) for the stages (shipped 2.x modules, the beta npm preview, the v1.0 stable API) and the [open issues](https://github.com/rtorcato/js-common/issues) for day-to-day work.
267
+
224
268
  ## Contributing
225
269
 
226
270
  Contributions are welcome! Please read [CONTRIBUTORS.md](./CONTRIBUTORS.md) for guidelines.
@@ -231,6 +275,11 @@ Contributions are welcome! Please read [CONTRIBUTORS.md](./CONTRIBUTORS.md) for
231
275
  4. Run the test suite: `pnpm test`
232
276
  5. Submit a pull request
233
277
 
278
+ ## Related packages
279
+
280
+ - [@rtorcato/browser-common](https://github.com/rtorcato/browser-common) — Browser Web API wrappers (clipboard, observers, storage, etc.)
281
+ - [@rtorcato/repo-tooling](https://github.com/rtorcato/repo-tooling) — Project scaffolding for TypeScript libraries (Biome, Vitest, Husky, semantic-release)
282
+
234
283
  ## License
235
284
 
236
285
  MIT © [Richard Torcato](https://github.com/rtorcato)
@@ -1,5 +1,14 @@
1
1
  /**
2
2
  * Creates a new AbortController and returns its controller and signal.
3
+ *
4
+ * @example
5
+ * ```typescript
6
+ * const { controller, signal } = createAbortController()
7
+ * signal.aborted // false
8
+ * controller.abort()
9
+ * signal.aborted // true
10
+ * ```
11
+ *
3
12
  * @returns {{ controller: AbortController, signal: AbortSignal }}
4
13
  */
5
14
  export declare function createAbortController(): {
@@ -8,12 +17,33 @@ export declare function createAbortController(): {
8
17
  };
9
18
  /**
10
19
  * Returns a promise that rejects when the given AbortSignal is aborted.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const { controller, signal } = createAbortController()
24
+ * const pending = abortPromise(signal)
25
+ * controller.abort()
26
+ * await pending.catch((err) => err.name) // 'AbortError'
27
+ * ```
28
+ *
11
29
  * @param signal The AbortSignal to listen to.
12
30
  * @returns {Promise<never>}
13
31
  */
14
32
  export declare function abortPromise(signal: AbortSignal): Promise<never>;
15
33
  /**
16
34
  * Wraps a promise and rejects it if the signal is aborted.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const { controller, signal } = createAbortController()
39
+ * const slow = new Promise<string>((resolve) => setTimeout(() => resolve('done'), 50))
40
+ *
41
+ * await withAbort(slow, signal) // 'done'
42
+ *
43
+ * controller.abort()
44
+ * await withAbort(slow, signal).catch((err) => err.name) // 'AbortError'
45
+ * ```
46
+ *
17
47
  * @param promise The promise to wrap.
18
48
  * @param signal The AbortSignal.
19
49
  * @returns {Promise<T>}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/abortController/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,qBAAqB;;;EAGpC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,CAchE;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAEjF;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAEjG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/abortController/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB;;;EAGpC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,CAchE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAEjF;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAEjG"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=arrays.test-d.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arrays.test-d.d.ts","sourceRoot":"","sources":["../../src/arrays/arrays.test-d.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ import{a as n,b as a,c as t,e as u,h as s}from"../chunks/chunk-5IA77ERW.js";import"../chunks/chunk-ANN5UYCF.js";import{describe as o,expectTypeOf as e,it as r}from"vitest";o("arrays \u2014 types",()=>{r("first/last preserve element type and add undefined",()=>{e(n([1,2,3])).toEqualTypeOf(),e(n(["a","b"])).toEqualTypeOf(),e(a([{id:1}])).toEqualTypeOf()}),r("unique preserves element type",()=>{e(t([1,2,3])).toEqualTypeOf(),e(t(["a"])).toEqualTypeOf()}),r("chunk returns array of arrays of the element type",()=>{e(u([1,2,3],2)).toEqualTypeOf(),e(u(["a","b"],1)).toEqualTypeOf()}),r("groupBy preserves element type in the grouped arrays",()=>{let i=s([{kind:"a",n:1}],p=>p.kind);e(i).toEqualTypeOf()})});
@@ -1 +1 @@
1
- import"../chunks/chunk-ANN5UYCF.js";function u(n){return n[0]}function T(n){return n.length?n[n.length-1]:void 0}function f(n){return Array.from(new Set(n))}function i(n){return n.flat()}function l(n,e){if(e<1)throw new Error("Chunk size must be at least 1");let t=[];for(let r=0;r<n.length;r+=e)t.push(n.slice(r,r+e));return t}function s(n){return n.filter(Boolean)}function c(n){let e=n.slice();for(let t=e.length-1;t>0;t--){let r=Math.floor(Math.random()*(t+1));[e[t],e[r]]=[e[r],e[t]]}return e}function h(n,e){return n.reduce((t,r)=>{let o=e(r);return t[o]||(t[o]=[]),t[o].push(r),t},{})}export{l as chunk,s as compact,u as first,i as flatten,h as groupBy,T as last,c as shuffle,f as unique};
1
+ import{a,b,c,d,e,f,g,h}from"../chunks/chunk-5IA77ERW.js";import"../chunks/chunk-ANN5UYCF.js";export{e as chunk,f as compact,a as first,d as flatten,h as groupBy,b as last,g as shuffle,c as unique};
@@ -1,18 +1,30 @@
1
1
  /**
2
2
  * Converts a value to a boolean.
3
3
  * Accepts 'true', '1', 1 as true; 'false', '0', 0 as false.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * toBoolean('true') // true
8
+ * toBoolean('0') // false
9
+ * toBoolean(1) // true
10
+ * toBoolean(2) // false
11
+ * toBoolean(null) // false
12
+ * ```
13
+ *
4
14
  * @param value The value to convert.
5
15
  * @returns The boolean representation.
6
16
  */
7
17
  export declare function toBoolean(value: unknown): boolean;
8
- /**
9
- * Checks if a value is a boolean.
10
- * @param value The value to check.
11
- * @returns True if the value is a boolean, false otherwise.
12
- */
13
- export declare function isBoolean(value: unknown): value is boolean;
14
18
  /**
15
19
  * Returns the logical exclusive OR (XOR) of two booleans.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * xor(true, false) // true
24
+ * xor(true, true) // false
25
+ * xor(false, false) // false
26
+ * ```
27
+ *
16
28
  * @param a First boolean.
17
29
  * @param b Second boolean.
18
30
  * @returns True if exactly one of a or b is true.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/boolean/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWjD;AAED;;;;GAIG;AAEH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAElD;AAED;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAEvC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/boolean/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWjD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAElD;AAED;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAEvC"}
@@ -1 +1 @@
1
- import"../chunks/chunk-ANN5UYCF.js";function e(o){if(typeof o=="boolean")return o;if(typeof o=="string"){let n=o.trim().toLowerCase();if(n==="true"||n==="1")return!0;if(n==="false"||n==="0")return!1}return typeof o=="number"?o===1:!!o}function r(o){return typeof o=="boolean"}function t(o,n){return!!(o?!n:n)}function a(o,n){return o&&n}function f(o,n){return o||n}function u(o){return!o}export{a as and,r as isBoolean,u as not,f as or,e as toBoolean,t as xor};
1
+ import"../chunks/chunk-ANN5UYCF.js";function e(o){if(typeof o=="boolean")return o;if(typeof o=="string"){let n=o.trim().toLowerCase();if(n==="true"||n==="1")return!0;if(n==="false"||n==="0")return!1}return typeof o=="number"?o===1:!!o}function r(o,n){return!!(o?!n:n)}function t(o,n){return o&&n}function f(o,n){return o||n}function a(o){return!o}export{t as and,a as not,f as or,e as toBoolean,r as xor};
@@ -0,0 +1 @@
1
+ function u(n){return n[0]}function T(n){return n.length?n[n.length-1]:void 0}function f(n){return Array.from(new Set(n))}function i(n){return n.flat()}function l(n,e){if(e<1)throw new Error("Chunk size must be at least 1");let t=[];for(let r=0;r<n.length;r+=e)t.push(n.slice(r,r+e));return t}function s(n){return n.filter(Boolean)}function c(n){let e=n.slice();for(let t=e.length-1;t>0;t--){let r=Math.floor(Math.random()*(t+1));[e[t],e[r]]=[e[r],e[t]]}return e}function h(n,e){return n.reduce((t,r)=>{let o=e(r);return t[o]||(t[o]=[]),t[o].push(r),t},{})}export{u as a,T as b,f as c,i as d,l as e,s as f,c as g,h};
@@ -0,0 +1 @@
1
+ function i(e){return new Promise(r=>setTimeout(r,e))}async function u(e){try{return[null,await e]}catch(r){return[r,void 0]}}function s(e,r,t=new Error("Timeout")){return Promise.race([e,new Promise((n,o)=>setTimeout(()=>o(t),r))])}function m(e){return Promise.all(e)}function T(e){return Promise.allSettled(e)}function P(e){return Promise.race(e)}export{i as a,u as b,s as c,m as d,T as e,P as f};
@@ -0,0 +1 @@
1
+ function r(n){return Object.prototype.toString.call(n)==="[object Object]"}function s(n,o){let t={...n};for(let e in o)r(o[e])&&r(t[e])?t[e]=s(t[e],o[e]):t[e]=o[e];return t}function c(n,o){let t={...n};for(let e of o)delete t[e];return t}function i(n,o){let t={};for(let e of o)e in n&&(t[e]=n[e]);return t}function T(n){return structuredClone(n)}export{r as a,s as b,c,i as d,T as e};
@@ -0,0 +1 @@
1
+ import{b as e}from"./chunk-FBDKWWIQ.js";import{createRequire as t}from"node:module";import r from"pino";function o(){try{return t(import.meta.url).resolve("pino-pretty"),!0}catch{return!1}}var s=e()&&o()?r({level:"debug",transport:{target:"pino-pretty",options:{colorize:!0,ignore:"pid,hostname,time",levelFirst:!1,singleLine:!0,customColors:"err:red,info:blue",translateTime:"HH:MM:ss"}}}):r({level:"info"});export{s as a};
@@ -0,0 +1 @@
1
+ function o(n){let t=!1,e;return function(...r){return t||(t=!0,e=n.apply(this,r)),e}}function i(n,t){let e;return function(...r){clearTimeout(e),e=setTimeout(()=>n.apply(this,r),t)}}function u(n,t){let e=0;return function(...r){let a=Date.now();a-e>=t&&(e=a,n.apply(this,r))}}function T(...n){return t=>n.reduceRight((e,r)=>r(e),t)}function s(...n){return t=>n.reduce((e,r)=>r(e),t)}export{o as a,i as b,u as c,T as d,s as e};
@@ -0,0 +1,131 @@
1
+ // Catalog of the functions the CLI advertises, used by `list`, `interactive`
2
+ // and `add`.
3
+ //
4
+ // Three names per entry, and they are deliberately different things:
5
+ // - `name` the CLI verb (`js-common math avg`) — part of the CLI surface
6
+ // - `exportName` the real library export (`average`) — what `add` must print
7
+ // - `subpath` the published module the export lives in (`numbers`)
8
+ //
9
+ // `subpath` is per entry, not per category: the CLI groups by task ('math',
10
+ // 'system') while the library groups by module — 'math' spans `numbers` and
11
+ // `random`, 'system' spans `process` and `node`.
12
+ //
13
+ // `src/cli/catalog.test.ts` asserts both halves, so this cannot drift back into
14
+ // fiction: every `exportName` really is exported by `src/<subpath>/index.ts`,
15
+ // and every `name` really is a command registered in `src/cli/cli.ts`. The
16
+ // second check exists because it wasn't there — `file`, `security`, `validate`,
17
+ // `text pad` and `system ci` were advertised here for long enough that `list`
18
+ // printed commands that errored when run.
19
+ export const functionCategories = {
20
+ date: {
21
+ name: '📅 Date & Time',
22
+ functions: [
23
+ {
24
+ name: 'today',
25
+ description: "Get today's date (YYYY-MM-DD)",
26
+ exportName: 'today',
27
+ subpath: 'date',
28
+ example: `const date = today() // '${new Date().toISOString().slice(0, 10)}'`,
29
+ },
30
+ {
31
+ name: 'now',
32
+ description: 'Get current timestamp',
33
+ exportName: 'nowIso',
34
+ subpath: 'datetime',
35
+ example: `const timestamp = nowIso() // '${new Date().toISOString()}'`,
36
+ },
37
+ {
38
+ name: 'between',
39
+ description: 'Calculate days between dates',
40
+ exportName: 'daysBetween',
41
+ subpath: 'date',
42
+ example: `const days = daysBetween('2023-01-01', '2023-12-31') // 364`,
43
+ },
44
+ ],
45
+ },
46
+ math: {
47
+ name: '🔢 Mathematical',
48
+ functions: [
49
+ {
50
+ name: 'sum',
51
+ description: 'Calculate sum of numbers',
52
+ exportName: 'sum',
53
+ subpath: 'numbers',
54
+ example: `const total = sum([1, 2, 3, 4, 5]) // 15`,
55
+ },
56
+ {
57
+ name: 'avg',
58
+ description: 'Calculate average of numbers',
59
+ exportName: 'average',
60
+ subpath: 'numbers',
61
+ example: `const mean = average([10, 20, 30]) // 20`,
62
+ },
63
+ {
64
+ name: 'random',
65
+ description: 'Generate random number',
66
+ exportName: 'randomInt',
67
+ subpath: 'random',
68
+ example: `const num = randomInt(1, 100) // 42`,
69
+ },
70
+ {
71
+ name: 'round',
72
+ description: 'Round to decimal places',
73
+ exportName: 'roundTo',
74
+ subpath: 'numbers',
75
+ example: `const rounded = roundTo(3.14159, 2) // 3.14`,
76
+ },
77
+ {
78
+ name: 'clamp',
79
+ description: 'Clamp between min/max',
80
+ exportName: 'clamp',
81
+ subpath: 'numbers',
82
+ example: `const clamped = clamp(150, 0, 100) // 100`,
83
+ },
84
+ ],
85
+ },
86
+ text: {
87
+ name: '📝 Text Formatting',
88
+ functions: [
89
+ {
90
+ name: 'capitalize',
91
+ description: 'Capitalize first letter',
92
+ exportName: 'capitalize',
93
+ subpath: 'strings',
94
+ example: `const text = capitalize('hello world') // 'Hello world'`,
95
+ },
96
+ {
97
+ name: 'title',
98
+ description: 'Convert to title case',
99
+ exportName: 'titleCase',
100
+ subpath: 'strings',
101
+ example: `const heading = titleCase('hello world') // 'Hello World'`,
102
+ },
103
+ ],
104
+ },
105
+ system: {
106
+ name: '💻 System Info',
107
+ functions: [
108
+ {
109
+ name: 'pid',
110
+ description: 'Get process ID',
111
+ exportName: 'getProcessId',
112
+ subpath: 'process',
113
+ example: `const processId = getProcessId() // 12345`,
114
+ },
115
+ {
116
+ name: 'uptime',
117
+ description: 'Get process uptime',
118
+ exportName: 'getProcessUptime',
119
+ subpath: 'process',
120
+ example: `const uptime = getProcessUptime() // 123.45`,
121
+ },
122
+ {
123
+ name: 'node-version',
124
+ description: 'Get Node.js version',
125
+ exportName: 'getNodeMajorVersion',
126
+ subpath: 'node',
127
+ example: `const major = getNodeMajorVersion() // 22`,
128
+ },
129
+ ],
130
+ },
131
+ };