@simplysm/sd-claude 14.0.88 → 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 (135) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -17
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +39 -43
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +174 -80
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +41 -50
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +60 -26
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +109 -37
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +61 -44
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +39 -31
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +73 -85
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -39
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +55 -30
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +71 -67
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +82 -72
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +35 -36
  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 +38 -30
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +45 -50
  18. package/claude/references/sd-simplysm14/apis/core-browser/README.md +42 -55
  19. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +62 -0
  20. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +39 -38
  21. package/claude/references/sd-simplysm14/apis/core-common/README.md +95 -103
  22. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +59 -54
  23. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +86 -0
  24. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +57 -66
  25. package/claude/references/sd-simplysm14/apis/core-common/errors.md +86 -0
  26. package/claude/references/sd-simplysm14/apis/core-common/obj.md +60 -42
  27. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +55 -0
  28. package/claude/references/sd-simplysm14/apis/core-node/README.md +10 -8
  29. package/claude/references/sd-simplysm14/apis/core-node/consola.md +29 -32
  30. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +34 -22
  31. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +29 -25
  32. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +40 -53
  33. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +22 -29
  34. package/claude/references/sd-simplysm14/apis/core-node/worker.md +31 -31
  35. package/claude/references/sd-simplysm14/apis/excel/README.md +26 -26
  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/lint/README.md +27 -21
  43. package/claude/references/sd-simplysm14/apis/lint/rules.md +89 -49
  44. package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -62
  45. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +149 -67
  46. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +111 -99
  47. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +115 -72
  48. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +134 -92
  49. package/claude/references/sd-simplysm14/apis/orm-common/types.md +67 -52
  50. package/claude/references/sd-simplysm14/apis/orm-node/README.md +63 -26
  51. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +51 -40
  52. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +10 -12
  53. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +92 -45
  54. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +226 -108
  55. package/claude/references/sd-simplysm14/apis/service-client/README.md +90 -88
  56. package/claude/references/sd-simplysm14/apis/service-client/orm.md +37 -29
  57. package/claude/references/sd-simplysm14/apis/service-client/transport.md +45 -20
  58. package/claude/references/sd-simplysm14/apis/service-common/README.md +89 -40
  59. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +126 -34
  60. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +109 -54
  61. package/claude/references/sd-simplysm14/apis/service-server/README.md +70 -66
  62. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +47 -47
  63. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +71 -34
  64. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +31 -32
  65. package/claude/references/sd-simplysm14/apis/storage/README.md +34 -28
  66. package/claude/references/sd-simplysm14/manuals/client-app-structure.md +142 -140
  67. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -1
  68. package/claude/references/sd-simplysm14/manuals/client-service.md +19 -7
  69. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +2 -2
  70. package/claude/references/sd-simplysm14/manuals/client-system-log.md +11 -3
  71. package/claude/references/sd-simplysm14/manuals/data-log.md +0 -1
  72. package/claude/references/sd-simplysm14/manuals/orm.md +16 -0
  73. package/claude/rules/sd-design-rules.md +10 -0
  74. package/claude/skills/sd-docs/SKILL.md +58 -46
  75. package/claude/skills/sd-docs/references/{doc-rules.md → subagent-prompt.md} +103 -103
  76. package/claude/skills/sd-impl/SKILL.md +1 -1
  77. package/claude/skills/sd-spec/SKILL.md +858 -858
  78. package/claude/skills/sd-spec/references/example-spec.md +26 -36
  79. package/package.json +1 -1
  80. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +0 -47
  81. package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +0 -29
  82. package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
  83. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
  84. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
  85. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
  86. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
  87. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
  88. package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
  89. package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
  90. package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
  91. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
  92. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
  93. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
  94. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
  95. package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
  96. package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
  97. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
  98. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
  99. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
  100. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
  101. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
  102. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
  103. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
  104. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
  105. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
  106. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
  107. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
  108. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
  109. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
  110. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
  111. package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
  112. package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
  113. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
  114. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
  115. package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
  116. package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
  117. package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
  118. package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
  119. package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
  120. package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
  121. package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
  122. package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
  123. package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
  124. package/claude/skills/sd-review/evals/golden.jsonl +0 -2
  125. package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
  126. package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
  127. package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
  128. package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
  129. package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
  130. package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
  131. package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
  132. package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
  133. package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
  134. package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
  135. package/claude/skills/sd-use/evals/golden.jsonl +0 -6
@@ -1,138 +1,130 @@
1
1
  # @simplysm/core-common
2
2
 
3
- 브라우저·Node 공용 유틸리티 패키지. 에러 계층, 불변 날짜/시간 값, 큐·이벤트·로거, 컬렉션 프로토타입 확장, 객체·문자열·숫자·바이트·경로·직렬화 네임스페이스, 타입 유틸리티를 제공한다. 패키지를 import 하면 부수효과로 `Array`/`Set`/`Map` 프로토타입 확장이 주입된다.
3
+ 브라우저·Node 공용 유틸리티 패키지. 날짜/시간 타입, 에러 클래스, 배열/객체 조작, 직렬화, 비동기 큐, 로거, 환경변수 접근을 제공. 워크스페이스 거의 모든 패키지의 기반.
4
+
5
+ > 부수효과 주의: 이 패키지를 import 하면 `Array.prototype`·`Set.prototype`·`Map.prototype` 에 확장 메서드가 설치됨(전역 prototype 변경). 확장 메서드는 `array.toMap(...)` 처럼 메서드로 직접 호출.
4
6
 
5
7
  ## 사용 트리거 인덱스
6
8
 
7
- - **날짜·시간** — `DateTime`/`DateOnly`/`Time` 불변 값과 포맷(`dt`). 날짜 계산·주차·포맷이 필요할 때. 자세히: [datetime.md](./datetime.md)
8
- - **컬렉션 확장**`Array`/`Set`/`Map` 프로토타입 메서드(groupBy·distinct·orderBy·diffs·toTree·getOrCreate 등). 배열·맵·셋 가공 시. 자세히: [array-ext.md](./array-ext.md)
9
- - **obj 네임스페이스**깊은 clone·equal·merge·merge3, 경로 접근, pick/omit, 변환. 객체 비교·병합·정리 시. 자세히: [obj.md](./obj.md)
10
- - **직렬화 (json/xml/transfer)** — 커스텀 타입 보존 JSON·XML·Worker 전송. 영속화·통신·워커 전달 시. 자세히: [json-transfer.md](./json-transfer.md)
11
- - **에러**`SdError`/`ArgumentError`/`NotImplementedError`/`TimeoutError`. throw 메시지 체인·인자 덤프가 필요할 때. (아래 인라인)
12
- - **env**`env`/`parseBoolEnv`. 환경변수 read/write·불리언 파싱 시. (아래 인라인)
13
- - **값 타입** `Uuid`, `LazyGcMap`. UUID 생성·검증, 자동 만료 캐시 맵. (아래 인라인)
14
- - **큐·이벤트·로거**`DebounceQueue`/`SerialQueue`/`EventEmitter`/`createLogger`. 디바운스·직렬 실행·이벤트·태그 로깅 시. (아래 인라인)
15
- - **문자열·숫자·바이트·경로 (str/num/bytes/path/wait/err/primitive)** — 케이스 변환·조사·파싱·포맷·hex/base64·POSIX 경로·대기. (아래 인라인)
16
- - **코드 템플릿 태그·ZIP**`js`/`ts`/`html`/`tsql`/`mysql`/`pgsql` 하이라이팅 태그, `ZipArchive`. (아래 인라인)
17
- - **타입 유틸리티** — `Bytes`/`PrimitiveType*`/`DeepPartial`/`Type`. 원시 타입 매핑·생성자 타입이 필요할 때. (아래 인라인)
18
-
19
- ## 에러
20
-
21
- `SdError` 를 루트로 한 계층. 메시지는 상위→하위→원인 순서로 `" => "` 결합되어 출력된다.
22
-
23
- - `new SdError(cause: Error, ...messages)` / `new SdError(...messages)` — ES2024 `cause` 활용. `cause` 가 있으면 stack 에 원인 stack 을 이어 붙임. `messages` 는 역순 결합(뒤 인자가 더 하위).
24
- - `new ArgumentError(argObj)` / `new ArgumentError(message, argObj)` — 유효하지 않은 인자용. `argObj` 를 YAML 로 메시지에 포함(기본 메시지 "잘못된 인자입니다.").
25
- - `new NotImplementedError(message?)` 미구현 분기·추상 스텁용. 메시지 "미구현(: message)".
26
- - `new TimeoutError(count?, message?)` — 대기 초과용. `count` 는 시도 횟수. `wait.until` 이 최대 시도 초과 시 자동 throw.
27
-
28
- ```typescript
29
- throw new SdError(err, "API 호출 실패"); // "API 호출 실패 => <원인 메시지>"
30
- throw new ArgumentError("잘못된 사용자", { userId }); // YAML 인자 덤프 포함
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 확장·공용 타입** 단발성 유틸 (아래 인라인).
19
+
20
+ ## 로거
21
+
22
+ `createLogger(tag)` — consola 기반 lazy logger 인스턴스 생성. 모듈 레벨에서 호출해도 안전(첫 메서드 접근 시점까지 `consola.withTag` 생성을 지연하므로 이후 `setupConsola()` 의 level/reporters 변경이 반영됨).
23
+
24
+ - tag: string — 로그 prefix 로 표시되는 태그. 형식은 `<도메인>:<역할>` 또는 단일 토큰 권장. 메시지 본문에 `[패키지명]` 수동 prefix 를 넣지 말 것 — 그 역할은 tag 가 담당.
25
+
26
+ ```ts
27
+ import { createLogger } from "@simplysm/core-common";
28
+ const logger = createLogger("capacitor:auto-update");
29
+ logger.info("최신 버전 확인 중");
30
+ logger.error("checkPermissions 실패", err);
31
31
  ```
32
32
 
33
- ## env
33
+ `console.*` 직접 호출·`consola.withTag()` 직접 호출 금지 — 항상 `createLogger` 사용.
34
34
 
35
- - `env(key): string | undefined` — 환경변수 읽기(`process.env` 우선, 없으면 `import.meta.env`).
36
- - `env(key, value): void` — `process.env` 에 쓰기(process 가 있을 때만).
37
- - `parseBoolEnv(value): boolean` — `"true"`/`"1"`/`"yes"`/`"on"`(대소문자 무시) → `true`, 그 외 → `false`.
35
+ ## 환경변수
38
36
 
39
- ## 타입
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 판정에 사용.
40
40
 
41
- ### Uuid
41
+ ```ts
42
+ import { env, parseBoolEnv } from "@simplysm/core-common";
43
+ if (parseBoolEnv(env("DEV"))) { /* 개발 모드 분기 */ }
44
+ ```
45
+
46
+ ## 문자열 유틸 (`str` 네임스페이스)
42
47
 
43
- 암호학적으로 안전한 UUID v4(`crypto.getRandomValues` 기반) 불변 객체.
44
- - `Uuid.generate(): Uuid` v4 생성.
45
- - `Uuid.fromBytes(bytes): Uuid` 16바이트 `Uint8Array` 생성(길이≠16 이면 `ArgumentError`).
46
- - `new Uuid(uuidStr)` — 형식(`8-4-4-4-12` hex) 검증, 불일치 `ArgumentError`.
47
- - `toString(): string` / `toBytes(): Uint8Array` 문자열·16바이트 변환.
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 위치에 문자열 삽입한 새 문자열.
48
53
 
49
- ### LazyGcMap
54
+ ```ts
55
+ import { str } from "@simplysm/core-common";
56
+ `${name}${str.getKoreanSuffix(name, "을")} 저장했습니다`;
57
+ ```
50
58
 
51
- LRU 접근시간 기반 자동 만료 Map. **반드시 `dispose()` 호출** 해야 GC 타이머가 정리됨(아니면 메모리 누수).
52
- - `new LazyGcMap({ gcInterval?, expireTime, onExpire? })` — `expireTime`(ms): 마지막 접근 후 이 시간 지나면 삭제. `gcInterval`(ms): GC 주기, 기본 `expireTime/10`(최소 1000). `onExpire(key, value)`: 만료 시 콜백(async 가능, 에러 시 로그 후 계속).
53
- - `get(key)` — 조회(접근시간 갱신). `has(key)` — 존재 확인(갱신 안 함). `set(key, value)` — 저장(첫 set 에 GC 타이머 시작). `delete(key)` / `clear()`(인스턴스 재사용 가능) / `dispose()`(이후 사용 불가).
54
- - `getOrCreate(key, factory)` — 없으면 `factory()` 로 생성·저장(dispose 후 호출 시 throw).
55
- - `size` getter, `keys()`/`values()`/`entries()` 이터레이터.
59
+ ## 숫자 유틸 (`num` 네임스페이스)
56
60
 
57
- ## 큐·이벤트·로거
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"`.
58
66
 
59
- ### EventEmitter\<TEvents\>
67
+ ## 경로 유틸 (`path` 네임스페이스)
60
68
 
61
- `EventTarget` 래퍼. `TEvents` `{ 이벤트명: 데이터타입 }` 맵. 보통 상속해 사용.
62
- - `on(type, listener)` / `off(type, listener)` — 등록/해제(같은 리스너 중복 등록은 무시).
63
- - `emit(type, data?)` — 발행(데이터 타입이 `void` 면 인자 생략).
64
- - `listenerCount(type): number` — 리스너 수.
65
- - `dispose()` — 모든 리스너 제거.
69
+ POSIX 스타일(슬래시 `/`)만 지원. 브라우저·Capacitor 환경용. Windows 백슬래시 경로 미지원.
66
70
 
67
- ### DebounceQueue (extends EventEmitter\<{ error: SdError }\>)
71
+ - `path.join(...segments)`: string 세그먼트를 `/` 로 결합. 중간 중복 슬래시·빈 세그먼트 정리.
72
+ - `path.basename(filePath, ext?)`: → string — 파일명 추출. ext 가 주어지고 끝나면 그 확장자 제거.
73
+ - `path.extname(filePath)`: → string — 확장자 추출(`.` 포함). 숨김 파일(`.gitignore`)은 빈 문자열(Node 와 동일).
68
74
 
69
- 연속 호출 마지막만 실행하는 디바운스 큐. 실행 중 들어온 요청은 지연 없이 직후 즉시 처리.
70
- - `new DebounceQueue(delay?)` — `delay`(ms) 생략 시 다음 이벤트 루프에 즉시.
71
- - `run(fn)` — 대기 함수 교체(이전 대기 폐기). `dispose()` — 타이머·대기 정리.
72
- - 작업 에러는 `"error"` 리스너가 있으면 emit, 없으면 내부 로거로 출력.
75
+ ## UUID (`Uuid` 클래스)
73
76
 
74
- ### SerialQueue (extends EventEmitter\<{ error: SdError }\>)
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.
75
81
 
76
- 추가된 함수를 순차 실행. 한 작업이 실패해도 후속은 계속.
77
- - `new SerialQueue(gap?)` — `gap`(ms) 작업 사이 간격(기본 0).
78
- - `run(fn)` — 큐에 추가·실행. `dispose()` — 대기분 비움(실행 중 작업은 완료).
79
- - 에러 처리는 DebounceQueue 와 동일.
82
+ ## ZIP (`ZipArchive` 클래스)
80
83
 
81
- ### createLogger
84
+ ZIP 읽기/쓰기/압축/해제. 동일 파일 중복 해제 방지용 내부 캐시 사용. 사용 후 `close()` 필수.
82
85
 
83
- - `createLogger(tag): ConsolaInstance` — `consola.withTag` 지연 생성하는 lazy 로거. 모듈 레벨에서 선언해도 이후 `setupConsola` 변경(level/reporters)이 반영됨. `vi.spyOn` 호환.
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>` — 리더 닫고 캐시 비움.
84
93
 
85
- ## 문자열·숫자·바이트·경로·대기
94
+ ## 템플릿 태그 (코드 하이라이팅용)
86
95
 
87
- ### str 네임스페이스
88
- - `str.getKoreanSuffix(text, type)` — 받침에 따라 한국어 조사 반환. `type`: `"을"`(을/를)·`"은"`(은/는)·`"이"`(이/가)·`"와"`(과/와)·`"랑"`(이랑/랑)·`"로"`(으로/로, ㄹ받침 예외)·`"라"`(이라/라).
89
- - `str.replaceFullWidth(s)` — 전각 영문·숫자·공백·괄호 → 반각.
90
- - `str.toPascalCase`/`toCamelCase`/`toKebabCase`/`toSnakeCase(s)` — 케이스 변환(연속 대문자 분리, 기존 구분자 유지).
91
- - `str.isNullOrEmpty(s): s is "" | undefined` — null/undefined/빈 문자열 타입 가드.
92
- - `str.insert(s, index, insertString)` — 지정 위치에 문자열 삽입.
96
+ `js` / `ts` / `html` / `tsql` / `mysql` / `pgsql` — 모두 동일 동작: 템플릿 리터럴을 문자열로 결합하고 공통 들여쓰기를 제거(앞뒤 빈 줄 제거). IDE 하이라이팅·가독성 목적이며 SQL 이스케이프 등 기능 차이는 없음.
93
97
 
94
- ### num 네임스페이스
95
- - `num.parseInt(text)` / `num.parseFloat(text)` 비숫자 문자 제거 후 파싱(선행 `-` 만 음수 부호 유지). 실패 시 `undefined`. parseInt 는 소수부 버림.
96
- - `num.parseRoundedInt(text)` float 파싱 후 반올림 정수.
97
- - `num.isNullOrEmpty(val): val is 0 | undefined` — null/undefined/0 타입 가드.
98
- - `num.format(val, digit?)` 천 단위 구분 문자열. `digit: { max?, min? }` 소수 자릿수(min 부족분 0 채움). val 이 undefined 면 undefined.
98
+ ```ts
99
+ import { ts } from "@simplysm/core-common";
100
+ const code = ts`
101
+ interface User { name: string; }
102
+ `; // 들여쓰기 정규화된 문자열
103
+ ```
99
104
 
100
- ### bytes 네임스페이스 (`Bytes` = `Uint8Array`)
101
- - `bytes.concat(arrays)` — 여러 Uint8Array 결합.
102
- - `bytes.toHex(bytes)` / `bytes.fromHex(hex)` — hex 왕복(소문자 출력. fromHex 는 홀수 길이·비hex 문자 시 `ArgumentError`).
103
- - `bytes.toBase64(bytes)` / `bytes.fromBase64(base64)` — base64 왕복(fromBase64 는 공백·패딩 정규화, 잘못된 문자·길이 시 `ArgumentError`).
105
+ ## Set 확장 메서드 (`Set.prototype`)
104
106
 
105
- ### path 네임스페이스 (POSIX `/` 전용, 브라우저·Capacitor용)
106
- - `path.join(...segments)` 슬래시 결합(중복 슬래시 정리). `path.basename(filePath, ext?)` 파일명(ext 일치 제거). `path.extname(filePath)` 확장자(숨김 파일은 문자열).
107
+ - `set.adds(...values)`: this 여러 값을 한 번에 add. 체이닝 가능.
108
+ - `set.toggle(value, addOrDel?)`: this 토글. `addOrDel` 생략 있으면 제거/없으면 추가, `"add"`=강제 추가, `"del"`=강제 제거. 조건부 추가/제거를 줄로.
107
109
 
108
- ### wait 네임스페이스
109
- - `wait.until(forwarder, milliseconds?, maxCount?)` — 조건이 true 될 때까지 대기. `milliseconds` 확인 간격(기본 100), `maxCount` 최대 시도(초과 시 `TimeoutError`, undefined 면 무제한).
110
- - `wait.time(millisecond)` — 지정 ms 대기 Promise.
110
+ ## Map 확장 메서드 (`Map.prototype`)
111
111
 
112
- ### err 네임스페이스
113
- - `err.message(error): string` — `unknown` 에러에서 메시지 추출(`Error` `.message`, 아니면 `String()`). catch 블록용.
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)`.
114
114
 
115
- ### primitive 네임스페이스
116
- - `primitive.typeStr(value): PrimitiveTypeStr` — 런타임 값 → 원시 타입 문자열(`"string"`|`"number"`|`"boolean"`|`"DateTime"`|`"DateOnly"`|`"Time"`|`"Uuid"`|`"Bytes"`). 미지원 타입은 `ArgumentError`.
115
+ ## 공용 타입 (`common.types`)
117
116
 
118
- ## 코드 템플릿 태그·ZIP
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 체크에 사용.
119
123
 
120
- 코드 하이라이팅용 태그 함수(동작은 문자열 결합 + 공통 들여쓰기 정규화로 동일):
121
- - `js`/`ts`/`html`/`tsql`/`mysql`/`pgsql` — 각각 JS·TS·HTML·MSSQL·MySQL·PostgreSQL 하이라이팅 의도. `` js`...` `` 형태로 사용.
124
+ ## 원시 타입 추론 (`primitive` 네임스페이스)
122
125
 
123
- ### ZipArchive
124
- ZIP 읽기/쓰기/압축/해제 클래스. 내부 캐시로 중복 해제 방지. **사용 후 `close()` 필수**.
125
- - `new ZipArchive(data?)` — `data`(`Blob | Bytes`) 생략 시 새 아카이브.
126
- - `get(fileName): Promise<Bytes | undefined>` — 특정 파일 추출. `exists(fileName): Promise<boolean>` — 존재 확인.
127
- - `extractAll(progressCallback?): Promise<Map<string, Bytes | undefined>>` — 전체 추출. `progressCallback(progress)` 의 `progress: ZipArchiveProgress { fileName, totalSize, extractedSize }`.
128
- - `write(fileName, bytes)` — 캐시에 파일 추가. `compress(): Promise<Bytes>` — 캐시 내용을 ZIP 으로 압축(내부에서 extractAll 호출, 대용량 메모리 주의). `close()` — 리더 닫고 캐시 비움.
126
+ - `primitive.typeStr(value)`: → PrimitiveTypeStr — 런타임 값에서 원시 타입 문자열 추론(`"hello"`→`"string"`, `new DateTime()`→`"DateTime"`, `Uint8Array`→`"Bytes"`). 지원 안 되는 타입이면 ArgumentError. ORM 컬럼 타입 판정 등에 사용.
129
127
 
130
- ## 타입 유틸리티
128
+ ## 에러 메시지 추출 (`err` 네임스페이스)
131
129
 
132
- `common.types` 타입(네임스페이스 아닌 직접 export):
133
- - `Bytes` = `Uint8Array` — 바이너리 타입 별칭.
134
- - `PrimitiveTypeMap` — `{ string, number, boolean, DateTime, DateOnly, Time, Uuid, Bytes }` 원시 타입 매핑(orm-common 과 공유).
135
- - `PrimitiveTypeStr` = `keyof PrimitiveTypeMap` — 원시 타입 문자열 key.
136
- - `PrimitiveType` = `PrimitiveTypeMap[PrimitiveTypeStr] | undefined` — 원시 타입 union.
137
- - `DeepPartial<TObject>` — 모든 속성을 재귀적으로 optional 로(원시 타입은 그대로, object/array 에만 재귀).
138
- - `Type<TInstance>` — 클래스 생성자 타입(`new (...args) => TInstance`). DI·팩토리·`instanceof` 체크용.
130
+ - `err.message(e)`: → string — `unknown` 에러에서 메시지 추출. Error 면 `.message`, 아니면 `String(e)`. catch 블록의 `unknown` 을 안전하게 문자열화할 때.
@@ -1,72 +1,77 @@
1
- # @simplysm/core-common — 컬렉션 프로토타입 확장
1
+ # @simplysm/core-common — 배열 확장 메서드
2
2
 
3
- `import "@simplysm/core-common"`(또는 패키지의 다른 심볼 import)부수효과로 `Array`/`Set`/`Map` 프로토타입에 메서드가 주입된다. 별도 함수 호출이 아니라 인스턴스 메서드로 바로 쓴다. 메서드들은 `enumerable: false`정의되어 `for...in` 에 노출되지 않음. 컬렉션을 그룹화·중복제거·정렬·diff 할 때 함께 읽힌다.
3
+ `@simplysm/core-common` import 시 `Array.prototype` 설치되는 확장 메서드. 배열을 조회·그룹화·정렬·중복제거·Map/객체/트리 변환·diff/merge·집계할 함께 읽힘. 모두 `array.method(...)` 로 직접 호출. enumerable=false 로 설치되어 `for...in`·`Object.keys` 에 노출되지 않음.
4
4
 
5
- ## Array 조회 (ReadonlyArrayExt)
5
+ 표기: **읽기 전용**(원본 불변, 새 배열/값 반환) vs **@mutates**(원본 직접 수정). 정렬/중복제거는 두 버전(예: `orderBy` vs `orderByThis`)이 있음.
6
6
 
7
- - `single(predicate?)` 조건에 맞는 단일 요소. 없으면 `undefined`, 2개 이상이면 `ArgumentError` throw. "있다면 하나뿐" 을 단언할 때.
8
- - `first(predicate?)` / `last(predicate?)` — 첫/마지막 요소(predicate 생략 시 인덱스 끝). 없으면 `undefined`.
9
- - `filterExists()` — `null`/`undefined` 제거. 반환 타입 `NonNullable<T>[]`.
10
- - `ofType(type)` — 특정 타입 요소만. `type` 은 `PrimitiveTypeStr`("string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes") 또는 생성자(`Type<N>`). 문자열은 typeof/instanceof, 생성자는 `instanceof` + `constructor` 일치로 판정.
11
- - `sum(selector?)` — 합계. 비어 있으면 0. 숫자가 아니면 `ArgumentError`.
12
- - `min(selector?)` / `max(selector?)` — 최소/최대(문자열·숫자). 비어 있으면 `undefined`. 그 외 타입은 `ArgumentError`.
13
- - `shuffle()` — Fisher-Yates 셔플한 새 배열.
7
+ ## 조회 / 필터
14
8
 
15
- 비동기:
16
- - `filterAsync(predicate)` / `mapAsync(selector)`순차 실행(await 보장). 호출 순서가 중요할 때.
17
- - `parallelAsync(fn)` `Promise.all` 병렬. 하나라도 reject 전체 즉시 reject.
18
- - `mapMany(selector?)` 매핑 후 1단계 평탄화 + `filterExists`. selector 생략 자기 자신 평탄화.
19
- - `mapManyAsync(selector?)` — `mapMany` 비동기(순차) 버전.
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[]>` — 비동기 조건 필터(**순차** 실행).
20
15
 
21
- ## Array — 그룹화·변환
16
+ ## 비동기 매핑
22
17
 
23
- - `groupBy(keySelector, valueSelector?)` key 별 `{ key, values }[]`. 원시 key 는 O(n)(Map), 객체 key 는 O(n²)(깊은 비교). 객체 key 가 필요 없으면 `toArrayMap` 권장.
24
- - `toMap(keySelector, valueSelector?)` — `Map<K,V>`. key 중복 `ArgumentError`.
25
- - `toMapAsync(...)``toMap` 비동기(순차) 버전. selector Promise 반환 가능.
26
- - `toArrayMap(keySelector, valueSelector?)` `Map<K, V[]>`. 같은 key 값들을 배열로 누적.
27
- - `toSetMap(keySelector, valueSelector?)` — `Map<K, Set<V>>`. 값 중복 자동 제거.
28
- - `toMapValues(keySelector, valueSelector)` — key 별로 모은 `items[]` 를 `valueSelector(items)` 로 집계해 `Map<K,V>`.
29
- - `toObject(keySelector, valueSelector?)` — `Record<string,V>`. key(문자열) 중복 시 `ArgumentError`(단 기존 값이 null 이면 덮어쓰기 허용).
30
- - `toTree(keyProp, parentKey)` — 평면 배열 → 트리. `parentKey` 가 null/undefined 인 항목이 루트, 각 노드에 `children` 추가(원본은 clone). 반환 `TreeArray<T>[]`.
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<...>` 비동기 매핑 평탄화(순차).
31
22
 
32
- ## Array 중복제거·정렬·diff
23
+ ## 그룹화 / Map·객체 변환
33
24
 
34
- - `distinct(options?)` — 중복 제거(새 배열). `options`: `boolean`(=`{matchAddress}`) 또는 `{ matchAddress?, keyFn? }`. `matchAddress:true` 참조 비교(O(n)), `keyFn` 이면 key 비교(O(n)), 없으면 객체는 깊은 비교(O(n²)).
35
- - `orderBy(selector?)` / `orderByDesc(selector?)`오름/내림차순 배열. selector string|number|DateTime|DateOnly|Time|undefined 반환. null/undefined 는 오름차순에서 앞.
36
- - `diffs(target, options?)` 두 배열 비교 결과 `ArrayDiffsResult<T,P>[]`. `options.keys`(key 비교 속성)·`options.excludes`(비교 제외 속성). 전체 일치 우선, 없으면 key 일치를 UPDATE 로. target 잔여는 INSERT, source 잔여는 DELETE.
37
- - `oneWayDiffs(orgItems, keyPropNameOrGetValFn, options?)` source(this) 를 기준으로 원본 대비 변경 분류. `keyPropNameOrGetValFn`: 키 속성명 또는 `(item) => 키값`. key 값이 없거나 원본에 없으면 `create`, 다르면 `update`, 같으면 `same`(옵션 `includeSame:true` 일 때만 포함). `options.excludes`/`options.includes` 비교 범위 조정. 반환 `ArrayOneWayDiffResult<T>[]`.
38
- - `merge(target, options?)` `diffs` 결과를 적용해 병합한 배열. UPDATE 는 `obj.merge` 로 깊은 병합, INSERT 는 추가. `options` 는 `diffs` 와 동일.
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.
39
32
 
40
- ## Array — 원본 변경 (MutableArrayExt, @mutates)
33
+ ## 트리화
41
34
 
42
- 원본 배열을 직접 수정하고 보통 `this` 반환(체이닝).
43
- - `distinctThis(options?)` — 원본에서 중복 제거(역순 splice).
44
- - `orderByThis(selector?)` / `orderByDescThis(selector?)` — 원본 in-place 정렬.
45
- - `insert(index, ...items)` — 지정 위치 삽입.
46
- - `remove(itemOrSelector)` — 값 일치 또는 조건 함수에 맞는 항목 전부 제거(역순 순회).
47
- - `toggle(item)` — 있으면 제거, 없으면 push.
48
- - `clear()` — 전부 비움.
35
+ - `toTree(keyProp, parentKey)`: → `TreeArray<T>[]` — 평면 배열을 트리로. `parentKey` 값이 null/undefined 인 항목이 루트, 각 노드에 `children` 추가(항목은 clone 됨). 내부 `toArrayMap` 으로 O(n). `TreeArray<T> = T & { children: TreeArray<T>[] }`.
49
36
 
50
- ## Set 확장
37
+ ```ts
38
+ items.toTree("id", "parentId");
39
+ // [{ id: 1, name: "root", children: [{ id: 2, ..., children: [] }] }]
40
+ ```
41
+
42
+ ## 정렬 / 중복제거
43
+
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 — 원본에서 중복 제거(첫 등장만 유지).
48
+
49
+ ## diff / merge
50
+
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 됨.
56
+
57
+ ## 집계
51
58
 
52
- - `adds(...values)`여러 일괄 추가, `this` 반환.
53
- - `toggle(value, addOrDel?)` `addOrDel` 생략 자동 토글(있으면 제거/없으면 추가), `"add"`=강제 추가, `"del"`=강제 제거. 조건부 추가/제거를 줄로. `this` 반환.
59
+ - `sum(selector?)`: → number 합계(빈 배열 0). 숫자 아닌 값이면 ArgumentError.
60
+ - `min(selector?)` / `max(selector?)`: `string | number | undefined` 최소/최대. 숫자·문자열만 허용(아니면 ArgumentError). 배열 undefined.
54
61
 
55
- ## Map 확장
62
+ ## 외 @mutates
56
63
 
57
- - `getOrCreate(key, newValueOrFactory)` key 가 없으면 값을 설정 후 반환, 있으면 기존 값. 두 번째 인자가 함수면 팩토리로 인식해 호출됨 함수 자체를 값으로 저장하려면 `() => fn` 처럼 팩토리로 감쌀 것.
58
- - `update(key, updateFn)` 현재 값(`v | undefined`) 받아 값을 설정. key 없어도 `updateFn(undefined)` 호출됨. 카운터 증가·배열 누적 등에.
64
+ - `insert(index, ...items)`: thisindex 위치에 삽입.
65
+ - `remove(item)` / `remove(selector)`: this 일치 항목(또는 조건 매칭) 모두 제거(역순 순회 O(n)).
66
+ - `toggle(item)`: → this — 있으면 제거·없으면 push.
67
+ - `clear()`: → this — 전체 비움.
59
68
 
60
- ## 내보낸 타입
69
+ ## shuffle
61
70
 
62
- - `ArrayDiffsResult<TOriginal, TOther>` — `{ source: undefined, target }`(INSERT) | `{ source, target: undefined }`(DELETE) | `{ source, target }`(UPDATE).
63
- - `ArrayOneWayDiffResult<TItem>` — `{ type: "create"|"update"|"same", item, orgItem }` (create 는 `orgItem: undefined`).
64
- - `TreeArray<TNode>` — `TNode & { children: TreeArray<TNode>[] }`.
65
- - `ComparableType` — `string | number | boolean | DateTime | DateOnly | Time | undefined`. 정렬/비교 가능 타입.
71
+ - `shuffle()`: `T[]` (새 배열) Fisher–Yates 무작위 섞기. 원본 불변.
66
72
 
67
- ```typescript
68
- const orders = items.toArrayMap((it) => it.customerId); // Map<id, item[]>
69
- const sorted = items.orderBy((it) => it.createdAt).distinct({ keyFn: (it) => it.id });
70
- const tree = rows.toTree("id", "parentId");
71
- const changes = current.oneWayDiffs(original, "id"); // create/update 분류
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 분류
72
77
  ```
@@ -0,0 +1,86 @@
1
+ # @simplysm/core-common — 비동기 런타임
2
+
3
+ 디바운스/직렬 실행, 타입 안전 이벤트, 조건 대기, 자동 만료 Map 이 필요할 때 함께 읽히는 묶음. 큐 클래스들은 `EventEmitter` 를 상속해 `"error"` 이벤트를 발행함.
4
+
5
+ ## EventEmitter<TEvents>
6
+
7
+ `EventTarget` 기반 타입 안전 이벤트 이미터(브라우저·Node 공용). 보통 상속해서 사용. `TEvents` 는 `{ 이벤트명: 데이터타입 }` 맵.
8
+
9
+ - `on(type, listener)`: → void — 리스너 등록. 같은 (type, listener) 중복 등록은 무시.
10
+ - `off(type, listener)`: → void — 리스너 제거.
11
+ - `emit(type, data)`: → void — 발행. 데이터 타입이 `void` 인 이벤트는 인자 없이 `emit(type)`.
12
+ - `listenerCount(type)`: → number — 해당 이벤트 리스너 수.
13
+ - `dispose()`: → void — 모든 리스너 제거.
14
+
15
+ ```ts
16
+ import { EventEmitter } from "@simplysm/core-common";
17
+ interface MyEvents { data: string; done: void; }
18
+ class MyEmitter extends EventEmitter<MyEvents> {}
19
+ const e = new MyEmitter();
20
+ e.on("data", (d) => console.log(d)); // d: string
21
+ e.emit("data", "hello");
22
+ e.emit("done");
23
+ ```
24
+
25
+ ## DebounceQueue
26
+
27
+ 짧은 시간 내 여러 호출 중 **마지막 요청만** 실행. 입력 자동완성·연속 상태 변경 일괄 처리에. `EventEmitter<{ error: SdError }>` 상속.
28
+
29
+ - `new DebounceQueue(delay?)` — delay: number(ms). 생략 시 즉시(다음 이벤트 루프).
30
+ - `run(fn)`: → void — `fn: () => void | Promise<void>` 등록. 이전 대기 fn 은 교체됨. delay 후 실행. 실행 중 들어온 요청은 delay 없이 현재 실행 직후 즉시 처리(요청 누락 방지).
31
+ - `dispose()`: → void — 대기 작업·타이머 정리(+ 리스너 제거).
32
+ - fn 에서 throw 시 `"error"` 리스너가 있으면 SdError 로 emit, 없으면 내부 logger.error.
33
+
34
+ ```ts
35
+ import { DebounceQueue } from "@simplysm/core-common";
36
+ const q = new DebounceQueue(300);
37
+ q.on("error", (err) => console.error(err));
38
+ q.run(() => save(value)); // 300ms 안에 다시 run 하면 이전 건 취소
39
+ ```
40
+
41
+ ## SerialQueue
42
+
43
+ 큐에 넣은 작업을 **순차** 실행(앞 작업 완료 후 다음). 에러가 나도 후속 작업은 계속. `EventEmitter<{ error: SdError }>` 상속.
44
+
45
+ - `new SerialQueue(gap?)` — gap: number(ms, 기본 0). 각 작업 사이 대기 간격.
46
+ - `run(fn)`: → void — `fn: () => void | Promise<void>` 추가 후 처리 시작.
47
+ - `dispose()`: → void — 대기 큐 비움(실행 중 작업은 완료됨)(+ 리스너 제거).
48
+ - fn throw 시 처리: DebounceQueue 와 동일(`"error"` emit 또는 logger.error).
49
+
50
+ ## wait 네임스페이스
51
+
52
+ - `wait.until(forwarder, milliseconds?, maxCount?)`: → `Promise<void>` — `forwarder()`(boolean | Promise<boolean>) 가 true 될 때까지 대기. 첫 호출에서 true 면 즉시 반환. milliseconds=확인 간격(기본 100). maxCount=최대 시도 횟수(미지정 무제한). 초과 시 **TimeoutError throw**.
53
+ - `wait.time(millisecond)`: → `Promise<void>` — 지정 시간만큼 sleep.
54
+
55
+ ```ts
56
+ import { wait } from "@simplysm/core-common";
57
+ await wait.until(() => isReady, 100, 50); // 100ms 간격 최대 50회, 초과 시 TimeoutError
58
+ await wait.time(500);
59
+ ```
60
+
61
+ ## LazyGcMap<TKey, TValue>
62
+
63
+ 마지막 접근 이후 일정 시간 지나면 항목을 자동 삭제하는 Map(LRU 접근 시간 갱신). GC 는 항목이 있을 때만 타이머로 동작. 사용 후 **반드시 `dispose()`** — 안 하면 타이머가 남아 메모리 누수.
64
+
65
+ - `new LazyGcMap({ expireTime, gcInterval?, onExpire? })`
66
+ - expireTime: number(ms) — 마지막 접근 후 이 시간 지나면 만료. (필수)
67
+ - gcInterval?: number(ms) — GC 점검 주기. 기본 `max(expireTime/10, 1000)`.
68
+ - onExpire?: `(key, value) => void | Promise<void>` — 만료 시 콜백(비동기 가능). 콜백 throw 는 logger.error 후 계속 진행. 만료 항목 정리·리소스 해제에.
69
+ - `get(key)`: → `V | undefined` — 조회(접근 시간 갱신=만료 연장).
70
+ - `has(key)`: → boolean — 존재 확인(접근 시간 갱신 안 함).
71
+ - `set(key, value)`: → void — 저장(GC 타이머 시작).
72
+ - `getOrCreate(key, factory)`: → V — 없으면 factory()로 생성·저장 후 반환(있으면 접근 시간 갱신). dispose 후 호출 시 throw.
73
+ - `delete(key)`: → boolean / `clear()`: → void(인스턴스 재사용 가능) / `dispose()`: → void(타이머 중지+정리, 이후 사용 불가).
74
+ - `values()` / `keys()` / `entries()`: → Iterator — 순회.
75
+ - `size`: → number — 항목 수.
76
+
77
+ ```ts
78
+ import { LazyGcMap } from "@simplysm/core-common";
79
+ const cache = new LazyGcMap<string, Conn>({ expireTime: 60000, onExpire: (k, c) => c.close() });
80
+ try {
81
+ const conn = cache.getOrCreate(key, () => createConn());
82
+ } finally {
83
+ // 앱/모듈 종료 시
84
+ cache.dispose();
85
+ }
86
+ ```