superjs-core 0.3.9 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,179 +1,167 @@
1
1
  # superjs-core
2
2
 
3
- > **All-in-one JavaScript toolkit — Standard Library + Dependency Scanner + 🇮🇩 Indonesia Validation + Logger + Typed Errors**
3
+ > **JavaScript toolkit all-in-one buat developer Indonesia — Standard Library + Dependency Scanner + 🇮🇩 Validasi NIK/NPWP/Phone + Logger + Typed Errors**
4
4
 
5
5
  ```bash
6
6
  npm install superjs-core
7
7
  ```
8
8
 
9
- One package for all your JavaScript needs: utility functions, async helpers, crypto, path manipulation, typed errors, structured logging, **plus** dependency health scanner and Indonesia-specific data validation (NIK, NPWP, Phone).
9
+ Satu package buat semua kebutuhan JavaScript lo: utility functions, async helpers, crypto, path manipulation, typed errors, structured logging, **plus** dependency health scanner dan validasi data Indonesia (NIK, NPWP, Phone).
10
+
11
+ **100% buat programmer Indonesia. Zero dependency runtime.**
10
12
 
11
13
  ---
12
14
 
13
- ## Features
15
+ ## Fitur Unggulan 🇮🇩
14
16
 
15
- - **90+ functions** — 16 modules covering everything from `deepClone` to `terbilang`
16
- - ✅ **Tree-shakeable** — import only what you need via subpath exports
17
- - **TypeScript strict** — full type safety, zero `any`
18
- - **Zero runtime dependencies**commander + picocolors are CLI-only
19
- - **ESM-first** target ES2022, optimized for Node 18+ and modern browsers
20
- - **Biome linted** consistent code style enforced
17
+ | Fitur | Fungsi |
18
+ |-------|--------|
19
+ | **Validasi NIK** | `isNIK('3201010203940001')` validasi 16 digit + tanggal lahir |
20
+ | **Validasi NPWP** | `isNPWP('12.345.678.9-012.344')`dengan checksum otomatis |
21
+ | **Validasi Nomor HP** | `isPhone('08123456789')` support semua prefix Indonesia |
22
+ | **Terbilang** | `terbilang(1500000)` "satu juta lima ratus ribu" |
23
+ | **Format Rupiah** | `formatRupiah(1500000)` → "Rp1.500.000" |
24
+ | **Format Waktu** | `timeAgo(new Date(...))` → "5 detik yang lalu" |
25
+ | **Timezone WIB/WITA/WIT** | `formatInTimezone(date, 'HH', TIMEZONE_WIB)` → "07" |
26
+ | **Dependency Scanner** | `npx dep-exray .` — scan project lo |
21
27
 
22
28
  ---
23
29
 
24
- ## Modules
30
+ ## 16 Modules
25
31
 
26
- | Module | Key Functions |
27
- |--------|---------------|
28
- | **core** | deepClone, deepMerge, debounce, throttle, memoize, retry, once |
29
- | **math** | add/sub/mul/div (safe float), median, stddev, percentile, correlation, formatCurrency |
30
- | **date** | formatDate, parseDate, timeAgo, Duration, timezone helpers (WIB/WITA/WIT) |
31
- | **collection** | sortBy, groupBy, shuffle, topoSort, slidingWindows, chunk |
32
- | **string** | camelCase, uuid, nanoid, slugify, levenshtein, fuzzyMatch, maskString |
32
+ | Module | Fungsi Unggulan |
33
+ |--------|----------------|
34
+ | **core** | deepClone, deepMerge, debounce, deepEqual, pipe, throttle, memoize |
35
+ | **math** | add/sub/mul/div (safe float), median, stddev, percentile, formatCurrency |
36
+ | **date** | formatDate, timeAgo, Duration, timezone helpers (WIB/WITA/WIT) |
37
+ | **collection** | sortBy, groupBy, shuffle, topoSort, slidingWindows, deepGet, deepSet |
38
+ | **string** | camelCase, uuid, nanoid, slugify, **terbilang**, **formatRupiah**, maskString, formatBytes |
33
39
  | **async** | sleep, parallelMap, Queue, Semaphore, memoizeAsync, retryAsync |
34
- | **io** | parseCsv, stringifyCsv, safeJsonParse, env, envInt, envBool |
40
+ | **io** | parseCsv, stringifyCsv, safeJsonParse, env |
35
41
  | **type** | 20+ type guards (isString, isNil, assertDefined, getType) |
36
42
  | **crypto** | hash, base64, generateToken, generateOTP, constantTimeEqual |
37
43
  | **path** | join, resolve, basename, dirname, extname, normalize |
38
- | **validation** | isNIK, isNPWP, isPhone("id"), isEmail, isURL |
44
+ | **color** | hexToRgb, rgbToHex, lighten, darken, contrastRatio, meetsWCAG |
45
+ | **validation** | **isNIK**, **isNPWP**, **isPhone("id")**, isEmail, isURL |
39
46
  | **error** | createError (typed + HTTP status), TypedError, MultiError |
40
47
  | **logger** | Logger class, child loggers, console/JSON/file transports |
41
48
  | **dep-exray** | scanProject, generateReport, analyzeUsage, CLI: `npx dep-exray .` |
42
49
 
43
- ### 🇮🇩 Indonesian Locale
44
-
45
- | Function | Description |
46
- |----------|-------------|
47
- | `terbilang(value)` | Convert numbers to Indonesian words ("satu juta lima ratus ribu") |
48
- | `formatRupiah(value)` | Format as Rupiah ("Rp1.500.000") |
49
- | `isNIK(value)` | Validate Indonesian NIK (16-digit ID number) |
50
- | `isNPWP(value)` | Validate Indonesian NPWP (tax ID with checksum) |
51
- | `isPhone(value)` | Validate Indonesian phone numbers |
52
-
53
50
  ---
54
51
 
55
- ## Quick Examples
52
+ ## Contoh Kode
56
53
 
57
54
  ```typescript
58
- import { deepClone, debounce } from "superjs-core"
59
- import { formatDate, timeAgo } from "superjs-core/date"
60
- import { groupBy, topoSort } from "superjs-core/collection"
55
+ import { deepClone } from "superjs-core"
56
+ import { formatDate, timeAgo, TIMEZONE_WIB } from "superjs-core/date"
61
57
  import { Queue } from "superjs-core/async"
62
- import { uuid, maskString, terbilang, formatRupiah } from "superjs-core/string"
63
- import { generateToken } from "superjs-core/crypto"
58
+ import { uuid, maskString, terbilang, formatRupiah, formatBytes } from "superjs-core/string"
64
59
  import { isNIK, isNPWP, isPhone } from "superjs-core/validation"
65
60
  import { createError } from "superjs-core/error"
66
61
  import { Logger } from "superjs-core/logger"
67
- import { median, stddev, formatCurrency } from "superjs-core/math"
68
- import { scanProject } from "superjs-core/dep-exray"
62
+ import { hexToRgb, contrastRatio } from "superjs-core/color"
69
63
 
70
- // Deep clone with circular reference support
71
- const cloned = deepClone({ a: 1, b: { c: new Date() } })
72
-
73
- // Safe math (0.1 + 0.2 = 0.3 ✅)
74
- import { add } from "superjs-core/math"
75
- console.log(add(0.1, 0.2)) // 0.3
64
+ // Validasi data Indonesia
65
+ isNIK("3201010203940001") // true
66
+ isNPWP("12.345.678.9-012.344") // true
67
+ isPhone("08123456789") // true
76
68
 
77
- // Date formatting without moment
78
- console.log(formatDate(new Date(), "DD/MM/YYYY")) // "28/06/2026"
79
- console.log(timeAgo(new Date(Date.now() - 5000))) // "5 seconds ago"
69
+ // Konversi angka ke kata
70
+ terbilang(1500000) // "satu juta lima ratus ribu"
71
+ formatRupiah(1500000) // "Rp1.500.000"
80
72
 
81
- // Priority task queue
82
- const queue = new Queue({ concurrency: 2 })
83
- await queue.add(() => fetch("/api/data"))
73
+ // Relative time
74
+ timeAgo(new Date(Date.now() - 5000)) // "5 detik yang lalu"
84
75
 
85
- // Indonesia validation
86
- isNIK("3201010203940001") // true
87
- isNPWP("12.345.678.9-012.344") // true
88
- isPhone("08123456789") // true
76
+ // Mask data sensitif (PDPA compliance)
77
+ maskString("08123456789") // "081*****789"
89
78
 
90
- // Indonesian locale
91
- terbilang(1500000) // "satu juta lima ratus ribu"
92
- formatRupiah(1500000) // "Rp1.500.000"
79
+ // Format file size
80
+ formatBytes(1048576) // "1 MB"
93
81
 
94
- // Statistics
95
- median([1, 2, 3, 4, 5]) // 3
96
- percentile([1, 2, 3, 4, 5], 90) // 4.6
97
- formatCurrency(1500000, { locale: "en-US", currency: "USD" }) // "$1,500,000"
82
+ // Color utilities
83
+ hexToRgb("#ff0000") // { r: 255, g: 0, b: 0 }
84
+ contrastRatio("#000000", "#ffffff") // 21
98
85
 
99
86
  // Typed errors
100
- throw createError("VALIDATION_ERROR", "Email required", { details: { field: "email" } })
87
+ throw createError("VALIDATION_ERROR", "Email wajib diisi")
101
88
 
102
89
  // Structured logger
103
90
  const log = new Logger({ level: "info", name: "app" })
104
91
  log.info("Server started", { port: 3000 })
105
-
106
- // Dependency scanning
107
- const report = await scanProject({ path: "./my-project" })
108
- console.log(report.totalEstimatedSize) // "2.3 MB"
109
92
  ```
110
93
 
111
94
  ---
112
95
 
113
- ## dep-exray — Dependency Health Scanner (built-in)
96
+ ## dep-exray — Dependency Health Scanner
114
97
 
115
- **Scan your project to find bloated, unused, or vulnerable dependencies.**
98
+ **Scan project lo buat nemuin dependency yang gak kepake, bloated, atau punya CVE.**
116
99
 
117
100
  ```bash
118
101
  npx dep-exray .
119
102
  npx dep-exray /path/to/project --json --verbose
120
103
  ```
121
104
 
122
- ### Features
123
- - Detect replacements: lodash → superjs-core, moment → superjs-core/date, uuid → native crypto.randomUUID()
124
- - Estimate dependency size in MB/KB
125
- - CVE detection from known vulnerability database
126
- - JSON output for CI/CD integration
127
- - Usage analyzer — detects whether dependencies are actually imported
105
+ ### Fitur
106
+ - Deteksi replacement: lodash → superjs-core, moment → superjs-core/date, uuid → native crypto.randomUUID()
107
+ - Estimasi ukuran dependency
108
+ - CVE detection
109
+ - JSON output untuk CI/CD
110
+ - Usage analyzer
111
+
112
+ ---
113
+
114
+ ## Quick Start
115
+
116
+ ```bash
117
+ git clone https://github.com/superdevids/superjs.git
118
+ cd superjs/packages/core
119
+ npm install
120
+ npx tsup # Build
121
+ npx vitest run # Test (810 tests)
122
+ npx dep-exray . # Scan project sendiri
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Statistik Test
128
+
129
+ | File Tes | Jumlah |
130
+ |----------|--------|
131
+ | 18 file | **810** passing ✅ |
128
132
 
129
133
  ---
130
134
 
131
- ## Project Structure
135
+ ## Struktur Project
132
136
 
133
137
  ```
134
138
  packages/core/
135
139
  ├── src/
136
- │ ├── core/ # deepClone, debounce, retry, once
140
+ │ ├── core/ # deepClone, debounce, deepEqual, pipe
137
141
  │ ├── math/ # add, median, stddev, formatCurrency
138
- │ ├── date/ # formatDate, timeAgo, Duration, timezone
139
- │ ├── collection/ # groupBy, topoSort, slidingWindows
142
+ │ ├── date/ # formatDate, timeAgo, Duration, WIB/WITA/WIT
143
+ │ ├── collection/ # groupBy, topoSort, deepGet, deepSet
140
144
  │ ├── string/ # camelCase, terbilang, formatRupiah
141
145
  │ ├── async/ # sleep, Queue, Semaphore, memoizeAsync
142
146
  │ ├── io/ # parseCsv, safeJsonParse, env
143
147
  │ ├── type/ # 20+ type guards
144
148
  │ ├── crypto/ # hash, generateToken, base64
145
149
  │ ├── path/ # join, resolve, basename
150
+ │ ├── color/ # hexToRgb, lighten, darken, contrastRatio
146
151
  │ ├── validation/ # isNIK, isNPWP, isPhone, isEmail, isURL
147
152
  │ ├── error/ # createError, TypedError, MultiError
148
153
  │ ├── logger/ # Logger, transports
149
154
  │ └── dep-exray/ # Dependency scanner
150
- ├── tests/ # 757 tests
151
- ├── dist/ # Built output
152
- ├── tsup.config.ts
153
- ├── vitest.config.ts
154
- ├── biome.json
155
+ ├── tests/ # 810 tests
156
+ ├── dist/ # Hasil build
155
157
  └── package.json
156
158
  ```
157
159
 
158
160
  ---
159
161
 
160
- ## Test Stats
161
-
162
- | Test Files | Tests |
163
- |-----------|-------|
164
- | 17 | **757** passing |
165
-
166
- ---
167
-
168
162
  ## Roadmap
169
163
 
170
- See [ROADMAP.md](./ROADMAP.md) for full details.
171
-
172
- ### Priority
173
- - **P0 ✅** validation, error, logger modules
174
- - **P1 ✅** async (Queue, Semaphore), math (stats), string (terbilang), collection (topoSort), date (timeAgo)
175
- - **P2** core (pipe/compose, Result type), signal module, crypto (AES-GCM)
176
- - **P3** ml, color modules
164
+ Lihat [ROADMAP.md](./ROADMAP.md) untuk detail lengkap.
177
165
 
178
166
  ---
179
167
 
@@ -77,5 +77,20 @@ declare function topoSort<T extends {
77
77
  }>(items: T[]): T[];
78
78
  declare function slidingWindows<T>(items: T[], size: number, step?: number): T[][];
79
79
  declare function tumblingWindows<T>(items: T[], size: number): T[][];
80
+ /**
81
+ * Gets a nested value from an object using a dot-separated path.
82
+ *
83
+ * @example deepGet({ a: { b: 2 } }, 'a.b') // 2
84
+ * @example deepGet({ a: { b: 2 } }, 'a.c') // undefined
85
+ */
86
+ declare function deepGet<T = unknown>(obj: unknown, path: string, default_?: T): T | undefined;
87
+ /**
88
+ * Sets a nested value in an object using a dot-separated path.
89
+ * Creates intermediate objects/arrays as needed.
90
+ *
91
+ * @example deepSet({ a: { b: 2 } }, 'a.b', 3) // { a: { b: 3 } }
92
+ * @example deepSet({}, 'a.b.c', 1) // { a: { b: { c: 1 } } }
93
+ */
94
+ declare function deepSet<T extends Record<string, unknown>>(obj: T, path: string, value: unknown): T;
80
95
 
81
- export { type SortDirection, chunk, first, flatten, groupBy, isEmpty, keyBy, last, omit, orderBy, pick, pluck, sample, sampleSize, shuffle, slidingWindows, sortBy, topoSort, tumblingWindows, uniq, uniqueBy };
96
+ export { type SortDirection, chunk, deepGet, deepSet, first, flatten, groupBy, isEmpty, keyBy, last, omit, orderBy, pick, pluck, sample, sampleSize, shuffle, slidingWindows, sortBy, topoSort, tumblingWindows, uniq, uniqueBy };
@@ -170,8 +170,38 @@ function tumblingWindows(items, size) {
170
170
  }
171
171
  return result;
172
172
  }
173
+ function deepGet(obj, path, default_) {
174
+ const keys = path.split(".");
175
+ let current = obj;
176
+ for (const key of keys) {
177
+ if (current === null || current === void 0) return default_;
178
+ if (typeof current !== "object") return default_;
179
+ const curObj = current;
180
+ if (!(key in curObj)) return default_;
181
+ current = curObj[key];
182
+ }
183
+ return current ?? default_;
184
+ }
185
+ function deepSet(obj, path, value) {
186
+ const keys = path.split(".");
187
+ const result = { ...obj };
188
+ let current = result;
189
+ for (let i = 0; i < keys.length - 1; i++) {
190
+ const key = keys[i];
191
+ const next = current[key];
192
+ if (next === null || next === void 0 || typeof next !== "object") {
193
+ const isArray = /^\d+$/.test(keys[i + 1]);
194
+ current[key] = isArray ? [] : {};
195
+ }
196
+ current = current[key];
197
+ }
198
+ current[keys[keys.length - 1]] = value;
199
+ return result;
200
+ }
173
201
  export {
174
202
  chunk,
203
+ deepGet,
204
+ deepSet,
175
205
  first,
176
206
  flatten,
177
207
  groupBy,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/collection/index.ts"],"sourcesContent":["/**\r\n * Groups an array of items by a key extracted from each item.\r\n *\r\n * @example groupBy([{ type: 'a' }, { type: 'b' }, { type: 'a' }], x => x.type)\r\n * // => { a: [{ type: 'a' }, { type: 'a' }], b: [{ type: 'b' }] }\r\n */\r\nexport function groupBy<T, K extends string | number | symbol>(items: T[], keyFn: (item: T) => K): Record<K, T[]> {\r\n const result = {} as Record<K, T[]>\r\n for (const item of items) {\r\n const key = keyFn(item)\r\n if (!result[key]) result[key] = []\r\n result[key]!.push(item)\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Creates an object keyed by the result of keyFn for each item.\r\n *\r\n * @example keyBy([{ id: 1, name: 'a' }, { id: 2, name: 'b' }], x => x.id)\r\n * // => { 1: { id: 1, name: 'a' }, 2: { id: 2, name: 'b' } }\r\n */\r\nexport function keyBy<T, K extends string | number | symbol>(items: T[], keyFn: (item: T) => K): Record<K, T> {\r\n const result = {} as Record<K, T>\r\n for (const item of items) {\r\n const key = keyFn(item)\r\n result[key] = item\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Returns a copy of the object with the specified keys omitted.\r\n */\r\nexport function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {\r\n const result = { ...obj } as T\r\n for (const key of keys) {\r\n delete result[key]\r\n }\r\n return result as Omit<T, K>\r\n}\r\n\r\n/**\r\n * Returns a copy of the object with only the specified keys.\r\n */\r\nexport function pick<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {\r\n const result = {} as Pick<T, K>\r\n for (const key of keys) {\r\n if (key in obj) {\r\n result[key] = obj[key]\r\n }\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Extracts a property value from each item in an array.\r\n */\r\nexport function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {\r\n return items.map(item => item[key])\r\n}\r\n\r\n/**\r\n * Randomizes array order in-place using the Fisher-Yates algorithm.\r\n */\r\nexport function shuffle<T>(items: T[]): T[] {\r\n const result = [...items]\r\n for (let i = result.length - 1; i > 0; i--) {\r\n const j = Math.floor(Math.random() * (i + 1))\r\n const temp = result[i]!\r\n result[i] = result[j]!\r\n result[j] = temp\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Returns a random element from the array, or undefined if empty.\r\n */\r\nexport function sample<T>(items: T[]): T | undefined {\r\n return items.length > 0 ? items[Math.floor(Math.random() * items.length)] : undefined\r\n}\r\n\r\n/**\r\n * Returns an array of `size` random elements (without duplicates).\r\n */\r\nexport function sampleSize<T>(items: T[], size: number): T[] {\r\n if (size <= 0 || items.length === 0) return []\r\n const pool = shuffle(items)\r\n return pool.slice(0, Math.min(size, items.length))\r\n}\r\n\r\n/**\r\n * Splits an array into chunks of the specified size.\r\n */\r\nexport function chunk<T>(items: T[], size: number): T[][] {\r\n if (size <= 0 || items.length === 0) return []\r\n const result: T[][] = []\r\n for (let i = 0; i < items.length; i += size) {\r\n result.push(items.slice(i, i + size))\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Returns a sorted copy of the array using the provided criteria functions.\r\n * Earlier criteria take precedence.\r\n */\r\nfunction compareValues(a: unknown, b: unknown): number {\r\n if (a === b) return 0\r\n if (a == null) return -1\r\n if (b == null) return 1\r\n if (typeof a === 'string' && typeof b === 'string') return a < b ? -1 : 1\r\n if (typeof a === 'number' && typeof b === 'number') return a < b ? -1 : 1\r\n return String(a) < String(b) ? -1 : 1\r\n}\r\n\r\nexport function sortBy<T>(items: T[], ...criteria: Array<(item: T) => unknown>): T[] {\r\n return [...items].sort((a, b) => {\r\n for (const criterion of criteria) {\r\n const cmp = compareValues(criterion(a), criterion(b))\r\n if (cmp !== 0) return cmp\r\n }\r\n return 0\r\n })\r\n}\r\n\r\n/**\r\n * Returns a sorted copy of the array by a single key and direction.\r\n */\r\nexport function orderBy<T>(items: T[], key: (item: T) => unknown, direction: SortDirection = 'asc'): T[] {\r\n return [...items].sort((a, b) => {\r\n const cmp = compareValues(key(a), key(b))\r\n return direction === 'asc' ? cmp : -cmp\r\n })\r\n}\r\n\r\n/**\r\n * Returns unique elements based on the result of keyFn.\r\n */\r\nexport function uniqueBy<T>(items: T[], keyFn: (item: T) => unknown): T[] {\r\n const seen = new Set<unknown>()\r\n return items.filter(item => {\r\n const key = keyFn(item)\r\n if (seen.has(key)) return false\r\n seen.add(key)\r\n return true\r\n })\r\n}\r\n\r\n/**\r\n * Flattens an array of arrays one level.\r\n */\r\nexport function flatten<T>(items: T[][]): T[] {\r\n const result: T[] = []\r\n for (const sub of items) {\r\n for (const item of sub) {\r\n result.push(item)\r\n }\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Returns an array with unique primitive values.\r\n */\r\nexport function uniq<T>(items: T[]): T[] {\r\n return [...new Set(items)]\r\n}\r\n\r\n/**\r\n * Returns the first element, or undefined if empty.\r\n */\r\nexport function first<T>(items: T[]): T | undefined {\r\n return items[0]\r\n}\r\n\r\n/**\r\n * Returns the last element, or undefined if empty.\r\n */\r\nexport function last<T>(items: T[]): T | undefined {\r\n return items[items.length - 1]\r\n}\r\n\r\n/**\r\n * Checks if a value is empty.\r\n * Works for arrays, objects, strings, Map, and Set.\r\n */\r\nexport function isEmpty(value: unknown): boolean {\r\n if (Array.isArray(value)) return value.length === 0\r\n if (typeof value === 'string') return value.length === 0\r\n if (value instanceof Map || value instanceof Set) return value.size === 0\r\n if (value !== null && typeof value === 'object') return Object.keys(value as Record<string, unknown>).length === 0\r\n return false\r\n}\r\n\r\nexport type SortDirection = 'asc' | 'desc'\r\n\r\nexport function topoSort<T extends { id: string; dependencies?: string[] }>(items: T[]): T[] {\r\n const adj = new Map<string, string[]>()\r\n const inDegree = new Map<string, number>()\r\n const itemMap = new Map<string, T>()\r\n\r\n for (const item of items) {\r\n itemMap.set(item.id, item)\r\n if (!adj.has(item.id)) adj.set(item.id, [])\r\n if (!inDegree.has(item.id)) inDegree.set(item.id, 0)\r\n }\r\n\r\n for (const item of items) {\r\n if (item.dependencies) {\r\n for (const depId of item.dependencies) {\r\n adj.get(depId)?.push(item.id)\r\n inDegree.set(item.id, (inDegree.get(item.id) ?? 0) + 1)\r\n }\r\n }\r\n }\r\n\r\n const queue: string[] = []\r\n for (const [id, degree] of inDegree) {\r\n if (degree === 0) queue.push(id)\r\n }\r\n\r\n const sorted: string[] = []\r\n while (queue.length > 0) {\r\n const id = queue.shift()!\r\n sorted.push(id)\r\n for (const neighbor of adj.get(id) ?? []) {\r\n const newDegree = (inDegree.get(neighbor) ?? 1) - 1\r\n inDegree.set(neighbor, newDegree)\r\n if (newDegree === 0) queue.push(neighbor)\r\n }\r\n }\r\n\r\n if (sorted.length !== items.length) {\r\n throw new Error('Circular dependency detected')\r\n }\r\n\r\n return sorted.map(id => itemMap.get(id)!)\r\n}\r\n\r\nexport function slidingWindows<T>(items: T[], size: number, step: number = 1): T[][] {\r\n if (size <= 0 || items.length === 0 || step <= 0) return []\r\n const result: T[][] = []\r\n for (let i = 0; i + size <= items.length; i += step) {\r\n result.push(items.slice(i, i + size))\r\n }\r\n return result\r\n}\r\n\r\nexport function tumblingWindows<T>(items: T[], size: number): T[][] {\r\n if (size <= 0 || items.length === 0) return []\r\n const result: T[][] = []\r\n for (let i = 0; i < items.length; i += size) {\r\n result.push(items.slice(i, i + size))\r\n }\r\n return result\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/collection/index.ts"],"sourcesContent":["/**\r\n * Groups an array of items by a key extracted from each item.\r\n *\r\n * @example groupBy([{ type: 'a' }, { type: 'b' }, { type: 'a' }], x => x.type)\r\n * // => { a: [{ type: 'a' }, { type: 'a' }], b: [{ type: 'b' }] }\r\n */\r\nexport function groupBy<T, K extends string | number | symbol>(items: T[], keyFn: (item: T) => K): Record<K, T[]> {\r\n const result = {} as Record<K, T[]>\r\n for (const item of items) {\r\n const key = keyFn(item)\r\n if (!result[key]) result[key] = []\r\n result[key]!.push(item)\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Creates an object keyed by the result of keyFn for each item.\r\n *\r\n * @example keyBy([{ id: 1, name: 'a' }, { id: 2, name: 'b' }], x => x.id)\r\n * // => { 1: { id: 1, name: 'a' }, 2: { id: 2, name: 'b' } }\r\n */\r\nexport function keyBy<T, K extends string | number | symbol>(items: T[], keyFn: (item: T) => K): Record<K, T> {\r\n const result = {} as Record<K, T>\r\n for (const item of items) {\r\n const key = keyFn(item)\r\n result[key] = item\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Returns a copy of the object with the specified keys omitted.\r\n */\r\nexport function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {\r\n const result = { ...obj } as T\r\n for (const key of keys) {\r\n delete result[key]\r\n }\r\n return result as Omit<T, K>\r\n}\r\n\r\n/**\r\n * Returns a copy of the object with only the specified keys.\r\n */\r\nexport function pick<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {\r\n const result = {} as Pick<T, K>\r\n for (const key of keys) {\r\n if (key in obj) {\r\n result[key] = obj[key]\r\n }\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Extracts a property value from each item in an array.\r\n */\r\nexport function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {\r\n return items.map(item => item[key])\r\n}\r\n\r\n/**\r\n * Randomizes array order in-place using the Fisher-Yates algorithm.\r\n */\r\nexport function shuffle<T>(items: T[]): T[] {\r\n const result = [...items]\r\n for (let i = result.length - 1; i > 0; i--) {\r\n const j = Math.floor(Math.random() * (i + 1))\r\n const temp = result[i]!\r\n result[i] = result[j]!\r\n result[j] = temp\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Returns a random element from the array, or undefined if empty.\r\n */\r\nexport function sample<T>(items: T[]): T | undefined {\r\n return items.length > 0 ? items[Math.floor(Math.random() * items.length)] : undefined\r\n}\r\n\r\n/**\r\n * Returns an array of `size` random elements (without duplicates).\r\n */\r\nexport function sampleSize<T>(items: T[], size: number): T[] {\r\n if (size <= 0 || items.length === 0) return []\r\n const pool = shuffle(items)\r\n return pool.slice(0, Math.min(size, items.length))\r\n}\r\n\r\n/**\r\n * Splits an array into chunks of the specified size.\r\n */\r\nexport function chunk<T>(items: T[], size: number): T[][] {\r\n if (size <= 0 || items.length === 0) return []\r\n const result: T[][] = []\r\n for (let i = 0; i < items.length; i += size) {\r\n result.push(items.slice(i, i + size))\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Returns a sorted copy of the array using the provided criteria functions.\r\n * Earlier criteria take precedence.\r\n */\r\nfunction compareValues(a: unknown, b: unknown): number {\r\n if (a === b) return 0\r\n if (a == null) return -1\r\n if (b == null) return 1\r\n if (typeof a === 'string' && typeof b === 'string') return a < b ? -1 : 1\r\n if (typeof a === 'number' && typeof b === 'number') return a < b ? -1 : 1\r\n return String(a) < String(b) ? -1 : 1\r\n}\r\n\r\nexport function sortBy<T>(items: T[], ...criteria: Array<(item: T) => unknown>): T[] {\r\n return [...items].sort((a, b) => {\r\n for (const criterion of criteria) {\r\n const cmp = compareValues(criterion(a), criterion(b))\r\n if (cmp !== 0) return cmp\r\n }\r\n return 0\r\n })\r\n}\r\n\r\n/**\r\n * Returns a sorted copy of the array by a single key and direction.\r\n */\r\nexport function orderBy<T>(items: T[], key: (item: T) => unknown, direction: SortDirection = 'asc'): T[] {\r\n return [...items].sort((a, b) => {\r\n const cmp = compareValues(key(a), key(b))\r\n return direction === 'asc' ? cmp : -cmp\r\n })\r\n}\r\n\r\n/**\r\n * Returns unique elements based on the result of keyFn.\r\n */\r\nexport function uniqueBy<T>(items: T[], keyFn: (item: T) => unknown): T[] {\r\n const seen = new Set<unknown>()\r\n return items.filter(item => {\r\n const key = keyFn(item)\r\n if (seen.has(key)) return false\r\n seen.add(key)\r\n return true\r\n })\r\n}\r\n\r\n/**\r\n * Flattens an array of arrays one level.\r\n */\r\nexport function flatten<T>(items: T[][]): T[] {\r\n const result: T[] = []\r\n for (const sub of items) {\r\n for (const item of sub) {\r\n result.push(item)\r\n }\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Returns an array with unique primitive values.\r\n */\r\nexport function uniq<T>(items: T[]): T[] {\r\n return [...new Set(items)]\r\n}\r\n\r\n/**\r\n * Returns the first element, or undefined if empty.\r\n */\r\nexport function first<T>(items: T[]): T | undefined {\r\n return items[0]\r\n}\r\n\r\n/**\r\n * Returns the last element, or undefined if empty.\r\n */\r\nexport function last<T>(items: T[]): T | undefined {\r\n return items[items.length - 1]\r\n}\r\n\r\n/**\r\n * Checks if a value is empty.\r\n * Works for arrays, objects, strings, Map, and Set.\r\n */\r\nexport function isEmpty(value: unknown): boolean {\r\n if (Array.isArray(value)) return value.length === 0\r\n if (typeof value === 'string') return value.length === 0\r\n if (value instanceof Map || value instanceof Set) return value.size === 0\r\n if (value !== null && typeof value === 'object') return Object.keys(value as Record<string, unknown>).length === 0\r\n return false\r\n}\r\n\r\nexport type SortDirection = 'asc' | 'desc'\r\n\r\nexport function topoSort<T extends { id: string; dependencies?: string[] }>(items: T[]): T[] {\r\n const adj = new Map<string, string[]>()\r\n const inDegree = new Map<string, number>()\r\n const itemMap = new Map<string, T>()\r\n\r\n for (const item of items) {\r\n itemMap.set(item.id, item)\r\n if (!adj.has(item.id)) adj.set(item.id, [])\r\n if (!inDegree.has(item.id)) inDegree.set(item.id, 0)\r\n }\r\n\r\n for (const item of items) {\r\n if (item.dependencies) {\r\n for (const depId of item.dependencies) {\r\n adj.get(depId)?.push(item.id)\r\n inDegree.set(item.id, (inDegree.get(item.id) ?? 0) + 1)\r\n }\r\n }\r\n }\r\n\r\n const queue: string[] = []\r\n for (const [id, degree] of inDegree) {\r\n if (degree === 0) queue.push(id)\r\n }\r\n\r\n const sorted: string[] = []\r\n while (queue.length > 0) {\r\n const id = queue.shift()!\r\n sorted.push(id)\r\n for (const neighbor of adj.get(id) ?? []) {\r\n const newDegree = (inDegree.get(neighbor) ?? 1) - 1\r\n inDegree.set(neighbor, newDegree)\r\n if (newDegree === 0) queue.push(neighbor)\r\n }\r\n }\r\n\r\n if (sorted.length !== items.length) {\r\n throw new Error('Circular dependency detected')\r\n }\r\n\r\n return sorted.map(id => itemMap.get(id)!)\r\n}\r\n\r\nexport function slidingWindows<T>(items: T[], size: number, step: number = 1): T[][] {\r\n if (size <= 0 || items.length === 0 || step <= 0) return []\r\n const result: T[][] = []\r\n for (let i = 0; i + size <= items.length; i += step) {\r\n result.push(items.slice(i, i + size))\r\n }\r\n return result\r\n}\r\n\r\nexport function tumblingWindows<T>(items: T[], size: number): T[][] {\r\n if (size <= 0 || items.length === 0) return []\r\n const result: T[][] = []\r\n for (let i = 0; i < items.length; i += size) {\r\n result.push(items.slice(i, i + size))\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Gets a nested value from an object using a dot-separated path.\r\n *\r\n * @example deepGet({ a: { b: 2 } }, 'a.b') // 2\r\n * @example deepGet({ a: { b: 2 } }, 'a.c') // undefined\r\n */\r\nexport function deepGet<T = unknown>(obj: unknown, path: string, default_?: T): T | undefined {\r\n const keys = path.split('.')\r\n let current: unknown = obj\r\n for (const key of keys) {\r\n if (current === null || current === undefined) return default_\r\n if (typeof current !== 'object') return default_\r\n const curObj = current as Record<string, unknown>\r\n if (!(key in curObj)) return default_\r\n current = curObj[key]\r\n }\r\n return (current as T) ?? default_\r\n}\r\n\r\n/**\r\n * Sets a nested value in an object using a dot-separated path.\r\n * Creates intermediate objects/arrays as needed.\r\n *\r\n * @example deepSet({ a: { b: 2 } }, 'a.b', 3) // { a: { b: 3 } }\r\n * @example deepSet({}, 'a.b.c', 1) // { a: { b: { c: 1 } } }\r\n */\r\nexport function deepSet<T extends Record<string, unknown>>(obj: T, path: string, value: unknown): T {\r\n const keys = path.split('.')\r\n const result = { ...obj } as Record<string, unknown>\r\n let current = result\r\n for (let i = 0; i < keys.length - 1; i++) {\r\n const key = keys[i]!\r\n const next = current[key]\r\n if (next === null || next === undefined || typeof next !== 'object') {\r\n const isArray = /^\\d+$/.test(keys[i + 1]!)\r\n current[key] = isArray ? [] : {}\r\n }\r\n current = current[key] as Record<string, unknown>\r\n }\r\n current[keys[keys.length - 1]!] = value\r\n return result as T\r\n}\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Converts a hex color string to RGB values.
3
+ *
4
+ * @example hexToRgb('#ff0000') // { r: 255, g: 0, b: 0 }
5
+ * @example hexToRgb('#f00') // { r: 255, g: 0, b: 0 }
6
+ * @example hexToRgb('#FF8800') // { r: 255, g: 136, b: 0 }
7
+ */
8
+ declare function hexToRgb(hex: string): {
9
+ r: number;
10
+ g: number;
11
+ b: number;
12
+ } | null;
13
+ /**
14
+ * Converts RGB values to a hex color string.
15
+ *
16
+ * @example rgbToHex(255, 0, 0) // "#ff0000"
17
+ * @example rgbToHex(255, 136, 0) // "#ff8800"
18
+ */
19
+ declare function rgbToHex(r: number, g: number, b: number): string;
20
+ /**
21
+ * Lightens a hex color by a given percentage (0-100).
22
+ *
23
+ * @example lighten('#ff0000', 20) // "#ff3333"
24
+ * @example lighten('#0000ff', 50) // "#7f7fff"
25
+ */
26
+ declare function lighten(hex: string, percent: number): string;
27
+ /**
28
+ * Darkens a hex color by a given percentage (0-100).
29
+ *
30
+ * @example darken('#ff0000', 20) // "#cc0000"
31
+ * @example darken('#00ff00', 50) // "#008000"
32
+ */
33
+ declare function darken(hex: string, percent: number): string;
34
+ /**
35
+ * Checks the WCAG contrast ratio between two hex colors.
36
+ * Returns the ratio as a number (1-21). WCAG AA requires 4.5:1 for normal text.
37
+ *
38
+ * @example contrastRatio('#000000', '#ffffff') // 21
39
+ * @example contrastRatio('#ff0000', '#ffffff') // 3.99
40
+ */
41
+ declare function contrastRatio(hex1: string, hex2: string): number;
42
+ /**
43
+ * Checks if a hex color meets WCAG AA contrast ratio (4.5:1) against another color.
44
+ *
45
+ * @example meetsWCAG('#000000', '#ffffff') // true (black on white)
46
+ * @example meetsWCAG('#999999', '#ffffff') // false (gray on white)
47
+ */
48
+ declare function meetsWCAG(hex1: string, hex2: string, level?: 'AA' | 'AAA'): boolean;
49
+
50
+ export { contrastRatio, darken, hexToRgb, lighten, meetsWCAG, rgbToHex };
@@ -0,0 +1,66 @@
1
+ // src/color/index.ts
2
+ function hexToRgb(hex) {
3
+ let h = hex.replace("#", "");
4
+ if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
5
+ if (h.length !== 6) return null;
6
+ const num = Number.parseInt(h, 16);
7
+ if (isNaN(num)) return null;
8
+ return {
9
+ r: num >> 16 & 255,
10
+ g: num >> 8 & 255,
11
+ b: num & 255
12
+ };
13
+ }
14
+ function rgbToHex(r, g, b) {
15
+ const toHex = (n) => Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, "0");
16
+ return "#" + toHex(r) + toHex(g) + toHex(b);
17
+ }
18
+ function lighten(hex, percent) {
19
+ const rgb = hexToRgb(hex);
20
+ if (!rgb) return hex;
21
+ const factor = percent / 100;
22
+ return rgbToHex(
23
+ rgb.r + (255 - rgb.r) * factor,
24
+ rgb.g + (255 - rgb.g) * factor,
25
+ rgb.b + (255 - rgb.b) * factor
26
+ );
27
+ }
28
+ function darken(hex, percent) {
29
+ const rgb = hexToRgb(hex);
30
+ if (!rgb) return hex;
31
+ const factor = percent / 100;
32
+ return rgbToHex(
33
+ rgb.r * (1 - factor),
34
+ rgb.g * (1 - factor),
35
+ rgb.b * (1 - factor)
36
+ );
37
+ }
38
+ function contrastRatio(hex1, hex2) {
39
+ const lum1 = relativeLuminance(hex1);
40
+ const lum2 = relativeLuminance(hex2);
41
+ const lighter = Math.max(lum1, lum2);
42
+ const darker = Math.min(lum1, lum2);
43
+ return Number(((lighter + 0.05) / (darker + 0.05)).toFixed(2));
44
+ }
45
+ function relativeLuminance(hex) {
46
+ const rgb = hexToRgb(hex);
47
+ if (!rgb) return 0;
48
+ const vals = [rgb.r / 255, rgb.g / 255, rgb.b / 255].map((c) => {
49
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
50
+ });
51
+ return 0.2126 * vals[0] + 0.7152 * vals[1] + 0.0722 * vals[2];
52
+ }
53
+ function meetsWCAG(hex1, hex2, level) {
54
+ const ratio = contrastRatio(hex1, hex2);
55
+ const threshold = level === "AAA" ? 7 : 4.5;
56
+ return ratio >= threshold;
57
+ }
58
+ export {
59
+ contrastRatio,
60
+ darken,
61
+ hexToRgb,
62
+ lighten,
63
+ meetsWCAG,
64
+ rgbToHex
65
+ };
66
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/color/index.ts"],"sourcesContent":["/**\n * Converts a hex color string to RGB values.\n *\n * @example hexToRgb('#ff0000') // { r: 255, g: 0, b: 0 }\n * @example hexToRgb('#f00') // { r: 255, g: 0, b: 0 }\n * @example hexToRgb('#FF8800') // { r: 255, g: 136, b: 0 }\n */\nexport function hexToRgb(hex: string): { r: number; g: number; b: number } | null {\n let h = hex.replace('#', '')\n if (h.length === 3) h = h[0]! + h[0] + h[1]! + h[1] + h[2]! + h[2]\n if (h.length !== 6) return null\n const num = Number.parseInt(h, 16)\n if (isNaN(num)) return null\n return {\n r: (num >> 16) & 255,\n g: (num >> 8) & 255,\n b: num & 255,\n }\n}\n\n/**\n * Converts RGB values to a hex color string.\n *\n * @example rgbToHex(255, 0, 0) // \"#ff0000\"\n * @example rgbToHex(255, 136, 0) // \"#ff8800\"\n */\nexport function rgbToHex(r: number, g: number, b: number): string {\n const toHex = (n: number) => Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, '0')\n return '#' + toHex(r) + toHex(g) + toHex(b)\n}\n\n/**\n * Lightens a hex color by a given percentage (0-100).\n *\n * @example lighten('#ff0000', 20) // \"#ff3333\"\n * @example lighten('#0000ff', 50) // \"#7f7fff\"\n */\nexport function lighten(hex: string, percent: number): string {\n const rgb = hexToRgb(hex)\n if (!rgb) return hex\n const factor = percent / 100\n return rgbToHex(\n rgb.r + (255 - rgb.r) * factor,\n rgb.g + (255 - rgb.g) * factor,\n rgb.b + (255 - rgb.b) * factor,\n )\n}\n\n/**\n * Darkens a hex color by a given percentage (0-100).\n *\n * @example darken('#ff0000', 20) // \"#cc0000\"\n * @example darken('#00ff00', 50) // \"#008000\"\n */\nexport function darken(hex: string, percent: number): string {\n const rgb = hexToRgb(hex)\n if (!rgb) return hex\n const factor = percent / 100\n return rgbToHex(\n rgb.r * (1 - factor),\n rgb.g * (1 - factor),\n rgb.b * (1 - factor),\n )\n}\n\n/**\n * Checks the WCAG contrast ratio between two hex colors.\n * Returns the ratio as a number (1-21). WCAG AA requires 4.5:1 for normal text.\n *\n * @example contrastRatio('#000000', '#ffffff') // 21\n * @example contrastRatio('#ff0000', '#ffffff') // 3.99\n */\nexport function contrastRatio(hex1: string, hex2: string): number {\n const lum1 = relativeLuminance(hex1)\n const lum2 = relativeLuminance(hex2)\n const lighter = Math.max(lum1, lum2)\n const darker = Math.min(lum1, lum2)\n return Number(((lighter + 0.05) / (darker + 0.05)).toFixed(2))\n}\n\nfunction relativeLuminance(hex: string): number {\n const rgb = hexToRgb(hex)\n if (!rgb) return 0\n const vals = [rgb.r / 255, rgb.g / 255, rgb.b / 255].map((c) => {\n return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)\n })\n return 0.2126 * vals[0]! + 0.7152 * vals[1]! + 0.0722 * vals[2]!\n}\n\n/**\n * Checks if a hex color meets WCAG AA contrast ratio (4.5:1) against another color.\n *\n * @example meetsWCAG('#000000', '#ffffff') // true (black on white)\n * @example meetsWCAG('#999999', '#ffffff') // false (gray on white)\n */\nexport function meetsWCAG(hex1: string, hex2: string, level?: 'AA' | 'AAA'): boolean {\n const ratio = contrastRatio(hex1, hex2)\n const threshold = level === 'AAA' ? 7 : 4.5\n return ratio >= threshold\n}\n"],"mappings":";AAOO,SAAS,SAAS,KAAyD;AAChF,MAAI,IAAI,IAAI,QAAQ,KAAK,EAAE;AAC3B,MAAI,EAAE,WAAW,EAAG,KAAI,EAAE,CAAC,IAAK,EAAE,CAAC,IAAI,EAAE,CAAC,IAAK,EAAE,CAAC,IAAI,EAAE,CAAC,IAAK,EAAE,CAAC;AACjE,MAAI,EAAE,WAAW,EAAG,QAAO;AAC3B,QAAM,MAAM,OAAO,SAAS,GAAG,EAAE;AACjC,MAAI,MAAM,GAAG,EAAG,QAAO;AACvB,SAAO;AAAA,IACL,GAAI,OAAO,KAAM;AAAA,IACjB,GAAI,OAAO,IAAK;AAAA,IAChB,GAAG,MAAM;AAAA,EACX;AACF;AAQO,SAAS,SAAS,GAAW,GAAW,GAAmB;AAChE,QAAM,QAAQ,CAAC,MAAc,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AACnG,SAAO,MAAM,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC;AAC5C;AAQO,SAAS,QAAQ,KAAa,SAAyB;AAC5D,QAAM,MAAM,SAAS,GAAG;AACxB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,SAAS,UAAU;AACzB,SAAO;AAAA,IACL,IAAI,KAAK,MAAM,IAAI,KAAK;AAAA,IACxB,IAAI,KAAK,MAAM,IAAI,KAAK;AAAA,IACxB,IAAI,KAAK,MAAM,IAAI,KAAK;AAAA,EAC1B;AACF;AAQO,SAAS,OAAO,KAAa,SAAyB;AAC3D,QAAM,MAAM,SAAS,GAAG;AACxB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,SAAS,UAAU;AACzB,SAAO;AAAA,IACL,IAAI,KAAK,IAAI;AAAA,IACb,IAAI,KAAK,IAAI;AAAA,IACb,IAAI,KAAK,IAAI;AAAA,EACf;AACF;AASO,SAAS,cAAc,MAAc,MAAsB;AAChE,QAAM,OAAO,kBAAkB,IAAI;AACnC,QAAM,OAAO,kBAAkB,IAAI;AACnC,QAAM,UAAU,KAAK,IAAI,MAAM,IAAI;AACnC,QAAM,SAAS,KAAK,IAAI,MAAM,IAAI;AAClC,SAAO,SAAS,UAAU,SAAS,SAAS,OAAO,QAAQ,CAAC,CAAC;AAC/D;AAEA,SAAS,kBAAkB,KAAqB;AAC9C,QAAM,MAAM,SAAS,GAAG;AACxB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,OAAO,CAAC,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,GAAG,EAAE,IAAI,CAAC,MAAM;AAC9D,WAAO,KAAK,UAAU,IAAI,QAAQ,KAAK,KAAK,IAAI,SAAS,OAAO,GAAG;AAAA,EACrE,CAAC;AACD,SAAO,SAAS,KAAK,CAAC,IAAK,SAAS,KAAK,CAAC,IAAK,SAAS,KAAK,CAAC;AAChE;AAQO,SAAS,UAAU,MAAc,MAAc,OAA+B;AACnF,QAAM,QAAQ,cAAc,MAAM,IAAI;AACtC,QAAM,YAAY,UAAU,QAAQ,IAAI;AACxC,SAAO,SAAS;AAClB;","names":[]}
@@ -91,5 +91,29 @@ declare function identity<T>(value: T): T;
91
91
  * @returns A function that runs only once.
92
92
  */
93
93
  declare function once<T extends (...args: unknown[]) => unknown>(fn: T): (...args: Parameters<T>) => ReturnType<T>;
94
+ /**
95
+ * Deep equality check between two values. Supports primitives, objects,
96
+ * arrays, Date, RegExp, Map, Set, and nested structures. Circular
97
+ * references are handled.
98
+ *
99
+ * @example deepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } }) // true
100
+ * @example deepEqual({ a: 1 }, { a: 2 }) // false
101
+ */
102
+ declare function deepEqual(a: unknown, b: unknown): boolean;
103
+ /**
104
+ * Performs left-to-right function composition.
105
+ *
106
+ * @example const fn = pipe((x: number) => x + 1, (x: number) => x * 2)
107
+ * fn(3) // 8
108
+ */
109
+ declare function pipe<T>(initial: T, ...fns: Array<(arg: T) => T>): T;
110
+ declare function pipe<T, R>(initial: T, ...fns: Array<(arg: unknown) => unknown>): R;
111
+ /**
112
+ * Performs right-to-left function composition.
113
+ *
114
+ * @example const fn = compose((x: number) => x * 2, (x: number) => x + 1)
115
+ * fn(3) // 8 — same as (3+1)*2
116
+ */
117
+ declare function compose<T>(...fns: Array<(arg: T) => T>): (initial: T) => T;
94
118
 
95
- export { type DebounceOptions, type DebouncedFunction, type MemoizedFunction, type RetryOptions, debounce, deepClone, deepMerge, identity, memoize, noop, once, retry, throttle };
119
+ export { type DebounceOptions, type DebouncedFunction, type MemoizedFunction, type RetryOptions, compose, debounce, deepClone, deepEqual, deepMerge, identity, memoize, noop, once, pipe, retry, throttle };
@@ -258,14 +258,65 @@ function once(fn) {
258
258
  return result;
259
259
  };
260
260
  }
261
+ function deepEqual(a, b) {
262
+ if (Object.is(a, b)) return true;
263
+ if (a === null || b === null || typeof a !== typeof b) return false;
264
+ if (typeof a !== "object") return false;
265
+ const aObj = a;
266
+ const bObj = b;
267
+ if (Array.isArray(a) && Array.isArray(b)) {
268
+ if (a.length !== b.length) return false;
269
+ for (let i = 0; i < a.length; i++) {
270
+ if (!deepEqual(a[i], b[i])) return false;
271
+ }
272
+ return true;
273
+ }
274
+ if (a instanceof Date && b instanceof Date) {
275
+ return a.getTime() === b.getTime();
276
+ }
277
+ if (a instanceof RegExp && b instanceof RegExp) {
278
+ return a.source === b.source && a.flags === b.flags;
279
+ }
280
+ if (a instanceof Map && b instanceof Map) {
281
+ if (a.size !== b.size) return false;
282
+ for (const [k, v] of a) {
283
+ if (!b.has(k) || !deepEqual(v, b.get(k))) return false;
284
+ }
285
+ return true;
286
+ }
287
+ if (a instanceof Set && b instanceof Set) {
288
+ if (a.size !== b.size) return false;
289
+ for (const v of a) {
290
+ if (!b.has(v)) return false;
291
+ }
292
+ return true;
293
+ }
294
+ const keysA = Object.keys(aObj);
295
+ const keysB = Object.keys(bObj);
296
+ if (keysA.length !== keysB.length) return false;
297
+ for (const key of keysA) {
298
+ if (!Object.prototype.hasOwnProperty.call(bObj, key)) return false;
299
+ if (!deepEqual(aObj[key], bObj[key])) return false;
300
+ }
301
+ return true;
302
+ }
303
+ function pipe(initial, ...fns) {
304
+ return fns.reduce((acc, fn) => fn(acc), initial);
305
+ }
306
+ function compose(...fns) {
307
+ return (initial) => fns.reduceRight((acc, fn) => fn(acc), initial);
308
+ }
261
309
  export {
310
+ compose,
262
311
  debounce,
263
312
  deepClone,
313
+ deepEqual,
264
314
  deepMerge,
265
315
  identity,
266
316
  memoize,
267
317
  noop,
268
318
  once,
319
+ pipe,
269
320
  retry,
270
321
  throttle
271
322
  };