@simplysm/core-common 14.0.49 → 14.0.51

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 (46) hide show
  1. package/README.md +57 -152
  2. package/dist/errors/sd-error.d.ts.map +1 -1
  3. package/dist/errors/sd-error.js +1 -1
  4. package/dist/errors/sd-error.js.map +1 -1
  5. package/dist/utils/json.js.map +1 -1
  6. package/dist/utils/obj.js.map +1 -1
  7. package/docs/errors/argument-error.md +26 -0
  8. package/docs/errors/not-implemented-error.md +33 -0
  9. package/docs/errors/sd-error.md +38 -0
  10. package/docs/errors/timeout-error.md +36 -0
  11. package/docs/extensions/array.md +125 -0
  12. package/docs/extensions/map.md +43 -0
  13. package/docs/extensions/set.md +35 -0
  14. package/docs/features/debounce-queue.md +48 -0
  15. package/docs/features/event-emitter.md +52 -0
  16. package/docs/features/serial-queue.md +44 -0
  17. package/docs/type-utils/common-types.md +100 -0
  18. package/docs/type-utils/env.md +42 -0
  19. package/docs/types/date-only.md +86 -0
  20. package/docs/types/date-time.md +106 -0
  21. package/docs/types/lazy-gc-map.md +59 -0
  22. package/docs/types/time.md +62 -0
  23. package/docs/types/uuid.md +41 -0
  24. package/docs/utils/bytes.md +36 -0
  25. package/docs/utils/dt.md +60 -0
  26. package/docs/utils/err.md +26 -0
  27. package/docs/utils/json.md +58 -0
  28. package/docs/utils/num.md +56 -0
  29. package/docs/utils/obj.md +107 -0
  30. package/docs/utils/path.md +30 -0
  31. package/docs/utils/primitive.md +28 -0
  32. package/docs/utils/str.md +63 -0
  33. package/docs/utils/template-strings.md +49 -0
  34. package/docs/utils/transfer.md +35 -0
  35. package/docs/utils/wait.md +35 -0
  36. package/docs/utils/xml.md +49 -0
  37. package/docs/utils/zip-archive.md +77 -0
  38. package/package.json +1 -1
  39. package/src/errors/sd-error.ts +1 -4
  40. package/src/utils/json.ts +1 -1
  41. package/src/utils/obj.ts +2 -2
  42. package/docs/errors.md +0 -82
  43. package/docs/extensions.md +0 -167
  44. package/docs/features.md +0 -136
  45. package/docs/types.md +0 -245
  46. package/docs/utils.md +0 -591
@@ -0,0 +1,63 @@
1
+ # str
2
+
3
+ 문자열 유틸리티 네임스페이스.
4
+
5
+ ```typescript
6
+ import { str } from "@simplysm/core-common";
7
+ ```
8
+
9
+ ## Functions
10
+
11
+ | Function | Signature | Description |
12
+ |----------|-----------|-------------|
13
+ | `getKoreanSuffix` | `(text, type) => string` | 받침 유무에 따라 적절한 한국어 조사 반환 |
14
+ | `replaceFullWidth` | `(str) => string` | 전각 문자(A-Z, a-z, 0-9, 전각 공백, 전각 괄호)를 반각 문자로 변환 |
15
+ | `toPascalCase` | `(str) => string` | PascalCase로 변환 (`-`, `_`, `.` 구분자 지원) |
16
+ | `toCamelCase` | `(str) => string` | camelCase로 변환 |
17
+ | `toKebabCase` | `(str) => string` | kebab-case로 변환 |
18
+ | `toSnakeCase` | `(str) => string` | snake_case로 변환 |
19
+ | `isNullOrEmpty` | `(str: string \| undefined) => str is "" \| undefined` | 타입 가드: undefined, null, 빈 문자열이면 true |
20
+ | `insert` | `(str, index, insertString) => string` | 특정 위치에 문자열 삽입 |
21
+
22
+ ## `getKoreanSuffix` — 조사 타입
23
+
24
+ | `type` | 받침 있음 | 받침 없음 | 설명 |
25
+ |--------|----------|----------|------|
26
+ | `"을"` | 을 | 를 | 목적격 조사 |
27
+ | `"은"` | 은 | 는 | 주격 보조사 |
28
+ | `"이"` | 이 | 가 | 주격 조사 |
29
+ | `"와"` | 과 | 와 | 접속 조사 |
30
+ | `"랑"` | 이랑 | 랑 | 접속 조사 |
31
+ | `"로"` | 으로 (ㄹ 받침은 "로") | 로 | 도구격 조사 |
32
+ | `"라"` | 이라 | 라 | 서술격 조사 |
33
+
34
+ ## Usage
35
+
36
+ ```typescript
37
+ import { str } from "@simplysm/core-common";
38
+
39
+ // 한국어 조사
40
+ str.getKoreanSuffix("Apple", "을"); // "를"
41
+ str.getKoreanSuffix("책", "이"); // "이"
42
+ str.getKoreanSuffix("파일", "을"); // "을"
43
+
44
+ // 전각 → 반각
45
+ str.replaceFullWidth("A123"); // "A123"
46
+
47
+ // 케이스 변환
48
+ str.toPascalCase("hello-world"); // "HelloWorld"
49
+ str.toCamelCase("HelloWorld"); // "helloWorld"
50
+ str.toKebabCase("HelloWorld"); // "hello-world"
51
+ str.toSnakeCase("HelloWorld"); // "hello_world"
52
+
53
+ // null/empty 검사 (타입 가드)
54
+ const name: string | undefined = getValue();
55
+ if (str.isNullOrEmpty(name)) {
56
+ // name: "" | undefined
57
+ } else {
58
+ // name: string (non-empty)
59
+ }
60
+
61
+ // 문자열 삽입
62
+ str.insert("Hello World", 5, ","); // "Hello, World"
63
+ ```
@@ -0,0 +1,49 @@
1
+ # Template Strings
2
+
3
+ IDE 코드 하이라이팅 지원용 태그드 템플릿 리터럴 함수. 실제 동작은 문자열 결합 + 들여쓰기 정규화(앞뒤 빈 줄 제거, 공통 들여쓰기 제거)이다.
4
+
5
+ ```typescript
6
+ export function js(strings: TemplateStringsArray, ...values: unknown[]): string;
7
+ export function ts(strings: TemplateStringsArray, ...values: unknown[]): string;
8
+ export function html(strings: TemplateStringsArray, ...values: unknown[]): string;
9
+ export function tsql(strings: TemplateStringsArray, ...values: unknown[]): string;
10
+ export function mysql(strings: TemplateStringsArray, ...values: unknown[]): string;
11
+ export function pgsql(strings: TemplateStringsArray, ...values: unknown[]): string;
12
+ ```
13
+
14
+ 직접 named import로 사용한다 (네임스페이스 아님):
15
+
16
+ ```typescript
17
+ import { js, ts, html, tsql, mysql, pgsql } from "@simplysm/core-common";
18
+ ```
19
+
20
+ ## Functions
21
+
22
+ | Function | Description |
23
+ |----------|-------------|
24
+ | `js` | JavaScript 코드 하이라이팅용 |
25
+ | `ts` | TypeScript 코드 하이라이팅용 |
26
+ | `html` | HTML 마크업 하이라이팅용 |
27
+ | `tsql` | MSSQL T-SQL 하이라이팅용 |
28
+ | `mysql` | MySQL SQL 하이라이팅용 |
29
+ | `pgsql` | PostgreSQL SQL 하이라이팅용 |
30
+
31
+ ## Usage
32
+
33
+ ```typescript
34
+ import { ts, tsql } from "@simplysm/core-common";
35
+
36
+ const code = ts`
37
+ interface User {
38
+ name: string;
39
+ age: number;
40
+ }
41
+ `;
42
+ // "interface User {\n name: string;\n age: number;\n}"
43
+
44
+ const query = tsql`
45
+ SELECT TOP 10 *
46
+ FROM Users
47
+ WHERE Name = ${keyword}
48
+ `;
49
+ ```
@@ -0,0 +1,35 @@
1
+ # transfer
2
+
3
+ Worker 간 전송 가능한 객체 변환 유틸리티 네임스페이스. `structuredClone`이 지원하지 않는 커스텀 타입을 처리한다.
4
+
5
+ ```typescript
6
+ import { transfer } from "@simplysm/core-common";
7
+ ```
8
+
9
+ ## Functions
10
+
11
+ | Function | Signature | Description |
12
+ |----------|-----------|-------------|
13
+ | `encode` | `(obj) => { result: unknown; transferList: ArrayBuffer[] }` | Simplysm 타입을 포함한 객체를 Worker 전송 가능한 형태로 변환. 순환 참조 시 `TypeError` 발생 |
14
+ | `decode` | `(obj) => unknown` | `encode()`로 변환된 객체를 원래 Simplysm 타입으로 복원 |
15
+
16
+ ## 지원 타입
17
+
18
+ `Date`, `DateTime`, `DateOnly`, `Time`, `Uuid`, `RegExp`, `Error` (cause, code, detail 포함), `Uint8Array`, `Array`, `Map`, `Set`, 일반 객체
19
+
20
+ `Uint8Array`는 zero-copy 전송을 위해 `ArrayBuffer`를 `transferList`에 추가한다. `SharedArrayBuffer`는 이미 공유 메모리이므로 `transferList`에 추가하지 않는다.
21
+
22
+ ## Usage
23
+
24
+ ```typescript
25
+ import { transfer } from "@simplysm/core-common";
26
+
27
+ // Worker로 데이터 전송
28
+ const { result, transferList } = transfer.encode(data);
29
+ worker.postMessage(result, transferList);
30
+
31
+ // Worker에서 데이터 수신
32
+ self.onmessage = (event) => {
33
+ const decoded = transfer.decode(event.data);
34
+ };
35
+ ```
@@ -0,0 +1,35 @@
1
+ # wait
2
+
3
+ 비동기 대기 유틸리티 네임스페이스.
4
+
5
+ ```typescript
6
+ import { wait } from "@simplysm/core-common";
7
+ ```
8
+
9
+ ## Functions
10
+
11
+ | Function | Signature | Description |
12
+ |----------|-----------|-------------|
13
+ | `until` | `(forwarder, milliseconds?, maxCount?) => Promise<void>` | 조건이 true가 될 때까지 대기 |
14
+ | `time` | `(millisecond: number) => Promise<void>` | 지정된 시간만큼 대기 |
15
+
16
+ ## `until` — Parameters
17
+
18
+ | Param | Type | Default | Description |
19
+ |-------|------|---------|-------------|
20
+ | `forwarder` | `() => boolean \| Promise<boolean>` | - | 조건 함수. 첫 번째 호출에서 true이면 즉시 반환 |
21
+ | `milliseconds` | `number` | `100` | 확인 간격 (ms) |
22
+ | `maxCount` | `number` | `undefined` | 최대 시도 횟수. 초과 시 `TimeoutError` 발생. 미지정 시 무제한 |
23
+
24
+ ## Usage
25
+
26
+ ```typescript
27
+ import { wait } from "@simplysm/core-common";
28
+
29
+ // 조건 대기
30
+ await wait.until(() => isReady);
31
+ await wait.until(() => isReady, 100, 50); // 100ms 간격, 최대 50회
32
+
33
+ // 시간 대기
34
+ await wait.time(1000); // 1초 대기
35
+ ```
@@ -0,0 +1,49 @@
1
+ # xml
2
+
3
+ XML 변환 유틸리티 네임스페이스. `fast-xml-parser` 기반.
4
+
5
+ ```typescript
6
+ import { xml } from "@simplysm/core-common";
7
+ ```
8
+
9
+ ## Functions
10
+
11
+ | Function | Signature | Description |
12
+ |----------|-----------|-------------|
13
+ | `parse` | `(str, options?) => unknown` | XML 문자열을 객체로 파싱 |
14
+ | `stringify` | `(obj, options?) => string` | 객체를 XML 문자열로 직렬화 |
15
+
16
+ ## `parse` — 파싱 규칙
17
+
18
+ - 속성: `$` 객체에 그룹화
19
+ - 텍스트 노드: `_` key에 저장
20
+ - 자식 요소: 배열로 변환 (루트 요소 제외)
21
+
22
+ ## `parse` — options
23
+
24
+ | Field | Type | Description |
25
+ |-------|------|-------------|
26
+ | `stripTagPrefix` | `boolean` | 태그 접두사(네임스페이스) 제거 여부. 속성은 접두사 유지 |
27
+
28
+ ## Usage
29
+
30
+ ```typescript
31
+ import { xml } from "@simplysm/core-common";
32
+
33
+ // 파싱
34
+ const result = xml.parse('<root id="1"><item>hello</item></root>');
35
+ // { root: { $: { id: "1" }, item: [{ _: "hello" }] } }
36
+
37
+ // 네임스페이스 제거
38
+ const clean = xml.parse('<ns:root><ns:item>hello</ns:item></ns:root>', { stripTagPrefix: true });
39
+ // { root: { item: [{ _: "hello" }] } }
40
+
41
+ // 직렬화
42
+ const str = xml.stringify({
43
+ root: {
44
+ $: { id: "1" },
45
+ item: [{ _: "hello" }, { _: "world" }],
46
+ },
47
+ });
48
+ // '<root id="1"><item>hello</item><item>world</item></root>'
49
+ ```
@@ -0,0 +1,77 @@
1
+ # ZipArchive
2
+
3
+ ZIP 파일 처리 클래스. ZIP 파일의 읽기, 쓰기, 압축, 해제를 처리한다. 동일 파일의 중복 해제를 방지하기 위해 내부 캐싱을 사용한다. `@zip.js/zip.js` 기반.
4
+
5
+ ```typescript
6
+ import { ZipArchive } from "@simplysm/core-common";
7
+
8
+ export class ZipArchive {
9
+ constructor(data?: Blob | Bytes);
10
+ }
11
+ ```
12
+
13
+ ## Members
14
+
15
+ | Member | Kind | Type | Description |
16
+ |--------|------|------|-------------|
17
+ | `extractAll` | method | `(progressCallback?) => Promise<Map<string, Bytes \| undefined>>` | 모든 파일 추출 |
18
+ | `get` | method | `(fileName: string) => Promise<Bytes \| undefined>` | 특정 파일 추출. 내부 캐시 활용 |
19
+ | `exists` | method | `(fileName: string) => Promise<boolean>` | 파일 존재 여부 확인 |
20
+ | `write` | method | `(fileName: string, bytes: Bytes) => void` | 파일 쓰기 (캐시에 저장) |
21
+ | `compress` | method | `() => Promise<Bytes>` | 캐시된 파일을 ZIP으로 압축 |
22
+ | `close` | method | `() => Promise<void>` | 리더 닫기 및 캐시 비우기 |
23
+
24
+ ## Related Types
25
+
26
+ ### `ZipArchiveProgress`
27
+
28
+ `extractAll()` 진행률 콜백의 파라미터 타입:
29
+
30
+ ```typescript
31
+ export interface ZipArchiveProgress {
32
+ fileName: string;
33
+ totalSize: number;
34
+ extractedSize: number;
35
+ }
36
+ ```
37
+
38
+ | Field | Type | Description |
39
+ |-------|------|-------------|
40
+ | `fileName` | `string` | 현재 처리 중인 파일명 |
41
+ | `totalSize` | `number` | 전체 파일 크기 (바이트) |
42
+ | `extractedSize` | `number` | 현재까지 추출된 크기 (바이트) |
43
+
44
+ ## Usage
45
+
46
+ ```typescript
47
+ import { ZipArchive } from "@simplysm/core-common";
48
+
49
+ // ZIP 파일 읽기
50
+ const archive = new ZipArchive(zipBytes);
51
+ try {
52
+ const content = await archive.get("file.txt");
53
+ } finally {
54
+ await archive.close();
55
+ }
56
+
57
+ // ZIP 파일 생성
58
+ const newArchive = new ZipArchive();
59
+ try {
60
+ newArchive.write("file.txt", textBytes);
61
+ newArchive.write("data.json", jsonBytes);
62
+ const zipBytes = await newArchive.compress();
63
+ } finally {
64
+ await newArchive.close();
65
+ }
66
+
67
+ // 모든 파일 추출 (진행률 보고 포함)
68
+ const archive2 = new ZipArchive(zipBytes);
69
+ try {
70
+ const files = await archive2.extractAll((progress) => {
71
+ const pct = (progress.extractedSize / progress.totalSize * 100).toFixed(1);
72
+ console.log(`${progress.fileName}: ${pct}%`);
73
+ });
74
+ } finally {
75
+ await archive2.close();
76
+ }
77
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/core-common",
3
- "version": "14.0.49",
3
+ "version": "14.0.51",
4
4
  "description": "심플리즘 패키지 - 코어 (common)",
5
5
  "author": "심플리즘",
6
6
  "license": "Apache-2.0",
@@ -48,10 +48,7 @@ export class SdError extends Error {
48
48
 
49
49
  // captureStackTrace는 V8 엔진(Node.js, Chrome)에서만 사용 가능
50
50
  if ("captureStackTrace" in Error) {
51
- (Error.captureStackTrace as (targetObject: object, constructorOpt?: Function) => void)(
52
- this,
53
- new.target,
54
- );
51
+ (Error as unknown as Record<string, Function>)["captureStackTrace"](this, new.target);
55
52
  }
56
53
 
57
54
  // 원인 체인 stack을 현재 stack에 추가
package/src/utils/json.ts CHANGED
@@ -131,7 +131,7 @@ export function stringify(
131
131
  // toJSON 메서드가 있으면 호출 (Date, DateTime 같은 커스텀 타입은 위에서 이미 처리됨)
132
132
  if (
133
133
  "toJSON" in currValue &&
134
- typeof (currValue as { toJSON: unknown }).toJSON === "function"
134
+ typeof currValue.toJSON === "function"
135
135
  ) {
136
136
  const toJsonResult = (currValue as { toJSON: (key?: string) => unknown }).toJSON(key);
137
137
  seen.delete(currValue);
package/src/utils/obj.ts CHANGED
@@ -866,8 +866,8 @@ function nullToUndefinedImpl<TObject>(obj: TObject, seen: WeakSet<object>): TObj
866
866
  }
867
867
 
868
868
  if (typeof obj === "object") {
869
- if (seen.has(obj as object)) return obj;
870
- seen.add(obj as object);
869
+ if (seen.has(obj)) return obj;
870
+ seen.add(obj);
871
871
  const objRec = obj as Record<string, unknown>;
872
872
  for (const key of Object.keys(obj)) {
873
873
  objRec[key] = nullToUndefinedImpl(objRec[key], seen);
package/docs/errors.md DELETED
@@ -1,82 +0,0 @@
1
- # Errors
2
-
3
- ## `SdError`
4
-
5
- 트리 구조 에러 체인을 지원하는 에러 클래스. ES2024 `cause` 속성을 활용한다.
6
-
7
- ```typescript
8
- export class SdError extends Error {
9
- override cause?: Error;
10
-
11
- /** 원인 에러를 감싸서 생성. 메시지는 역순으로 결합됨 */
12
- constructor(cause: Error, ...messages: string[]);
13
- /** 메시지만으로 생성 */
14
- constructor(...messages: string[]);
15
- }
16
- ```
17
-
18
- 생성자에 전달된 메시지들은 역순으로 결합된다. `cause`가 `Error` 인스턴스이면 그 메시지도 체인 끝에 추가된다.
19
-
20
- ```typescript
21
- throw new SdError(err, "API 호출 실패", "사용자 로드 실패");
22
- // message: "사용자 로드 실패 => API 호출 실패 => 원본 에러 메시지"
23
-
24
- throw new SdError("잘못된 상태", "처리 불가");
25
- // message: "처리 불가 => 잘못된 상태"
26
- ```
27
-
28
- ## `ArgumentError`
29
-
30
- 유효하지 않은 인자를 전달받았을 때 발생하는 에러. 인자 객체를 YAML 형식으로 메시지에 포함하여 디버깅을 용이하게 한다.
31
-
32
- ```typescript
33
- export class ArgumentError extends SdError {
34
- /** 기본 메시지("잘못된 인자입니다.")와 함께 인자 객체를 YAML 형식으로 출력 */
35
- constructor(argObj: Record<string, unknown>);
36
- /** 커스텀 메시지와 함께 인자 객체를 YAML 형식으로 출력 */
37
- constructor(message: string, argObj: Record<string, unknown>);
38
- }
39
- ```
40
-
41
- ```typescript
42
- throw new ArgumentError({ userId: 123, name: null });
43
- // message: "잘못된 인자입니다.\n\nuserId: 123\nname: null"
44
-
45
- throw new ArgumentError("잘못된 사용자", { userId: 123 });
46
- // message: "잘못된 사용자\n\nuserId: 123"
47
- ```
48
-
49
- ## `NotImplementedError`
50
-
51
- 아직 구현되지 않은 기능이 호출되었을 때 발생하는 에러.
52
-
53
- ```typescript
54
- export class NotImplementedError extends SdError {
55
- constructor(message?: string);
56
- }
57
- ```
58
-
59
- ```typescript
60
- throw new NotImplementedError("서브클래스에서 구현 필요");
61
- // message: "미구현: 서브클래스에서 구현 필요"
62
- ```
63
-
64
- ## `TimeoutError`
65
-
66
- 대기 시간이 초과되었을 때 발생하는 에러. `wait.until()`에서 최대 시도 횟수를 초과하면 자동으로 발생한다.
67
-
68
- ```typescript
69
- export class TimeoutError extends SdError {
70
- constructor(count?: number, message?: string);
71
- }
72
- ```
73
-
74
- | Parameter | Type | Description |
75
- |-----------|------|-------------|
76
- | `count` | `number \| undefined` | 시도 횟수 |
77
- | `message` | `string \| undefined` | 추가 메시지 |
78
-
79
- ```typescript
80
- throw new TimeoutError(50, "API 응답 대기 초과");
81
- // message: "대기 시간 초과(50회 시도): API 응답 대기 초과"
82
- ```
@@ -1,167 +0,0 @@
1
- # Extensions (Prototype)
2
-
3
- `@simplysm/core-common`을 import하면 `Array`, `Map`, `Set` 프로토타입 확장이 자동 등록된다.
4
-
5
- ```typescript
6
- import "@simplysm/core-common"; // side-effect import — 확장 등록
7
- ```
8
-
9
- ## Array Extensions (Immutable)
10
-
11
- 새 배열을 반환하며 원본 배열을 변경하지 않는다.
12
-
13
- | Method | Signature | Description |
14
- |--------|-----------|-------------|
15
- | `single` | `(predicate?) => T \| undefined` | 조건에 맞는 단일 요소 반환. 2개 이상이면 `ArgumentError` 발생 |
16
- | `first` | `(predicate?) => T \| undefined` | 첫 번째 요소 반환 |
17
- | `last` | `(predicate?) => T \| undefined` | 마지막 요소 반환 |
18
- | `filterExists` | `() => NonNullable<T>[]` | null/undefined 제거 |
19
- | `ofType` | `(type) => TNarrow[]` | 특정 타입의 요소만 필터 (`PrimitiveTypeStr` 또는 생성자) |
20
- | `mapAsync` | `(selector) => Promise<TResult[]>` | 비동기 매핑 (순차 실행) |
21
- | `filterAsync` | `(predicate) => Promise<T[]>` | 비동기 필터 (순차 실행) |
22
- | `mapMany` | `(selector?) => TResult[]` | 매핑 후 평탄화 (또는 중첩 배열 평탄화) |
23
- | `mapManyAsync` | `(selector?) => Promise<TResult[]>` | 비동기 매핑 후 평탄화 (순차 실행) |
24
- | `parallelAsync` | `(fn) => Promise<TResult[]>` | 비동기 병렬 처리 (`Promise.all` 사용) |
25
- | `groupBy` | `(keySelector, valueSelector?) => { key, values }[]` | key 기준 그룹화. 객체 key는 O(n²), 원시 key는 O(n) |
26
- | `toMap` | `(keySelector, valueSelector?) => Map<TKey, TValue>` | Map으로 변환. 중복 key이면 `ArgumentError` 발생 |
27
- | `toMapAsync` | `(keySelector, valueSelector?) => Promise<Map<TKey, TValue>>` | 비동기 Map으로 변환 |
28
- | `toArrayMap` | `(keySelector, valueSelector?) => Map<TKey, TValue[]>` | 그룹 Map으로 변환. O(n) 성능 |
29
- | `toSetMap` | `(keySelector, valueSelector?) => Map<TKey, Set<TValue>>` | 그룹 Set Map으로 변환 |
30
- | `toMapValues` | `(keySelector, valueSelector) => Map<TKey, TValue>` | 그룹화 후 그룹별로 값 변환 |
31
- | `toObject` | `(keySelector, valueSelector?) => Record<string, TValue>` | 일반 객체로 변환. 중복 key이면 `ArgumentError` 발생 |
32
- | `toTree` | `(keyProp, parentKey) => TreeArray<T>[]` | 평면 배열을 트리 구조로 변환. O(n) 복잡도 |
33
- | `distinct` | `(options?) => T[]` | 중복 제거. 객체 배열에서 `keyFn` 없이 사용하면 O(n²) |
34
- | `orderBy` | `(selector?) => T[]` | 오름차순 정렬 |
35
- | `orderByDesc` | `(selector?) => T[]` | 내림차순 정렬 |
36
- | `diffs` | `(target, options?) => ArrayDiffsResult<T, P>[]` | 두 배열 비교 (INSERT/DELETE/UPDATE) |
37
- | `oneWayDiffs` | `(orgItems, keyPropNameOrGetValFn, options?) => ArrayOneWayDiffResult<T>[]` | 단방향 차이 계산 (create/update/same) |
38
- | `merge` | `(target, options?) => (T \| P \| T&P)[]` | 두 배열 병합 |
39
- | `sum` | `(selector?) => number` | 합계. 빈 배열이면 0 |
40
- | `min` | `(selector?) => T \| undefined` | 최솟값 |
41
- | `max` | `(selector?) => T \| undefined` | 최댓값 |
42
- | `shuffle` | `() => T[]` | 무작위 섞기 |
43
-
44
- ### `toTree` 상세
45
-
46
- ```typescript
47
- interface Item {
48
- id: number;
49
- parentId?: number;
50
- name: string;
51
- }
52
-
53
- const items: Item[] = [
54
- { id: 1, name: "root" },
55
- { id: 2, parentId: 1, name: "child1" },
56
- { id: 3, parentId: 2, name: "grandchild" },
57
- ];
58
-
59
- const tree = items.toTree("id", "parentId");
60
- // [{ id: 1, name: "root", children: [
61
- // { id: 2, name: "child1", children: [
62
- // { id: 3, name: "grandchild", children: [] }
63
- // ]}
64
- // ]}]
65
- ```
66
-
67
- ### `diffs` 상세
68
-
69
- ```typescript
70
- const diffs = newItems.diffs(oldItems, { keys: ["id"], excludes: ["updatedAt"] });
71
- // ArrayDiffsResult: { source: undefined, target: item } (INSERT)
72
- // { source: item, target: undefined } (DELETE)
73
- // { source: item, target: item } (UPDATE)
74
- ```
75
-
76
- ### `oneWayDiffs` 상세
77
-
78
- ```typescript
79
- const diffs = newItems.oneWayDiffs(orgItems, "id", { includeSame: false });
80
- // ArrayOneWayDiffResult: { type: "create", item, orgItem: undefined }
81
- // { type: "update", item, orgItem }
82
- // { type: "same", item, orgItem }
83
- ```
84
-
85
- ## Array Extensions (Mutable)
86
-
87
- 원본 배열을 직접 변경한다.
88
-
89
- | Method | Signature | Description |
90
- |--------|-----------|-------------|
91
- | `insert` | `(index, ...items) => this` | 지정 위치에 항목 삽입 |
92
- | `remove` | `(item \| selector) => this` | 항목 또는 조건에 맞는 항목 제거 |
93
- | `toggle` | `(item) => this` | 항목 토글 (있으면 제거, 없으면 추가) |
94
- | `clear` | `() => this` | 배열 비우기 |
95
- | `distinctThis` | `(options?) => T[]` | 원본 배열에서 중복 제거 |
96
- | `orderByThis` | `(selector?) => T[]` | 원본 배열을 오름차순 정렬 |
97
- | `orderByDescThis` | `(selector?) => T[]` | 원본 배열을 내림차순 정렬 |
98
-
99
- ## Map Extensions
100
-
101
- | Method | Signature | Description |
102
- |--------|-----------|-------------|
103
- | `getOrCreate` | `(key, newValue \| newValueFn) => V` | key가 없으면 새 값을 설정하고 반환. V 타입이 함수이면 팩토리로 감싸야 함 |
104
- | `update` | `(key, updateFn) => void` | 기존 값을 기반으로 업데이트. key가 없어도 `updateFn`이 호출됨 |
105
-
106
- ```typescript
107
- const map = new Map<string, number[]>();
108
- const arr = map.getOrCreate("key", []); // 없으면 [] 설정 후 반환
109
-
110
- const countMap = new Map<string, number>();
111
- countMap.update("key", (v) => (v ?? 0) + 1); // 카운터 증가
112
- ```
113
-
114
- ## Set Extensions
115
-
116
- | Method | Signature | Description |
117
- |--------|-----------|-------------|
118
- | `adds` | `(...values: T[]) => this` | 여러 값을 한 번에 추가 |
119
- | `toggle` | `(value, addOrDel?) => this` | 값 토글. `addOrDel`로 강제 추가/제거 가능 |
120
-
121
- ```typescript
122
- const set = new Set<number>([1, 2, 3]);
123
- set.adds(4, 5, 6); // 여러 항목 추가
124
- set.toggle(2); // 2가 있으므로 제거 → {1, 3, 4, 5, 6}
125
- set.toggle(99, "add"); // 강제 추가
126
- set.toggle(99, "del"); // 강제 제거
127
- ```
128
-
129
- ## Exported Types
130
-
131
- ### `ArrayDiffsResult<TOriginal, TOther>`
132
-
133
- `diffs()` 메서드의 반환 타입. discriminated union으로 `source`와 `target` 중 하나가 `undefined`이면 INSERT/DELETE, 둘 다 있으면 UPDATE.
134
-
135
- ```typescript
136
- export type ArrayDiffsResult<TOriginal, TOther> =
137
- | { source: undefined; target: TOther } // INSERT
138
- | { source: TOriginal; target: undefined } // DELETE
139
- | { source: TOriginal; target: TOther }; // UPDATE
140
- ```
141
-
142
- ### `ArrayOneWayDiffResult<TItem>`
143
-
144
- `oneWayDiffs()` 메서드의 반환 타입. `type` 필드로 분기.
145
-
146
- ```typescript
147
- export type ArrayOneWayDiffResult<TItem> =
148
- | { type: "create"; item: TItem; orgItem: undefined }
149
- | { type: "update"; item: TItem; orgItem: TItem }
150
- | { type: "same"; item: TItem; orgItem: TItem };
151
- ```
152
-
153
- ### `TreeArray<TNode>`
154
-
155
- `toTree()` 메서드의 반환 타입. 원본 타입에 `children` 속성이 추가된다.
156
-
157
- ```typescript
158
- export type TreeArray<TNode> = TNode & { children: TreeArray<TNode>[] };
159
- ```
160
-
161
- ### `ComparableType`
162
-
163
- `orderBy`, `orderByDesc`, `orderByThis`, `orderByDescThis`의 selector 반환 타입.
164
-
165
- ```typescript
166
- export type ComparableType = string | number | boolean | DateTime | DateOnly | Time | undefined;
167
- ```