@vielzeug/arsenal 1.1.2 → 2.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 (294) hide show
  1. package/README.md +31 -75
  2. package/dist/_dev.cjs +1 -1
  3. package/dist/_dev.cjs.map +1 -1
  4. package/dist/_dev.js +2 -5
  5. package/dist/_dev.js.map +1 -1
  6. package/dist/array/chunk.cjs +1 -1
  7. package/dist/array/chunk.cjs.map +1 -1
  8. package/dist/array/chunk.d.ts +2 -1
  9. package/dist/array/chunk.d.ts.map +1 -1
  10. package/dist/array/chunk.js +5 -7
  11. package/dist/array/chunk.js.map +1 -1
  12. package/dist/array/compare.cjs +1 -1
  13. package/dist/array/compare.cjs.map +1 -1
  14. package/dist/array/compare.d.ts.map +1 -1
  15. package/dist/array/compare.js +13 -14
  16. package/dist/array/compare.js.map +1 -1
  17. package/dist/array/index.cjs +1 -0
  18. package/dist/array/index.js +29 -0
  19. package/dist/array/search.cjs +1 -1
  20. package/dist/array/search.cjs.map +1 -1
  21. package/dist/array/search.d.ts +8 -77
  22. package/dist/array/search.d.ts.map +1 -1
  23. package/dist/array/search.js +21 -38
  24. package/dist/array/search.js.map +1 -1
  25. package/dist/array/sort.cjs +1 -1
  26. package/dist/array/sort.cjs.map +1 -1
  27. package/dist/array/sort.d.ts +1 -1
  28. package/dist/array/sort.d.ts.map +1 -1
  29. package/dist/array/sort.js +9 -10
  30. package/dist/array/sort.js.map +1 -1
  31. package/dist/arsenal.cjs +1 -1
  32. package/dist/arsenal.cjs.map +1 -1
  33. package/dist/arsenal.iife.js +1 -1
  34. package/dist/arsenal.iife.js.map +1 -1
  35. package/dist/arsenal.js +1 -1
  36. package/dist/arsenal.js.map +1 -1
  37. package/dist/async/attempt.cjs +1 -1
  38. package/dist/async/attempt.cjs.map +1 -1
  39. package/dist/async/attempt.d.ts +3 -8
  40. package/dist/async/attempt.d.ts.map +1 -1
  41. package/dist/async/attempt.js +3 -10
  42. package/dist/async/attempt.js.map +1 -1
  43. package/dist/async/index.cjs +1 -0
  44. package/dist/async/index.d.ts +1 -1
  45. package/dist/async/index.d.ts.map +1 -1
  46. package/dist/async/index.js +9 -0
  47. package/dist/async/parallel.cjs +1 -1
  48. package/dist/async/parallel.cjs.map +1 -1
  49. package/dist/async/parallel.d.ts +1 -1
  50. package/dist/async/parallel.d.ts.map +1 -1
  51. package/dist/async/parallel.js +15 -16
  52. package/dist/async/parallel.js.map +1 -1
  53. package/dist/async/retry.cjs +1 -1
  54. package/dist/async/retry.cjs.map +1 -1
  55. package/dist/async/retry.d.ts +1 -1
  56. package/dist/async/retry.d.ts.map +1 -1
  57. package/dist/async/retry.js +14 -15
  58. package/dist/async/retry.js.map +1 -1
  59. package/dist/async/taskPool.cjs +2 -0
  60. package/dist/async/taskPool.cjs.map +1 -0
  61. package/dist/async/taskPool.d.ts +14 -0
  62. package/dist/async/taskPool.d.ts.map +1 -0
  63. package/dist/async/taskPool.js +51 -0
  64. package/dist/async/taskPool.js.map +1 -0
  65. package/dist/cache/cache.cjs +2 -0
  66. package/dist/cache/cache.cjs.map +1 -0
  67. package/dist/cache/cache.d.ts +17 -0
  68. package/dist/cache/cache.d.ts.map +1 -0
  69. package/dist/cache/cache.js +59 -0
  70. package/dist/cache/cache.js.map +1 -0
  71. package/dist/cache/index.cjs +1 -0
  72. package/dist/cache/index.d.ts +1 -1
  73. package/dist/cache/index.d.ts.map +1 -1
  74. package/dist/cache/index.js +3 -0
  75. package/dist/cache/memo.cjs +1 -1
  76. package/dist/cache/memo.cjs.map +1 -1
  77. package/dist/cache/memo.d.ts +2 -2
  78. package/dist/cache/memo.js +24 -23
  79. package/dist/cache/memo.js.map +1 -1
  80. package/dist/errors.cjs +1 -1
  81. package/dist/errors.cjs.map +1 -1
  82. package/dist/errors.d.ts +0 -7
  83. package/dist/errors.d.ts.map +1 -1
  84. package/dist/errors.js +2 -2
  85. package/dist/errors.js.map +1 -1
  86. package/dist/function/debounce.cjs +1 -1
  87. package/dist/function/debounce.cjs.map +1 -1
  88. package/dist/function/debounce.js +15 -17
  89. package/dist/function/debounce.js.map +1 -1
  90. package/dist/function/index.cjs +1 -0
  91. package/dist/function/index.d.ts +0 -2
  92. package/dist/function/index.d.ts.map +1 -1
  93. package/dist/function/index.js +8 -0
  94. package/dist/function/pipe.cjs +1 -1
  95. package/dist/function/pipe.cjs.map +1 -1
  96. package/dist/function/pipe.js +1 -1
  97. package/dist/function/pipe.js.map +1 -1
  98. package/dist/guards/index.cjs +1 -0
  99. package/dist/guards/index.d.ts +0 -3
  100. package/dist/guards/index.d.ts.map +1 -1
  101. package/dist/guards/index.js +17 -0
  102. package/dist/guards/isEqual.cjs.map +1 -1
  103. package/dist/guards/isEqual.js.map +1 -1
  104. package/dist/index.cjs +1 -1
  105. package/dist/index.d.ts +2 -91
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +30 -118
  108. package/dist/math/allocate.cjs +1 -1
  109. package/dist/math/allocate.cjs.map +1 -1
  110. package/dist/math/allocate.d.ts +1 -1
  111. package/dist/math/allocate.d.ts.map +1 -1
  112. package/dist/math/allocate.js +13 -14
  113. package/dist/math/allocate.js.map +1 -1
  114. package/dist/math/index.cjs +1 -0
  115. package/dist/math/index.d.ts +0 -3
  116. package/dist/math/index.d.ts.map +1 -1
  117. package/dist/math/index.js +17 -0
  118. package/dist/math/mod.cjs +1 -1
  119. package/dist/math/mod.cjs.map +1 -1
  120. package/dist/math/mod.d.ts +1 -1
  121. package/dist/math/mod.d.ts.map +1 -1
  122. package/dist/math/mod.js +4 -5
  123. package/dist/math/mod.js.map +1 -1
  124. package/dist/math/range.cjs +1 -1
  125. package/dist/math/range.cjs.map +1 -1
  126. package/dist/math/range.d.ts +1 -1
  127. package/dist/math/range.d.ts.map +1 -1
  128. package/dist/math/range.js +14 -15
  129. package/dist/math/range.js.map +1 -1
  130. package/dist/math/sum.cjs +1 -1
  131. package/dist/math/sum.cjs.map +1 -1
  132. package/dist/math/sum.d.ts +2 -2
  133. package/dist/math/sum.d.ts.map +1 -1
  134. package/dist/math/sum.js +6 -7
  135. package/dist/math/sum.js.map +1 -1
  136. package/dist/object/getPath.cjs +1 -1
  137. package/dist/object/getPath.cjs.map +1 -1
  138. package/dist/object/getPath.d.ts +12 -42
  139. package/dist/object/getPath.d.ts.map +1 -1
  140. package/dist/object/getPath.js +21 -22
  141. package/dist/object/getPath.js.map +1 -1
  142. package/dist/object/index.cjs +1 -0
  143. package/dist/object/index.d.ts +1 -1
  144. package/dist/object/index.d.ts.map +1 -1
  145. package/dist/object/index.js +15 -0
  146. package/dist/object/merge.cjs +1 -1
  147. package/dist/object/merge.cjs.map +1 -1
  148. package/dist/object/merge.d.ts +1 -1
  149. package/dist/object/merge.d.ts.map +1 -1
  150. package/dist/object/merge.js +2 -3
  151. package/dist/object/merge.js.map +1 -1
  152. package/dist/object/prune.cjs +1 -1
  153. package/dist/object/prune.cjs.map +1 -1
  154. package/dist/object/prune.d.ts.map +1 -1
  155. package/dist/object/prune.js +22 -23
  156. package/dist/object/prune.js.map +1 -1
  157. package/dist/object/tryParseJson.cjs +2 -0
  158. package/dist/object/tryParseJson.cjs.map +1 -0
  159. package/dist/object/tryParseJson.d.ts +9 -0
  160. package/dist/object/tryParseJson.d.ts.map +1 -0
  161. package/dist/object/tryParseJson.js +18 -0
  162. package/dist/object/tryParseJson.js.map +1 -0
  163. package/dist/random/_index.cjs +2 -0
  164. package/dist/random/_index.cjs.map +1 -0
  165. package/dist/random/_index.d.ts +3 -0
  166. package/dist/random/_index.d.ts.map +1 -0
  167. package/dist/random/_index.js +10 -0
  168. package/dist/random/_index.js.map +1 -0
  169. package/dist/random/draw.cjs +1 -1
  170. package/dist/random/draw.cjs.map +1 -1
  171. package/dist/random/draw.d.ts +3 -2
  172. package/dist/random/draw.d.ts.map +1 -1
  173. package/dist/random/draw.js +12 -12
  174. package/dist/random/draw.js.map +1 -1
  175. package/dist/random/index.cjs +1 -0
  176. package/dist/random/index.d.ts +1 -0
  177. package/dist/random/index.d.ts.map +1 -1
  178. package/dist/random/index.js +5 -0
  179. package/dist/random/random.cjs +1 -1
  180. package/dist/random/random.cjs.map +1 -1
  181. package/dist/random/random.d.ts +2 -1
  182. package/dist/random/random.d.ts.map +1 -1
  183. package/dist/random/random.js +5 -6
  184. package/dist/random/random.js.map +1 -1
  185. package/dist/random/shuffle.cjs +1 -1
  186. package/dist/random/shuffle.cjs.map +1 -1
  187. package/dist/random/shuffle.d.ts +2 -1
  188. package/dist/random/shuffle.d.ts.map +1 -1
  189. package/dist/random/shuffle.js +7 -7
  190. package/dist/random/shuffle.js.map +1 -1
  191. package/dist/random/source.d.ts +4 -0
  192. package/dist/random/source.d.ts.map +1 -0
  193. package/dist/string/index.cjs +1 -0
  194. package/dist/string/index.d.ts +0 -2
  195. package/dist/string/index.d.ts.map +1 -1
  196. package/dist/string/index.js +12 -0
  197. package/dist/string/similarity.cjs +1 -1
  198. package/dist/string/similarity.cjs.map +1 -1
  199. package/dist/string/similarity.d.ts.map +1 -1
  200. package/dist/string/similarity.js +16 -17
  201. package/dist/string/similarity.js.map +1 -1
  202. package/dist/string/truncate.cjs +1 -1
  203. package/dist/string/truncate.cjs.map +1 -1
  204. package/dist/string/truncate.d.ts +2 -1
  205. package/dist/string/truncate.d.ts.map +1 -1
  206. package/dist/string/truncate.js +10 -11
  207. package/dist/string/truncate.js.map +1 -1
  208. package/dist/types.d.ts +1 -1
  209. package/dist/types.d.ts.map +1 -1
  210. package/package.json +11 -16
  211. package/dist/_common/_secureRandomIndex.cjs +0 -2
  212. package/dist/_common/_secureRandomIndex.cjs.map +0 -1
  213. package/dist/_common/_secureRandomIndex.d.ts +0 -2
  214. package/dist/_common/_secureRandomIndex.d.ts.map +0 -1
  215. package/dist/_common/_secureRandomIndex.js +0 -9
  216. package/dist/_common/_secureRandomIndex.js.map +0 -1
  217. package/dist/async/queue.cjs +0 -2
  218. package/dist/async/queue.cjs.map +0 -1
  219. package/dist/async/queue.d.ts +0 -42
  220. package/dist/async/queue.d.ts.map +0 -1
  221. package/dist/async/queue.js +0 -72
  222. package/dist/async/queue.js.map +0 -1
  223. package/dist/cache/stash.cjs +0 -2
  224. package/dist/cache/stash.cjs.map +0 -1
  225. package/dist/cache/stash.d.ts +0 -111
  226. package/dist/cache/stash.d.ts.map +0 -1
  227. package/dist/cache/stash.js +0 -92
  228. package/dist/cache/stash.js.map +0 -1
  229. package/dist/function/constant.cjs +0 -2
  230. package/dist/function/constant.cjs.map +0 -1
  231. package/dist/function/constant.d.ts +0 -5
  232. package/dist/function/constant.d.ts.map +0 -1
  233. package/dist/function/constant.js +0 -8
  234. package/dist/function/constant.js.map +0 -1
  235. package/dist/function/identity.cjs +0 -2
  236. package/dist/function/identity.cjs.map +0 -1
  237. package/dist/function/identity.d.ts +0 -5
  238. package/dist/function/identity.d.ts.map +0 -1
  239. package/dist/function/identity.js +0 -8
  240. package/dist/function/identity.js.map +0 -1
  241. package/dist/guards/isArray.cjs +0 -2
  242. package/dist/guards/isArray.cjs.map +0 -1
  243. package/dist/guards/isArray.d.ts +0 -16
  244. package/dist/guards/isArray.d.ts.map +0 -1
  245. package/dist/guards/isArray.js +0 -8
  246. package/dist/guards/isArray.js.map +0 -1
  247. package/dist/guards/isBoolean.cjs +0 -2
  248. package/dist/guards/isBoolean.cjs.map +0 -1
  249. package/dist/guards/isBoolean.d.ts +0 -23
  250. package/dist/guards/isBoolean.d.ts.map +0 -1
  251. package/dist/guards/isBoolean.js +0 -8
  252. package/dist/guards/isBoolean.js.map +0 -1
  253. package/dist/guards/isString.cjs +0 -2
  254. package/dist/guards/isString.cjs.map +0 -1
  255. package/dist/guards/isString.d.ts +0 -15
  256. package/dist/guards/isString.d.ts.map +0 -1
  257. package/dist/guards/isString.js +0 -8
  258. package/dist/guards/isString.js.map +0 -1
  259. package/dist/math/abs.cjs +0 -2
  260. package/dist/math/abs.cjs.map +0 -1
  261. package/dist/math/abs.d.ts +0 -17
  262. package/dist/math/abs.d.ts.map +0 -1
  263. package/dist/math/abs.js +0 -8
  264. package/dist/math/abs.js.map +0 -1
  265. package/dist/math/max.cjs +0 -2
  266. package/dist/math/max.cjs.map +0 -1
  267. package/dist/math/max.d.ts +0 -21
  268. package/dist/math/max.d.ts.map +0 -1
  269. package/dist/math/max.js +0 -12
  270. package/dist/math/max.js.map +0 -1
  271. package/dist/math/min.cjs +0 -2
  272. package/dist/math/min.cjs.map +0 -1
  273. package/dist/math/min.d.ts +0 -21
  274. package/dist/math/min.d.ts.map +0 -1
  275. package/dist/math/min.js +0 -12
  276. package/dist/math/min.js.map +0 -1
  277. package/dist/object/parseJSON.cjs +0 -2
  278. package/dist/object/parseJSON.cjs.map +0 -1
  279. package/dist/object/parseJSON.d.ts +0 -32
  280. package/dist/object/parseJSON.d.ts.map +0 -1
  281. package/dist/object/parseJSON.js +0 -16
  282. package/dist/object/parseJSON.js.map +0 -1
  283. package/dist/string/endsWith.cjs +0 -2
  284. package/dist/string/endsWith.cjs.map +0 -1
  285. package/dist/string/endsWith.d.ts +0 -5
  286. package/dist/string/endsWith.d.ts.map +0 -1
  287. package/dist/string/endsWith.js +0 -8
  288. package/dist/string/endsWith.js.map +0 -1
  289. package/dist/string/startsWith.cjs +0 -2
  290. package/dist/string/startsWith.cjs.map +0 -1
  291. package/dist/string/startsWith.d.ts +0 -5
  292. package/dist/string/startsWith.d.ts.map +0 -1
  293. package/dist/string/startsWith.js +0 -8
  294. package/dist/string/startsWith.js.map +0 -1
package/README.md CHANGED
@@ -1,24 +1,9 @@
1
1
  # @vielzeug/arsenal
2
2
 
3
- > Tree-shakeable, zero-dependency utility library for arrays, async control flow, objects, strings, functions, math, random, and typed checks.
3
+ > Tree-shakeable, zero-dependency TypeScript utilities with focused category entry points.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@vielzeug/arsenal)](https://www.npmjs.com/package/@vielzeug/arsenal) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- <details>
8
- <summary>Quick Reference</summary>
9
-
10
- **Package:** `@vielzeug/arsenal` &nbsp;·&nbsp; **Category:** Utilities
11
-
12
- **Key exports:** `chunk`, `debounce`, `throttle`, `allOf`, `clamp`, `isEqual`, `retry`, `sleep`, `stringify`, `fuzzy`, `getPath`, `deepMerge`, `diff`, `stash`, `memo`
13
-
14
- **When to use:** Tree-shakeable, zero-dependency utility library for arrays, async control flow, objects, strings, functions, math, random, and typed checks.
15
-
16
- **Related:** [`@vielzeug/coins`](https://www.npmjs.com/package/@vielzeug/coins) · [`@vielzeug/tempo`](https://www.npmjs.com/package/@vielzeug/tempo)
17
-
18
- </details>
19
-
20
- `@vielzeug/arsenal` is part of Vielzeug and ships as a zero-dependency TypeScript package with ESM+CJS output.
21
-
22
7
  ## Installation
23
8
 
24
9
  ```sh
@@ -30,67 +15,38 @@ yarn add @vielzeug/arsenal
30
15
  ## Quick Start
31
16
 
32
17
  ```ts
33
- import {
34
- allOf,
35
- chunk,
36
- deepMerge,
37
- diff,
38
- filterMap,
39
- fuzzy,
40
- groupBy,
41
- noneOf,
42
- partial,
43
- pick,
44
- queue,
45
- retry,
46
- stringify,
47
- } from '@vielzeug/arsenal';
48
-
49
- const users = [
50
- { id: 1, name: 'Alice', role: 'admin' },
51
- { id: 2, name: 'Bob', role: 'user' },
52
- ];
53
- const pages = chunk([1, 2, 3, 4, 5], 2);
54
- const byRole = groupBy(users, (u) => u.role);
55
- const safe = pick(users[0], ['id', 'name']);
56
-
57
- const q = queue({ concurrency: 2 });
58
- await q.add(() => fetch('/api/a'));
59
-
60
- const data = await retry(() => fetch('/api/health').then((r) => r.json()), {
61
- times: 3,
62
- delay: 200,
63
- });
64
-
65
- const doubleAll = partial((factor: number, values: number[]) => values.map((n) => n * factor), 2);
66
- const doubled = filterMap(doubleAll([1, 2, 3]), (n) => (n > 2 ? n : undefined));
67
-
68
- // Compose predicates — allOf / anyOf / noneOf
69
- const isWorkingAge = allOf<number>(
70
- (age) => age >= 18,
71
- (age) => age < 65,
72
- );
73
- const evens = [1, 2, 3, 4].filter(noneOf((n: number) => n % 2 !== 0));
74
-
75
- // Fuzzy search — filter mode or scored mode
76
- const filtered = fuzzy(users, 'alice'); // User[]
77
- const ranked = fuzzy(users, 'alice', { scored: true }); // ScoredResult<User>[]
78
- // [{ item: { name: 'Alice', ... }, score: 0.91 }, ...]
79
-
80
- // Deep merge with optional array strategy
81
- deepMerge({ a: { x: 1 } }, { a: { y: 2 } }); // { a: { x: 1, y: 2 } }
82
- deepMerge({ tags: ['a'] }, { tags: ['b'] }, { arrayStrategy: 'concat' }); // { tags: ['a', 'b'] }
83
-
84
- // Structured object diff
85
- diff({ port: 3000, host: 'localhost' }, { port: 4000 });
86
- // { added: [], removed: ['host'], changed: { port: { before: 3000, after: 4000 } } }
87
-
88
- // Deterministic cache keys
89
- const key = stringify({ sort: 'asc', filter: { role: 'admin' } });
18
+ import { chunk, groupBy, retry } from '@vielzeug/arsenal';
19
+ import { fuzzyFilter } from '@vielzeug/arsenal/array';
20
+ import { taskPool } from '@vielzeug/arsenal/async';
21
+ import { cache } from '@vielzeug/arsenal/cache';
22
+ import { tryParseJson } from '@vielzeug/arsenal/object';
23
+
24
+ const parsed = tryParseJson('{"users":[{"name":"Alice"}]}');
25
+ const users = parsed.ok ? (parsed.value as { users: { name: string }[] }).users : [];
26
+ const matches = fuzzyFilter(users, 'ali', { select: (user) => user.name });
27
+
28
+ const pool = taskPool({ concurrency: 2 });
29
+ const data = await pool.run((signal) => retry(() => fetch('/api', { signal }).then((response) => response.json())));
30
+
31
+ const responses = cache<string, unknown>({ ttlMs: 60_000 });
32
+ const profile = await responses.getOrLoad('/profile', () => fetch('/profile').then((response) => response.json()));
33
+
34
+ console.log(chunk([1, 2, 3], 2), groupBy(matches, (user) => user.name), data, profile);
35
+ pool.dispose();
90
36
  ```
91
37
 
92
- > **Money utilities** (`currency`, `exchange`) have moved to [`@vielzeug/coins`](https://www.npmjs.com/package/@vielzeug/coins).
93
- > **Date utilities** (`expires`, `timeDiff`, `dateRange`) are available in [`@vielzeug/tempo`](https://www.npmjs.com/package/@vielzeug/tempo).
38
+ ## Entry Points
39
+
40
+ - `@vielzeug/arsenal` — curated common utilities
41
+ - `@vielzeug/arsenal/array` — array transforms and fuzzy search
42
+ - `@vielzeug/arsenal/async` — cancellation, retry, task pools, timing
43
+ - `@vielzeug/arsenal/cache` — in-memory cache and memoization
44
+ - `@vielzeug/arsenal/function` — function composition and timing
45
+ - `@vielzeug/arsenal/guards` — type guards and predicates
46
+ - `@vielzeug/arsenal/math` — numeric and statistical helpers
47
+ - `@vielzeug/arsenal/object` — object transforms, paths, hash, JSON parsing result
48
+ - `@vielzeug/arsenal/random` — cryptographic random helpers
49
+ - `@vielzeug/arsenal/string` — text transforms and similarity
94
50
 
95
51
  ## Documentation
96
52
 
package/dist/_dev.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=!globalThis.__ARSENAL_PROD__;function t(t){e&&console.warn(`[@vielzeug/arsenal] ${t}`)}exports.warn=t;
1
+ function e(e){}exports.warn=e;
2
2
  //# sourceMappingURL=_dev.cjs.map
package/dist/_dev.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __ARSENAL_PROD__?: boolean }).__ARSENAL_PROD__;\n\n/** @internal @security Messages may include user data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/arsenal] ${msg}`);\n}\n"],"mappings":"AAAA,IAAM,EAAQ,CAAE,WAA8C,iBAG9D,SAAgB,EAAK,EAAmB,CAClC,GAAO,QAAQ,KAAK,uBAAuB,GAAK,CACtD"}
1
+ {"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __ARSENAL_PROD__?: boolean }).__ARSENAL_PROD__;\n\n/** @internal @security Messages may include user data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/arsenal] ${msg}`);\n}\n"],"mappings":"AAGA,SAAgB,EAAK,EAAmB,CAExC"}
package/dist/_dev.js CHANGED
@@ -1,9 +1,6 @@
1
1
  //#region src/_dev.ts
2
- var e = !globalThis.__ARSENAL_PROD__;
3
- function t(t) {
4
- e && console.warn(`[@vielzeug/arsenal] ${t}`);
5
- }
2
+ function e(e) {}
6
3
  //#endregion
7
- export { t as warn };
4
+ export { e as warn };
8
5
 
9
6
  //# sourceMappingURL=_dev.js.map
package/dist/_dev.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __ARSENAL_PROD__?: boolean }).__ARSENAL_PROD__;\n\n/** @internal @security Messages may include user data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/arsenal] ${msg}`);\n}\n"],"mappings":";AAAA,IAAM,IAAQ,CAAE,WAA8C;AAG9D,SAAgB,EAAK,GAAmB;CACtC,AAAI,KAAO,QAAQ,KAAK,uBAAuB,GAAK;AACtD"}
1
+ {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __ARSENAL_PROD__?: boolean }).__ARSENAL_PROD__;\n\n/** @internal @security Messages may include user data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/arsenal] ${msg}`);\n}\n"],"mappings":";AAGA,SAAgB,EAAK,GAAmB,CAExC"}
@@ -1,2 +1,2 @@
1
- const e=require("../errors.cjs"),t=require("../guards/isString.cjs");function n(n,r=2){if(!Array.isArray(n)&&!t.isString(n))throw new e.ArsenalValidationError(`chunk: argument must be an array or string`);if(r<1)throw new e.ArsenalValidationError(`chunk: size must be at least 1`);return Array.from({length:Math.ceil(n.length/r)},(e,t)=>n.slice(t*r,t*r+r))}exports.chunk=n;
1
+ function e(e,t=2){if(!Array.isArray(e)&&typeof e!=`string`)throw TypeError(`chunk: argument must be an array or string`);if(!Number.isInteger(t)||t<1)throw RangeError(`chunk: size must be a positive integer`);return Array.from({length:Math.ceil(e.length/t)},(n,r)=>e.slice(r*t,r*t+t))}exports.chunk=e;
2
2
  //# sourceMappingURL=chunk.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"chunk.cjs","names":[],"sources":["../../src/array/chunk.ts"],"sourcesContent":["import { ArsenalValidationError } from '../errors';\nimport { isString } from '../guards/isString';\n\n/**\n * Splits an array or string into chunks of a specified size.\n *\n * @example\n * ```ts\n * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]\n * chunk('hello', 2) // ['he', 'll', 'o']\n * ```\n *\n * @param input - The input array or string to be chunked.\n * @param size - The size of each chunk (default 2).\n *\n * @returns An array of chunks.\n *\n * @throws {ArsenalValidationError} If the input is not an array or string, or if the chunk size is less than 1.\n */\nexport function chunk(input: string, size?: number): string[];\nexport function chunk<T>(input: T[], size?: number): T[][];\nexport function chunk<T>(input: T[] | string, size = 2): string[] | T[][] {\n if (!Array.isArray(input) && !isString(input))\n throw new ArsenalValidationError('chunk: argument must be an array or string');\n\n if (size < 1) throw new ArsenalValidationError('chunk: size must be at least 1');\n\n return Array.from({ length: Math.ceil(input.length / size) }, (_, i) => input.slice(i * size, i * size + size)) as\n string[] | T[][];\n}\n"],"mappings":"qEAqBA,SAAgB,EAAS,EAAqB,EAAO,EAAqB,CACxE,GAAI,CAAC,MAAM,QAAQ,CAAK,GAAK,CAAC,EAAA,SAAS,CAAK,EAC1C,MAAM,IAAI,EAAA,uBAAuB,4CAA4C,EAE/E,GAAI,EAAO,EAAG,MAAM,IAAI,EAAA,uBAAuB,gCAAgC,EAE/E,OAAO,MAAM,KAAK,CAAE,OAAQ,KAAK,KAAK,EAAM,OAAS,CAAI,CAAE,GAAI,EAAG,IAAM,EAAM,MAAM,EAAI,EAAM,EAAI,EAAO,CAAI,CAAC,CAEhH"}
1
+ {"version":3,"file":"chunk.cjs","names":[],"sources":["../../src/array/chunk.ts"],"sourcesContent":["/**\n * Splits an array or string into chunks of a specified size.\n *\n * @example\n * ```ts\n * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]\n * chunk('hello', 2) // ['he', 'll', 'o']\n * ```\n *\n * @param input - The input array or string to be chunked.\n * @param size - The size of each chunk (default 2).\n *\n * @returns An array of chunks.\n *\n * @throws {TypeError} If the input is not an array or string.\n * @throws {RangeError} If the chunk size is not a positive integer.\n */\nexport function chunk(input: string, size?: number): string[];\nexport function chunk<T>(input: T[], size?: number): T[][];\nexport function chunk<T>(input: T[] | string, size = 2): string[] | T[][] {\n if (!Array.isArray(input) && typeof input !== 'string')\n throw new TypeError('chunk: argument must be an array or string');\n\n if (!Number.isInteger(size) || size < 1) throw new RangeError('chunk: size must be a positive integer');\n\n return Array.from({ length: Math.ceil(input.length / size) }, (_, i) => input.slice(i * size, i * size + size)) as\n string[] | T[][];\n}\n"],"mappings":"AAmBA,SAAgB,EAAS,EAAqB,EAAO,EAAqB,CACxE,GAAI,CAAC,MAAM,QAAQ,CAAK,GAAK,OAAO,GAAU,SAC5C,MAAU,UAAU,4CAA4C,EAElE,GAAI,CAAC,OAAO,UAAU,CAAI,GAAK,EAAO,EAAG,MAAU,WAAW,wCAAwC,EAEtG,OAAO,MAAM,KAAK,CAAE,OAAQ,KAAK,KAAK,EAAM,OAAS,CAAI,CAAE,GAAI,EAAG,IAAM,EAAM,MAAM,EAAI,EAAM,EAAI,EAAO,CAAI,CAAC,CAEhH"}
@@ -12,7 +12,8 @@
12
12
  *
13
13
  * @returns An array of chunks.
14
14
  *
15
- * @throws {ArsenalValidationError} If the input is not an array or string, or if the chunk size is less than 1.
15
+ * @throws {TypeError} If the input is not an array or string.
16
+ * @throws {RangeError} If the chunk size is not a positive integer.
16
17
  */
17
18
  export declare function chunk(input: string, size?: number): string[];
18
19
  export declare function chunk<T>(input: T[], size?: number): T[][];
@@ -1 +1 @@
1
- {"version":3,"file":"chunk.d.ts","sourceRoot":"","sources":["../../src/array/chunk.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;AAC9D,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,EAAE,CAAC"}
1
+ {"version":3,"file":"chunk.d.ts","sourceRoot":"","sources":["../../src/array/chunk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;AAC9D,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,EAAE,CAAC"}
@@ -1,12 +1,10 @@
1
- import { ArsenalValidationError as e } from "../errors.js";
2
- import { isString as t } from "../guards/isString.js";
3
1
  //#region src/array/chunk.ts
4
- function n(n, r = 2) {
5
- if (!Array.isArray(n) && !t(n)) throw new e("chunk: argument must be an array or string");
6
- if (r < 1) throw new e("chunk: size must be at least 1");
7
- return Array.from({ length: Math.ceil(n.length / r) }, (e, t) => n.slice(t * r, t * r + r));
2
+ function e(e, t = 2) {
3
+ if (!Array.isArray(e) && typeof e != "string") throw TypeError("chunk: argument must be an array or string");
4
+ if (!Number.isInteger(t) || t < 1) throw RangeError("chunk: size must be a positive integer");
5
+ return Array.from({ length: Math.ceil(e.length / t) }, (n, r) => e.slice(r * t, r * t + t));
8
6
  }
9
7
  //#endregion
10
- export { n as chunk };
8
+ export { e as chunk };
11
9
 
12
10
  //# sourceMappingURL=chunk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chunk.js","names":[],"sources":["../../src/array/chunk.ts"],"sourcesContent":["import { ArsenalValidationError } from '../errors';\nimport { isString } from '../guards/isString';\n\n/**\n * Splits an array or string into chunks of a specified size.\n *\n * @example\n * ```ts\n * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]\n * chunk('hello', 2) // ['he', 'll', 'o']\n * ```\n *\n * @param input - The input array or string to be chunked.\n * @param size - The size of each chunk (default 2).\n *\n * @returns An array of chunks.\n *\n * @throws {ArsenalValidationError} If the input is not an array or string, or if the chunk size is less than 1.\n */\nexport function chunk(input: string, size?: number): string[];\nexport function chunk<T>(input: T[], size?: number): T[][];\nexport function chunk<T>(input: T[] | string, size = 2): string[] | T[][] {\n if (!Array.isArray(input) && !isString(input))\n throw new ArsenalValidationError('chunk: argument must be an array or string');\n\n if (size < 1) throw new ArsenalValidationError('chunk: size must be at least 1');\n\n return Array.from({ length: Math.ceil(input.length / size) }, (_, i) => input.slice(i * size, i * size + size)) as\n string[] | T[][];\n}\n"],"mappings":";;;AAqBA,SAAgB,EAAS,GAAqB,IAAO,GAAqB;CACxE,IAAI,CAAC,MAAM,QAAQ,CAAK,KAAK,CAAC,EAAS,CAAK,GAC1C,MAAM,IAAI,EAAuB,4CAA4C;CAE/E,IAAI,IAAO,GAAG,MAAM,IAAI,EAAuB,gCAAgC;CAE/E,OAAO,MAAM,KAAK,EAAE,QAAQ,KAAK,KAAK,EAAM,SAAS,CAAI,EAAE,IAAI,GAAG,MAAM,EAAM,MAAM,IAAI,GAAM,IAAI,IAAO,CAAI,CAAC;AAEhH"}
1
+ {"version":3,"file":"chunk.js","names":[],"sources":["../../src/array/chunk.ts"],"sourcesContent":["/**\n * Splits an array or string into chunks of a specified size.\n *\n * @example\n * ```ts\n * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]\n * chunk('hello', 2) // ['he', 'll', 'o']\n * ```\n *\n * @param input - The input array or string to be chunked.\n * @param size - The size of each chunk (default 2).\n *\n * @returns An array of chunks.\n *\n * @throws {TypeError} If the input is not an array or string.\n * @throws {RangeError} If the chunk size is not a positive integer.\n */\nexport function chunk(input: string, size?: number): string[];\nexport function chunk<T>(input: T[], size?: number): T[][];\nexport function chunk<T>(input: T[] | string, size = 2): string[] | T[][] {\n if (!Array.isArray(input) && typeof input !== 'string')\n throw new TypeError('chunk: argument must be an array or string');\n\n if (!Number.isInteger(size) || size < 1) throw new RangeError('chunk: size must be a positive integer');\n\n return Array.from({ length: Math.ceil(input.length / size) }, (_, i) => input.slice(i * size, i * size + size)) as\n string[] | T[][];\n}\n"],"mappings":";AAmBA,SAAgB,EAAS,GAAqB,IAAO,GAAqB;CACxE,IAAI,CAAC,MAAM,QAAQ,CAAK,KAAK,OAAO,KAAU,UAC5C,MAAU,UAAU,4CAA4C;CAElE,IAAI,CAAC,OAAO,UAAU,CAAI,KAAK,IAAO,GAAG,MAAU,WAAW,wCAAwC;CAEtG,OAAO,MAAM,KAAK,EAAE,QAAQ,KAAK,KAAK,EAAM,SAAS,CAAI,EAAE,IAAI,GAAG,MAAM,EAAM,MAAM,IAAI,GAAM,IAAI,IAAO,CAAI,CAAC;AAEhH"}
@@ -1,2 +1,2 @@
1
- const e=require("../errors.cjs");var t=(t,n)=>{if(t===n)return 0;if(t===void 0)return 1;if(n===void 0)return-1;if(t===null)return n===null?0:-1;if(n===null)return 1;if(typeof t==`string`&&typeof n==`string`)return t.localeCompare(n);if(typeof t==`number`&&typeof n==`number`)return t===n?0:t>n?1:-1;if(t instanceof Date&&n instanceof Date){let e=t.getTime(),r=n.getTime();return e===r?0:e>r?1:-1}throw new e.ArsenalValidationError(`compare: unsupported types "${typeof t}" and "${typeof n}". Supported: number, string, Date, null, undefined.`)};exports.compare=t;
1
+ var e=(e,t)=>{if(e===t)return 0;if(e===void 0)return 1;if(t===void 0)return-1;if(e===null)return t===null?0:-1;if(t===null)return 1;if(typeof e==`string`&&typeof t==`string`)return e.localeCompare(t);if(typeof e==`number`&&typeof t==`number`)return e===t?0:e>t?1:-1;if(e instanceof Date&&t instanceof Date){let n=e.getTime(),r=t.getTime();return n===r?0:n>r?1:-1}throw TypeError(`compare: unsupported types "${typeof e}" and "${typeof t}". Supported: number, string, Date, null, undefined.`)};exports.compare=e;
2
2
  //# sourceMappingURL=compare.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"compare.cjs","names":[],"sources":["../../src/array/compare.ts"],"sourcesContent":["import { ArsenalValidationError } from '../errors';\n\n/**\n * Compares two values and returns a number suitable for use as a sort comparator.\n *\n * - Numbers and Dates: returns `0`, `1`, or `-1`.\n * - Strings: delegates to `String.prototype.localeCompare`, which may return any integer\n * (not just `0`, `1`, `-1`) depending on locale and engine.\n * - `null` sorts before all non-null values; `undefined` sorts last.\n *\n * @example\n * ```ts\n * compare('a', 'b'); // negative (locale-dependent)\n * compare(1, 2); // -1\n * compare(new Date('2023-01-01'), new Date('2023-01-02')); // -1\n * compare('a', 'a'); // 0\n * compare(1, 1); // 0\n * compare(new Date('2023-01-01'), new Date('2023-01-01')); // 0\n * ```\n *\n * @param a - The first value to compare.\n * @param b - The second value to compare.\n *\n * @returns A negative number, zero, or a positive number.\n *\n * Pass directly as an `Array.sort` comparator:\n * ```ts\n * [3, 1, 2].sort(compare); // [1, 2, 3]\n * ['banana', 'apple', 'cherry'].sort(compare); // ['apple', 'banana', 'cherry']\n * ```\n */\nexport const compare = (a: unknown, b: unknown): number => {\n if (a === b) return 0;\n\n if (a === undefined) return 1;\n\n if (b === undefined) return -1;\n\n if (a === null) return b === null ? 0 : -1;\n\n if (b === null) return 1;\n\n if (typeof a === 'string' && typeof b === 'string') {\n return a.localeCompare(b);\n }\n\n if (typeof a === 'number' && typeof b === 'number') {\n return a === b ? 0 : a > b ? 1 : -1;\n }\n\n if (a instanceof Date && b instanceof Date) {\n const at = a.getTime();\n const bt = b.getTime();\n\n return at === bt ? 0 : at > bt ? 1 : -1;\n }\n\n throw new ArsenalValidationError(\n `compare: unsupported types \"${typeof a}\" and \"${typeof b}\". Supported: number, string, Date, null, undefined.`,\n );\n};\n"],"mappings":"iCA+BA,IAAa,GAAW,EAAY,IAAuB,CACzD,GAAI,IAAM,EAAG,MAAO,GAEpB,GAAI,IAAM,IAAA,GAAW,MAAO,GAE5B,GAAI,IAAM,IAAA,GAAW,MAAO,GAE5B,GAAI,IAAM,KAAM,OAAO,IAAM,KAAO,EAAI,GAExC,GAAI,IAAM,KAAM,MAAO,GAEvB,GAAI,OAAO,GAAM,UAAY,OAAO,GAAM,SACxC,OAAO,EAAE,cAAc,CAAC,EAG1B,GAAI,OAAO,GAAM,UAAY,OAAO,GAAM,SACxC,OAAO,IAAM,EAAI,EAAI,EAAI,EAAI,EAAI,GAGnC,GAAI,aAAa,MAAQ,aAAa,KAAM,CAC1C,IAAM,EAAK,EAAE,QAAQ,EACf,EAAK,EAAE,QAAQ,EAErB,OAAO,IAAO,EAAK,EAAI,EAAK,EAAK,EAAI,EACvC,CAEA,MAAM,IAAI,EAAA,uBACR,+BAA+B,OAAO,EAAE,SAAS,OAAO,EAAE,qDAC5D,CACF"}
1
+ {"version":3,"file":"compare.cjs","names":[],"sources":["../../src/array/compare.ts"],"sourcesContent":["/**\n * Compares two values and returns a number suitable for use as a sort comparator.\n *\n * - Numbers and Dates: returns `0`, `1`, or `-1`.\n * - Strings: delegates to `String.prototype.localeCompare`, which may return any integer\n * (not just `0`, `1`, `-1`) depending on locale and engine.\n * - `null` sorts before all non-null values; `undefined` sorts last.\n *\n * @example\n * ```ts\n * compare('a', 'b'); // negative (locale-dependent)\n * compare(1, 2); // -1\n * compare(new Date('2023-01-01'), new Date('2023-01-02')); // -1\n * compare('a', 'a'); // 0\n * compare(1, 1); // 0\n * compare(new Date('2023-01-01'), new Date('2023-01-01')); // 0\n * ```\n *\n * @param a - The first value to compare.\n * @param b - The second value to compare.\n *\n * @returns A negative number, zero, or a positive number.\n *\n * Pass directly as an `Array.sort` comparator:\n * ```ts\n * [3, 1, 2].sort(compare); // [1, 2, 3]\n * ['banana', 'apple', 'cherry'].sort(compare); // ['apple', 'banana', 'cherry']\n * ```\n */\nexport const compare = (a: unknown, b: unknown): number => {\n if (a === b) return 0;\n\n if (a === undefined) return 1;\n\n if (b === undefined) return -1;\n\n if (a === null) return b === null ? 0 : -1;\n\n if (b === null) return 1;\n\n if (typeof a === 'string' && typeof b === 'string') {\n return a.localeCompare(b);\n }\n\n if (typeof a === 'number' && typeof b === 'number') {\n return a === b ? 0 : a > b ? 1 : -1;\n }\n\n if (a instanceof Date && b instanceof Date) {\n const at = a.getTime();\n const bt = b.getTime();\n\n return at === bt ? 0 : at > bt ? 1 : -1;\n }\n\n throw new TypeError(\n `compare: unsupported types \"${typeof a}\" and \"${typeof b}\". Supported: number, string, Date, null, undefined.`,\n );\n};\n"],"mappings":"AA6BA,IAAa,GAAW,EAAY,IAAuB,CACzD,GAAI,IAAM,EAAG,MAAO,GAEpB,GAAI,IAAM,IAAA,GAAW,MAAO,GAE5B,GAAI,IAAM,IAAA,GAAW,MAAO,GAE5B,GAAI,IAAM,KAAM,OAAO,IAAM,KAAO,EAAI,GAExC,GAAI,IAAM,KAAM,MAAO,GAEvB,GAAI,OAAO,GAAM,UAAY,OAAO,GAAM,SACxC,OAAO,EAAE,cAAc,CAAC,EAG1B,GAAI,OAAO,GAAM,UAAY,OAAO,GAAM,SACxC,OAAO,IAAM,EAAI,EAAI,EAAI,EAAI,EAAI,GAGnC,GAAI,aAAa,MAAQ,aAAa,KAAM,CAC1C,IAAM,EAAK,EAAE,QAAQ,EACf,EAAK,EAAE,QAAQ,EAErB,OAAO,IAAO,EAAK,EAAI,EAAK,EAAK,EAAI,EACvC,CAEA,MAAU,UACR,+BAA+B,OAAO,EAAE,SAAS,OAAO,EAAE,qDAC5D,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/array/compare.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,OAAO,GAAI,GAAG,OAAO,EAAE,GAAG,OAAO,KAAG,MA6BhD,CAAC"}
1
+ {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/array/compare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,OAAO,GAAI,GAAG,OAAO,EAAE,GAAG,OAAO,KAAG,MA6BhD,CAAC"}
@@ -1,20 +1,19 @@
1
- import { ArsenalValidationError as e } from "../errors.js";
2
1
  //#region src/array/compare.ts
3
- var t = (t, n) => {
4
- if (t === n) return 0;
5
- if (t === void 0) return 1;
6
- if (n === void 0) return -1;
7
- if (t === null) return n === null ? 0 : -1;
8
- if (n === null) return 1;
9
- if (typeof t == "string" && typeof n == "string") return t.localeCompare(n);
10
- if (typeof t == "number" && typeof n == "number") return t === n ? 0 : t > n ? 1 : -1;
11
- if (t instanceof Date && n instanceof Date) {
12
- let e = t.getTime(), r = n.getTime();
13
- return e === r ? 0 : e > r ? 1 : -1;
2
+ var e = (e, t) => {
3
+ if (e === t) return 0;
4
+ if (e === void 0) return 1;
5
+ if (t === void 0) return -1;
6
+ if (e === null) return t === null ? 0 : -1;
7
+ if (t === null) return 1;
8
+ if (typeof e == "string" && typeof t == "string") return e.localeCompare(t);
9
+ if (typeof e == "number" && typeof t == "number") return e === t ? 0 : e > t ? 1 : -1;
10
+ if (e instanceof Date && t instanceof Date) {
11
+ let n = e.getTime(), r = t.getTime();
12
+ return n === r ? 0 : n > r ? 1 : -1;
14
13
  }
15
- throw new e(`compare: unsupported types "${typeof t}" and "${typeof n}". Supported: number, string, Date, null, undefined.`);
14
+ throw TypeError(`compare: unsupported types "${typeof e}" and "${typeof t}". Supported: number, string, Date, null, undefined.`);
16
15
  };
17
16
  //#endregion
18
- export { t as compare };
17
+ export { e as compare };
19
18
 
20
19
  //# sourceMappingURL=compare.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compare.js","names":[],"sources":["../../src/array/compare.ts"],"sourcesContent":["import { ArsenalValidationError } from '../errors';\n\n/**\n * Compares two values and returns a number suitable for use as a sort comparator.\n *\n * - Numbers and Dates: returns `0`, `1`, or `-1`.\n * - Strings: delegates to `String.prototype.localeCompare`, which may return any integer\n * (not just `0`, `1`, `-1`) depending on locale and engine.\n * - `null` sorts before all non-null values; `undefined` sorts last.\n *\n * @example\n * ```ts\n * compare('a', 'b'); // negative (locale-dependent)\n * compare(1, 2); // -1\n * compare(new Date('2023-01-01'), new Date('2023-01-02')); // -1\n * compare('a', 'a'); // 0\n * compare(1, 1); // 0\n * compare(new Date('2023-01-01'), new Date('2023-01-01')); // 0\n * ```\n *\n * @param a - The first value to compare.\n * @param b - The second value to compare.\n *\n * @returns A negative number, zero, or a positive number.\n *\n * Pass directly as an `Array.sort` comparator:\n * ```ts\n * [3, 1, 2].sort(compare); // [1, 2, 3]\n * ['banana', 'apple', 'cherry'].sort(compare); // ['apple', 'banana', 'cherry']\n * ```\n */\nexport const compare = (a: unknown, b: unknown): number => {\n if (a === b) return 0;\n\n if (a === undefined) return 1;\n\n if (b === undefined) return -1;\n\n if (a === null) return b === null ? 0 : -1;\n\n if (b === null) return 1;\n\n if (typeof a === 'string' && typeof b === 'string') {\n return a.localeCompare(b);\n }\n\n if (typeof a === 'number' && typeof b === 'number') {\n return a === b ? 0 : a > b ? 1 : -1;\n }\n\n if (a instanceof Date && b instanceof Date) {\n const at = a.getTime();\n const bt = b.getTime();\n\n return at === bt ? 0 : at > bt ? 1 : -1;\n }\n\n throw new ArsenalValidationError(\n `compare: unsupported types \"${typeof a}\" and \"${typeof b}\". Supported: number, string, Date, null, undefined.`,\n );\n};\n"],"mappings":";;AA+BA,IAAa,KAAW,GAAY,MAAuB;CACzD,IAAI,MAAM,GAAG,OAAO;CAEpB,IAAI,MAAM,KAAA,GAAW,OAAO;CAE5B,IAAI,MAAM,KAAA,GAAW,OAAO;CAE5B,IAAI,MAAM,MAAM,OAAO,MAAM,OAAO,IAAI;CAExC,IAAI,MAAM,MAAM,OAAO;CAEvB,IAAI,OAAO,KAAM,YAAY,OAAO,KAAM,UACxC,OAAO,EAAE,cAAc,CAAC;CAG1B,IAAI,OAAO,KAAM,YAAY,OAAO,KAAM,UACxC,OAAO,MAAM,IAAI,IAAI,IAAI,IAAI,IAAI;CAGnC,IAAI,aAAa,QAAQ,aAAa,MAAM;EAC1C,IAAM,IAAK,EAAE,QAAQ,GACf,IAAK,EAAE,QAAQ;EAErB,OAAO,MAAO,IAAK,IAAI,IAAK,IAAK,IAAI;CACvC;CAEA,MAAM,IAAI,EACR,+BAA+B,OAAO,EAAE,SAAS,OAAO,EAAE,qDAC5D;AACF"}
1
+ {"version":3,"file":"compare.js","names":[],"sources":["../../src/array/compare.ts"],"sourcesContent":["/**\n * Compares two values and returns a number suitable for use as a sort comparator.\n *\n * - Numbers and Dates: returns `0`, `1`, or `-1`.\n * - Strings: delegates to `String.prototype.localeCompare`, which may return any integer\n * (not just `0`, `1`, `-1`) depending on locale and engine.\n * - `null` sorts before all non-null values; `undefined` sorts last.\n *\n * @example\n * ```ts\n * compare('a', 'b'); // negative (locale-dependent)\n * compare(1, 2); // -1\n * compare(new Date('2023-01-01'), new Date('2023-01-02')); // -1\n * compare('a', 'a'); // 0\n * compare(1, 1); // 0\n * compare(new Date('2023-01-01'), new Date('2023-01-01')); // 0\n * ```\n *\n * @param a - The first value to compare.\n * @param b - The second value to compare.\n *\n * @returns A negative number, zero, or a positive number.\n *\n * Pass directly as an `Array.sort` comparator:\n * ```ts\n * [3, 1, 2].sort(compare); // [1, 2, 3]\n * ['banana', 'apple', 'cherry'].sort(compare); // ['apple', 'banana', 'cherry']\n * ```\n */\nexport const compare = (a: unknown, b: unknown): number => {\n if (a === b) return 0;\n\n if (a === undefined) return 1;\n\n if (b === undefined) return -1;\n\n if (a === null) return b === null ? 0 : -1;\n\n if (b === null) return 1;\n\n if (typeof a === 'string' && typeof b === 'string') {\n return a.localeCompare(b);\n }\n\n if (typeof a === 'number' && typeof b === 'number') {\n return a === b ? 0 : a > b ? 1 : -1;\n }\n\n if (a instanceof Date && b instanceof Date) {\n const at = a.getTime();\n const bt = b.getTime();\n\n return at === bt ? 0 : at > bt ? 1 : -1;\n }\n\n throw new TypeError(\n `compare: unsupported types \"${typeof a}\" and \"${typeof b}\". Supported: number, string, Date, null, undefined.`,\n );\n};\n"],"mappings":";AA6BA,IAAa,KAAW,GAAY,MAAuB;CACzD,IAAI,MAAM,GAAG,OAAO;CAEpB,IAAI,MAAM,KAAA,GAAW,OAAO;CAE5B,IAAI,MAAM,KAAA,GAAW,OAAO;CAE5B,IAAI,MAAM,MAAM,OAAO,MAAM,OAAO,IAAI;CAExC,IAAI,MAAM,MAAM,OAAO;CAEvB,IAAI,OAAO,KAAM,YAAY,OAAO,KAAM,UACxC,OAAO,EAAE,cAAc,CAAC;CAG1B,IAAI,OAAO,KAAM,YAAY,OAAO,KAAM,UACxC,OAAO,MAAM,IAAI,IAAI,IAAI,IAAI,IAAI;CAGnC,IAAI,aAAa,QAAQ,aAAa,MAAM;EAC1C,IAAM,IAAK,EAAE,QAAQ,GACf,IAAK,EAAE,QAAQ;EAErB,OAAO,MAAO,IAAK,IAAI,IAAK,IAAK,IAAI;CACvC;CAEA,MAAU,UACR,+BAA+B,OAAO,EAAE,SAAS,OAAO,EAAE,qDAC5D;AACF"}
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./chunk.cjs"),t=require("./compact.cjs"),n=require("./compare.cjs"),r=require("./compareBy.cjs"),i=require("./contains.cjs"),a=require("./countBy.cjs"),o=require("./difference.cjs"),s=require("./drop.cjs"),c=require("./dropLast.cjs"),l=require("./filterMap.cjs"),u=require("./first.cjs"),d=require("./flatten.cjs"),f=require("./groupBy.cjs"),p=require("./indexBy.cjs"),m=require("./intersection.cjs"),h=require("./last.cjs"),g=require("./partition.cjs"),_=require("./replace.cjs"),v=require("./rotate.cjs"),y=require("./search.cjs"),b=require("./sort.cjs"),x=require("./take.cjs"),S=require("./takeLast.cjs"),C=require("./toggle.cjs"),w=require("./union.cjs"),T=require("./uniq.cjs"),E=require("./unzip.cjs"),D=require("./zip.cjs");exports.chunk=e.chunk,exports.compact=t.compact,exports.compare=n.compare,exports.compareBy=r.compareBy,exports.contains=i.contains,exports.countBy=a.countBy,exports.difference=o.difference,exports.drop=s.drop,exports.dropLast=c.dropLast,exports.filterMap=l.filterMap,exports.first=u.first,exports.flatten=d.flatten,exports.fuzzyFilter=y.fuzzyFilter,exports.fuzzyScore=y.fuzzyScore,exports.groupBy=f.groupBy,exports.indexBy=p.indexBy,exports.intersection=m.intersection,exports.last=h.last,exports.partition=g.partition,exports.replace=_.replace,exports.rotate=v.rotate,exports.sort=b.sort,exports.take=x.take,exports.takeLast=S.takeLast,exports.toggle=C.toggle,exports.union=w.union,exports.uniq=T.uniq,exports.unzip=E.unzip,exports.zip=D.zip;
@@ -0,0 +1,29 @@
1
+ import { chunk as e } from "./chunk.js";
2
+ import { compact as t } from "./compact.js";
3
+ import { compare as n } from "./compare.js";
4
+ import { compareBy as r } from "./compareBy.js";
5
+ import { contains as i } from "./contains.js";
6
+ import { countBy as a } from "./countBy.js";
7
+ import { difference as o } from "./difference.js";
8
+ import { drop as s } from "./drop.js";
9
+ import { dropLast as c } from "./dropLast.js";
10
+ import { filterMap as l } from "./filterMap.js";
11
+ import { first as u } from "./first.js";
12
+ import { flatten as d } from "./flatten.js";
13
+ import { groupBy as f } from "./groupBy.js";
14
+ import { indexBy as p } from "./indexBy.js";
15
+ import { intersection as m } from "./intersection.js";
16
+ import { last as h } from "./last.js";
17
+ import { partition as g } from "./partition.js";
18
+ import { replace as _ } from "./replace.js";
19
+ import { rotate as v } from "./rotate.js";
20
+ import { fuzzyFilter as y, fuzzyScore as b } from "./search.js";
21
+ import { sort as x } from "./sort.js";
22
+ import { take as S } from "./take.js";
23
+ import { takeLast as C } from "./takeLast.js";
24
+ import { toggle as w } from "./toggle.js";
25
+ import { union as T } from "./union.js";
26
+ import { uniq as E } from "./uniq.js";
27
+ import { unzip as D } from "./unzip.js";
28
+ import { zip as O } from "./zip.js";
29
+ export { e as chunk, t as compact, n as compare, r as compareBy, i as contains, a as countBy, o as difference, s as drop, c as dropLast, l as filterMap, u as first, d as flatten, y as fuzzyFilter, b as fuzzyScore, f as groupBy, p as indexBy, m as intersection, h as last, g as partition, _ as replace, v as rotate, x as sort, S as take, C as takeLast, w as toggle, T as union, E as uniq, D as unzip, O as zip };
@@ -1,2 +1,2 @@
1
- const e=require("../errors.cjs"),t=require("../guards/isString.cjs"),n=require("../guards/isNil.cjs"),r=require("../guards/isNumber.cjs"),i=require("../string/similarity.cjs");var a=10,o=1e3;function s(t){if(t===void 0)return a;if(!Number.isInteger(t)||t<0)throw new e.ArsenalValidationError(`fuzzy: maxDepth must be a non-negative integer, got ${t}`);return Math.min(t,o)}var c=e=>e.normalize(`NFKD`).replace(/\p{M}/gu,``);function l(e,a,o,s,u=0){return u>s||n.isNil(e)?0:t.isString(e)||r.isNumber(e)?i.similarity(o?c(String(e)):String(e),a):Array.isArray(e)?e.reduce((e,t)=>Math.max(e,l(t,a,o,s,u+1)),0):typeof e==`object`?Object.values(e).reduce((e,t)=>Math.max(e,l(t,a,o,s,u+1)),0):0}function u(e,t,n,r,i){return l(e,t,r,i)>=n}function d(e,t,n={}){let{fields:r,normalize:i=!1,threshold:a=.25}=n,o=s(n.maxDepth);if(!t)return[...e];let l=t.trim().toLowerCase();if(!l)return[...e];let d=i?c(l):l;return e.filter(e=>r&&r.length>0&&typeof e==`object`&&e?r.some(t=>u(e[t],d,a,i,o)):u(e,d,a,i,o))}function f(e,t,n={}){let{fields:r,normalize:i=!1,threshold:a=.25}=n,o=s(n.maxDepth);if(!t||!t.trim())return e.map(e=>({item:e,score:1}));let u=t.trim().toLowerCase(),d=i?c(u):u;return e.map(e=>({item:e,score:r&&r.length>0&&typeof e==`object`&&e?r.reduce((t,n)=>Math.max(t,l(e[n],d,i,o)),0):l(e,d,i,o)})).filter(({score:e})=>e>=a).sort((e,t)=>t.score-e.score)}function p(e,t,n={}){let{scored:r,...i}=n;return r?f(e,t,i):d(e,t,i)}exports.fuzzy=p,exports.fuzzyFilter=d,exports.fuzzyScore=f;
1
+ const e=require("../string/similarity.cjs");var t=e=>e.normalize(`NFKD`).replace(/\p{M}/gu,``),n=(e,t)=>{let n=t(e);return typeof n==`string`?[n]:n},r=(e,n)=>{let r=e.trim().toLowerCase();return n?t(r):r},i=(r,i,a,o)=>n(r,o).reduce((n,r)=>{let o=a?t(r.toLowerCase()):r.toLowerCase();return Math.max(n,e.similarity(o,i))},0),a=e=>e;function o(e,t,n={}){let{normalize:o=!1,threshold:s=.25}=n,c=`select`in n?n.select:a,l=r(t,o);return l?e.filter(e=>i(e,l,o,c)>=s):[...e]}function s(e,t,n={}){let{normalize:o=!1,threshold:s=.25}=n,c=`select`in n?n.select:a,l=r(t,o);return l?e.map(e=>({item:e,score:i(e,l,o,c)})).filter(e=>e.score>=s).sort((e,t)=>t.score-e.score):e.map(e=>({item:e,score:1}))}exports.fuzzyFilter=o,exports.fuzzyScore=s;
2
2
  //# sourceMappingURL=search.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.cjs","names":[],"sources":["../../src/array/search.ts"],"sourcesContent":["import { ArsenalValidationError } from '../errors';\nimport { isNil } from '../guards/isNil';\nimport { isNumber } from '../guards/isNumber';\nimport { isString } from '../guards/isString';\nimport { similarity } from '../string/similarity';\n\nexport type ScoredResult<T> = { item: T; score: number };\n\nexport type FuzzyOptions<T> = {\n fields?: ReadonlyArray<keyof T & string>;\n /**\n * Maximum recursive depth for full-tree scanning when `fields` is omitted. Values beyond\n * this depth are not searched. Must be a non-negative integer; silently clamped to\n * `HARD_MAX_SEEK_DEPTH` to bound worst-case recursion on untrusted, deeply-nested input.\n * Default: `10`.\n */\n maxDepth?: number;\n /** When `true`, applies Unicode NFKD normalization before comparison so accented characters match their base form (é ≈ e). Default: `false`. */\n normalize?: boolean;\n threshold?: number;\n};\n\n/** Default maximum recursive depth for full-tree scanning when `fields` is omitted and `maxDepth` isn't set. */\nconst DEFAULT_MAX_SEEK_DEPTH = 10;\n\n/**\n * Hard ceiling for `maxDepth`, regardless of caller input — bounds worst-case recursion depth\n * (and therefore call-stack usage) even when `maxDepth` is attacker-influenced.\n */\nconst HARD_MAX_SEEK_DEPTH = 1000;\n\n/** @security `maxDepth` may originate from untrusted input reaching a public API — validate and clamp. */\nfunction resolveMaxDepth(maxDepth: number | undefined): number {\n if (maxDepth === undefined) return DEFAULT_MAX_SEEK_DEPTH;\n\n if (!Number.isInteger(maxDepth) || maxDepth < 0) {\n throw new ArsenalValidationError(`fuzzy: maxDepth must be a non-negative integer, got ${maxDepth}`);\n }\n\n return Math.min(maxDepth, HARD_MAX_SEEK_DEPTH);\n}\n\nconst normalizeStr = (s: string): string => s.normalize('NFKD').replace(/\\p{M}/gu, '');\n\nfunction seekScore(item: unknown, query: string, normalize: boolean, maxDepth: number, depth = 0): number {\n if (depth > maxDepth) return 0;\n\n if (isNil(item)) return 0;\n\n if (isString(item) || isNumber(item)) {\n const a = normalize ? normalizeStr(String(item)) : String(item);\n\n return similarity(a, query);\n }\n\n if (Array.isArray(item))\n return (item as unknown[]).reduce<number>(\n (max, v) => Math.max(max, seekScore(v, query, normalize, maxDepth, depth + 1)),\n 0,\n );\n\n if (typeof item === 'object')\n return Object.values(item as Record<string, unknown>).reduce<number>(\n (max, v) => Math.max(max, seekScore(v, query, normalize, maxDepth, depth + 1)),\n 0,\n );\n\n return 0;\n}\n\nfunction seekValue(item: unknown, query: string, threshold: number, normalize: boolean, maxDepth: number): boolean {\n return seekScore(item, query, normalize, maxDepth) >= threshold;\n}\n\n/**\n * Performs a fuzzy filter on an array of items using string similarity.\n * Returns items whose similarity to `query` meets `threshold`.\n * When `fields` is provided, only those keys are searched; otherwise all values are scanned.\n *\n * For results sorted by relevance score, use `fuzzyScore`.\n *\n * @example\n * ```ts\n * const data = [{ name: 'John Doe', age: 25 }, { name: 'Jane Doe', age: 30 }];\n *\n * fuzzyFilter(data, 'doe'); // [{ name: 'John Doe' }, { name: 'Jane Doe' }]\n * fuzzyFilter(data, 'john', { fields: ['name'] });\n * ```\n *\n * @param array - The array of items to filter.\n * @param query - The string to search for.\n * @param [options.threshold=0.25] - Similarity threshold between 0 and 1. Higher = stricter.\n * @param [options.fields] - Limit search to these object keys. Searches all values when omitted.\n * @param [options.normalize=false] - NFKD normalization so accented chars match base form (é ≈ e).\n * @param [options.maxDepth=10] - Maximum recursive depth when scanning nested values (ignored when `fields` is set).\n * @throws {ArsenalValidationError} If `maxDepth` is provided and is not a non-negative integer.\n */\nexport function fuzzyFilter<T>(array: T[], query: string, options: FuzzyOptions<T> = {}): T[] {\n const { fields, normalize = false, threshold = 0.25 } = options;\n const maxDepth = resolveMaxDepth(options.maxDepth);\n\n if (!query) return [...array];\n\n const raw = query.trim().toLowerCase();\n\n if (!raw) return [...array];\n\n const searchTerm = normalize ? normalizeStr(raw) : raw;\n\n return array.filter((item) => {\n if (fields && fields.length > 0 && typeof item === 'object' && item !== null) {\n return fields.some((field) =>\n seekValue((item as Record<string, unknown>)[field], searchTerm, threshold, normalize, maxDepth),\n );\n }\n\n return seekValue(item, searchTerm, threshold, normalize, maxDepth);\n });\n}\n\n/**\n * Performs a fuzzy score on an array of items using string similarity.\n * Returns all items with their similarity score, filtered by `threshold`, sorted by score descending.\n * When `query` is empty, all items are returned with score `1`.\n *\n * For a simple filtered list without scores, use `fuzzyFilter`.\n *\n * @example\n * ```ts\n * const data = [{ name: 'John Doe', age: 25 }, { name: 'Jane Doe', age: 30 }];\n *\n * fuzzyScore(data, 'john', { fields: ['name'] });\n * // [{ item: { name: 'John Doe', age: 25 }, score: 0.8 }, ...]\n * ```\n *\n * @param array - The array of items to score.\n * @param query - The string to search for.\n * @param [options.threshold=0.25] - Minimum score to include in results.\n * @param [options.fields] - Limit scoring to these object keys. Scores all values when omitted.\n * @param [options.normalize=false] - NFKD normalization so accented chars match base form (é ≈ e).\n * @param [options.maxDepth=10] - Maximum recursive depth when scanning nested values (ignored when `fields` is set).\n * @throws {ArsenalValidationError} If `maxDepth` is provided and is not a non-negative integer.\n */\nexport function fuzzyScore<T>(array: T[], query: string, options: FuzzyOptions<T> = {}): ScoredResult<T>[] {\n const { fields, normalize = false, threshold = 0.25 } = options;\n const maxDepth = resolveMaxDepth(options.maxDepth);\n\n if (!query || !query.trim()) {\n return array.map((item) => ({ item, score: 1 }));\n }\n\n const raw = query.trim().toLowerCase();\n const searchTerm = normalize ? normalizeStr(raw) : raw;\n\n const scored = array.map((item) => {\n const score =\n fields && fields.length > 0 && typeof item === 'object' && item !== null\n ? fields.reduce<number>(\n (max, field) =>\n Math.max(max, seekScore((item as Record<string, unknown>)[field], searchTerm, normalize, maxDepth)),\n 0,\n )\n : seekScore(item, searchTerm, normalize, maxDepth);\n\n return { item, score };\n });\n\n return scored.filter(({ score }) => score >= threshold).sort((a, b) => b.score - a.score);\n}\n\n/**\n * Fuzzy-searches an array. In filter mode (default) returns `T[]`.\n * Pass `{ scored: true }` to return `ScoredResult<T>[]` sorted by relevance descending.\n *\n * @example\n * ```ts\n * const users = [{ name: 'Alice' }, { name: 'Bob' }];\n *\n * fuzzy(users, 'ali'); // [{ name: 'Alice' }]\n * fuzzy(users, 'ali', { scored: true }); // [{ item: { name: 'Alice' }, score: 0.9 }]\n * fuzzy(users, 'ali', { fields: ['name'] }); // field-restricted\n * ```\n */\nexport function fuzzy<T>(array: T[], query: string, options: FuzzyOptions<T> & { scored: true }): ScoredResult<T>[];\nexport function fuzzy<T>(array: T[], query: string, options?: FuzzyOptions<T> & { scored?: false }): T[];\nexport function fuzzy<T>(\n array: T[],\n query: string,\n options: FuzzyOptions<T> & { scored?: boolean } = {},\n): T[] | ScoredResult<T>[] {\n const { scored, ...rest } = options;\n\n return scored ? fuzzyScore(array, query, rest) : fuzzyFilter(array, query, rest);\n}\n"],"mappings":"gLAuBA,IAAM,EAAyB,GAMzB,EAAsB,IAG5B,SAAS,EAAgB,EAAsC,CAC7D,GAAI,IAAa,IAAA,GAAW,OAAO,EAEnC,GAAI,CAAC,OAAO,UAAU,CAAQ,GAAK,EAAW,EAC5C,MAAM,IAAI,EAAA,uBAAuB,uDAAuD,GAAU,EAGpG,OAAO,KAAK,IAAI,EAAU,CAAmB,CAC/C,CAEA,IAAM,EAAgB,GAAsB,EAAE,UAAU,MAAM,CAAC,CAAC,QAAQ,UAAW,EAAE,EAErF,SAAS,EAAU,EAAe,EAAe,EAAoB,EAAkB,EAAQ,EAAW,CAuBxG,OAtBI,EAAQ,GAER,EAAA,MAAM,CAAI,EAAU,EAEpB,EAAA,SAAS,CAAI,GAAK,EAAA,SAAS,CAAI,EAG1B,EAAA,WAFG,EAAY,EAAa,OAAO,CAAI,CAAC,EAAI,OAAO,CAAI,EAEzC,CAAK,EAGxB,MAAM,QAAQ,CAAI,EACZ,EAAmB,QACxB,EAAK,IAAM,KAAK,IAAI,EAAK,EAAU,EAAG,EAAO,EAAW,EAAU,EAAQ,CAAC,CAAC,EAC7E,CACF,EAEE,OAAO,GAAS,SACX,OAAO,OAAO,CAA+B,CAAC,CAAC,QACnD,EAAK,IAAM,KAAK,IAAI,EAAK,EAAU,EAAG,EAAO,EAAW,EAAU,EAAQ,CAAC,CAAC,EAC7E,CACF,EAEK,CACT,CAEA,SAAS,EAAU,EAAe,EAAe,EAAmB,EAAoB,EAA2B,CACjH,OAAO,EAAU,EAAM,EAAO,EAAW,CAAQ,GAAK,CACxD,CAyBA,SAAgB,EAAe,EAAY,EAAe,EAA2B,CAAC,EAAQ,CAC5F,GAAM,CAAE,SAAQ,YAAY,GAAO,YAAY,KAAS,EAClD,EAAW,EAAgB,EAAQ,QAAQ,EAEjD,GAAI,CAAC,EAAO,MAAO,CAAC,GAAG,CAAK,EAE5B,IAAM,EAAM,EAAM,KAAK,CAAC,CAAC,YAAY,EAErC,GAAI,CAAC,EAAK,MAAO,CAAC,GAAG,CAAK,EAE1B,IAAM,EAAa,EAAY,EAAa,CAAG,EAAI,EAEnD,OAAO,EAAM,OAAQ,GACf,GAAU,EAAO,OAAS,GAAK,OAAO,GAAS,UAAY,EACtD,EAAO,KAAM,GAClB,EAAW,EAAiC,GAAQ,EAAY,EAAW,EAAW,CAAQ,CAChG,EAGK,EAAU,EAAM,EAAY,EAAW,EAAW,CAAQ,CAClE,CACH,CAyBA,SAAgB,EAAc,EAAY,EAAe,EAA2B,CAAC,EAAsB,CACzG,GAAM,CAAE,SAAQ,YAAY,GAAO,YAAY,KAAS,EAClD,EAAW,EAAgB,EAAQ,QAAQ,EAEjD,GAAI,CAAC,GAAS,CAAC,EAAM,KAAK,EACxB,OAAO,EAAM,IAAK,IAAU,CAAE,OAAM,MAAO,CAAE,EAAE,EAGjD,IAAM,EAAM,EAAM,KAAK,CAAC,CAAC,YAAY,EAC/B,EAAa,EAAY,EAAa,CAAG,EAAI,EAenD,OAbe,EAAM,IAAK,IAUjB,CAAE,OAAM,MARb,GAAU,EAAO,OAAS,GAAK,OAAO,GAAS,UAAY,EACvD,EAAO,QACJ,EAAK,IACJ,KAAK,IAAI,EAAK,EAAW,EAAiC,GAAQ,EAAY,EAAW,CAAQ,CAAC,EACpG,CACF,EACA,EAAU,EAAM,EAAY,EAAW,CAAQ,CAEhC,EAGhB,CAAA,CAAO,QAAQ,CAAE,WAAY,GAAS,CAAS,CAAC,CAAC,MAAM,EAAG,IAAM,EAAE,MAAQ,EAAE,KAAK,CAC1F,CAiBA,SAAgB,EACd,EACA,EACA,EAAkD,CAAC,EAC1B,CACzB,GAAM,CAAE,SAAQ,GAAG,GAAS,EAE5B,OAAO,EAAS,EAAW,EAAO,EAAO,CAAI,EAAI,EAAY,EAAO,EAAO,CAAI,CACjF"}
1
+ {"version":3,"file":"search.cjs","names":[],"sources":["../../src/array/search.ts"],"sourcesContent":["import { similarity } from '../string/similarity';\n\nexport type ScoredResult<T> = { item: T; score: number };\n\nexport type FuzzyOptions = {\n normalize?: boolean;\n threshold?: number;\n};\n\nexport type FuzzySelection<T> = FuzzyOptions & {\n select: (item: T) => string | readonly string[];\n};\n\nconst normalizeText = (value: string): string => value.normalize('NFKD').replace(/\\p{M}/gu, '');\n\nconst termsFor = <T>(item: T, select: (item: T) => string | readonly string[]): readonly string[] => {\n const selected = select(item);\n\n return typeof selected === 'string' ? [selected] : selected;\n};\n\nconst prepareQuery = (query: string, normalize: boolean): string => {\n const trimmed = query.trim().toLowerCase();\n\n return normalize ? normalizeText(trimmed) : trimmed;\n};\n\nconst score = <T>(\n item: T,\n query: string,\n normalize: boolean,\n select: (item: T) => string | readonly string[],\n): number => {\n return termsFor(item, select).reduce((best, term) => {\n const value = normalize ? normalizeText(term.toLowerCase()) : term.toLowerCase();\n\n return Math.max(best, similarity(value, query));\n }, 0);\n};\n\nconst stringSelection = (value: string): string => value;\n\nexport function fuzzyFilter(array: readonly string[], query: string, options?: FuzzyOptions): string[];\nexport function fuzzyFilter<T>(array: readonly T[], query: string, options: FuzzySelection<T>): T[];\nexport function fuzzyFilter<T>(\n array: readonly T[],\n query: string,\n options: FuzzyOptions | FuzzySelection<T> = {},\n): T[] {\n const { normalize = false, threshold = 0.25 } = options;\n const select = 'select' in options ? options.select : (stringSelection as (item: T) => string);\n const searchTerm = prepareQuery(query, normalize);\n\n if (!searchTerm) return [...array];\n\n return array.filter((item) => score(item, searchTerm, normalize, select) >= threshold);\n}\n\nexport function fuzzyScore(array: readonly string[], query: string, options?: FuzzyOptions): ScoredResult<string>[];\nexport function fuzzyScore<T>(array: readonly T[], query: string, options: FuzzySelection<T>): ScoredResult<T>[];\nexport function fuzzyScore<T>(\n array: readonly T[],\n query: string,\n options: FuzzyOptions | FuzzySelection<T> = {},\n): ScoredResult<T>[] {\n const { normalize = false, threshold = 0.25 } = options;\n const select = 'select' in options ? options.select : (stringSelection as (item: T) => string);\n const searchTerm = prepareQuery(query, normalize);\n\n if (!searchTerm) return array.map((item) => ({ item, score: 1 }));\n\n return array\n .map((item) => ({ item, score: score(item, searchTerm, normalize, select) }))\n .filter((result) => result.score >= threshold)\n .sort((a, b) => b.score - a.score);\n}\n"],"mappings":"4CAaA,IAAM,EAAiB,GAA0B,EAAM,UAAU,MAAM,CAAC,CAAC,QAAQ,UAAW,EAAE,EAExF,GAAe,EAAS,IAAuE,CACnG,IAAM,EAAW,EAAO,CAAI,EAE5B,OAAO,OAAO,GAAa,SAAW,CAAC,CAAQ,EAAI,CACrD,EAEM,GAAgB,EAAe,IAA+B,CAClE,IAAM,EAAU,EAAM,KAAK,CAAC,CAAC,YAAY,EAEzC,OAAO,EAAY,EAAc,CAAO,EAAI,CAC9C,EAEM,GACJ,EACA,EACA,EACA,IAEO,EAAS,EAAM,CAAM,CAAC,CAAC,QAAQ,EAAM,IAAS,CACnD,IAAM,EAAQ,EAAY,EAAc,EAAK,YAAY,CAAC,EAAI,EAAK,YAAY,EAE/E,OAAO,KAAK,IAAI,EAAM,EAAA,WAAW,EAAO,CAAK,CAAC,CAChD,EAAG,CAAC,EAGA,EAAmB,GAA0B,EAInD,SAAgB,EACd,EACA,EACA,EAA4C,CAAC,EACxC,CACL,GAAM,CAAE,YAAY,GAAO,YAAY,KAAS,EAC1C,EAAS,WAAY,EAAU,EAAQ,OAAU,EACjD,EAAa,EAAa,EAAO,CAAS,EAIhD,OAFK,EAEE,EAAM,OAAQ,GAAS,EAAM,EAAM,EAAY,EAAW,CAAM,GAAK,CAAS,EAF7D,CAAC,GAAG,CAAK,CAGnC,CAIA,SAAgB,EACd,EACA,EACA,EAA4C,CAAC,EAC1B,CACnB,GAAM,CAAE,YAAY,GAAO,YAAY,KAAS,EAC1C,EAAS,WAAY,EAAU,EAAQ,OAAU,EACjD,EAAa,EAAa,EAAO,CAAS,EAIhD,OAFK,EAEE,EACJ,IAAK,IAAU,CAAE,OAAM,MAAO,EAAM,EAAM,EAAY,EAAW,CAAM,CAAE,EAAE,CAAC,CAC5E,OAAQ,GAAW,EAAO,OAAS,CAAS,CAAC,CAC7C,MAAM,EAAG,IAAM,EAAE,MAAQ,EAAE,KAAK,EALX,EAAM,IAAK,IAAU,CAAE,OAAM,MAAO,CAAE,EAAE,CAMlE"}
@@ -2,84 +2,15 @@ export type ScoredResult<T> = {
2
2
  item: T;
3
3
  score: number;
4
4
  };
5
- export type FuzzyOptions<T> = {
6
- fields?: ReadonlyArray<keyof T & string>;
7
- /**
8
- * Maximum recursive depth for full-tree scanning when `fields` is omitted. Values beyond
9
- * this depth are not searched. Must be a non-negative integer; silently clamped to
10
- * `HARD_MAX_SEEK_DEPTH` to bound worst-case recursion on untrusted, deeply-nested input.
11
- * Default: `10`.
12
- */
13
- maxDepth?: number;
14
- /** When `true`, applies Unicode NFKD normalization before comparison so accented characters match their base form (é ≈ e). Default: `false`. */
5
+ export type FuzzyOptions = {
15
6
  normalize?: boolean;
16
7
  threshold?: number;
17
8
  };
18
- /**
19
- * Performs a fuzzy filter on an array of items using string similarity.
20
- * Returns items whose similarity to `query` meets `threshold`.
21
- * When `fields` is provided, only those keys are searched; otherwise all values are scanned.
22
- *
23
- * For results sorted by relevance score, use `fuzzyScore`.
24
- *
25
- * @example
26
- * ```ts
27
- * const data = [{ name: 'John Doe', age: 25 }, { name: 'Jane Doe', age: 30 }];
28
- *
29
- * fuzzyFilter(data, 'doe'); // [{ name: 'John Doe' }, { name: 'Jane Doe' }]
30
- * fuzzyFilter(data, 'john', { fields: ['name'] });
31
- * ```
32
- *
33
- * @param array - The array of items to filter.
34
- * @param query - The string to search for.
35
- * @param [options.threshold=0.25] - Similarity threshold between 0 and 1. Higher = stricter.
36
- * @param [options.fields] - Limit search to these object keys. Searches all values when omitted.
37
- * @param [options.normalize=false] - NFKD normalization so accented chars match base form (é ≈ e).
38
- * @param [options.maxDepth=10] - Maximum recursive depth when scanning nested values (ignored when `fields` is set).
39
- * @throws {ArsenalValidationError} If `maxDepth` is provided and is not a non-negative integer.
40
- */
41
- export declare function fuzzyFilter<T>(array: T[], query: string, options?: FuzzyOptions<T>): T[];
42
- /**
43
- * Performs a fuzzy score on an array of items using string similarity.
44
- * Returns all items with their similarity score, filtered by `threshold`, sorted by score descending.
45
- * When `query` is empty, all items are returned with score `1`.
46
- *
47
- * For a simple filtered list without scores, use `fuzzyFilter`.
48
- *
49
- * @example
50
- * ```ts
51
- * const data = [{ name: 'John Doe', age: 25 }, { name: 'Jane Doe', age: 30 }];
52
- *
53
- * fuzzyScore(data, 'john', { fields: ['name'] });
54
- * // [{ item: { name: 'John Doe', age: 25 }, score: 0.8 }, ...]
55
- * ```
56
- *
57
- * @param array - The array of items to score.
58
- * @param query - The string to search for.
59
- * @param [options.threshold=0.25] - Minimum score to include in results.
60
- * @param [options.fields] - Limit scoring to these object keys. Scores all values when omitted.
61
- * @param [options.normalize=false] - NFKD normalization so accented chars match base form (é ≈ e).
62
- * @param [options.maxDepth=10] - Maximum recursive depth when scanning nested values (ignored when `fields` is set).
63
- * @throws {ArsenalValidationError} If `maxDepth` is provided and is not a non-negative integer.
64
- */
65
- export declare function fuzzyScore<T>(array: T[], query: string, options?: FuzzyOptions<T>): ScoredResult<T>[];
66
- /**
67
- * Fuzzy-searches an array. In filter mode (default) returns `T[]`.
68
- * Pass `{ scored: true }` to return `ScoredResult<T>[]` sorted by relevance descending.
69
- *
70
- * @example
71
- * ```ts
72
- * const users = [{ name: 'Alice' }, { name: 'Bob' }];
73
- *
74
- * fuzzy(users, 'ali'); // [{ name: 'Alice' }]
75
- * fuzzy(users, 'ali', { scored: true }); // [{ item: { name: 'Alice' }, score: 0.9 }]
76
- * fuzzy(users, 'ali', { fields: ['name'] }); // field-restricted
77
- * ```
78
- */
79
- export declare function fuzzy<T>(array: T[], query: string, options: FuzzyOptions<T> & {
80
- scored: true;
81
- }): ScoredResult<T>[];
82
- export declare function fuzzy<T>(array: T[], query: string, options?: FuzzyOptions<T> & {
83
- scored?: false;
84
- }): T[];
9
+ export type FuzzySelection<T> = FuzzyOptions & {
10
+ select: (item: T) => string | readonly string[];
11
+ };
12
+ export declare function fuzzyFilter(array: readonly string[], query: string, options?: FuzzyOptions): string[];
13
+ export declare function fuzzyFilter<T>(array: readonly T[], query: string, options: FuzzySelection<T>): T[];
14
+ export declare function fuzzyScore(array: readonly string[], query: string, options?: FuzzyOptions): ScoredResult<string>[];
15
+ export declare function fuzzyScore<T>(array: readonly T[], query: string, options: FuzzySelection<T>): ScoredResult<T>[];
85
16
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/array/search.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAC5B,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;IACzC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gJAAgJ;IAChJ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAsDF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,YAAY,CAAC,CAAC,CAAM,GAAG,CAAC,EAAE,CAqB5F;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,YAAY,CAAC,CAAC,CAAM,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE,CAyBzG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;AACpH,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG;IAAE,MAAM,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG,CAAC,EAAE,CAAC"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/array/search.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,YAAY,GAAG;IAC7C,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;CACjD,CAAC;AA+BF,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,EAAE,CAAC;AACvG,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;AAepG,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;AACpH,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC"}
@@ -1,47 +1,30 @@
1
- import { ArsenalValidationError as e } from "../errors.js";
2
- import { isString as t } from "../guards/isString.js";
3
- import { isNil as n } from "../guards/isNil.js";
4
- import { isNumber as r } from "../guards/isNumber.js";
5
- import { similarity as i } from "../string/similarity.js";
1
+ import { similarity as e } from "../string/similarity.js";
6
2
  //#region src/array/search.ts
7
- var a = 10, o = 1e3;
8
- function s(t) {
9
- if (t === void 0) return a;
10
- if (!Number.isInteger(t) || t < 0) throw new e(`fuzzy: maxDepth must be a non-negative integer, got ${t}`);
11
- return Math.min(t, o);
3
+ var t = (e) => e.normalize("NFKD").replace(/\p{M}/gu, ""), n = (e, t) => {
4
+ let n = t(e);
5
+ return typeof n == "string" ? [n] : n;
6
+ }, r = (e, n) => {
7
+ let r = e.trim().toLowerCase();
8
+ return n ? t(r) : r;
9
+ }, i = (r, i, a, o) => n(r, o).reduce((n, r) => {
10
+ let o = a ? t(r.toLowerCase()) : r.toLowerCase();
11
+ return Math.max(n, e(o, i));
12
+ }, 0), a = (e) => e;
13
+ function o(e, t, n = {}) {
14
+ let { normalize: o = !1, threshold: s = .25 } = n, c = "select" in n ? n.select : a, l = r(t, o);
15
+ return l ? e.filter((e) => i(e, l, o, c) >= s) : [...e];
12
16
  }
13
- var c = (e) => e.normalize("NFKD").replace(/\p{M}/gu, "");
14
- function l(e, a, o, s, u = 0) {
15
- return u > s || n(e) ? 0 : t(e) || r(e) ? i(o ? c(String(e)) : String(e), a) : Array.isArray(e) ? e.reduce((e, t) => Math.max(e, l(t, a, o, s, u + 1)), 0) : typeof e == "object" ? Object.values(e).reduce((e, t) => Math.max(e, l(t, a, o, s, u + 1)), 0) : 0;
16
- }
17
- function u(e, t, n, r, i) {
18
- return l(e, t, r, i) >= n;
19
- }
20
- function d(e, t, n = {}) {
21
- let { fields: r, normalize: i = !1, threshold: a = .25 } = n, o = s(n.maxDepth);
22
- if (!t) return [...e];
23
- let l = t.trim().toLowerCase();
24
- if (!l) return [...e];
25
- let d = i ? c(l) : l;
26
- return e.filter((e) => r && r.length > 0 && typeof e == "object" && e ? r.some((t) => u(e[t], d, a, i, o)) : u(e, d, a, i, o));
27
- }
28
- function f(e, t, n = {}) {
29
- let { fields: r, normalize: i = !1, threshold: a = .25 } = n, o = s(n.maxDepth);
30
- if (!t || !t.trim()) return e.map((e) => ({
17
+ function s(e, t, n = {}) {
18
+ let { normalize: o = !1, threshold: s = .25 } = n, c = "select" in n ? n.select : a, l = r(t, o);
19
+ return l ? e.map((e) => ({
20
+ item: e,
21
+ score: i(e, l, o, c)
22
+ })).filter((e) => e.score >= s).sort((e, t) => t.score - e.score) : e.map((e) => ({
31
23
  item: e,
32
24
  score: 1
33
25
  }));
34
- let u = t.trim().toLowerCase(), d = i ? c(u) : u;
35
- return e.map((e) => ({
36
- item: e,
37
- score: r && r.length > 0 && typeof e == "object" && e ? r.reduce((t, n) => Math.max(t, l(e[n], d, i, o)), 0) : l(e, d, i, o)
38
- })).filter(({ score: e }) => e >= a).sort((e, t) => t.score - e.score);
39
- }
40
- function p(e, t, n = {}) {
41
- let { scored: r, ...i } = n;
42
- return r ? f(e, t, i) : d(e, t, i);
43
26
  }
44
27
  //#endregion
45
- export { p as fuzzy, d as fuzzyFilter, f as fuzzyScore };
28
+ export { o as fuzzyFilter, s as fuzzyScore };
46
29
 
47
30
  //# sourceMappingURL=search.js.map