@simplysm/sd-claude 14.0.89 → 14.0.90

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 (79) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -17
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +52 -30
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +200 -38
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +41 -53
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +66 -22
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +127 -40
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +60 -43
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +56 -20
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +74 -74
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +50 -40
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +55 -15
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +59 -42
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +77 -62
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +8 -7
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +71 -43
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +22 -14
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +19 -19
  18. package/claude/references/sd-simplysm14/apis/core-browser/README.md +17 -17
  19. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +28 -28
  20. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +37 -37
  21. package/claude/references/sd-simplysm14/apis/core-common/README.md +87 -219
  22. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +54 -98
  23. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +57 -99
  24. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +60 -103
  25. package/claude/references/sd-simplysm14/apis/core-common/errors.md +42 -47
  26. package/claude/references/sd-simplysm14/apis/core-common/obj.md +42 -88
  27. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +55 -0
  28. package/claude/references/sd-simplysm14/apis/core-node/README.md +6 -7
  29. package/claude/references/sd-simplysm14/apis/core-node/consola.md +17 -12
  30. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +14 -13
  31. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +9 -8
  32. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +14 -13
  33. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +4 -8
  34. package/claude/references/sd-simplysm14/apis/core-node/worker.md +14 -12
  35. package/claude/references/sd-simplysm14/apis/excel/README.md +22 -22
  36. package/claude/references/sd-simplysm14/apis/excel/cell.md +37 -29
  37. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +29 -15
  38. package/claude/references/sd-simplysm14/apis/excel/style.md +33 -27
  39. package/claude/references/sd-simplysm14/apis/excel/utils.md +29 -19
  40. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +78 -55
  41. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +42 -45
  42. package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -8
  43. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +118 -67
  44. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +83 -86
  45. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +102 -93
  46. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +138 -81
  47. package/claude/references/sd-simplysm14/apis/orm-common/types.md +49 -44
  48. package/claude/references/sd-simplysm14/apis/orm-node/README.md +42 -42
  49. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +44 -33
  50. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +11 -10
  51. package/claude/references/sd-simplysm14/apis/service-client/README.md +56 -52
  52. package/claude/references/sd-simplysm14/apis/service-client/orm.md +33 -28
  53. package/claude/references/sd-simplysm14/apis/service-client/transport.md +23 -21
  54. package/claude/references/sd-simplysm14/apis/service-common/README.md +83 -48
  55. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +126 -34
  56. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +109 -54
  57. package/claude/references/sd-simplysm14/apis/service-server/README.md +69 -81
  58. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +46 -43
  59. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +63 -37
  60. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +40 -30
  61. package/claude/references/sd-simplysm14/apis/storage/README.md +17 -17
  62. package/claude/references/sd-simplysm14/manuals/client-app-structure.md +142 -140
  63. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -1
  64. package/claude/references/sd-simplysm14/manuals/client-service.md +19 -7
  65. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +2 -2
  66. package/claude/references/sd-simplysm14/manuals/client-system-log.md +11 -3
  67. package/claude/references/sd-simplysm14/manuals/data-log.md +0 -1
  68. package/claude/references/sd-simplysm14/manuals/orm.md +16 -0
  69. package/claude/rules/sd-design-rules.md +10 -0
  70. package/claude/skills/sd-demo/SKILL.md +0 -6
  71. package/claude/skills/sd-docs/SKILL.md +58 -0
  72. package/claude/{workflows/sd-docs.rules.md → skills/sd-docs/references/subagent-prompt.md} +103 -103
  73. package/claude/skills/sd-impl/SKILL.md +7 -4
  74. package/claude/skills/sd-spec/SKILL.md +842 -15
  75. package/claude/skills/sd-spec/references/example-spec.md +26 -36
  76. package/package.json +1 -1
  77. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +0 -53
  78. package/claude/skills/sd-spec/references/spec-authoring.md +0 -519
  79. package/claude/workflows/sd-docs.js +0 -84
@@ -1,262 +1,130 @@
1
1
  # @simplysm/core-common
2
2
 
3
- 런타임 무관(Node.js·브라우저·Worker) 공통 유틸리티 패키지. 값 타입(날짜·UUID), 에러 트리, 비동기 큐/이벤트, Array/Set/Map 프로토타입 확장, 객체/문자열/숫자/바이트/경로/JSON/XML 유틸 네임스페이스를 제공. 패키지를 import 하면 부수효과로 `Array`/`Set`/`Map` 프로토타입 확장이 주입된다.
3
+ 브라우저·Node 공용 유틸리티 패키지. 날짜/시간 타입, 에러 클래스, 배열/객체 조작, 직렬화, 비동기 큐, 로거, 환경변수 접근을 제공. 워크스페이스 거의 모든 패키지의 기반.
4
4
 
5
- ## 사용 트리거 인덱스
6
-
7
- - **에러 클래스** (`SdError`/`ArgumentError`/`NotImplementedError`/`TimeoutError`) — 원인 체인을 가진 에러를 throw 하거나 `instanceof` 로 분기할 때. 자세히: [errors.md](./errors.md)
8
- - **날짜/시간 값 타입** (`DateTime`/`DateOnly`/`Time`, `dt` 네임스페이스) — 불변 날짜·시간 값을 만들고 파싱·산술·포맷할 때. 자세히: [datetime.md](./datetime.md)
9
- - **Array 확장 메서드** (`Array.prototype` 전역 확장) — `single`/`groupBy`/`distinct`/`orderBy`/`diffs`/`toTree` 등 컬렉션 가공이 필요할 때. 자세히: [array-ext.md](./array-ext.md)
10
- - **객체 유틸** (`obj` 네임스페이스, `DeepPartial`/`Type`) — 깊은 복사·동등성·병합·체인 경로 접근·타입 안전 키 순회가 필요할 때. 자세히: [obj.md](./obj.md)
11
- - **JSON/Worker 직렬화** (`json`/`transfer` 네임스페이스) — 커스텀 타입(날짜·UUID·Map·Set·Error)을 보존하며 JSON 또는 Worker 메시지로 직렬화할 때. 자세히: [json-transfer.md](./json-transfer.md)
12
- - **비동기 런타임** (`DebounceQueue`/`SerialQueue`/`EventEmitter`/`LazyGcMap`/`createLogger`) — 디바운스·직렬 실행·타입 안전 이벤트·자동 만료 캐시·태그 로거가 필요할 때. 자세히: [async-runtime.md](./async-runtime.md)
13
- - **`Uuid`** — UUID v4 생성·검증·바이트 변환이 필요할 때. (아래 인라인 "값 타입 보조 — Uuid")
14
- - **환경변수** (`env`/`parseBoolEnv`) — process.env / import.meta.env 를 런타임 무관하게 읽고 쓸 때. (아래 인라인 "환경변수")
15
- - **Set/Map 확장** (`Set.prototype.adds`/`toggle`, `Map.prototype.getOrCreate`/`update`) — Set/Map 을 체이닝으로 다룰 때. (아래 인라인 "Set/Map 확장")
16
- - **문자열 유틸** (`str` 네임스페이스) — 한국어 조사·케이스 변환·전각 변환·빈 문자열 가드가 필요할 때. (아래 인라인 "str")
17
- - **숫자 유틸** (`num` 네임스페이스) — 느슨한 정수/실수 파싱·천단위 포맷이 필요할 때. (아래 인라인 "num")
18
- - **바이트 유틸** (`bytes` 네임스페이스) — Uint8Array hex/base64/concat 변환이 필요할 때. (아래 인라인 "bytes")
19
- - **경로 유틸** (`path` 네임스페이스) — 브라우저에서 POSIX 경로 join/basename/extname 이 필요할 때. (아래 인라인 "path")
20
- - **XML 유틸** (`xml` 네임스페이스) — XML 파싱/직렬화가 필요할 때. (아래 인라인 "xml")
21
- - **대기 유틸** (`wait` 네임스페이스) — 조건 폴링·지연이 필요할 때. (아래 인라인 "wait")
22
- - **에러 메시지 추출** (`err` 네임스페이스) — catch 의 `unknown` 에서 메시지 문자열을 뽑을 때. (아래 인라인 "err")
23
- - **원시 타입 추론** (`primitive` 네임스페이스, `PrimitiveType*`/`Bytes`) — 런타임 값에서 ORM 원시 타입 문자열을 얻을 때. (아래 인라인 "primitive / 공통 타입")
24
- - **코드 템플릿 태그** (`js`/`ts`/`html`/`tsql`/`mysql`/`pgsql`) — IDE 하이라이팅 + 들여쓰기 정규화가 필요할 때. (아래 인라인 "템플릿 태그")
25
- - **`ZipArchive`** — ZIP 읽기/쓰기/압축/해제가 필요할 때. (아래 인라인 "ZipArchive")
26
-
27
- ## 값 타입 보조 — Uuid
28
-
29
- ```typescript
30
- class Uuid {
31
- static generate(): Uuid; // crypto.getRandomValues 기반 v4 생성
32
- static fromBytes(bytes: Bytes): Uuid; // 16바이트 Uint8Array → Uuid (16바이트 아니면 ArgumentError)
33
- constructor(uuid: string); // 형식 검증 후 보관 (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, 위반 시 ArgumentError)
34
- toString(): string; // UUID 문자열 반환
35
- toBytes(): Bytes; // 16바이트 Uint8Array 반환
36
- }
37
- ```
38
-
39
- - `generate()` — 새 랜덤 UUID 가 필요할 때(엔티티 PK 등). 암호학적으로 안전한 난수 사용.
40
- - `fromBytes(bytes)` / `toBytes()` — 바이너리 저장/전송과 문자열 표현 사이를 오갈 때. 입력 바이트 길이가 16 이 아니면 `ArgumentError` throw.
41
- - `new Uuid(str)` — 외부에서 받은 문자열을 검증해 값으로 승격할 때. 형식 불일치 시 `ArgumentError` throw.
42
-
43
- ```typescript
44
- const id = Uuid.generate();
45
- const restored = new Uuid(id.toString());
46
- ```
47
-
48
- ## 환경변수 — env / parseBoolEnv
49
-
50
- ```typescript
51
- function env(key: string): string | undefined; // 읽기: process.env 우선, 없으면 import.meta.env
52
- function env(key: string, value: string): void; // 쓰기: process.env[key] = value (process 존재 시)
53
- function parseBoolEnv(value: unknown): boolean; // "true"/"1"/"yes"/"on"(대소문자 무시) → true, 그 외 false
54
- ```
55
-
56
- - `env(key)` — 단일 인자. Node 면 `process.env`, 브라우저(Vite) 면 `import.meta.env` 에서 조회. 둘 다 없으면 `undefined`. "값 없음"을 빈 문자열로 치환하지 않음.
57
- - `env(key, value)` — 2번째 인자 전달 시 쓰기 모드. `process` 가 없는 환경(순수 브라우저)에서는 아무 동작 안 함.
58
- - `parseBoolEnv(value)` — boolean 플래그용 환경변수를 해석할 때. 위 4개 리터럴만 true.
59
-
60
- ```typescript
61
- if (parseBoolEnv(env("DEV"))) { /* 개발 모드 */ }
62
- ```
63
-
64
- ## Set/Map 확장
65
-
66
- 전역 `Set.prototype` / `Map.prototype` 에 메서드를 추가(import 시 자동 적용, `enumerable: false`).
67
-
68
- ```typescript
69
- interface Set<T> {
70
- adds(...values: T[]): this; // 여러 값 일괄 추가, this 반환(체이닝)
71
- toggle(value: T, addOrDel?: "add" | "del"): this; // 토글: 인자 생략 시 있으면 제거/없으면 추가
72
- }
73
- interface Map<K, V> {
74
- getOrCreate(key: K, newValue: V): V; // 값 직접 지정 (key 없을 때만 set)
75
- getOrCreate(key: K, newValueFn: () => V): V; // 팩토리 지연 생성 (비싼 연산용)
76
- update(key: K, updateFn: (v: V | undefined) => V): void; // 현재 값(없으면 undefined) → 새 값으로 갱신
77
- }
78
- ```
79
-
80
- - `Set.toggle(value, addOrDel)` — `addOrDel` 생략 시 존재 여부로 자동 토글, `"add"` 면 강제 추가, `"del"` 면 강제 제거. 조건부 선택 상태(`isOn ? "add" : "del"`) 표현에 사용.
81
- - `Map.getOrCreate` — 2번째 인자가 함수면 팩토리로 인식되어 호출됨. 함수 자체를 값으로 저장하려면 `() => myFn` 처럼 한 번 더 감싼다.
82
- - `Map.update` — key 가 없어도 `updateFn(undefined)` 가 호출되어 새 값이 set 됨. 카운터 증가(`(v) => (v ?? 0) + 1`)·배열 누적에 사용.
5
+ > 부수효과 주의: 이 패키지를 import 하면 `Array.prototype`·`Set.prototype`·`Map.prototype` 에 확장 메서드가 설치됨(전역 prototype 변경). 확장 메서드는 `array.toMap(...)` 처럼 메서드로 직접 호출.
83
6
 
84
- ```typescript
85
- new Set([1, 2]).adds(3, 4).toggle(2); // {1, 3, 4}
86
- countMap.update("k", (v) => (v ?? 0) + 1);
87
- ```
7
+ ## 사용 트리거 인덱스
88
8
 
89
- ## str
9
+ - **에러 클래스** — `throw` 할 때, 에러 원인 체인을 만들 때, catch 에서 분기할 때. SdError/ArgumentError/NotImplementedError/TimeoutError. 자세히: [errors.md](./errors.md)
10
+ - **날짜/시간 값 타입** — 날짜·시간을 불변 값으로 다루거나 파싱·포맷·산술할 때. DateTime/DateOnly/Time + `dt` 포맷 네임스페이스. 자세히: [datetime.md](./datetime.md)
11
+ - **배열 확장 메서드** — 배열을 그룹화·정렬·중복제거·Map변환·트리화·diff/merge 할 때. `Array.prototype` 확장. 자세히: [array-ext.md](./array-ext.md)
12
+ - **객체 유틸 (`obj` 네임스페이스)** — 깊은 복사/비교/병합, 체인 경로 접근, key 변환을 할 때. 자세히: [obj.md](./obj.md)
13
+ - **직렬화/Worker 전송** — 커스텀 타입(DateTime·Uuid·Map·Set·Error 등) 포함 데이터를 JSON·XML·바이트·Worker 메시지로 주고받을 때. `json`/`xml`/`bytes`/`transfer` 네임스페이스. 자세히: [serialization.md](./serialization.md)
14
+ - **비동기 큐·이벤트·대기** — 디바운스/직렬 실행, 타입 안전 이벤트, 조건 대기, 자동 만료 Map 이 필요할 때. DebounceQueue/SerialQueue/EventEmitter/wait/LazyGcMap. 자세히: [async-runtime.md](./async-runtime.md)
15
+ - **로거** — 모듈 어디서든 로그를 찍을 때 (아래 인라인).
16
+ - **환경변수** — 환경변수를 읽고/쓰고/boolean 파싱할 때 (아래 인라인).
17
+ - **문자열/숫자/경로 유틸** — 한국어 조사, 케이스 변환, 숫자 파싱·포맷, POSIX 경로 조작 (아래 인라인).
18
+ - **UUID·ZIP·템플릿 태그·Set/Map 확장·공용 타입** — 그 외 단발성 유틸 (아래 인라인).
90
19
 
91
- `import * as str` 로 사용하는 문자열 유틸 네임스페이스.
20
+ ## 로거
92
21
 
93
- ```typescript
94
- str.getKoreanSuffix(text: string, type: "을"|"은"|"이"|"와"|"랑"|"로"|"라"): string;
95
- str.replaceFullWidth(str: string): string;
96
- str.toPascalCase(str: string): string;
97
- str.toCamelCase(str: string): string;
98
- str.toKebabCase(str: string): string;
99
- str.toSnakeCase(str: string): string;
100
- str.isNullOrEmpty(str: string | undefined): str is "" | undefined;
101
- str.insert(str: string, index: number, insertString: string): string;
102
- ```
22
+ `createLogger(tag)` — consola 기반 lazy logger 인스턴스 생성. 모듈 레벨에서 호출해도 안전(첫 메서드 접근 시점까지 `consola.withTag` 생성을 지연하므로 이후 `setupConsola()` 의 level/reporters 변경이 반영됨).
103
23
 
104
- - `getKoreanSuffix(text, type)`마지막 글자 받침 유무로 조사 선택. `type` 쌍의 대표 글자: `"을"`=을/를, `"은"`=은/는, `"이"`=이/가, `"와"`=과/와, `"랑"`=이랑/랑, `"로"`=으로/로(단 받침 ㄹ이면 "로"), `"라"`=이라/라. 한글 아닌 글자로 끝나면 받침 없음 처리.
105
- - `replaceFullWidth(str)` — 전각 영문/숫자/공백/괄호를 반각으로. 스캔된 바코드·일본어 입력 정규화에 사용.
106
- - `toPascalCase`/`toCamelCase`/`toKebabCase`/`toSnakeCase` — `-` `_` `.` 구분자 또는 대문자 경계 기준 케이스 변환. kebab/snake 는 연속 대문자를 각각 분리(`XMLParser`→`x-m-l-parser`)하고 기존 구분자는 보존.
107
- - `isNullOrEmpty(str)` — 타입 가드. true 면 `"" | undefined`, false 면 비어있지 않은 `string` 으로 좁혀짐.
108
- - `insert(str, index, insertString)` — 지정 위치에 삽입한 새 문자열 반환(원본 불변).
24
+ - tag: string로그 prefix 표시되는 태그. 형식은 `<도메인>:<역할>` 또는 단일 토큰 권장. 메시지 본문에 `[패키지명]` 수동 prefix 넣지 역할은 tag 담당.
109
25
 
110
- ```typescript
111
- str.getKoreanSuffix("사과", "을"); // ""
112
- str.toKebabCase("HelloWorld"); // "hello-world"
26
+ ```ts
27
+ import { createLogger } from "@simplysm/core-common";
28
+ const logger = createLogger("capacitor:auto-update");
29
+ logger.info("최신 버전 확인 중");
30
+ logger.error("checkPermissions 실패", err);
113
31
  ```
114
32
 
115
- ## num
33
+ `console.*` 직접 호출·`consola.withTag()` 직접 호출 금지 — 항상 `createLogger` 사용.
116
34
 
117
- ```typescript
118
- num.parseInt(text: unknown): number | undefined; // 비숫자 제거 후 정수 (소수점 이하 버림)
119
- num.parseFloat(text: unknown): number | undefined; // 비숫자 제거 후 실수
120
- num.parseRoundedInt(text: unknown): number | undefined; // parseFloat 후 반올림
121
- num.isNullOrEmpty(val: number | undefined): val is 0 | undefined;
122
- num.format(val: number, digit?: { max?: number; min?: number }): string;
123
- ```
35
+ ## 환경변수
124
36
 
125
- - `parseInt`/`parseFloat` `0-9 . -` 문자를 제거하고 파싱. 선행 `-` 만 음수 부호로 유지하고 중간 하이픈은 제거(`"010-1234"`→`101234`). 파싱 불가 시 결측(`undefined`) 그대로 반환. 숫자 입력은 그대로(정수는 trunc).
126
- - `parseRoundedInt` — 소수 반올림이 필요할 (`"12.6"`→13).
127
- - `isNullOrEmpty(val)` — 타입 가드. null/undefined/0 이면 true `0 | undefined`. "0 미입력을 같이 비움 처리"가 필요한 화면 가드용.
128
- - `format(val, digit)` — `toLocaleString` 기반 천단위 구분. `digit.max` 최대 소수 자릿수, `digit.min` 최소(부족분 0 채움). `val` 이 결측이면 결과도 `undefined`(오버로드).
37
+ - `env(key)`: `string | undefined` 환경변수 읽기. `process.env[key]` 우선, 없으면 `import.meta.env[key]` fallback. Node·브라우저(Vite) 양쪽에서 동작.
38
+ - `env(key, value)`: → `void``process.env[key]` 쓰기 (Node 환경에서만 적용, 브라우저에선 무동작).
39
+ - `parseBoolEnv(value)`: → `boolean`환경변수 문자열을 boolean 으로 해석. `"true"|"1"|"yes"|"on"` (대소문자 무시) 이면 true, 외(빈 값·undefined 포함) false. 플래그성 env 판정에 사용.
129
40
 
130
- ```typescript
131
- num.parseInt("1,234원"); // 1234
132
- num.format(1234.5, { min: 2 }); // "1,234.50"
41
+ ```ts
42
+ import { env, parseBoolEnv } from "@simplysm/core-common";
43
+ if (parseBoolEnv(env("DEV"))) { /* 개발 모드 분기 */ }
133
44
  ```
134
45
 
135
- ## bytes
46
+ ## 문자열 유틸 (`str` 네임스페이스)
136
47
 
137
- ```typescript
138
- bytes.concat(arrays: Bytes[]): Bytes;
139
- bytes.toHex(bytes: Bytes): string; // 소문자 hex
140
- bytes.fromHex(hex: string): Bytes; // 홀수 길이/비hex 문자 ArgumentError
141
- bytes.toBase64(bytes: Bytes): string;
142
- bytes.fromBase64(base64: string): Bytes; // 비base64 문자/잘못된 길이 → ArgumentError
143
- ```
48
+ - `str.getKoreanSuffix(text, type)`: → string — 받침 유무로 한국어 조사 선택. type: `"을"`(을/를)·`"은"`(은/는)·`"이"`(이/가)·`"와"`(과/와)·`"랑"`(이랑/랑)·`"로"`(으로/로, 받침 ㄹ 은 "로")·`"라"`(이라/라). 빈 문자열·한글 아님이면 받침 없음 조사 반환. 동적 메시지 조립에 사용.
49
+ - `str.replaceFullWidth(s)`: → string — 전각 영문/숫자/공백/괄호를 반각으로 변환. OCR·외부 입력 정규화에 사용.
50
+ - `str.toPascalCase(s)` / `toCamelCase(s)` / `toKebabCase(s)` / `toSnakeCase(s)`: → string 케이스 변환. `-`·`_`·`.` 구분자와 대문자 경계를 인식. 코드 생성·식별자 정규화에 사용.
51
+ - `str.isNullOrEmpty(s)`: → `s is "" | undefined` — null/undefined/빈 문자열 판정(타입 가드). false 분기에서 non-empty string 으로 좁혀짐.
52
+ - `str.insert(s, index, insertString)`: string — index 위치에 문자열 삽입한 새 문자열.
144
53
 
145
- - `concat(arrays)` — 여러 Uint8Array 를 하나로 이어붙인 새 배열. 청크 결합에 사용.
146
- - `toHex`/`fromHex` 바이너리를 16진 문자열로 표기/복원. `fromHex` 는 길이가 짝수이고 `[0-9a-fA-F]` 만 허용(위반 시 throw).
147
- - `toBase64`/`fromBase64` — Node 의존 없는 자체 구현 base64. `fromBase64` 는 공백/패딩 정규화 후 검증, 길이 나머지 1 이면 throw.
148
-
149
- ## path
150
-
151
- ```typescript
152
- path.join(...segments: string[]): string; // POSIX(슬래시) join, 중복 슬래시 정리
153
- path.basename(filePath: string, ext?: string): string; // 마지막 세그먼트, ext 일치 시 제거
154
- path.extname(filePath: string): string; // 확장자(점 포함), 숨김파일은 ""
54
+ ```ts
55
+ import { str } from "@simplysm/core-common";
56
+ `${name}${str.getKoreanSuffix(name, "을")} 저장했습니다`;
155
57
  ```
156
58
 
157
- - POSIX 슬래시(`/`) 전용. Windows 백슬래시 경로는 미지원. 브라우저·Capacitor 환경에서 Node `path` 대체용.
158
- - `basename(p, ext)` — `ext` 가 끝과 일치하면 그만큼 잘라 확장자 없는 이름 반환.
159
- - `extname` — `.gitignore` 같은 선행 점 파일은 빈 문자열(`""`).
59
+ ## 숫자 유틸 (`num` 네임스페이스)
160
60
 
161
- ## xml
61
+ - `num.parseInt(text)`: → `number | undefined` — 비숫자 문자 제거 후 정수 파싱. 소수점은 버림(`"12.34"`→12). 선행 `-` 만 음수 부호로 유지, 중간 `-` 제거(`"010-1234"`→101234). 파싱 불가면 undefined.
62
+ - `num.parseFloat(text)`: → `number | undefined` — 위와 동일 규칙으로 실수 파싱.
63
+ - `num.parseRoundedInt(text)`: → `number | undefined` — float 파싱 후 반올림한 정수. 소수 입력을 반올림 정수로 받을 때.
64
+ - `num.isNullOrEmpty(val)`: → `val is 0 | undefined` — null/undefined/0 판정(타입 가드). false 분기에서 0 아닌 숫자로 좁혀짐.
65
+ - `num.format(val, digit?)`: → string(또는 입력이 undefined 면 undefined) — 천 단위 구분자 포함 포맷. `digit.max`=최대 소수 자릿수, `digit.min`=최소 소수 자릿수(부족분 0 채움). `format(1234.567, { max: 2 })`→`"1,234.57"`.
162
66
 
163
- ```typescript
164
- xml.parse(str: string, options?: { stripTagPrefix?: boolean }): unknown;
165
- xml.stringify(obj: unknown, options?: XmlBuilderOptions): string;
166
- ```
67
+ ## 경로 유틸 (`path` 네임스페이스)
167
68
 
168
- - `parse` `fast-xml-parser` 기반. 속성은 `$` 객체, 텍스트는 `_` 키, 자식 요소는 배열로. `stripTagPrefix: true` 면 `ns:tag` 의 네임스페이스 접두사 제거(속성 접두사는 유지).
169
- - `stringify` — `$`(속성)·`_`(텍스트) 규약으로 XML 문자열 생성. `options` 로 fast-xml-parser 빌더 옵션 덮어쓰기.
69
+ POSIX 스타일(슬래시 `/`)만 지원. 브라우저·Capacitor 환경용. Windows 백슬래시 경로 미지원.
170
70
 
171
- ```typescript
172
- xml.parse('<root id="1"><item>hi</item></root>');
173
- // { root: { $: { id: "1" }, item: [{ _: "hi" }] } }
174
- ```
71
+ - `path.join(...segments)`: → string — 세그먼트를 `/` 로 결합. 중간 중복 슬래시·빈 세그먼트 정리.
72
+ - `path.basename(filePath, ext?)`: → string — 파일명 추출. ext 가 주어지고 끝나면 그 확장자 제거.
73
+ - `path.extname(filePath)`: string 확장자 추출(`.` 포함). 숨김 파일(`.gitignore`)은 문자열(Node 동일).
175
74
 
176
- ## wait
75
+ ## UUID (`Uuid` 클래스)
177
76
 
178
- ```typescript
179
- wait.until(forwarder: () => boolean | Promise<boolean>, milliseconds?: number, maxCount?: number): Promise<void>;
180
- wait.time(millisecond: number): Promise<void>;
181
- ```
77
+ - `Uuid.generate()`: → Uuid — `crypto.getRandomValues` 기반 암호학적 안전 UUID v4 생성.
78
+ - `new Uuid(uuidStr)` 문자열로 생성. 형식 불일치면 ArgumentError throw.
79
+ - `Uuid.fromBytes(bytes)`: → Uuid — 16바이트 Uint8Array 로 생성. 길이≠16 이면 ArgumentError.
80
+ - 인스턴스: `toString()` → 문자열, `toBytes()` → 16바이트 Uint8Array.
182
81
 
183
- - `until(fn, interval, maxCount)` — `fn` 이 true 될 때까지 `interval`(기본 100ms) 간격으로 폴링. 첫 호출에서 true 면 즉시 반환. `maxCount` 지정 시 초과하면 `TimeoutError` throw(미지정이면 무제한).
184
- - `time(ms)` — `setTimeout` 기반 지연 Promise.
82
+ ## ZIP (`ZipArchive` 클래스)
185
83
 
186
- ```typescript
187
- await wait.until(() => ready, 100, 50); // 최대 50회(5초) 폴링
188
- await wait.time(300);
189
- ```
84
+ ZIP 읽기/쓰기/압축/해제. 동일 파일 중복 해제 방지용 내부 캐시 사용. 사용 후 `close()` 필수.
190
85
 
191
- ## err
86
+ - `new ZipArchive(data?)` — data(`Blob | Uint8Array`) 주면 읽기용, 생략하면 새 아카이브.
87
+ - `extractAll(progressCallback?)`: → `Promise<Map<string, Bytes | undefined>>` — 모든 파일 추출. 콜백은 `{ fileName, totalSize, extractedSize }` 진행률 수신.
88
+ - `get(fileName)`: → `Promise<Bytes | undefined>` — 단일 파일 추출(없으면 undefined).
89
+ - `exists(fileName)`: → `Promise<boolean>` — 파일 존재 여부.
90
+ - `write(fileName, bytes)`: → void — 파일을 캐시에 등록(아직 압축 전).
91
+ - `compress()`: → `Promise<Bytes>` — 캐시된 전체 파일을 ZIP 바이트로 압축. 내부적으로 `extractAll()` 호출(대용량 시 메모리 주의).
92
+ - `close()`: → `Promise<void>` — 리더 닫고 캐시 비움.
192
93
 
193
- ```typescript
194
- err.message(error: unknown): string; // Error 면 .message, 아니면 String(error)
195
- ```
94
+ ## 템플릿 태그 (코드 하이라이팅용)
196
95
 
197
- - catch 블록의 `unknown` 에러에서 안전하게 메시지 문자열을 뽑을 때.
96
+ `js` / `ts` / `html` / `tsql` / `mysql` / `pgsql` — 모두 동일 동작: 템플릿 리터럴을 문자열로 결합하고 공통 들여쓰기를 제거(앞뒤 빈 줄 제거). IDE 하이라이팅·가독성 목적이며 SQL 이스케이프 등 기능 차이는 없음.
198
97
 
199
- ```typescript
200
- try { /* ... */ } catch (e) { logger.error(err.message(e)); }
98
+ ```ts
99
+ import { ts } from "@simplysm/core-common";
100
+ const code = ts`
101
+ interface User { name: string; }
102
+ `; // 들여쓰기 정규화된 문자열
201
103
  ```
202
104
 
203
- ## primitive / 공통 타입
105
+ ## Set 확장 메서드 (`Set.prototype`)
204
106
 
205
- ```typescript
206
- primitive.typeStr(value: PrimitiveTypeMap[PrimitiveTypeStr]): PrimitiveTypeStr; // 런타임 원시 타입 문자열
107
+ - `set.adds(...values)`: → this — 여러 값을 한 번에 add. 체이닝 가능.
108
+ - `set.toggle(value, addOrDel?)`: this 토글. `addOrDel` 생략 시 있으면 제거/없으면 추가, `"add"`=강제 추가, `"del"`=강제 제거. 조건부 추가/제거를 한 줄로.
207
109
 
208
- type Bytes = Uint8Array;
209
- type PrimitiveTypeMap = { string; number; boolean; DateTime; DateOnly; Time; Uuid; Bytes };
210
- type PrimitiveTypeStr = keyof PrimitiveTypeMap;
211
- type PrimitiveType = PrimitiveTypeMap[PrimitiveTypeStr] | undefined;
212
- ```
110
+ ## Map 확장 메서드 (`Map.prototype`)
213
111
 
214
- - `primitive.typeStr(value)` — 값의 런타임 타입을 보고 `"string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes"` 하나 반환. 8종 값이면 `ArgumentError` throw. ORM 컬럼 타입 추론과 공유.
215
- - `Bytes`바이너리 표준 별칭(`Uint8Array`). Buffer 대신 사용.
216
- - `PrimitiveType` — 원시 타입 union + `undefined`. 결측 보존을 위해 `undefined` 포함.
217
- - `DeepPartial<T>` / `Type<T>` 타입 유틸은 [obj.md](./obj.md) 의 "타입 유틸리티" 참조.
112
+ - `map.getOrCreate(key, newValue)` / `getOrCreate(key, newValueFn)`: → V key 없으면 값(또는 팩토리 호출 결과) 설정 반환, 있으면 기존 값. 주의: V 가 함수 타입이면 두 번째 인자 함수가 팩토리로 인식되어 호출됨 — 함수를 값으로 저장하려면 `() => myFn` 으로 감쌀 것.
113
+ - `map.update(key, updateFn)`: → void `updateFn(현재값 | undefined)` 결과로 값 설정. key 가 없어도 호출됨. 카운터 증가·배열 누적에 사용. 예: `m.update(k, v => (v ?? 0) + 1)`.
218
114
 
219
- ## 템플릿 태그
115
+ ## 공용 타입 (`common.types`)
220
116
 
221
- ```typescript
222
- js / ts / html / tsql / mysql / pgsql (strings: TemplateStringsArray, ...values: unknown[]): string;
223
- ```
117
+ - `Bytes` = `Uint8Array` — 바이너리 데이터 별칭. Buffer 대신 사용.
118
+ - `PrimitiveTypeMap` 원시 타입 문자열 실제 타입 매핑(`string`/`number`/`boolean`/`DateTime`/`DateOnly`/`Time`/`Uuid`/`Bytes`). orm-common 공유.
119
+ - `PrimitiveTypeStr` = `keyof PrimitiveTypeMap` — 원시 타입 문자열 key union.
120
+ - `PrimitiveType` — 모든 원시 타입 값의 union(+ undefined).
121
+ - `DeepPartial<T>` — 모든 속성을 재귀적으로 optional 화. 원시/날짜 타입은 그대로 두고 object/array 만 재귀.
122
+ - `Type<TInstance>` — 클래스 생성자 타입(`new (...args) => TInstance`). DI·팩토리·instanceof 체크에 사용.
224
123
 
225
- - 6개 태그 모두 동작은 동일: 보간 후 공통 들여쓰기 제거 + 앞뒤 빈 줄 trim. 차이는 IDE 의 언어 하이라이팅 힌트뿐(태그 이름이 곧 언어). 보간 값이 null/undefined 면 빈 문자열로 치환.
124
+ ## 원시 타입 추론 (`primitive` 네임스페이스)
226
125
 
227
- ```typescript
228
- const sql = mysql`
229
- SELECT *
230
- FROM users
231
- `; // 공통 들여쓰기가 제거된 두 줄
232
- ```
126
+ - `primitive.typeStr(value)`: → PrimitiveTypeStr — 런타임 값에서 원시 타입 문자열 추론(`"hello"`→`"string"`, `new DateTime()`→`"DateTime"`, `Uint8Array`→`"Bytes"`). 지원 안 되는 타입이면 ArgumentError. ORM 컬럼 타입 판정 등에 사용.
233
127
 
234
- ## ZipArchive
235
-
236
- ```typescript
237
- class ZipArchive {
238
- constructor(data?: Blob | Bytes); // 데이터 생략 시 새(쓰기용) 아카이브
239
- extractAll(progressCallback?: (p: { fileName: string; totalSize: number; extractedSize: number }) => void): Promise<Map<string, Bytes | undefined>>;
240
- get(fileName: string): Promise<Bytes | undefined>;
241
- exists(fileName: string): Promise<boolean>;
242
- write(fileName: string, bytes: Bytes): void; // 캐시에만 기록
243
- compress(): Promise<Bytes>;
244
- close(): Promise<void>;
245
- }
246
- ```
128
+ ## 에러 메시지 추출 (`err` 네임스페이스)
247
129
 
248
- - `constructor(data)` — `Blob`/`Bytes` 전달 읽기용, 생략 쓰기용 아카이브.
249
- - `get`/`exists` — 단일 파일 추출/존재 확인. 결과는 내부 캐시에 보관해 재추출 방지.
250
- - `extractAll(cb)` — 전체 추출. `cb` 는 파일별 진행(`fileName`, 전체 바이트 `totalSize`, 누적 추출 `extractedSize`)을 보고.
251
- - `write(name, bytes)` — 캐시에만 기록. 실제 압축 산출은 `compress()` 호출 시.
252
- - `compress()` — 캐시(필요 시 `extractAll`)의 모든 파일을 ZIP 바이트로. 대용량은 전부 메모리 로드되므로 주의.
253
- - `close()` — 리더 닫고 캐시 비움. `try/finally` 에서 호출 권장.
254
-
255
- ```typescript
256
- const archive = new ZipArchive(zipBytes);
257
- try {
258
- const content = await archive.get("file.txt");
259
- } finally {
260
- await archive.close();
261
- }
262
- ```
130
+ - `err.message(e)`: → string — `unknown` 에러에서 메시지 추출. Error `.message`, 아니면 `String(e)`. catch 블록의 `unknown` 을 안전하게 문자열화할 때.
@@ -1,121 +1,77 @@
1
- # @simplysm/core-common — Array 확장 메서드
1
+ # @simplysm/core-common — 배열 확장 메서드
2
2
 
3
- 패키지를 import 하면 부수효과로 `Array.prototype` 에 메서드가 주입된다(`enumerable: false`, `for...in` 비노출). 함수 호출이 아니라 배열 인스턴스 메서드로 직접 사용. 조회/그룹/정렬/diff 등 읽기 메서드(`ReadonlyArrayExt`)는 배열을 반환하고, 변형 메서드(`MutableArrayExt`, 이름에 `This` 붙거나 insert/remove/toggle/clear)는 원본을 직접 수정한다.
3
+ `@simplysm/core-common` import `Array.prototype` 에 설치되는 확장 메서드. 배열을 조회·그룹화·정렬·중복제거·Map/객체/트리 변환·diff/merge·집계할 함께 읽힘. 모두 `array.method(...)` 직접 호출. enumerable=false 설치되어 `for...in`·`Object.keys` 노출되지 않음.
4
4
 
5
- ## 조회·필터
5
+ 표기: **읽기 전용**(원본 불변, 새 배열/값 반환) vs **@mutates**(원본 직접 수정). 정렬/중복제거는 두 버전(예: `orderBy` vs `orderByThis`)이 있음.
6
6
 
7
- ```typescript
8
- single(predicate?): TItem | undefined; // 조건 일치 1건. 2건 이상이면 ArgumentError
9
- first(predicate?): TItem | undefined; // 첫 일치(없으면 undefined)
10
- last(predicate?): TItem | undefined; // 마지막 일치
11
- filterExists(): NonNullable<TItem>[]; // null/undefined 제거
12
- ofType(type: PrimitiveTypeStr | Type<T>): T[]; // 타입별 필터
13
- filterAsync(predicate: (item, i) => Promise<boolean>): Promise<TItem[]>; // 순차 비동기 필터
14
- ```
15
-
16
- - `single(predicate)` — "정확히 1건" 보장이 필요할 때. 조건 일치가 2건 이상이면 `ArgumentError` throw, 0건이면 `undefined`. predicate 생략 시 배열 전체 대상.
17
- - `first`/`last` — predicate 생략 시 각각 `[0]`/마지막 요소.
18
- - `ofType(type)` — `"string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes"` 문자열이면 해당 원시 타입으로, 생성자(`Type<N>`)면 `instanceof`/constructor 일치로 필터. 혼합 배열에서 특정 타입만 뽑을 때.
19
- - `filterAsync` — predicate 가 Promise 인 경우. 병렬이 아니라 **순차** 실행(부수효과 순서 보장).
7
+ ## 조회 / 필터
20
8
 
21
- ## 매핑·평탄화
9
+ - `single(predicate?)`: → `T | undefined` — 조건에 맞는 단 하나 반환. **2개 이상이면 ArgumentError throw**. 0개면 undefined. "유일해야 한다"는 불변식 검증에 사용.
10
+ - `first(predicate?)`: → `T | undefined` — 첫 요소(predicate 있으면 첫 매칭). 없으면 undefined.
11
+ - `last(predicate?)`: → `T | undefined` — 마지막 요소(predicate 있으면 뒤에서 첫 매칭).
12
+ - `filterExists()`: → `NonNullable<T>[]` — null/undefined 제거(타입도 좁혀짐).
13
+ - `ofType(type)`: → 좁혀진 배열 — PrimitiveTypeStr(`"string"`·`"number"`·`"boolean"`·`"DateTime"`·`"DateOnly"`·`"Time"`·`"Uuid"`·`"Bytes"`) 또는 생성자(`Type<N>`)로 필터. 문자열이면 typeof/instanceof, 생성자면 `instanceof` 또는 `constructor` 일치. 혼합 배열에서 특정 타입만 뽑을 때.
14
+ - `filterAsync(predicate)`: → `Promise<T[]>` — 비동기 조건 필터(**순차** 실행).
22
15
 
23
- ```typescript
24
- mapAsync(selector: (item, i) => Promise<R>): Promise<R[]>; // 순차
25
- parallelAsync(fn: (item, i) => Promise<R>): Promise<R[]>; // Promise.all 병렬
26
- mapMany(): U[]; // 중첩 배열 1단 평탄화
27
- mapMany(selector: (item, i) => R[]): R[]; // 매핑 후 평탄화
28
- mapManyAsync(selector: (item, i) => Promise<R[]>): Promise<R[]>; // 순차 매핑 후 평탄화
29
- ```
16
+ ## 비동기 매핑
30
17
 
31
- - `mapAsync` vs `parallelAsync`둘 다 비동기 매핑이나 `mapAsync` 한 건씩 순차, `parallelAsync` 는 `Promise.all` 동시 실행. `parallelAsync` 는 하나라도 reject 되면 전체 즉시 reject.
32
- - `mapMany` — 평탄화 내부적으로 `filterExists` 적용되어 null/undefined 제거됨. selector 없으면 자신을 1단 flat.
18
+ - `mapAsync(selector)`: `Promise<R[]>` — 비동기 매핑(**순차** 실행, 순서 보존).
19
+ - `parallelAsync(fn)`: → `Promise<R[]>``Promise.all` 기반 **병렬** 실행. 하나라도 reject 전체 reject. 독립 IO 동시에 돌릴 때.
20
+ - `mapMany(selector?)`: → 평탄화 배열 — selector 매핑 후 1단계 flat + `filterExists`. selector 없으면 자신을 flat. 중첩 배열 펼칠 때.
21
+ - `mapManyAsync(selector?)`: → `Promise<...>` — 비동기 매핑 후 평탄화(순차).
33
22
 
34
- ## 그룹화·Map/객체 변환
23
+ ## 그룹화 / Map·객체 변환
35
24
 
36
- ```typescript
37
- groupBy(keySelector, valueSelector?): { key; values }[];
38
- toMap(keySelector, valueSelector?): Map<K, V>;
39
- toMapAsync(keySelector, valueSelector?): Promise<Map<K, V>>;
40
- toArrayMap(keySelector, valueSelector?): Map<K, V[]>;
41
- toSetMap(keySelector, valueSelector?): Map<K, Set<V>>;
42
- toMapValues(keySelector, valueSelector: (items) => V): Map<K, V>;
43
- toObject(keySelector: (item, i) => string, valueSelector?): Record<string, V>;
44
- ```
25
+ - `groupBy(keySelector, valueSelector?)`: → `{ key, values }[]` — key 별 그룹. 원시 key 는 O(n), 객체 key 는 깊은 비교 O(n²). 객체 key 가 불필요하면 `toArrayMap` 권장.
26
+ - `toMap(keySelector, valueSelector?)`: `Map<K, V|T>` — key→단일값. **중복 key 면 ArgumentError**. 1:1 인덱싱에 사용.
27
+ - `toMapAsync(keySelector, valueSelector?)`: → `Promise<Map>` — 위의 비동기(순차) 버전(selector 가 Promise 반환 허용).
28
+ - `toArrayMap(keySelector, valueSelector?)`: → `Map<K, (V|T)[]>` — key→값 배열(중복 허용). O(n) 그룹화의 기본 선택지.
29
+ - `toSetMap(keySelector, valueSelector?)`: → `Map<K, Set<V|T>>` — key→Set(중복 자동 제거).
30
+ - `toMapValues(keySelector, valueSelector)`: → `Map<K, V>` — key 별로 모은 **항목 배열 전체**를 valueSelector(items)→집계값으로 변환. 그룹별 합계 등.
31
+ - `toObject(keySelector, valueSelector?)`: → `Record<string, V|T>` — key(string)→값 객체. 중복 key(둘 다 non-null) ArgumentError.
45
32
 
46
- - `groupBy(keySelector)` — `{ key, values }` 배열. 원시 key 는 Map 으로 O(n), 객체 key 는 깊은 비교로 O(n²). 원시 key 만 필요하면 `toArrayMap` 이 항상 O(n).
47
- - `toMap` — key→단일 값. key 중복 시 뒤 값이 덮어씀. `toArrayMap`/`toSetMap` — key→배열/Set(다대일 집계).
48
- - `toMapValues(keySelector, valueSelector)` — 같은 key 의 항목 배열을 받아 단일 값으로 집계(`(items) => items.sum(...)` 등).
49
- - `toObject` — key 가 반드시 string. `valueSelector` 생략 시 값은 원소 자체.
33
+ ## 트리화
50
34
 
51
- ## 트리·중복·정렬
35
+ - `toTree(keyProp, parentKey)`: → `TreeArray<T>[]` — 평면 배열을 트리로. `parentKey` 값이 null/undefined 인 항목이 루트, 각 노드에 `children` 추가(항목은 clone 됨). 내부 `toArrayMap` 으로 O(n). `TreeArray<T> = T & { children: TreeArray<T>[] }`.
52
36
 
53
- ```typescript
54
- toTree(keyProp: K, parentKey: P): TreeArray<TItem>[]; // TreeArray<T> = T & { children: TreeArray<T>[] }
55
- distinct(options?: boolean | { matchAddress?: boolean; keyFn?: (item) => string | number }): TItem[];
56
- orderBy(selector?): TItem[];
57
- orderByDesc(selector?): TItem[];
37
+ ```ts
38
+ items.toTree("id", "parentId");
39
+ // [{ id: 1, name: "root", children: [{ id: 2, ..., children: [] }] }]
58
40
  ```
59
41
 
60
- - `toTree(keyProp, parentKey)` — 평면 배열을 `children` 트리로. `parentKey` 값이 null/undefined 인 항목이 루트. 내부 `toArrayMap` 사용 O(n), 원본은 복사되고 `children` 추가.
61
- - `distinct(options)` — 중복 제거. `true`/`{matchAddress:true}` 면 참조(Set) 비교, `keyFn` 지정 시 key 기준 O(n). 객체 배열을 옵션 없이 쓰면 깊은 비교 O(n²)(대량 데이터엔 `keyFn` 권장).
62
- - `orderBy`/`orderByDesc(selector)` — selector 반환 타입은 `string|number|DateOnly|DateTime|Time|undefined`. 새 배열 반환. 결측은 비교에서 그대로 처리.
42
+ ## 정렬 / 중복제거
63
43
 
64
- ```typescript
65
- const tree = items.toTree("id", "parentId");
66
- const uniq = users.distinct({ keyFn: (u) => u.id });
67
- ```
44
+ - `orderBy(selector?)` / `orderByDesc(selector?)`: → `T[]` (새 배열) — selector 가 반환한 string/number/boolean/DateTime/DateOnly/Time/undefined 로 정렬. null/undefined 는 오름차순 앞·내림차순 뒤. 날짜타입은 tick 비교, 문자열은 `localeCompare`. selector 없으면 요소 자체.
45
+ - `orderByThis(selector?)` / `orderByDescThis(selector?)`: → `T[]` @mutates — 원본을 in-place 정렬.
46
+ - `distinct(options?)`: `T[]` (새 배열) — 중복 제거. options: `true`/`{ matchAddress: true }`=참조 비교 O(n), `{ keyFn }`=커스텀 key O(n), 미지정=원시는 값·객체는 깊은 비교 O(n²). 대량 객체 배열엔 `keyFn` 권장.
47
+ - `distinctThis(options?)`: → `T[]` @mutates — 원본에서 중복 제거(첫 등장만 유지).
68
48
 
69
- ## diff·merge
70
-
71
- ```typescript
72
- type ArrayDiffsResult<O, T> =
73
- | { source: undefined; target: T } // INSERT
74
- | { source: O; target: undefined } // DELETE
75
- | { source: O; target: T }; // UPDATE
76
- type ArrayOneWayDiffResult<T> =
77
- | { type: "create"; item: T; orgItem: undefined }
78
- | { type: "update"; item: T; orgItem: T }
79
- | { type: "same"; item: T; orgItem: T };
80
-
81
- diffs(target, options?): ArrayDiffsResult<TItem, TOther>[];
82
- oneWayDiffs(orgItems, keyPropNameOrGetValFn, options?): ArrayOneWayDiffResult<TItem>[];
83
- merge(target, options?): (TItem | TOther | (TItem & TOther))[];
84
- ```
49
+ ## diff / merge
85
50
 
86
- - `diffs(target, options)` — 두 배열을 비교해 INSERT/DELETE/UPDATE 분류. `options.keys` 매칭 key 목록, `options.excludes` 비교 제외 속성. target 에 중복 key 있으면 매칭만 사용. 서버 동기화 대상 산출에 사용.
87
- - `oneWayDiffs(orgItems, keyPropNameOrGetValFn, options)` — 현재(this) 배열을 원본(`orgItems`, 배열 또는 `Map`) 대비 create/update/same 으로 분류. `keyPropNameOrGetValFn` 은 key 속성명 또는 key 추출 함수. `options.includeSame` 동일건 포함 여부, `excludes`/`includes` 비교 속성 제한.
88
- - `merge(target, options)` — 배열을 key 기준 병합(없으면 추가, 있으면 속성 합침). `options.keys`/`excludes` 는 `diffs` 와 동일 의미.
51
+ - `diffs(target, options?)`: → `ArrayDiffsResult<T,P>[]` — 두 배열 비교. 결과 항목은 `{ source: undefined, target }`(INSERT) / `{ source, target: undefined }`(DELETE) / `{ source, target }`(UPDATE). options: `keys`=동일성 판정 key(없으면 전체 깊은 비교), `excludes`=비교 제외 속성. target 에 중복 key 매칭만.
52
+ - `oneWayDiffs(orgItems, keyPropNameOrGetValFn, options?)`: → `ArrayOneWayDiffResult<T>[]` — 현재 배열을 원본(배열 또는 `Map<key, item>`) 대비 분류. 결과 type: `"create"`(key 값 없거나 원본에 없음)·`"update"`(값 다름)·`"same"`(같음, `includeSame: true` 일 때만 포함). keyPropNameOrGetValFn 은 key 속성명 또는 `(item)=>키값` 함수. options.excludes/includes 비교 범위 조정. 저장 INSERT/UPDATE 판정에 사용.
53
+ - `merge(target, options?)`: → `(T|P|(T&P))[]` (새 배열) `diffs` 결과로 source 기준 삼아 병합. UPDATE 는 `obj.merge` 로 깊은 병합, INSERT 는 추가, DELETE 유지(원본 보존). options 는 `diffs` 와 동일.
54
+
55
+ `ArrayDiffsResult`·`ArrayOneWayDiffResult`·`TreeArray`·`ComparableType`(=`string | number | boolean | DateTime | DateOnly | Time | undefined`) 타입이 함께 export 됨.
89
56
 
90
57
  ## 집계
91
58
 
92
- ```typescript
93
- sum(selector?: (item, i) => number): number; // 배열이면 0
94
- min(selector?): TProp | undefined;
95
- max(selector?): TProp | undefined;
96
- shuffle(): TItem[];
97
- ```
59
+ - `sum(selector?)`: → number — 합계(빈 배열 0). 숫자 아닌 값이면 ArgumentError.
60
+ - `min(selector?)` / `max(selector?)`: `string | number | undefined` — 최소/최대. 숫자·문자열만 허용(아니면 ArgumentError).배열 undefined.
98
61
 
99
- - `sum` selector 생략 시 원소를 숫자로 더함. 빈 배열은 0.
100
- - `min`/`max` — selector 없으면 원소가 `number|string` 일 때만. 비면 `undefined`.
101
- - `shuffle`무작위 순서의 새 배열.
102
-
103
- ## 변형 메서드 (@mutates 원본 직접 수정)
104
-
105
- ```typescript
106
- distinctThis(options?): TItem[]; // distinct 의 in-place 판
107
- orderByThis(selector?): TItem[]; // 오름차순 in-place
108
- orderByDescThis(selector?): TItem[]; // 내림차순 in-place
109
- insert(index: number, ...items: TItem[]): this;
110
- remove(item: TItem): this;
111
- remove(selector: (item, i) => boolean): this;
112
- toggle(item: TItem): this; // 있으면 제거, 없으면 추가
113
- clear(): this; // 비우기
114
- ```
62
+ ## @mutates
63
+
64
+ - `insert(index, ...items)`: → this index 위치에 삽입.
65
+ - `remove(item)` / `remove(selector)`: → this — 일치 항목(또는 조건 매칭) 모두 제거(역순 순회 O(n)).
66
+ - `toggle(item)`: this 있으면 제거·없으면 push.
67
+ - `clear()`: → this — 전체 비움.
68
+
69
+ ## shuffle
115
70
 
116
- - `*This` 계열·`insert`/`remove`/`toggle`/`clear` 는 원본 배열을 직접 바꾸고 `this`(또는 배열) 반환해 체이닝 가능. 배열이 필요하면 `This` 없는 `orderBy`/`distinct` 사용.
117
- - `remove` — 값 또는 조건 함수 오버로드. `toggle` — 멤버십 토글로 선택 상태 관리에 사용.
71
+ - `shuffle()`: `T[]` ( 배열) Fisher–Yates 무작위 섞기. 원본 불변.
118
72
 
119
- ```typescript
120
- list.remove((x) => x.deleted).orderByThis((x) => x.seq);
73
+ ```ts
74
+ const grouped = users.toArrayMap((u) => u.teamId); // Map<teamId, User[]>
75
+ const sorted = items.orderBy((x) => x.createdAt); // DateTime 기준 오름차순
76
+ const diff = next.oneWayDiffs(prev, "id"); // create/update/same 분류
121
77
  ```