@simplysm/core-common 14.0.47 → 14.0.49

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/docs/utils.md ADDED
@@ -0,0 +1,591 @@
1
+ # Utils
2
+
3
+ ## `obj` namespace
4
+
5
+ 깊은 복사, 비교, 병합, 객체 조작 유틸리티.
6
+
7
+ ### `obj.clone<TObj>(source: TObj): TObj`
8
+
9
+ 깊은 복사. 순환 참조 지원. `DateTime`, `DateOnly`, `Time`, `Uuid`, `Uint8Array`, `Error`, `Date`, `RegExp`, `Map`, `Set`, `Array` 등 커스텀 타입 복사를 지원한다.
10
+
11
+ ```typescript
12
+ const copied = obj.clone({ nested: { data: [1, 2, 3] } });
13
+ ```
14
+
15
+ ### `obj.equal(source, target, options?): boolean`
16
+
17
+ 깊은 동등성 비교. `EqualOptions` 참조.
18
+
19
+ ```typescript
20
+ obj.equal(a, b, { topLevelExcludes: ["updatedAt"] });
21
+ obj.equal(arr1, arr2, { ignoreArrayIndex: true }); // 순서 무시 비교 (O(n²))
22
+ ```
23
+
24
+ ### `EqualOptions` (interface)
25
+
26
+ | Field | Type | Description |
27
+ |-------|------|-------------|
28
+ | `topLevelIncludes` | `string[] \| undefined` | 비교할 key 목록 (최상위 레벨만) |
29
+ | `topLevelExcludes` | `string[] \| undefined` | 비교에서 제외할 key 목록 (최상위 레벨만) |
30
+ | `ignoreArrayIndex` | `boolean \| undefined` | 배열 순서 무시 여부. `true`이면 O(n²) |
31
+ | `shallow` | `boolean \| undefined` | 얕은 비교 여부 |
32
+
33
+ ### `obj.merge<TSource, TMergeTarget>(source, target, options?): TSource & TMergeTarget`
34
+
35
+ 두 객체를 깊은 병합. `source` 기준으로 `target` 값으로 덮어씀.
36
+
37
+ ### `MergeOptions` (interface)
38
+
39
+ | Field | Type | Description |
40
+ |-------|------|-------------|
41
+ | `arrayProcess` | `"replace" \| "concat" \| undefined` | Array 처리 방식. `"replace"`: target으로 교체 (기본값), `"concat"`: 병합 (중복 제거) |
42
+ | `useDelTargetNull` | `boolean \| undefined` | target이 null일 때 해당 key를 삭제할지 여부 |
43
+
44
+ ### `obj.merge3<S, O, T>(source, origin, target, optionsObj?): { conflict: boolean; result: O & S & T }`
45
+
46
+ 3방향 병합. source, origin, target 세 객체를 비교하여 병합한다. source와 origin이 같고 target이 다르면 target 값, target과 origin이 같고 source가 다르면 source 값, 세 값이 모두 다르면 충돌(origin 값 유지).
47
+
48
+ ### `Merge3KeyOptions` (interface)
49
+
50
+ | Field | Type | Description |
51
+ |-------|------|-------------|
52
+ | `keys` | `string[] \| undefined` | 비교할 하위 key 목록 (equal의 topLevelIncludes와 동일) |
53
+ | `excludes` | `string[] \| undefined` | 비교에서 제외할 하위 key 목록 |
54
+ | `ignoreArrayIndex` | `boolean \| undefined` | array 순서를 무시할지 여부 |
55
+
56
+ ### `obj.omit<T, K extends keyof T>(obj, keys): Omit<T, K>`
57
+
58
+ 객체에서 지정된 key를 제외한 새 객체 반환.
59
+
60
+ ```typescript
61
+ const noId = obj.omit(user, ["id", "password"]);
62
+ ```
63
+
64
+ ### `obj.omitByFilter<T>(item, omitKeyFn): T`
65
+
66
+ key 조건으로 필터링하여 해당 key를 제외한 새 객체 반환.
67
+
68
+ ```typescript
69
+ const filtered = obj.omitByFilter(data, (key) => key.startsWith("_"));
70
+ ```
71
+
72
+ ### `obj.pick<T, K extends keyof T>(obj, keys): Pick<T, K>`
73
+
74
+ 객체에서 지정된 key만 포함한 새 객체 반환.
75
+
76
+ ```typescript
77
+ const onlyName = obj.pick(user, ["name", "email"]);
78
+ ```
79
+
80
+ ### `obj.getChainValue(obj, chain, optional?): unknown`
81
+
82
+ 체인 표현식으로 중첩 객체 값 읽기.
83
+
84
+ ```typescript
85
+ obj.getChainValue(data, "user.profile.name");
86
+ obj.getChainValue(data, "items[0].id", true); // optional: 오류 없이 undefined 반환
87
+ ```
88
+
89
+ ### `obj.getChainValueByDepth<TObject, TKey>(obj, key, depth): TObject[TKey] | undefined`
90
+
91
+ 단일 key를 depth 횟수만큼 재귀적으로 따라간다. 예: `getChainValueByDepth({ parent: { parent: { name: 'a' } } }, 'parent', 2)` → `{ name: 'a' }`
92
+
93
+ ### `obj.setChainValue(obj, chain, value): void`
94
+
95
+ 체인 표현식으로 중첩 객체 값 쓰기.
96
+
97
+ ### `obj.deleteChainValue(obj, chain): void`
98
+
99
+ 체인 표현식으로 중첩 객체 값 삭제.
100
+
101
+ ### `obj.clearUndefined<T extends object>(obj): T`
102
+
103
+ `undefined` 값인 속성을 모두 삭제한다.
104
+
105
+ ### `obj.clear<T>(obj): Record<string, never>`
106
+
107
+ 객체의 모든 속성을 삭제한다.
108
+
109
+ ### `obj.nullToUndefined<TObject>(obj): TObject | undefined`
110
+
111
+ JSON `null` 값을 재귀적으로 `undefined`로 변환한다.
112
+
113
+ ### `obj.unflatten(flatObj): Record<string, unknown>`
114
+
115
+ `"a.b.c"` 형식의 키를 가진 평면 객체를 중첩 객체로 변환.
116
+
117
+ ### `obj.keys<T>(obj): (keyof T)[]`
118
+
119
+ `Object.keys`의 타입 안전 래퍼.
120
+
121
+ ### `obj.entries<T>(obj): Entries<T>`
122
+
123
+ `Object.entries`의 타입 안전 래퍼.
124
+
125
+ ### `obj.fromEntries<T>(entryPairs): object`
126
+
127
+ `Object.fromEntries`의 타입 안전 래퍼.
128
+
129
+ ### `obj.map<TSource, TNewKey, TNewValue>(source, fn): Record<TNewKey, TNewValue>`
130
+
131
+ 객체의 각 항목을 변환하여 새 객체 생성.
132
+
133
+ ### `UndefToOptional<TObject>` (type)
134
+
135
+ `{ a: string | undefined }` → `{ a?: string }` 변환 (undefined 타입을 optional로).
136
+
137
+ ### `OptionalToUndef<TObject>` (type)
138
+
139
+ `{ a?: string }` → `{ a: string | undefined }` 변환 (optional을 undefined 포함 타입으로).
140
+
141
+ ---
142
+
143
+ ## `str` namespace
144
+
145
+ 문자열 유틸리티 함수.
146
+
147
+ | Function | Signature | Description |
148
+ |----------|-----------|-------------|
149
+ | `getKoreanSuffix` | `(text, type) => string` | 받침 유무에 따라 적절한 한국어 조사 반환 |
150
+ | `replaceFullWidth` | `(str) => string` | 전각 문자를 반각 문자로 변환 |
151
+ | `toPascalCase` | `(str) => string` | PascalCase로 변환 |
152
+ | `toCamelCase` | `(str) => string` | camelCase로 변환 |
153
+ | `toKebabCase` | `(str) => string` | kebab-case로 변환 |
154
+ | `toSnakeCase` | `(str) => string` | snake_case로 변환 |
155
+ | `isNullOrEmpty` | `(str) => str is "" \| undefined` | 빈 문자열 또는 undefined 검사 (타입 가드) |
156
+ | `insert` | `(str, index, insertString) => string` | 특정 위치에 문자열 삽입 |
157
+
158
+ `getKoreanSuffix` 지원 타입: `"을"`, `"은"`, `"이"`, `"와"`, `"랑"`, `"로"`, `"라"`
159
+
160
+ ```typescript
161
+ str.getKoreanSuffix("Apple", "을") // "를"
162
+ str.getKoreanSuffix("책", "이") // "이"
163
+ str.toCamelCase("HelloWorld") // "helloWorld"
164
+ str.toKebabCase("HelloWorld") // "hello-world"
165
+ ```
166
+
167
+ ---
168
+
169
+ ## `num` namespace
170
+
171
+ 숫자 유틸리티 함수.
172
+
173
+ | Function | Signature | Description |
174
+ |----------|-----------|-------------|
175
+ | `parseInt` | `(text) => number \| undefined` | 문자열을 정수로 파싱. 비숫자 문자 제거 후 파싱 |
176
+ | `parseFloat` | `(text) => number \| undefined` | 문자열을 float로 파싱. 비숫자 문자 제거 후 파싱 |
177
+ | `parseRoundedInt` | `(text) => number \| undefined` | float로 파싱 후 반올림하여 정수 반환 |
178
+ | `isNullOrEmpty` | `(val) => val is 0 \| undefined` | undefined, null, 0 검사 (타입 가드) |
179
+ | `format` | `(val, digit?) => string \| undefined` | 천 단위 구분자가 포함된 문자열로 포맷 |
180
+
181
+ `num.format` digit 옵션:
182
+
183
+ | Field | Type | Description |
184
+ |-------|------|-------------|
185
+ | `max` | `number \| undefined` | 최대 소수점 자릿수 |
186
+ | `min` | `number \| undefined` | 최소 소수점 자릿수 (부족하면 0으로 채움) |
187
+
188
+ ```typescript
189
+ num.parseInt("1,234") // 1234
190
+ num.format(1234.567, { max: 2 }) // "1,234.57"
191
+ num.format(1234, { min: 2 }) // "1,234.00"
192
+ ```
193
+
194
+ ---
195
+
196
+ ## `bytes` namespace
197
+
198
+ `Uint8Array` 유틸리티 함수.
199
+
200
+ | Function | Signature | Description |
201
+ |----------|-----------|-------------|
202
+ | `concat` | `(arrays: Bytes[]) => Bytes` | 여러 Uint8Array 결합 |
203
+ | `toHex` | `(bytes) => string` | Uint8Array를 소문자 hex 문자열로 변환 |
204
+ | `fromHex` | `(hex) => Bytes` | hex 문자열을 Uint8Array로 변환 |
205
+ | `toBase64` | `(bytes) => string` | Uint8Array를 Base64 문자열로 변환 |
206
+ | `fromBase64` | `(base64) => Bytes` | Base64 문자열을 Uint8Array로 변환 |
207
+
208
+ ```typescript
209
+ bytes.toHex(new Uint8Array([255, 0, 127])) // "ff007f"
210
+ bytes.fromHex("ff007f") // Uint8Array([255, 0, 127])
211
+ bytes.toBase64(new Uint8Array([72, 101, 108, 108, 111])) // "SGVsbG8="
212
+ ```
213
+
214
+ ---
215
+
216
+ ## `path` namespace
217
+
218
+ POSIX 스타일 경로 유틸리티 (Node.js `path` 모듈 대체, 브라우저 환경 지원).
219
+
220
+ **주의**: 슬래시(`/`)만 지원한다. Windows 백슬래시(`\`)는 지원하지 않는다.
221
+
222
+ | Function | Signature | Description |
223
+ |----------|-----------|-------------|
224
+ | `join` | `(...segments: string[]) => string` | 경로 결합 |
225
+ | `basename` | `(filePath, ext?) => string` | 파일명 추출 |
226
+ | `extname` | `(filePath) => string` | 파일 확장자 추출. 숨김 파일은 빈 문자열 반환 |
227
+
228
+ ```typescript
229
+ path.join("/a/b", "c/d") // "/a/b/c/d"
230
+ path.basename("/a/b/c.ts") // "c.ts"
231
+ path.basename("/a/b/c.ts", ".ts") // "c"
232
+ path.extname("/a/b/c.ts") // ".ts"
233
+ path.extname(".gitignore") // ""
234
+ ```
235
+
236
+ ---
237
+
238
+ ## `json` namespace
239
+
240
+ 커스텀 타입을 지원하는 JSON 직렬화/역직렬화.
241
+
242
+ ### `json.stringify(obj, options?): string`
243
+
244
+ `DateTime`, `DateOnly`, `Time`, `Uuid`, `Set`, `Map`, `Error`, `Uint8Array` 등 커스텀 타입 직렬화 지원.
245
+
246
+ 옵션:
247
+
248
+ | Field | Type | Description |
249
+ |-------|------|-------------|
250
+ | `space` | `string \| number \| undefined` | JSON 들여쓰기 |
251
+ | `replacer` | `(key, value) => unknown \| undefined` | 커스텀 replacer. 기본 타입 변환 전에 호출됨 |
252
+ | `redactBytes` | `boolean \| undefined` | `true`이면 Uint8Array 내용을 `"__hidden__"`으로 대체 (로깅용). 이 옵션으로 직렬화된 결과는 `parse()`로 복원 불가 |
253
+
254
+ ### `json.parse<TResult>(json): TResult`
255
+
256
+ `__type__` 마커 기반으로 커스텀 타입 복원. JSON `null` 값은 `undefined`로 변환된다.
257
+
258
+ ```typescript
259
+ const serialized = json.stringify({ date: new DateTime(), id: Uuid.generate() });
260
+ const restored = json.parse(serialized); // DateTime, Uuid 복원됨
261
+ ```
262
+
263
+ ---
264
+
265
+ ## `xml` namespace
266
+
267
+ XML 파싱/직렬화 (fast-xml-parser 래퍼).
268
+
269
+ ### `xml.parse(str, options?): unknown`
270
+
271
+ XML 문자열을 객체로 파싱. 파싱 결과 구조:
272
+ - 속성: `$` 객체에 그룹화
273
+ - 텍스트 노드: `_` key에 저장
274
+ - 자식 요소: 배열로 변환 (루트 요소 제외)
275
+
276
+ | Option | Type | Description |
277
+ |--------|------|-------------|
278
+ | `stripTagPrefix` | `boolean \| undefined` | 태그 접두사(네임스페이스) 제거 여부 |
279
+
280
+ ```typescript
281
+ xml.parse('<root id="1"><item>hello</item></root>');
282
+ // { root: { $: { id: "1" }, item: [{ _: "hello" }] } }
283
+ ```
284
+
285
+ ### `xml.stringify(obj, options?): string`
286
+
287
+ 객체를 XML 문자열로 직렬화. `options`는 `fast-xml-parser`의 `XmlBuilderOptions`.
288
+
289
+ ```typescript
290
+ xml.stringify({ root: { $: { id: "1" }, item: [{ _: "hello" }] } });
291
+ // '<root id="1"><item>hello</item></root>'
292
+ ```
293
+
294
+ ---
295
+
296
+ ## `wait` namespace
297
+
298
+ 비동기 대기 유틸리티.
299
+
300
+ ### `wait.until(forwarder, milliseconds?, maxCount?): Promise<void>`
301
+
302
+ 조건이 `true`가 될 때까지 대기.
303
+
304
+ | Parameter | Type | Description |
305
+ |-----------|------|-------------|
306
+ | `forwarder` | `() => boolean \| Promise<boolean>` | 조건 함수 |
307
+ | `milliseconds` | `number \| undefined` | 확인 간격 (ms). 기본값: 100 |
308
+ | `maxCount` | `number \| undefined` | 최대 시도 횟수. 초과하면 `TimeoutError` 발생. `undefined`이면 무제한 |
309
+
310
+ ### `wait.time(millisecond): Promise<void>`
311
+
312
+ 지정된 시간(ms)만큼 대기.
313
+
314
+ ```typescript
315
+ await wait.until(() => isReady, 100, 50); // 100ms 간격, 최대 50회
316
+ await wait.time(1000); // 1초 대기
317
+ ```
318
+
319
+ ---
320
+
321
+ ## `transfer` namespace
322
+
323
+ Worker 간 데이터 전송을 위한 직렬화/역직렬화. `structuredClone`이 지원하지 않는 커스텀 타입을 처리한다.
324
+
325
+ 지원 타입: `Date`, `DateTime`, `DateOnly`, `Time`, `Uuid`, `RegExp`, `Error`, `Uint8Array`, `Array`, `Map`, `Set`, 일반 객체
326
+
327
+ ### `transfer.encode(obj): { result: unknown; transferList: ArrayBuffer[] }`
328
+
329
+ 객체를 Worker로 전송 가능한 형태로 직렬화. 순환 참조 감지 및 객체 캐싱 지원.
330
+
331
+ ### `transfer.decode(obj): unknown`
332
+
333
+ 직렬화된 객체를 Simplysm 타입을 사용하는 객체로 역직렬화.
334
+
335
+ ```typescript
336
+ // Worker로 데이터 전송
337
+ const { result, transferList } = transfer.encode(data);
338
+ worker.postMessage(result, transferList);
339
+
340
+ // Worker에서 수신
341
+ const decoded = transfer.decode(event.data);
342
+ ```
343
+
344
+ ---
345
+
346
+ ## `err` namespace
347
+
348
+ 에러 메시지 추출 유틸리티.
349
+
350
+ ### `err.message(err: unknown): string`
351
+
352
+ `catch` 블록의 `unknown` 에러에서 메시지를 추출한다. `Error` 인스턴스이면 `message` 속성을 반환하고, 그렇지 않으면 `String(err)` 결과를 반환한다.
353
+
354
+ ```typescript
355
+ try {
356
+ doSomething();
357
+ } catch (e) {
358
+ console.log(err.message(e)); // 항상 string
359
+ }
360
+ ```
361
+
362
+ ---
363
+
364
+ ## `dt` namespace
365
+
366
+ 날짜/시간 포맷 유틸리티. 주로 `DateTime`, `DateOnly`, `Time` 클래스 내부에서 사용되지만 직접 호출도 가능하다.
367
+
368
+ ### `dt.format(formatString, args): string`
369
+
370
+ 형식 문자열에 따라 날짜/시간을 문자열로 변환.
371
+
372
+ 지원 형식 패턴:
373
+
374
+ | 패턴 | 설명 | 예시 |
375
+ |------|------|------|
376
+ | `yyyy` | 4자리 연도 | `2024` |
377
+ | `yy` | 2자리 연도 | `24` |
378
+ | `MM` | 0 채움 월 | `01~12` |
379
+ | `M` | 월 | `1~12` |
380
+ | `ddd` | 요일 (한국어) | `일, 월, 화, 수, 목, 금, 토` |
381
+ | `dd` | 0 채움 일 | `01~31` |
382
+ | `d` | 일 | `1~31` |
383
+ | `tt` | 오전/오후 | `AM, PM` |
384
+ | `hh` | 0 채움 12시간 | `01~12` |
385
+ | `h` | 12시간 | `1~12` |
386
+ | `HH` | 0 채움 24시간 | `00~23` |
387
+ | `H` | 24시간 | `0~23` |
388
+ | `mm` | 0 채움 분 | `00~59` |
389
+ | `m` | 분 | `0~59` |
390
+ | `ss` | 0 채움 초 | `00~59` |
391
+ | `s` | 초 | `0~59` |
392
+ | `fff` | 밀리초 (3자리) | `000~999` |
393
+ | `ff` | 밀리초 (2자리) | `00~99` |
394
+ | `f` | 밀리초 (1자리) | `0~9` |
395
+ | `zzz` | 타임존 오프셋 (±HH:mm) | `+09:00` |
396
+ | `zz` | 타임존 오프셋 (±HH) | `+09` |
397
+ | `z` | 타임존 오프셋 (±H) | `+9` |
398
+
399
+ ### `dt.normalizeMonth(year, month, day): DtNormalizedMonth`
400
+
401
+ 월 설정 시 연/월/일 정규화. 월이 1-12 범위를 벗어나면 연도를 조정한다.
402
+
403
+ ### `DtNormalizedMonth` (interface)
404
+
405
+ | Field | Type | Description |
406
+ |-------|------|-------------|
407
+ | `year` | `number` | 정규화된 연도 |
408
+ | `month` | `number` | 정규화된 월 (1-12) |
409
+ | `day` | `number` | 정규화된 일 |
410
+
411
+ ### `dt.convert12To24(rawHour, isPM): number`
412
+
413
+ 12시간 형식을 24시간 형식으로 변환.
414
+
415
+ ---
416
+
417
+ ## `primitive` namespace
418
+
419
+ ### `primitive.typeStr(value: PrimitiveTypeMap[PrimitiveTypeStr]): PrimitiveTypeStr`
420
+
421
+ 런타임에 값의 타입을 확인하고 해당하는 `PrimitiveTypeStr`을 반환한다.
422
+
423
+ ```typescript
424
+ primitive.typeStr("hello") // "string"
425
+ primitive.typeStr(123) // "number"
426
+ primitive.typeStr(new DateTime()) // "DateTime"
427
+ primitive.typeStr(new Uint8Array()) // "Bytes"
428
+ ```
429
+
430
+ ---
431
+
432
+ ## Direct Exports
433
+
434
+ ### Template String Tags
435
+
436
+ IDE 코드 하이라이팅 지원용 태그드 템플릿 리터럴. 실제 동작은 문자열 결합 + 들여쓰기 정규화다.
437
+
438
+ | Function | Description |
439
+ |----------|-------------|
440
+ | `js` | JavaScript 코드 하이라이팅용 |
441
+ | `ts` | TypeScript 코드 하이라이팅용 |
442
+ | `html` | HTML 마크업 하이라이팅용 |
443
+ | `tsql` | MSSQL T-SQL 하이라이팅용 |
444
+ | `mysql` | MySQL SQL 하이라이팅용 |
445
+ | `pgsql` | PostgreSQL SQL 하이라이팅용 |
446
+
447
+ 모든 태그 함수의 시그니처:
448
+
449
+ ```typescript
450
+ function tagName(strings: TemplateStringsArray, ...values: unknown[]): string
451
+ ```
452
+
453
+ 들여쓰기 정규화: 앞뒤 빈 줄을 제거하고 최소 들여쓰기를 제거한다.
454
+
455
+ ```typescript
456
+ const query = tsql`
457
+ SELECT TOP 10 *
458
+ FROM Users
459
+ WHERE Name = ${name}
460
+ `;
461
+ // "SELECT TOP 10 *\nFROM Users\nWHERE Name = ..."
462
+ ```
463
+
464
+ ### `ZipArchive`
465
+
466
+ ZIP 파일의 읽기, 쓰기, 압축, 해제를 처리하는 클래스. 동일 파일의 중복 해제를 방지하기 위해 내부 캐싱을 사용한다.
467
+
468
+ ```typescript
469
+ export class ZipArchive {
470
+ constructor(data?: Blob | Bytes);
471
+
472
+ async extractAll(progressCallback?: (progress: ZipArchiveProgress) => void): Promise<Map<string, Bytes | undefined>>;
473
+ async get(fileName: string): Promise<Bytes | undefined>;
474
+ async exists(fileName: string): Promise<boolean>;
475
+ write(fileName: string, bytes: Bytes): void;
476
+ async compress(): Promise<Bytes>;
477
+ async close(): Promise<void>;
478
+ }
479
+ ```
480
+
481
+ | Method | Description |
482
+ |--------|-------------|
483
+ | `constructor(data?)` | ZIP 데이터로 생성. 생략하면 새 아카이브 생성 |
484
+ | `extractAll(progressCallback?)` | 모든 파일 추출. 진행률 콜백 지원 |
485
+ | `get(fileName)` | 특정 파일 추출. 캐싱 사용 |
486
+ | `exists(fileName)` | 파일 존재 여부 확인 |
487
+ | `write(fileName, bytes)` | 파일 쓰기 (캐시에 저장) |
488
+ | `compress()` | 캐시된 파일을 ZIP으로 압축 |
489
+ | `close()` | 리더 닫기 및 캐시 비우기 |
490
+
491
+ ```typescript
492
+ // ZIP 파일 읽기
493
+ const archive = new ZipArchive(zipBytes);
494
+ try {
495
+ const content = await archive.get("file.txt");
496
+ } finally {
497
+ await archive.close();
498
+ }
499
+
500
+ // ZIP 파일 생성
501
+ const newArchive = new ZipArchive();
502
+ try {
503
+ newArchive.write("file.txt", textBytes);
504
+ const zipBytes = await newArchive.compress();
505
+ } finally {
506
+ await newArchive.close();
507
+ }
508
+
509
+ // 진행률 표시
510
+ const archive2 = new ZipArchive(zipBytes);
511
+ try {
512
+ const files = await archive2.extractAll((progress) => {
513
+ console.log(`${progress.fileName}: ${progress.extractedSize}/${progress.totalSize}`);
514
+ });
515
+ } finally {
516
+ await archive2.close();
517
+ }
518
+ ```
519
+
520
+ ### `ZipArchiveProgress` (interface)
521
+
522
+ | Field | Type | Description |
523
+ |-------|------|-------------|
524
+ | `fileName` | `string` | 현재 처리 중인 파일 이름 |
525
+ | `totalSize` | `number` | 전체 파일 크기 합계 (bytes) |
526
+ | `extractedSize` | `number` | 현재까지 추출된 크기 (bytes) |
527
+
528
+ ---
529
+
530
+ ## Type Utilities
531
+
532
+ ### `Bytes`
533
+
534
+ `Uint8Array`의 별칭. `Buffer` 대신 사용한다.
535
+
536
+ ```typescript
537
+ export type Bytes = Uint8Array;
538
+ ```
539
+
540
+ ### `PrimitiveTypeMap`
541
+
542
+ 원시 타입 문자열 key → 타입 매핑.
543
+
544
+ | Key | Type |
545
+ |-----|------|
546
+ | `"string"` | `string` |
547
+ | `"number"` | `number` |
548
+ | `"boolean"` | `boolean` |
549
+ | `"DateTime"` | `DateTime` |
550
+ | `"DateOnly"` | `DateOnly` |
551
+ | `"Time"` | `Time` |
552
+ | `"Uuid"` | `Uuid` |
553
+ | `"Bytes"` | `Bytes` |
554
+
555
+ ### `PrimitiveTypeStr`
556
+
557
+ `keyof PrimitiveTypeMap` — 원시 타입 문자열 key union.
558
+
559
+ ```typescript
560
+ export type PrimitiveTypeStr = "string" | "number" | "boolean" | "DateTime" | "DateOnly" | "Time" | "Uuid" | "Bytes";
561
+ ```
562
+
563
+ ### `PrimitiveType`
564
+
565
+ 원시 타입 union (`PrimitiveTypeMap[PrimitiveTypeStr] | undefined`).
566
+
567
+ ### `DeepPartial<TObject>`
568
+
569
+ 객체의 모든 속성을 재귀적으로 `optional`로 변환. 원시 타입은 그대로 유지하고 object/array 타입에만 재귀적으로 `Partial`을 적용한다.
570
+
571
+ ```typescript
572
+ export type DeepPartial<TObject> = Partial<{
573
+ [K in keyof TObject]: TObject[K] extends PrimitiveType ? TObject[K] : DeepPartial<TObject[K]>;
574
+ }>;
575
+ ```
576
+
577
+ ### `Type<TInstance>` (interface)
578
+
579
+ 생성자 타입.
580
+
581
+ ```typescript
582
+ export interface Type<TInstance> extends Function {
583
+ new (...args: unknown[]): TInstance;
584
+ }
585
+ ```
586
+
587
+ ```typescript
588
+ function create<T>(ctor: Type<T>): T {
589
+ return new ctor();
590
+ }
591
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/core-common",
3
- "version": "14.0.47",
3
+ "version": "14.0.49",
4
4
  "description": "심플리즘 패키지 - 코어 (common)",
5
5
  "author": "심플리즘",
6
6
  "license": "Apache-2.0",
@@ -14,7 +14,8 @@
14
14
  "types": "./dist/index.d.ts",
15
15
  "files": [
16
16
  "dist",
17
- "src"
17
+ "src",
18
+ "docs"
18
19
  ],
19
20
  "sideEffects": [
20
21
  "./src/extensions/arr-ext.ts",
@@ -32,7 +33,7 @@
32
33
  "dependencies": {
33
34
  "@zip.js/zip.js": "^2.8.26",
34
35
  "consola": "^3.4.2",
35
- "fast-xml-parser": "^5.6.0",
36
+ "fast-xml-parser": "^5.7.1",
36
37
  "yaml": "^2.8.3"
37
38
  }
38
39
  }
@@ -250,7 +250,12 @@ export function decode(obj: unknown): unknown {
250
250
  return obj.map((item) => decode(item));
251
251
  }
252
252
 
253
- // 3. Map 재귀
253
+ // 3. Uint8Array (encode에서 그대로 반환하므로 decode에서도 그대로 반환)
254
+ if (obj instanceof Uint8Array) {
255
+ return obj;
256
+ }
257
+
258
+ // 4. Map 재귀
254
259
  if (obj instanceof Map) {
255
260
  const newMap = new Map<unknown, unknown>();
256
261
  for (const [k, v] of obj) {
@@ -259,7 +264,7 @@ export function decode(obj: unknown): unknown {
259
264
  return newMap;
260
265
  }
261
266
 
262
- // 4. Set 재귀
267
+ // 5. Set 재귀
263
268
  if (obj instanceof Set) {
264
269
  const newSet = new Set<unknown>();
265
270
  for (const v of obj) {
@@ -268,7 +273,7 @@ export function decode(obj: unknown): unknown {
268
273
  return newSet;
269
274
  }
270
275
 
271
- // 5. 객체 재귀
276
+ // 6. 객체 재귀
272
277
  if (typeof obj === "object") {
273
278
  const record = obj as Record<string, unknown>;
274
279
  const result: Record<string, unknown> = {};