@simplysm/core-common 13.0.0-beta.7 → 13.0.2

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 (98) hide show
  1. package/README.md +191 -815
  2. package/dist/common.types.js +4 -4
  3. package/dist/common.types.js.map +0 -1
  4. package/dist/env.js +2 -2
  5. package/dist/env.js.map +1 -2
  6. package/dist/errors/argument-error.js +1 -1
  7. package/dist/errors/argument-error.js.map +0 -1
  8. package/dist/errors/not-implemented-error.js +1 -1
  9. package/dist/errors/not-implemented-error.js.map +0 -1
  10. package/dist/errors/sd-error.js.map +0 -1
  11. package/dist/errors/timeout-error.js +1 -1
  12. package/dist/errors/timeout-error.js.map +0 -1
  13. package/dist/extensions/arr-ext.helpers.js +4 -4
  14. package/dist/extensions/arr-ext.helpers.js.map +0 -1
  15. package/dist/extensions/arr-ext.js +9 -9
  16. package/dist/extensions/arr-ext.js.map +0 -1
  17. package/dist/extensions/arr-ext.types.js.map +0 -1
  18. package/dist/extensions/map-ext.js.map +0 -1
  19. package/dist/extensions/set-ext.js.map +0 -1
  20. package/dist/features/debounce-queue.d.ts.map +1 -1
  21. package/dist/features/debounce-queue.js +4 -4
  22. package/dist/features/debounce-queue.js.map +1 -2
  23. package/dist/features/event-emitter.js.map +0 -1
  24. package/dist/features/serial-queue.d.ts.map +1 -1
  25. package/dist/features/serial-queue.js +5 -5
  26. package/dist/features/serial-queue.js.map +1 -2
  27. package/dist/globals.js.map +0 -1
  28. package/dist/index.js +30 -30
  29. package/dist/index.js.map +0 -1
  30. package/dist/types/date-only.js +2 -2
  31. package/dist/types/date-only.js.map +0 -1
  32. package/dist/types/date-time.js +2 -2
  33. package/dist/types/date-time.js.map +0 -1
  34. package/dist/types/lazy-gc-map.d.ts.map +1 -1
  35. package/dist/types/lazy-gc-map.js +2 -2
  36. package/dist/types/lazy-gc-map.js.map +1 -2
  37. package/dist/types/time.js +2 -2
  38. package/dist/types/time.js.map +0 -1
  39. package/dist/types/uuid.js +1 -1
  40. package/dist/types/uuid.js.map +0 -1
  41. package/dist/utils/bytes.js +1 -1
  42. package/dist/utils/bytes.js.map +0 -1
  43. package/dist/utils/date-format.js.map +0 -1
  44. package/dist/utils/json.js +8 -8
  45. package/dist/utils/json.js.map +0 -1
  46. package/dist/utils/num.js.map +0 -1
  47. package/dist/utils/obj.js +5 -5
  48. package/dist/utils/obj.js.map +0 -1
  49. package/dist/utils/path.js.map +0 -1
  50. package/dist/utils/primitive.js +5 -5
  51. package/dist/utils/primitive.js.map +0 -1
  52. package/dist/utils/str.js.map +0 -1
  53. package/dist/utils/template-strings.js.map +0 -1
  54. package/dist/utils/transferable.js +4 -4
  55. package/dist/utils/transferable.js.map +0 -1
  56. package/dist/utils/wait.js +1 -1
  57. package/dist/utils/wait.js.map +0 -1
  58. package/dist/utils/xml.js.map +0 -1
  59. package/dist/zip/sd-zip.js.map +0 -1
  60. package/docs/extensions.md +381 -0
  61. package/docs/features.md +94 -0
  62. package/docs/types.md +338 -0
  63. package/docs/utils.md +631 -0
  64. package/package.json +5 -3
  65. package/src/common.types.ts +91 -0
  66. package/src/env.ts +11 -0
  67. package/src/errors/argument-error.ts +40 -0
  68. package/src/errors/not-implemented-error.ts +32 -0
  69. package/src/errors/sd-error.ts +53 -0
  70. package/src/errors/timeout-error.ts +36 -0
  71. package/src/extensions/arr-ext.helpers.ts +53 -0
  72. package/src/extensions/arr-ext.ts +777 -0
  73. package/src/extensions/arr-ext.types.ts +258 -0
  74. package/src/extensions/map-ext.ts +86 -0
  75. package/src/extensions/set-ext.ts +68 -0
  76. package/src/features/debounce-queue.ts +116 -0
  77. package/src/features/event-emitter.ts +112 -0
  78. package/src/features/serial-queue.ts +94 -0
  79. package/src/globals.ts +12 -0
  80. package/src/index.ts +55 -0
  81. package/src/types/date-only.ts +329 -0
  82. package/src/types/date-time.ts +294 -0
  83. package/src/types/lazy-gc-map.ts +244 -0
  84. package/src/types/time.ts +210 -0
  85. package/src/types/uuid.ts +113 -0
  86. package/src/utils/bytes.ts +160 -0
  87. package/src/utils/date-format.ts +239 -0
  88. package/src/utils/json.ts +230 -0
  89. package/src/utils/num.ts +97 -0
  90. package/src/utils/obj.ts +956 -0
  91. package/src/utils/path.ts +40 -0
  92. package/src/utils/primitive.ts +33 -0
  93. package/src/utils/str.ts +252 -0
  94. package/src/utils/template-strings.ts +132 -0
  95. package/src/utils/transferable.ts +269 -0
  96. package/src/utils/wait.ts +40 -0
  97. package/src/utils/xml.ts +105 -0
  98. package/src/zip/sd-zip.ts +218 -0
package/README.md CHANGED
@@ -22,821 +22,6 @@ import "@simplysm/core-common";
22
22
  This import globally activates Array, Map, and Set prototype extensions.
23
23
  To use extension methods (`getOrCreate()`, `toggle()`, etc.), you must import this at app startup.
24
24
 
25
- ## Main Modules
26
-
27
- ### Errors
28
-
29
- Custom error classes. All are based on `SdError` and support cause chaining.
30
-
31
- | Class | Description |
32
- |--------|------|
33
- | `SdError` | Base error class (error tracking with cause chain, automatic nested stack integration) |
34
- | `ArgumentError` | Argument validation error (YAML formatting) |
35
- | `NotImplementedError` | Indicates unimplemented functionality |
36
- | `TimeoutError` | Timeout error |
37
-
38
- ```typescript
39
- import { SdError, ArgumentError, NotImplementedError, TimeoutError } from "@simplysm/core-common";
40
-
41
- // SdError: track errors with cause chain
42
- try {
43
- await fetch(url);
44
- } catch (err) {
45
- throw new SdError(err, "API call failed", "Failed to load user");
46
- // Result message: "Failed to load user => API call failed => original error message"
47
- }
48
-
49
- // ArgumentError: output argument object in YAML format
50
- throw new ArgumentError("Invalid user", { userId: 123 });
51
- // Result message: "Invalid user\n\nuserId: 123"
52
-
53
- // NotImplementedError: indicate unimplemented branch
54
- switch (type) {
55
- case "A": return handleA();
56
- case "B": throw new NotImplementedError(`Handling type ${type}`);
57
- }
58
-
59
- // TimeoutError: wait time exceeded
60
- throw new TimeoutError(5, "API response wait exceeded");
61
- // Result message: "Wait time exceeded(5): API response wait exceeded"
62
- ```
63
-
64
- ### Types
65
-
66
- Immutable custom type classes. All transformation methods return new instances.
67
-
68
- | Class | Description |
69
- |--------|------|
70
- | `DateTime` | Date + time (millisecond precision, local timezone) |
71
- | `DateOnly` | Date only (no time) |
72
- | `Time` | Time only (no date, 24-hour cycle) |
73
- | `Uuid` | UUID v4 (based on `crypto.getRandomValues`) |
74
- | `LazyGcMap` | Map with auto-expiration (LRU style) |
75
-
76
- #### DateTime
77
-
78
- ```typescript
79
- import { DateTime } from "@simplysm/core-common";
80
-
81
- // Creation
82
- const now = new DateTime(); // Current time
83
- const dt = new DateTime(2025, 1, 15, 10, 30, 0); // Year, month, day, hour, minute, second
84
- const fromTick = new DateTime(1705312200000); // Tick (milliseconds)
85
- const fromDate = new DateTime(new Date()); // Date object
86
-
87
- // Parsing
88
- DateTime.parse("2025-01-15 10:30:00"); // yyyy-MM-dd HH:mm:ss
89
- DateTime.parse("2025-01-15 10:30:00.123"); // yyyy-MM-dd HH:mm:ss.fff
90
- DateTime.parse("20250115103000"); // yyyyMMddHHmmss
91
- DateTime.parse("2025-01-15 오전 10:30:00"); // Korean AM/PM
92
- DateTime.parse("2025-01-15T10:30:00Z"); // ISO 8601
93
-
94
- // Properties (read-only)
95
- dt.year; // 2025
96
- dt.month; // 1 (1-12)
97
- dt.day; // 15
98
- dt.hour; // 10
99
- dt.minute; // 30
100
- dt.second; // 0
101
- dt.millisecond; // 0
102
- dt.tick; // Millisecond timestamp
103
- dt.dayOfWeek; // Day of week (Sun~Sat: 0~6)
104
- dt.isValid; // Validity check
105
-
106
- // Immutable transformations (return new instances)
107
- dt.setYear(2026); // Change year
108
- dt.setMonth(3); // Change month (day auto-adjusted)
109
- dt.addDays(7); // 7 days later
110
- dt.addHours(-2); // 2 hours ago
111
- dt.addMonths(1); // 1 month later
112
-
113
- // Formatting
114
- dt.toFormatString("yyyy-MM-dd"); // "2025-01-15"
115
- dt.toFormatString("yyyy년 M월 d일 (ddd)"); // "2025년 1월 15일 (수)"
116
- dt.toFormatString("tt h:mm:ss"); // "오전 10:30:00"
117
- dt.toString(); // "2025-01-15T10:30:00.000+09:00"
118
- ```
119
-
120
- #### DateOnly
121
-
122
- ```typescript
123
- import { DateOnly } from "@simplysm/core-common";
124
-
125
- // Creation and parsing
126
- const today = new DateOnly();
127
- const d = new DateOnly(2025, 1, 15);
128
- DateOnly.parse("2025-01-15"); // No timezone influence
129
- DateOnly.parse("20250115"); // No timezone influence
130
-
131
- // Immutable transformations
132
- d.addDays(30);
133
- d.addMonths(-1);
134
- d.setMonth(2); // Jan 31 -> Feb 28 (auto-adjusted)
135
-
136
- // Week calculation (ISO 8601 standard)
137
- d.getWeekSeqOfYear(); // { year: 2025, weekSeq: 3 }
138
- d.getWeekSeqOfMonth(); // { year: 2025, monthSeq: 1, weekSeq: 3 }
139
-
140
- // US-style week (Sunday start, first week with 1+ days)
141
- d.getWeekSeqOfYear(0, 1);
142
-
143
- // Reverse calculate date from week
144
- DateOnly.getDateByYearWeekSeq({ year: 2025, weekSeq: 2 }); // 2025-01-06 (Monday)
145
-
146
- // Formatting
147
- d.toFormatString("yyyy년 MM월 dd일"); // "2025년 01월 15일"
148
- d.toString(); // "2025-01-15"
149
- ```
150
-
151
- #### Time
152
-
153
- ```typescript
154
- import { Time } from "@simplysm/core-common";
155
-
156
- // Creation and parsing
157
- const now = new Time();
158
- const t = new Time(14, 30, 0);
159
- Time.parse("14:30:00"); // HH:mm:ss
160
- Time.parse("14:30:00.123"); // HH:mm:ss.fff
161
- Time.parse("오후 2:30:00"); // Korean AM/PM
162
-
163
- // 24-hour cycle
164
- t.addHours(12); // 14:30 + 12 hours = 02:30 (cycles, not next day)
165
- t.addMinutes(-60); // 14:30 - 60 minutes = 13:30
166
-
167
- // Formatting
168
- t.toFormatString("tt h:mm"); // "오후 2:30"
169
- t.toString(); // "14:30:00.000"
170
- ```
171
-
172
- #### Uuid
173
-
174
- ```typescript
175
- import { Uuid } from "@simplysm/core-common";
176
-
177
- // Generate new UUID (cryptographically secure)
178
- const id = Uuid.new();
179
-
180
- // Create from string
181
- const fromStr = new Uuid("550e8400-e29b-41d4-a716-446655440000");
182
-
183
- // Byte conversion
184
- const bytes = id.toBytes(); // Uint8Array (16 bytes)
185
- const fromBytes = Uuid.fromBytes(bytes);
186
-
187
- id.toString(); // "550e8400-e29b-41d4-a716-446655440000"
188
- ```
189
-
190
- #### LazyGcMap
191
-
192
- ```typescript
193
- import { LazyGcMap } from "@simplysm/core-common";
194
-
195
- // using statement (recommended)
196
- using map = new LazyGcMap<string, object>({
197
- gcInterval: 10000, // GC execution interval: 10 seconds
198
- expireTime: 60000, // Item expiration time: 60 seconds
199
- onExpire: (key, value) => {
200
- console.log(`Expired: ${key}`);
201
- },
202
- });
203
-
204
- map.set("key1", { data: "hello" });
205
- map.get("key1"); // Refreshes access time (LRU)
206
- map.getOrCreate("key2", () => ({})); // Create and return if not exists
207
- map.has("key1"); // Does not refresh access time
208
- map.delete("key1");
209
- ```
210
-
211
- ### Features
212
-
213
- Async operation control and event handling classes. All support `using` statements or `dispose()`.
214
-
215
- | Class | Description |
216
- |--------|------|
217
- | `DebounceQueue` | Async debounce queue (executes only last request) |
218
- | `SerialQueue` | Async serial queue (sequential execution) |
219
- | `EventEmitter` | EventTarget wrapper (type-safe events) |
220
-
221
- #### DebounceQueue
222
-
223
- ```typescript
224
- import { DebounceQueue } from "@simplysm/core-common";
225
-
226
- using queue = new DebounceQueue(300); // 300ms debounce
227
-
228
- // Error handling
229
- queue.on("error", (err) => console.error(err));
230
-
231
- // Only last call is executed
232
- queue.run(() => console.log("1")); // Ignored
233
- queue.run(() => console.log("2")); // Ignored
234
- queue.run(() => console.log("3")); // Executed after 300ms
235
- ```
236
-
237
- #### SerialQueue
238
-
239
- ```typescript
240
- import { SerialQueue } from "@simplysm/core-common";
241
-
242
- using queue = new SerialQueue(100); // 100ms interval between tasks
243
-
244
- queue.on("error", (err) => console.error(err));
245
-
246
- queue.run(async () => { await fetch("/api/1"); });
247
- queue.run(async () => { await fetch("/api/2"); }); // Runs after #1 completes
248
- queue.run(async () => { await fetch("/api/3"); }); // Runs after #2 completes
249
- ```
250
-
251
- #### EventEmitter
252
-
253
- ```typescript
254
- import { EventEmitter } from "@simplysm/core-common";
255
-
256
- interface MyEvents {
257
- data: string;
258
- error: Error;
259
- done: void;
260
- }
261
-
262
- class MyService extends EventEmitter<MyEvents> {
263
- process(): void {
264
- this.emit("data", "result data");
265
- this.emit("done"); // void type called without arguments
266
- }
267
- }
268
-
269
- const service = new MyService();
270
- service.on("data", (data) => console.log(data)); // data: string (type inferred)
271
- service.off("data", listener); // Remove listener
272
- service.listenerCount("data"); // Number of registered listeners
273
- service.dispose(); // Remove all listeners
274
- ```
275
-
276
- ### Utils
277
-
278
- Utility functions.
279
-
280
- #### Object utilities (obj)
281
-
282
- | Function | Description |
283
- |------|------|
284
- | `objClone` | Deep clone (supports circular references, custom types) |
285
- | `objEqual` | Deep comparison (include/exclude keys, array order ignore option) |
286
- | `objMerge` | Deep merge (source + target, array processing option) |
287
- | `objMerge3` | 3-way merge (conflict detection) |
288
- | `objOmit` | Exclude specific keys |
289
- | `objPick` | Select specific keys |
290
- | `objGetChainValue` | Query value by chain path (`"a.b[0].c"`) |
291
- | `objSetChainValue` | Set value by chain path |
292
- | `objDeleteChainValue` | Delete value by chain path |
293
- | `objKeys` | Type-safe `Object.keys` |
294
- | `objEntries` | Type-safe `Object.entries` |
295
- | `objFromEntries` | Type-safe `Object.fromEntries` |
296
- | `objMap` | Transform each entry of object and return new object |
297
-
298
- ```typescript
299
- import {
300
- objClone, objEqual, objMerge, objMerge3,
301
- objOmit, objPick, objGetChainValue, objSetChainValue,
302
- objKeys, objEntries, objMap,
303
- } from "@simplysm/core-common";
304
-
305
- // Deep clone (supports custom types like DateTime, Uuid)
306
- const cloned = objClone({ date: new DateTime(), nested: { arr: [1, 2] } });
307
-
308
- // Deep comparison
309
- objEqual({ a: 1, b: [2] }, { a: 1, b: [2] }); // true
310
- objEqual(arr1, arr2, { ignoreArrayIndex: true }); // Ignore array order
311
- objEqual(obj1, obj2, { topLevelExcludes: ["updatedAt"] }); // Exclude specific keys
312
-
313
- // Deep merge
314
- objMerge({ a: 1, b: { c: 2 } }, { b: { d: 3 } });
315
- // { a: 1, b: { c: 2, d: 3 } }
316
-
317
- // 3-way merge (conflict detection)
318
- const { conflict, result } = objMerge3(
319
- { a: 1, b: 2 }, // source (change #1)
320
- { a: 1, b: 1 }, // origin (base)
321
- { a: 2, b: 1 }, // target (change #2)
322
- );
323
- // conflict: false, result: { a: 2, b: 2 }
324
-
325
- // Key selection/exclusion
326
- objOmit(user, ["password", "email"]);
327
- objPick(user, ["name", "age"]);
328
-
329
- // Chain path
330
- objGetChainValue(obj, "a.b[0].c");
331
- objSetChainValue(obj, "a.b[0].c", "value");
332
-
333
- // Type-safe Object utilities
334
- objKeys(obj); // (keyof typeof obj)[]
335
- objEntries(obj); // [keyof typeof obj, typeof obj[keyof typeof obj]][]
336
- objMap(colors, (key, rgb) => [null, `rgb(${rgb})`]); // Transform values only (keep keys)
337
- ```
338
-
339
- #### JSON utilities (json)
340
-
341
- | Function | Description |
342
- |------|------|
343
- | `jsonStringify` | JSON serialization with custom type support |
344
- | `jsonParse` | JSON deserialization with custom type restoration |
345
-
346
- Serializes/restores `DateTime`, `DateOnly`, `Time`, `Uuid`, `Date`, `Set`, `Map`, `Error`, `Uint8Array` types using `__type__` metadata.
347
-
348
- ```typescript
349
- import { jsonStringify, jsonParse, DateTime, Uuid } from "@simplysm/core-common";
350
-
351
- const data = {
352
- createdAt: new DateTime(2025, 1, 15),
353
- id: Uuid.new(),
354
- tags: new Set(["a", "b"]),
355
- meta: new Map([["key", "value"]]),
356
- file: new Uint8Array([1, 2, 3]),
357
- };
358
-
359
- // Serialization (preserves custom types)
360
- const json = jsonStringify(data, { space: 2 });
361
-
362
- // Deserialization (restores custom types)
363
- const parsed = jsonParse(json);
364
- // parsed.createdAt instanceof DateTime === true
365
- // parsed.id instanceof Uuid === true
366
- // parsed.tags instanceof Set === true
367
-
368
- // For logging: hide binary data
369
- jsonStringify(data, { redactBytes: true });
370
- // Uint8Array content replaced with "__hidden__"
371
- ```
372
-
373
- #### XML utilities (xml)
374
-
375
- | Function | Description |
376
- |------|------|
377
- | `xmlParse` | Parse XML string to object (attributes: `$`, text: `_`) |
378
- | `xmlStringify` | Serialize object to XML string |
379
-
380
- ```typescript
381
- import { xmlParse, xmlStringify } from "@simplysm/core-common";
382
-
383
- const obj = xmlParse('<root id="1"><item>hello</item></root>');
384
- // { root: { $: { id: "1" }, item: [{ _: "hello" }] } }
385
-
386
- const xml = xmlStringify(obj);
387
- // '<root id="1"><item>hello</item></root>'
388
-
389
- // Remove namespace prefix
390
- xmlParse('<ns:root><ns:item>text</ns:item></ns:root>', { stripTagPrefix: true });
391
- // { root: { item: [{ _: "text" }] } }
392
- ```
393
-
394
- #### String utilities (str)
395
-
396
- | Function | Description |
397
- |------|------|
398
- | `strGetSuffix` | Korean postposition handling (을/를, 은/는, 이/가, 과/와, 이랑/랑, 으로/로, 이라/라) |
399
- | `strReplaceFullWidth` | Convert full-width characters to half-width |
400
- | `strToPascalCase` | PascalCase conversion |
401
- | `strToCamelCase` | camelCase conversion |
402
- | `strToKebabCase` | kebab-case conversion |
403
- | `strToSnakeCase` | snake_case conversion |
404
- | `strIsNullOrEmpty` | Check for undefined/null/empty string (type guard) |
405
- | `strInsert` | Insert at specific position in string |
406
-
407
- ```typescript
408
- import {
409
- strGetSuffix, strToCamelCase, strToKebabCase,
410
- strIsNullOrEmpty, strReplaceFullWidth,
411
- } from "@simplysm/core-common";
412
-
413
- // Korean postposition
414
- strGetSuffix("사과", "을"); // "를"
415
- strGetSuffix("책", "이"); // "이"
416
- strGetSuffix("서울", "로"); // "로" (ㄹ final consonant uses "로")
417
-
418
- // Case conversion
419
- strToCamelCase("hello-world"); // "helloWorld"
420
- strToKebabCase("HelloWorld"); // "hello-world"
421
-
422
- // Empty string check (type guard)
423
- if (strIsNullOrEmpty(name)) {
424
- // name: "" | undefined
425
- } else {
426
- // name: string (non-empty string)
427
- }
428
-
429
- // Full-width -> half-width
430
- strReplaceFullWidth("A123(株)"); // "A123(株)"
431
- ```
432
-
433
- #### Number utilities (num)
434
-
435
- | Function | Description |
436
- |------|------|
437
- | `numParseInt` | Parse string to integer (remove non-digit characters) |
438
- | `numParseFloat` | Parse string to float |
439
- | `numParseRoundedInt` | Round float and return integer |
440
- | `numFormat` | Thousands separator formatting |
441
- | `numIsNullOrEmpty` | Check for undefined/null/0 (type guard) |
442
-
443
- ```typescript
444
- import { numParseInt, numParseFloat, numFormat, numIsNullOrEmpty } from "@simplysm/core-common";
445
-
446
- numParseInt("12,345원"); // 12345
447
- numParseFloat("3.14%"); // 3.14
448
- numFormat(1234567, { max: 2 }); // "1,234,567"
449
- numFormat(1234, { min: 2, max: 2 }); // "1,234.00"
450
-
451
- if (numIsNullOrEmpty(count)) {
452
- // count: 0 | undefined
453
- }
454
- ```
455
-
456
- #### Date/time formatting (date-format)
457
-
458
- | Function | Description |
459
- |------|------|
460
- | `formatDate` | Convert date/time to string according to format string |
461
- | `normalizeMonth` | Normalize year/month/day when setting month |
462
-
463
- Supports the same format strings as C#:
464
-
465
- | Format | Description | Example |
466
- |------|------|------|
467
- | `yyyy` | 4-digit year | 2024 |
468
- | `yy` | 2-digit year | 24 |
469
- | `MM` | 0-padded month | 01~12 |
470
- | `M` | Month | 1~12 |
471
- | `ddd` | Day of week (Korean) | 일, 월, 화, 수, 목, 금, 토 |
472
- | `dd` | 0-padded day | 01~31 |
473
- | `d` | Day | 1~31 |
474
- | `tt` | AM/PM | 오전, 오후 |
475
- | `HH` | 0-padded 24-hour | 00~23 |
476
- | `hh` | 0-padded 12-hour | 01~12 |
477
- | `mm` | 0-padded minute | 00~59 |
478
- | `ss` | 0-padded second | 00~59 |
479
- | `fff` | Millisecond (3 digits) | 000~999 |
480
- | `zzz` | Timezone offset | +09:00 |
481
-
482
- ```typescript
483
- import { formatDate, normalizeMonth } from "@simplysm/core-common";
484
-
485
- formatDate("yyyy-MM-dd", { year: 2024, month: 3, day: 15 });
486
- // "2024-03-15"
487
-
488
- formatDate("yyyy년 M월 d일 (ddd)", { year: 2024, month: 3, day: 15 });
489
- // "2024년 3월 15일 (금)"
490
-
491
- normalizeMonth(2025, 13, 15); // { year: 2026, month: 1, day: 15 }
492
- normalizeMonth(2025, 2, 31); // { year: 2025, month: 2, day: 28 }
493
- ```
494
-
495
- #### Byte utilities (bytes)
496
-
497
- | Function | Description |
498
- |------|------|
499
- | `bytesConcat` | Concatenate multiple Uint8Arrays |
500
- | `bytesToHex` | Convert Uint8Array to hex string |
501
- | `bytesFromHex` | Convert hex string to Uint8Array |
502
- | `bytesToBase64` | Convert Uint8Array to base64 string |
503
- | `bytesFromBase64` | Convert base64 string to Uint8Array |
504
-
505
- ```typescript
506
- import { bytesConcat, bytesToHex, bytesFromHex, bytesToBase64, bytesFromBase64 } from "@simplysm/core-common";
507
-
508
- bytesConcat([new Uint8Array([1, 2]), new Uint8Array([3, 4])]);
509
- // Uint8Array([1, 2, 3, 4])
510
-
511
- bytesToHex(new Uint8Array([255, 0, 127])); // "ff007f"
512
- bytesFromHex("ff007f"); // Uint8Array([255, 0, 127])
513
-
514
- bytesToBase64(new Uint8Array([72, 101, 108, 108, 111])); // "SGVsbG8="
515
- bytesFromBase64("SGVsbG8="); // Uint8Array([72, 101, 108, 108, 111])
516
- ```
517
-
518
- #### Async wait (wait)
519
-
520
- | Function | Description |
521
- |------|------|
522
- | `waitTime` | Wait for specified time |
523
- | `waitUntil` | Wait until condition is true (max attempts limit) |
524
-
525
- ```typescript
526
- import { waitTime, waitUntil } from "@simplysm/core-common";
527
-
528
- await waitTime(1000); // Wait 1 second
529
-
530
- // Wait for condition (100ms interval, max 50 attempts = 5 seconds)
531
- await waitUntil(() => isReady, 100, 50);
532
- // Throws TimeoutError after 50 attempts
533
- ```
534
-
535
- #### Worker data conversion (transferable)
536
-
537
- | Function | Description |
538
- |------|------|
539
- | `transferableEncode` | Serialize custom types into Worker-transferable form |
540
- | `transferableDecode` | Deserialize Worker-received data to custom types |
541
-
542
- ```typescript
543
- import { transferableEncode, transferableDecode } from "@simplysm/core-common";
544
-
545
- // Send to Worker
546
- const { result, transferList } = transferableEncode(data);
547
- worker.postMessage(result, transferList);
548
-
549
- // Receive from Worker
550
- const decoded = transferableDecode(event.data);
551
- ```
552
-
553
- #### Path utilities (path)
554
-
555
- Replacement for Node.js `path` module. Supports POSIX-style paths (`/`) only.
556
-
557
- | Function | Description |
558
- |------|------|
559
- | `pathJoin` | Combine paths (`path.join` replacement) |
560
- | `pathBasename` | Extract filename (`path.basename` replacement) |
561
- | `pathExtname` | Extract extension (`path.extname` replacement) |
562
-
563
- ```typescript
564
- import { pathJoin, pathBasename, pathExtname } from "@simplysm/core-common";
565
-
566
- pathJoin("/home", "user", "file.txt"); // "/home/user/file.txt"
567
- pathBasename("/home/user/file.txt"); // "file.txt"
568
- pathBasename("file.txt", ".txt"); // "file"
569
- pathExtname("file.txt"); // ".txt"
570
- ```
571
-
572
- #### Template literal tags (template-strings)
573
-
574
- Tag functions for IDE code highlighting. Actual behavior is string combination + indentation cleanup.
575
-
576
- | Function | Description |
577
- |------|------|
578
- | `js` | JavaScript code highlighting |
579
- | `ts` | TypeScript code highlighting |
580
- | `html` | HTML markup highlighting |
581
- | `tsql` | MSSQL T-SQL highlighting |
582
- | `mysql` | MySQL SQL highlighting |
583
- | `pgsql` | PostgreSQL SQL highlighting |
584
-
585
- ```typescript
586
- import { tsql } from "@simplysm/core-common";
587
-
588
- const query = tsql`
589
- SELECT TOP 10 *
590
- FROM Users
591
- WHERE Name LIKE '%${keyword}%'
592
- `;
593
- ```
594
-
595
- #### Other utilities
596
-
597
- | Function/Type | Description |
598
- |-----------|------|
599
- | `getPrimitiveTypeStr` | Infer `PrimitiveTypeStr` from runtime value |
600
- | `env` | Environment variable object (`DEV`, `VER`, etc.) |
601
-
602
- ```typescript
603
- import { getPrimitiveTypeStr, env } from "@simplysm/core-common";
604
-
605
- getPrimitiveTypeStr("hello"); // "string"
606
- getPrimitiveTypeStr(123); // "number"
607
- getPrimitiveTypeStr(new DateTime()); // "DateTime"
608
-
609
- if (env.DEV) {
610
- console.log("Development mode");
611
- }
612
- console.log(`Version: ${env.VER}`);
613
- ```
614
-
615
- ### Zip
616
-
617
- ZIP file compression/decompression utility. Resources can be auto-cleaned with `await using`.
618
-
619
- | Class | Description |
620
- |--------|------|
621
- | `ZipArchive` | ZIP file read/write/compress/extract |
622
-
623
- ```typescript
624
- import { ZipArchive } from "@simplysm/core-common";
625
-
626
- // Read ZIP file
627
- await using archive = new ZipArchive(zipBytes);
628
- const content = await archive.get("file.txt");
629
- const exists = await archive.exists("data.json");
630
-
631
- // Extract all (with progress)
632
- const files = await archive.extractAll((progress) => {
633
- console.log(`${progress.fileName}: ${progress.extractedSize}/${progress.totalSize}`);
634
- });
635
-
636
- // Create ZIP file
637
- await using newArchive = new ZipArchive();
638
- newArchive.write("file.txt", textBytes);
639
- newArchive.write("data.json", jsonBytes);
640
- const zipBytes = await newArchive.compress();
641
- ```
642
-
643
- ### Type Utilities
644
-
645
- TypeScript utility types.
646
-
647
- | Type | Description |
648
- |------|------|
649
- | `Bytes` | Alias for `Uint8Array` (`Buffer` replacement) |
650
- | `PrimitiveTypeStr` | Primitive type string keys (`"string"`, `"number"`, `"boolean"`, `"DateTime"`, `"DateOnly"`, `"Time"`, `"Uuid"`, `"Bytes"`) |
651
- | `PrimitiveTypeMap` | Mapping from `PrimitiveTypeStr` to actual type |
652
- | `PrimitiveType` | Union of all Primitive types |
653
- | `DeepPartial<T>` | Recursively convert all properties to optional |
654
- | `Type<T>` | Constructor type (for dependency injection, factory patterns) |
655
- | `ObjUndefToOptional<T>` | Convert properties with `undefined` to optional |
656
- | `ObjOptionalToUndef<T>` | Convert optional properties to `required + undefined` union |
657
-
658
- ```typescript
659
- import type { DeepPartial, Type, Bytes } from "@simplysm/core-common";
660
-
661
- // DeepPartial: deep Partial
662
- interface Config {
663
- db: { host: string; port: number };
664
- }
665
- const partial: DeepPartial<Config> = { db: { host: "localhost" } };
666
-
667
- // Type: constructor type
668
- function create<T>(ctor: Type<T>): T {
669
- return new ctor();
670
- }
671
-
672
- // Bytes: Buffer replacement
673
- const data: Bytes = new Uint8Array([1, 2, 3]);
674
- ```
675
-
676
- ### Extensions
677
-
678
- Array, Map, Set prototype extensions. Activated by `import "@simplysm/core-common"`.
679
-
680
- #### Array extension methods
681
-
682
- **Query**:
683
-
684
- | Method | Description |
685
- |--------|------|
686
- | `single(predicate?)` | Return single element (error if 2+) |
687
- | `first(predicate?)` | Return first element |
688
- | `last(predicate?)` | Return last element |
689
-
690
- **Filtering**:
691
-
692
- | Method | Description |
693
- |--------|------|
694
- | `filterExists()` | Remove `null`/`undefined` |
695
- | `ofType(type)` | Filter by type (`PrimitiveTypeStr` or constructor) |
696
- | `filterAsync(predicate)` | Async filter |
697
-
698
- **Mapping/Transformation**:
699
-
700
- | Method | Description |
701
- |--------|------|
702
- | `mapAsync(selector)` | Async mapping (sequential execution) |
703
- | `mapMany(selector?)` | flat + filterExists |
704
- | `mapManyAsync(selector?)` | Async mapMany |
705
- | `parallelAsync(fn)` | Parallel async mapping (`Promise.all`) |
706
-
707
- **Grouping/Transformation**:
708
-
709
- | Method | Description |
710
- |--------|------|
711
- | `groupBy(keySelector, valueSelector?)` | Group by key |
712
- | `toMap(keySelector, valueSelector?)` | Convert to Map (error on duplicate key) |
713
- | `toMapAsync(keySelector, valueSelector?)` | Async Map conversion |
714
- | `toArrayMap(keySelector, valueSelector?)` | Convert to `Map<K, V[]>` |
715
- | `toSetMap(keySelector, valueSelector?)` | Convert to `Map<K, Set<V>>` |
716
- | `toMapValues(keySelector, valueSelector)` | Aggregate Map by group |
717
- | `toObject(keySelector, valueSelector?)` | Convert to `Record<string, V>` |
718
- | `toTree(key, parentKey)` | Convert to tree structure |
719
-
720
- **Deduplication**:
721
-
722
- | Method | Description |
723
- |--------|------|
724
- | `distinct(options?)` | Remove duplicates (return new array) |
725
- | `distinctThis(options?)` | Remove duplicates (modify original) |
726
-
727
- **Sorting**:
728
-
729
- | Method | Description |
730
- |--------|------|
731
- | `orderBy(selector?)` | Ascending sort (return new array) |
732
- | `orderByDesc(selector?)` | Descending sort (return new array) |
733
- | `orderByThis(selector?)` | Ascending sort (modify original) |
734
- | `orderByDescThis(selector?)` | Descending sort (modify original) |
735
-
736
- **Comparison/Merging**:
737
-
738
- | Method | Description |
739
- |--------|------|
740
- | `diffs(target, options?)` | Compare differences between two arrays |
741
- | `oneWayDiffs(orgItems, keyProp, options?)` | One-way diff comparison (create/update/same) |
742
- | `merge(target, options?)` | Merge arrays |
743
-
744
- **Aggregation**:
745
-
746
- | Method | Description |
747
- |--------|------|
748
- | `sum(selector?)` | Sum |
749
- | `min(selector?)` | Minimum |
750
- | `max(selector?)` | Maximum |
751
-
752
- **Mutation (modify original)**:
753
-
754
- | Method | Description |
755
- |--------|------|
756
- | `insert(index, ...items)` | Insert at specific position |
757
- | `remove(itemOrSelector)` | Remove item |
758
- | `toggle(item)` | Remove if exists, add if not |
759
- | `clear()` | Remove all items |
760
- | `shuffle()` | Shuffle (return new array) |
761
-
762
- ```typescript
763
- import "@simplysm/core-common";
764
-
765
- const users = [
766
- { id: 1, name: "Alice", dept: "dev" },
767
- { id: 2, name: "Bob", dept: "dev" },
768
- { id: 3, name: "Charlie", dept: "hr" },
769
- ];
770
-
771
- // Query
772
- users.single((u) => u.id === 1); // { id: 1, ... }
773
- users.first(); // { id: 1, ... }
774
- users.last(); // { id: 3, ... }
775
-
776
- // Grouping
777
- users.groupBy((u) => u.dept);
778
- // [{ key: "dev", values: [...] }, { key: "hr", values: [...] }]
779
-
780
- // Map conversion
781
- users.toMap((u) => u.id); // Map<number, User>
782
- users.toArrayMap((u) => u.dept); // Map<string, User[]>
783
-
784
- // Sorting
785
- users.orderBy((u) => u.name);
786
- users.orderByDesc((u) => u.id);
787
-
788
- // Filtering
789
- [1, null, 2, undefined, 3].filterExists(); // [1, 2, 3]
790
-
791
- // Deduplication
792
- [1, 2, 2, 3, 3].distinct(); // [1, 2, 3]
793
-
794
- // Async mapping (sequential execution)
795
- await ids.mapAsync(async (id) => await fetchUser(id));
796
-
797
- // Parallel async mapping
798
- await ids.parallelAsync(async (id) => await fetchUser(id));
799
- ```
800
-
801
- #### Map extension methods
802
-
803
- | Method | Description |
804
- |--------|------|
805
- | `getOrCreate(key, value)` | If key doesn't exist, set new value and return |
806
- | `update(key, updateFn)` | Update value for key using function |
807
-
808
- ```typescript
809
- const map = new Map<string, number[]>();
810
-
811
- // Create and return if value doesn't exist
812
- const arr = map.getOrCreate("key", []);
813
- arr.push(1);
814
-
815
- // Create with factory function (when computation is expensive)
816
- map.getOrCreate("key2", () => expensiveComputation());
817
-
818
- // Update value
819
- const countMap = new Map<string, number>();
820
- countMap.update("key", (v) => (v ?? 0) + 1); // Increment counter
821
- ```
822
-
823
- #### Set extension methods
824
-
825
- | Method | Description |
826
- |--------|------|
827
- | `adds(...values)` | Add multiple values at once |
828
- | `toggle(value, addOrDel?)` | Toggle value (remove if exists, add if not) |
829
-
830
- ```typescript
831
- const set = new Set<number>([1, 2, 3]);
832
-
833
- set.adds(4, 5, 6); // {1, 2, 3, 4, 5, 6}
834
- set.toggle(2); // 2 exists so remove -> {1, 3, 4, 5, 6}
835
- set.toggle(7); // 7 doesn't exist so add -> {1, 3, 4, 5, 6, 7}
836
- set.toggle(8, "add"); // Force add
837
- set.toggle(1, "del"); // Force delete
838
- ```
839
-
840
25
  ## Caveats
841
26
 
842
27
  ### Prototype Extension Conflicts
@@ -882,6 +67,197 @@ Be careful as user data in the form `{ __type__: "DateTime", data: "..." }` may
882
67
  - `jsonStringify`: throws TypeError on circular reference
883
68
  - `transferableEncode`: throws TypeError on circular reference (includes path information)
884
69
 
70
+ ---
71
+
72
+ ## Errors
73
+
74
+ - [`SdError`](docs/types.md#sderror) - Base error class with cause chain tracking
75
+ - [`ArgumentError`](docs/types.md#argumenterror) - Argument validation error with YAML formatting
76
+ - [`NotImplementedError`](docs/types.md#notimplementederror) - Indicates unimplemented functionality
77
+ - [`TimeoutError`](docs/types.md#timeouterror) - Timeout error
78
+
79
+ ## Custom Types
80
+
81
+ - [`DateTime`](docs/types.md#datetime) - Date + time (millisecond precision, local timezone)
82
+ - [`DateOnly`](docs/types.md#dateonly) - Date only (no time)
83
+ - [`Time`](docs/types.md#time) - Time only (no date, 24-hour cycle)
84
+ - [`Uuid`](docs/types.md#uuid) - UUID v4 (cryptographically secure)
85
+ - [`LazyGcMap`](docs/types.md#lazygcmap) - Map with auto-expiration (LRU style)
86
+
87
+ ## Features
88
+
89
+ - [`DebounceQueue`](docs/features.md#debouncequeue) - Async debounce queue (executes only last request)
90
+ - [`SerialQueue`](docs/features.md#serialqueue) - Async serial queue (sequential execution)
91
+ - [`EventEmitter`](docs/features.md#eventemitter) - EventTarget wrapper with type-safe events
92
+ - [`ZipArchive`](docs/features.md#ziparchive) - ZIP file compression/decompression utility
93
+
94
+ ## Object Utilities
95
+
96
+ - [`objClone`](docs/utils.md#objclone) - Deep clone (supports circular references, custom types)
97
+ - [`objEqual`](docs/utils.md#objequal) - Deep comparison with options
98
+ - [`objMerge`](docs/utils.md#objmerge) - Deep merge (source + target)
99
+ - [`objMerge3`](docs/utils.md#objmerge3) - 3-way merge with conflict detection
100
+ - [`objOmit`](docs/utils.md#objomit) - Exclude specific keys
101
+ - [`objPick`](docs/utils.md#objpick) - Select specific keys
102
+ - [`objGetChainValue`](docs/utils.md#objgetchainvalue) - Query value by chain path
103
+ - [`objSetChainValue`](docs/utils.md#objsetchainvalue) - Set value by chain path
104
+ - [`objDeleteChainValue`](docs/utils.md#objdeletechainvalue) - Delete value by chain path
105
+ - [`objKeys`](docs/utils.md#objkeys) - Type-safe `Object.keys`
106
+ - [`objEntries`](docs/utils.md#objentries) - Type-safe `Object.entries`
107
+ - [`objFromEntries`](docs/utils.md#objfromentries) - Type-safe `Object.fromEntries`
108
+ - [`objMap`](docs/utils.md#objmap) - Transform each entry of object
109
+
110
+ ## JSON Utilities
111
+
112
+ - [`jsonStringify`](docs/utils.md#jsonstringify) - JSON serialization with custom type support
113
+ - [`jsonParse`](docs/utils.md#jsonparse) - JSON deserialization with custom type restoration
114
+
115
+ ## XML Utilities
116
+
117
+ - [`xmlParse`](docs/utils.md#xmlparse) - Parse XML string to object
118
+ - [`xmlStringify`](docs/utils.md#xmlstringify) - Serialize object to XML string
119
+
120
+ ## String Utilities
121
+
122
+ - [`strGetSuffix`](docs/utils.md#strgetsuffix) - Korean postposition handling
123
+ - [`strReplaceFullWidth`](docs/utils.md#strreplacefullwidth) - Convert full-width to half-width
124
+ - [`strToPascalCase`](docs/utils.md#strtopascalcase) - PascalCase conversion
125
+ - [`strToCamelCase`](docs/utils.md#strtocamelcase) - camelCase conversion
126
+ - [`strToKebabCase`](docs/utils.md#strtokebabcase) - kebab-case conversion
127
+ - [`strToSnakeCase`](docs/utils.md#strtosnakecase) - snake_case conversion
128
+ - [`strIsNullOrEmpty`](docs/utils.md#strisnullorempty) - Check for undefined/null/empty (type guard)
129
+ - [`strInsert`](docs/utils.md#strinsert) - Insert at position in string
130
+
131
+ ## Number Utilities
132
+
133
+ - [`numParseInt`](docs/utils.md#numparseint) - Parse string to integer
134
+ - [`numParseFloat`](docs/utils.md#numparsefloat) - Parse string to float
135
+ - [`numParseRoundedInt`](docs/utils.md#numparseroundedint) - Round float and return integer
136
+ - [`numFormat`](docs/utils.md#numformat) - Thousands separator formatting
137
+ - [`numIsNullOrEmpty`](docs/utils.md#numisnullorempty) - Check for undefined/null/0 (type guard)
138
+
139
+ ## Date/Time Formatting
140
+
141
+ - [`formatDate`](docs/utils.md#formatdate) - Convert date/time to formatted string
142
+ - [`normalizeMonth`](docs/utils.md#normalizemonth) - Normalize year/month/day when setting month
143
+
144
+ ## Byte Utilities
145
+
146
+ - [`bytesConcat`](docs/utils.md#bytesconcat) - Concatenate multiple Uint8Arrays
147
+ - [`bytesToHex`](docs/utils.md#bytestohex) - Convert Uint8Array to hex string
148
+ - [`bytesFromHex`](docs/utils.md#bytesfromhex) - Convert hex string to Uint8Array
149
+ - [`bytesToBase64`](docs/utils.md#bytestobase64) - Convert Uint8Array to base64 string
150
+ - [`bytesFromBase64`](docs/utils.md#bytesfrombase64) - Convert base64 string to Uint8Array
151
+
152
+ ## Async Wait
153
+
154
+ - [`waitTime`](docs/utils.md#waittime) - Wait for specified time
155
+ - [`waitUntil`](docs/utils.md#waituntil) - Wait until condition is true
156
+
157
+ ## Worker Data Conversion
158
+
159
+ - [`transferableEncode`](docs/utils.md#transferableencode) - Serialize custom types for Worker transfer
160
+ - [`transferableDecode`](docs/utils.md#transferabledecode) - Deserialize Worker data to custom types
161
+
162
+ ## Path Utilities
163
+
164
+ - [`pathJoin`](docs/utils.md#pathjoin) - Combine paths (POSIX-style only)
165
+ - [`pathBasename`](docs/utils.md#pathbasename) - Extract filename
166
+ - [`pathExtname`](docs/utils.md#pathextname) - Extract extension
167
+
168
+ ## Template Literal Tags
169
+
170
+ - [`js`](docs/utils.md#js) - JavaScript code highlighting
171
+ - [`ts`](docs/utils.md#ts) - TypeScript code highlighting
172
+ - [`html`](docs/utils.md#html) - HTML markup highlighting
173
+ - [`tsql`](docs/utils.md#tsql) - MSSQL T-SQL highlighting
174
+ - [`mysql`](docs/utils.md#mysql) - MySQL SQL highlighting
175
+ - [`pgsql`](docs/utils.md#pgsql) - PostgreSQL SQL highlighting
176
+
177
+ ## Other Utilities
178
+
179
+ - [`getPrimitiveTypeStr`](docs/utils.md#getprimitivetypestr) - Infer `PrimitiveTypeStr` from runtime value
180
+ - [`env`](docs/utils.md#env) - Environment variable object
181
+
182
+ ## Type Utilities
183
+
184
+ - [`Bytes`](docs/types.md#bytes) - Alias for `Uint8Array`
185
+ - [`PrimitiveTypeStr`](docs/types.md#primitivetypestr) - Primitive type string keys
186
+ - [`PrimitiveTypeMap`](docs/types.md#primitivetypemap) - Mapping from type string to type
187
+ - [`PrimitiveType`](docs/types.md#primitivetype) - Union of all primitive types
188
+ - [`DeepPartial`](docs/types.md#deeppartial) - Recursively convert properties to optional
189
+ - [`Type`](docs/types.md#type) - Constructor type
190
+ - [`ObjUndefToOptional`](docs/types.md#objundeftooptional) - Convert `undefined` properties to optional
191
+ - [`ObjOptionalToUndef`](docs/types.md#objoptionaltoundef) - Convert optional properties to `required + undefined`
192
+ - [`ArrayDiffsResult`](docs/types.md#arraydiffsresult) - Result type of `Array.diffs()`
193
+ - [`ArrayDiffs2Result`](docs/types.md#arraydiffs2result) - Result type of `Array.oneWayDiffs()`
194
+ - [`TreeArray`](docs/types.md#treearray) - Result type of `Array.toTree()`
195
+
196
+ ## Array Extensions
197
+
198
+ ### Query
199
+ - [`single`](docs/extensions.md#single) - Return single element (error if 2+)
200
+ - [`first`](docs/extensions.md#first) - Return first element
201
+ - [`last`](docs/extensions.md#last) - Return last element
202
+
203
+ ### Filtering
204
+ - [`filterExists`](docs/extensions.md#filterexists) - Remove `null`/`undefined`
205
+ - [`ofType`](docs/extensions.md#oftype) - Filter by type
206
+ - [`filterAsync`](docs/extensions.md#filterasync) - Async filter
207
+
208
+ ### Mapping/Transformation
209
+ - [`mapAsync`](docs/extensions.md#mapasync) - Async mapping (sequential)
210
+ - [`mapMany`](docs/extensions.md#mapmany) - flat + filterExists
211
+ - [`mapManyAsync`](docs/extensions.md#mapmanyasync) - Async mapMany
212
+ - [`parallelAsync`](docs/extensions.md#parallelasync) - Parallel async mapping
213
+
214
+ ### Grouping
215
+ - [`groupBy`](docs/extensions.md#groupby) - Group by key
216
+ - [`toMap`](docs/extensions.md#tomap) - Convert to Map
217
+ - [`toMapAsync`](docs/extensions.md#tomapasync) - Async Map conversion
218
+ - [`toArrayMap`](docs/extensions.md#toarraymap) - Convert to `Map<K, V[]>`
219
+ - [`toSetMap`](docs/extensions.md#tosetmap) - Convert to `Map<K, Set<V>>`
220
+ - [`toMapValues`](docs/extensions.md#tomapvalues) - Aggregate Map by group
221
+ - [`toObject`](docs/extensions.md#toobject) - Convert to `Record<string, V>`
222
+ - [`toTree`](docs/extensions.md#totree) - Convert to tree structure
223
+
224
+ ### Deduplication
225
+ - [`distinct`](docs/extensions.md#distinct) - Remove duplicates (new array)
226
+ - [`distinctThis`](docs/extensions.md#distinctthis) - Remove duplicates (modify original)
227
+
228
+ ### Sorting
229
+ - [`orderBy`](docs/extensions.md#orderby) - Ascending sort (new array)
230
+ - [`orderByDesc`](docs/extensions.md#orderbydesc) - Descending sort (new array)
231
+ - [`orderByThis`](docs/extensions.md#orderbythis) - Ascending sort (modify original)
232
+ - [`orderByDescThis`](docs/extensions.md#orderbydescthis) - Descending sort (modify original)
233
+
234
+ ### Comparison/Merging
235
+ - [`diffs`](docs/extensions.md#diffs) - Compare differences between arrays
236
+ - [`oneWayDiffs`](docs/extensions.md#onewaydiffs) - One-way diff comparison
237
+ - [`merge`](docs/extensions.md#merge) - Merge arrays
238
+
239
+ ### Aggregation
240
+ - [`sum`](docs/extensions.md#sum) - Sum
241
+ - [`min`](docs/extensions.md#min) - Minimum
242
+ - [`max`](docs/extensions.md#max) - Maximum
243
+
244
+ ### Mutation
245
+ - [`insert`](docs/extensions.md#insert) - Insert at specific position
246
+ - [`remove`](docs/extensions.md#remove) - Remove item
247
+ - [`toggle`](docs/extensions.md#toggle) - Toggle item
248
+ - [`clear`](docs/extensions.md#clear) - Remove all items
249
+ - [`shuffle`](docs/extensions.md#shuffle) - Shuffle array
250
+
251
+ ## Map Extensions
252
+
253
+ - [`getOrCreate`](docs/extensions.md#getorcreate) - Get or create and return value
254
+ - [`update`](docs/extensions.md#update) - Update value using function
255
+
256
+ ## Set Extensions
257
+
258
+ - [`adds`](docs/extensions.md#adds) - Add multiple values at once
259
+ - [`toggle`](docs/extensions.md#toggle-1) - Toggle value (add/remove)
260
+
885
261
  ## License
886
262
 
887
263
  Apache-2.0