@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.
- package/README.md +191 -815
- package/dist/common.types.js +4 -4
- package/dist/common.types.js.map +0 -1
- package/dist/env.js +2 -2
- package/dist/env.js.map +1 -2
- package/dist/errors/argument-error.js +1 -1
- package/dist/errors/argument-error.js.map +0 -1
- package/dist/errors/not-implemented-error.js +1 -1
- package/dist/errors/not-implemented-error.js.map +0 -1
- package/dist/errors/sd-error.js.map +0 -1
- package/dist/errors/timeout-error.js +1 -1
- package/dist/errors/timeout-error.js.map +0 -1
- package/dist/extensions/arr-ext.helpers.js +4 -4
- package/dist/extensions/arr-ext.helpers.js.map +0 -1
- package/dist/extensions/arr-ext.js +9 -9
- package/dist/extensions/arr-ext.js.map +0 -1
- package/dist/extensions/arr-ext.types.js.map +0 -1
- package/dist/extensions/map-ext.js.map +0 -1
- package/dist/extensions/set-ext.js.map +0 -1
- package/dist/features/debounce-queue.d.ts.map +1 -1
- package/dist/features/debounce-queue.js +4 -4
- package/dist/features/debounce-queue.js.map +1 -2
- package/dist/features/event-emitter.js.map +0 -1
- package/dist/features/serial-queue.d.ts.map +1 -1
- package/dist/features/serial-queue.js +5 -5
- package/dist/features/serial-queue.js.map +1 -2
- package/dist/globals.js.map +0 -1
- package/dist/index.js +30 -30
- package/dist/index.js.map +0 -1
- package/dist/types/date-only.js +2 -2
- package/dist/types/date-only.js.map +0 -1
- package/dist/types/date-time.js +2 -2
- package/dist/types/date-time.js.map +0 -1
- package/dist/types/lazy-gc-map.d.ts.map +1 -1
- package/dist/types/lazy-gc-map.js +2 -2
- package/dist/types/lazy-gc-map.js.map +1 -2
- package/dist/types/time.js +2 -2
- package/dist/types/time.js.map +0 -1
- package/dist/types/uuid.js +1 -1
- package/dist/types/uuid.js.map +0 -1
- package/dist/utils/bytes.js +1 -1
- package/dist/utils/bytes.js.map +0 -1
- package/dist/utils/date-format.js.map +0 -1
- package/dist/utils/json.js +8 -8
- package/dist/utils/json.js.map +0 -1
- package/dist/utils/num.js.map +0 -1
- package/dist/utils/obj.js +5 -5
- package/dist/utils/obj.js.map +0 -1
- package/dist/utils/path.js.map +0 -1
- package/dist/utils/primitive.js +5 -5
- package/dist/utils/primitive.js.map +0 -1
- package/dist/utils/str.js.map +0 -1
- package/dist/utils/template-strings.js.map +0 -1
- package/dist/utils/transferable.js +4 -4
- package/dist/utils/transferable.js.map +0 -1
- package/dist/utils/wait.js +1 -1
- package/dist/utils/wait.js.map +0 -1
- package/dist/utils/xml.js.map +0 -1
- package/dist/zip/sd-zip.js.map +0 -1
- package/docs/extensions.md +381 -0
- package/docs/features.md +94 -0
- package/docs/types.md +338 -0
- package/docs/utils.md +631 -0
- package/package.json +5 -3
- package/src/common.types.ts +91 -0
- package/src/env.ts +11 -0
- package/src/errors/argument-error.ts +40 -0
- package/src/errors/not-implemented-error.ts +32 -0
- package/src/errors/sd-error.ts +53 -0
- package/src/errors/timeout-error.ts +36 -0
- package/src/extensions/arr-ext.helpers.ts +53 -0
- package/src/extensions/arr-ext.ts +777 -0
- package/src/extensions/arr-ext.types.ts +258 -0
- package/src/extensions/map-ext.ts +86 -0
- package/src/extensions/set-ext.ts +68 -0
- package/src/features/debounce-queue.ts +116 -0
- package/src/features/event-emitter.ts +112 -0
- package/src/features/serial-queue.ts +94 -0
- package/src/globals.ts +12 -0
- package/src/index.ts +55 -0
- package/src/types/date-only.ts +329 -0
- package/src/types/date-time.ts +294 -0
- package/src/types/lazy-gc-map.ts +244 -0
- package/src/types/time.ts +210 -0
- package/src/types/uuid.ts +113 -0
- package/src/utils/bytes.ts +160 -0
- package/src/utils/date-format.ts +239 -0
- package/src/utils/json.ts +230 -0
- package/src/utils/num.ts +97 -0
- package/src/utils/obj.ts +956 -0
- package/src/utils/path.ts +40 -0
- package/src/utils/primitive.ts +33 -0
- package/src/utils/str.ts +252 -0
- package/src/utils/template-strings.ts +132 -0
- package/src/utils/transferable.ts +269 -0
- package/src/utils/wait.ts +40 -0
- package/src/utils/xml.ts +105 -0
- 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
|