@simplysm/sd-claude 14.0.75 → 14.0.77

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 (66) hide show
  1. package/claude/output-styles/sd-tone.md +128 -0
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +28 -89
  3. package/claude/references/sd-simplysm14/apis/angular/app-structure.md +75 -32
  4. package/claude/references/sd-simplysm14/apis/angular/buttons.md +65 -29
  5. package/claude/references/sd-simplysm14/apis/angular/crud.md +86 -21
  6. package/claude/references/sd-simplysm14/apis/angular/forms.md +168 -42
  7. package/claude/references/sd-simplysm14/apis/angular/infrastructure.md +200 -49
  8. package/claude/references/sd-simplysm14/apis/angular/kanban.md +64 -20
  9. package/claude/references/sd-simplysm14/apis/angular/layout.md +75 -30
  10. package/claude/references/sd-simplysm14/apis/angular/modal.md +92 -40
  11. package/claude/references/sd-simplysm14/apis/angular/routing.md +86 -25
  12. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +72 -41
  13. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +113 -21
  14. package/claude/references/sd-simplysm14/apis/angular/sheet.md +108 -33
  15. package/claude/references/sd-simplysm14/apis/angular/toast.md +81 -30
  16. package/claude/references/sd-simplysm14/apis/angular/visual.md +140 -32
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +46 -43
  18. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +59 -48
  19. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +17 -7
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +43 -116
  21. package/claude/references/sd-simplysm14/apis/core-common/extensions.md +74 -109
  22. package/claude/references/sd-simplysm14/apis/core-common/features.md +40 -35
  23. package/claude/references/sd-simplysm14/apis/core-common/types.md +80 -106
  24. package/claude/references/sd-simplysm14/apis/core-common/utils.md +142 -111
  25. package/claude/references/sd-simplysm14/apis/core-node/README.md +7 -16
  26. package/claude/references/sd-simplysm14/apis/core-node/consola.md +33 -38
  27. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +25 -33
  28. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +27 -38
  29. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +32 -60
  30. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +14 -45
  31. package/claude/references/sd-simplysm14/apis/core-node/worker.md +35 -81
  32. package/claude/references/sd-simplysm14/apis/excel/README.md +178 -80
  33. package/claude/references/sd-simplysm14/apis/lint/README.md +5 -0
  34. package/claude/references/sd-simplysm14/apis/orm-node/README.md +1 -1
  35. package/claude/references/sd-simplysm14/apis/sd-claude/README.md +28 -5
  36. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +1 -1
  37. package/claude/references/sd-simplysm14/apis/service-client/README.md +57 -50
  38. package/claude/references/sd-simplysm14/apis/service-server/README.md +8 -15
  39. package/claude/references/sd-simplysm14/apis/service-server/auth.md +24 -16
  40. package/claude/references/sd-simplysm14/apis/service-server/builtin-services.md +55 -31
  41. package/claude/references/sd-simplysm14/apis/service-server/define-service.md +28 -44
  42. package/claude/references/sd-simplysm14/apis/service-server/internals.md +59 -18
  43. package/claude/references/sd-simplysm14/apis/service-server/server.md +37 -46
  44. package/claude/references/sd-simplysm14/manuals/client-component.md +3 -1
  45. package/claude/references/sd-simplysm14/manuals/logging.md +9 -8
  46. package/claude/rules/sd-base-rules.md +380 -217
  47. package/claude/settings.json +1 -0
  48. package/claude/skills/sd-commit/SKILL.md +31 -8
  49. package/claude/skills/sd-docs/SKILL.md +15 -10
  50. package/claude/skills/sd-docs/references/subagent-prompt.md +26 -8
  51. package/claude/skills/sd-impl/SKILL.md +1 -1
  52. package/claude/skills/sd-skill/references/skill-authoring.md +1 -1
  53. package/claude/skills/sd-spec/SKILL.md +22 -13
  54. package/claude/skills/sd-spec/references/spec-authoring.md +1 -1
  55. package/claude/skills/sd-unpack/SKILL.md +150 -26
  56. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/_common.cpython-314.pyc +0 -0
  57. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/eml_handler.cpython-314.pyc +0 -0
  58. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/office_com.cpython-314.pyc +0 -0
  59. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/pdf_handler.cpython-314.pyc +0 -0
  60. package/claude/skills/sd-unpack/scripts/handlers/_common.py +17 -2
  61. package/claude/skills/sd-unpack/scripts/handlers/eml_handler.py +100 -24
  62. package/claude/skills/sd-unpack/scripts/handlers/msg_handler.py +140 -27
  63. package/claude/skills/sd-unpack/scripts/handlers/office_com.py +698 -107
  64. package/claude/skills/sd-unpack/scripts/handlers/office_worker.py +34 -26
  65. package/claude/skills/sd-unpack/scripts/handlers/pdf_handler.py +130 -8
  66. package/package.json +1 -1
@@ -1,158 +1,189 @@
1
1
  # @simplysm/core-common — utils
2
2
 
3
- 네임스페이스 import: `import { obj, str, num, bytes, path, json, xml, wait, transfer, err, dt, primitive } from "@simplysm/core-common";`
4
-
5
- ## obj — 객체 조작
6
-
7
- ### 복사·비교·병합
8
-
9
- ```typescript
10
- obj.clone(src) // 깊은 복사. 순환 참조·DateTime/DateOnly/Time/Uuid/
11
- // Uint8Array/Date/RegExp/Error(cause)/Map/Set 지원.
12
- // 함수/Symbol 은 참조 유지, WeakMap/WeakSet 미지원.
13
- obj.equal(a, b, opt?) // 깊은 동등. opt: { topLevelIncludes?, topLevelExcludes?,
14
- // ignoreArrayIndex?, shallow? }
15
- // include/exclude 는 최상위 객체 키에만 적용. shallow=true 면 1단계 참조 비교.
16
- obj.merge(source, target, opt?) // 깊은 병합 (불변, 새 객체). opt:
17
- // { arrayProcess?: "replace"|"concat", useDelTargetNull? }
18
- obj.merge3(source, origin, target, optionsObj?) // 3-way merge. { conflict, result }
19
- // optionsObj 는 key별 { keys?, excludes?, ignoreArrayIndex? }
20
- ```
3
+ `utils/` 네임스페이스 일괄 export. import `import { obj, str, ..., js, ZipArchive } from "@simplysm/core-common"`.
21
4
 
22
- ### 조작
5
+ ## `obj` — 객체 조작
6
+
7
+ ```ts
8
+ obj.clone<T>(source): T
9
+ // 깊은 복사. 순환 참조 지원. Date/DateTime/DateOnly/Time/Uuid/RegExp/Error/Uint8Array/Array/Map/Set 모두 별도 처리.
10
+ // 프로토타입 체인 유지(Object.setPrototypeOf). 함수·Symbol은 참조 유지. WeakMap/WeakSet 미지원. getter/setter는 현재 값으로 평가.
11
+
12
+ obj.equal(source, target, options?: EqualOptions): boolean
13
+ interface EqualOptions {
14
+ topLevelIncludes?: string[] // 지정한 key만 비교 (최상위만, 객체 속성에만 적용)
15
+ topLevelExcludes?: string[] // 지정한 key 제외 (최상위만)
16
+ ignoreArrayIndex?: boolean // array 순서 무시. true면 O(n²) (permutation 매칭)
17
+ shallow?: boolean // 1단계 참조 비교
18
+ }
19
+ // null != null 분기, custom 타입은 tick/toString 기반 비교.
23
20
 
24
- ```typescript
25
- obj.omit(o, ["k1","k2"])
26
- obj.omitByFilter(o, (k) => k.startsWith("_"))
27
- obj.pick(o, ["k1","k2"])
28
- obj.keys(o) / obj.entries(o) / obj.fromEntries(pairs) // 타입 안전 Object.* 래퍼
29
- obj.map(o, (k, v) => [newK | null, newV]) // entry 변환 (newK=null 이면 원래 유지)
30
- ```
21
+ obj.merge<S, T>(source, target, opt?: MergeOptions): S & T
22
+ interface MergeOptions {
23
+ arrayProcess?: "replace" | "concat" // 기본 "replace"(target으로 교체). "concat"=합집합(Set 중복 제거)
24
+ useDelTargetNull?: boolean // target이 null이면 결과 key 삭제
25
+ }
26
+ // 불변 ( 객체 반환). 타입 다르면 target으로 덮어씀. Map은 재귀 머지.
31
27
 
32
- ### 체인 경로 (`"a.b[0].c"` 형식)
28
+ obj.merge3(source, origin, target, optionsObj?: Record<key, Merge3KeyOptions>):
29
+ { conflict: boolean; result: O & S & T }
30
+ // 3-way merge. source==origin → target 채택, target==origin → source 채택,
31
+ // source==target → 채택, 셋 다 다름 → conflict=true + origin 유지
32
+ interface Merge3KeyOptions { keys?; excludes?; ignoreArrayIndex? } // equal과 동일
33
33
 
34
- ```typescript
35
- obj.getChainValue(o, "a.b[0].c")
36
- obj.getChainValue(o, "a.b[0].c", true) // optional: 중간 null 만나면 undefined
37
- obj.setChainValue(o, "a.b.c", v) // 중간 객체 자동 생성
38
- obj.deleteChainValue(o, "a.b.c")
39
- obj.getChainValueByDepth(o, key, depth, optional?) // 같은 key 로 N단계 하강
40
- ```
34
+ obj.omit(item, omitKeys[]): Omit<T, K>
35
+ obj.omitByFilter(item, (key) => boolean): T // @internal
36
+ obj.pick(item, keys[]): Pick<T, K>
41
37
 
42
- ### 변환 (원본 변형 `@mutates`)
38
+ obj.getChainValue(o, "a.b[0].c", optional?: true): unknown
39
+ obj.getChainValueByDepth(o, key, depth, optional?): T[K] // 같은 key로 depth회 하강
40
+ obj.setChainValue(o, chain, value): void
41
+ obj.deleteChainValue(o, chain): void
43
42
 
44
- ```typescript
45
- obj.clearUndefined(o) // null/undefined 키 제거
46
- obj.clear(o) // 모든 제거
47
- obj.nullToUndefined(o) // 재귀, null undefined
48
- obj.unflatten({ "a.b.c": 1 }) // → { a: { b: { c: 1 } } }
49
- ```
43
+ obj.clearUndefined(o): T // @mutates null/undefined key 삭제
44
+ obj.clear(o): {} // @mutates 전체 비우기
45
+ obj.nullToUndefined(o): T // @mutates null→undefined (재귀, 순환 안전)
46
+ obj.unflatten({ "a.b.c": 1 }): { a: { b: { c: 1 } } } // @internal
50
47
 
51
- ### 타입 유틸
48
+ obj.keys(o): (keyof T)[] // 타입 안전 Object.keys
49
+ obj.entries(o): [K, V][] // 타입 안전 Object.entries
50
+ obj.fromEntries(pairs): Record
51
+ obj.map(o, (key, value) => [newKey | null, newValue]): Record
52
+ // null newKey → 기존 key 유지. key/value 동시 변환
52
53
 
53
- ```typescript
54
- obj.UndefToOptional<T> // { a: string|undefined } → { a?: string|undefined }
55
- obj.OptionalToUndef<T> // { a?: string } → { a: string|undefined }
54
+ type UndefToOptional<T> // { a, b: T|undefined } → { a, b?: T }
55
+ type OptionalToUndef<T> // { a, b? } → { a, b: T|undefined }
56
56
  ```
57
57
 
58
- ## str — 문자열
58
+ ## `str` — 문자열
59
59
 
60
- ```typescript
61
- str.getKoreanSuffix(text, "을"|"은"|"이"|"와"|"랑"|"로"|"라")
62
- // 받침 유무로 조사 결정. "로" 는 ㄹ 받침이면 "로".
63
- str.replaceFullWidth(s) // 전각 영숫자/공백/괄호 → 반각
64
- str.toPascalCase(s) / toCamelCase(s) / toKebabCase(s) / toSnakeCase(s)
65
- // case 함수는 기존 -/_ 구분자 보존, 연속 대문자 개별 분리 ("XMLParser" → "x-m-l-parser")
66
- str.isNullOrEmpty(s) // null|undefined|"" 타입 가드
67
- str.insert(s, idx, insertStr)
60
+ ```ts
61
+ str.getKoreanSuffix(text, type: "을"|"은"|"이"|"와"|"랑"|"로"|"라"): string
62
+ // 받침 유무로 조사 자동. "로"는 ㄹ 받침 예외(받침 있어도 "로"). 한글 외 문자→무받침 취급.
63
+ str.replaceFullWidth(str): string // 전각 영숫자/공백/괄호 → 반각
64
+ str.toPascalCase(s) / toCamelCase / toKebabCase / toSnakeCase
65
+ // PascalCase: 하이픈/언더/점 + 소문자 대문자. 첫글자 대문자화.
66
+ // kebab/snake: 대문자/대문자그룹 분리. 기존 구분자는 유지 (혼합 시 "hello-_world").
67
+ str.isNullOrEmpty(s): s is "" | undefined // 타입 가드
68
+ str.insert(s, index, insertString): string
68
69
  ```
69
70
 
70
- ## num — 숫자
71
+ ## `num` — 숫자
71
72
 
72
- ```typescript
73
- num.parseInt(text) // 비숫자 제거 정수 파싱. 선행 - 만 음수 부호, 중간 - 제거. 소수점은 trunc.
74
- num.parseFloat(text)
75
- num.parseRoundedInt(text) // float반올림
76
- num.isNullOrEmpty(v) // null|undefined|0 타입 가드
77
- num.format(v, { max?, min? }) // 단위 + 소수점 자릿수. toLocaleString 기반
73
+ ```ts
74
+ num.parseInt(text): number | undefined // 숫자 문자 제거. 선행 - 만 음수, 중간 - 제거. "010-1234-5678"→1012345678
75
+ num.parseFloat(text): number | undefined
76
+ num.parseRoundedInt(text): number | undefined // parseFloatMath.round
77
+ num.isNullOrEmpty(n): n is 0 | undefined // 타입 가드 (0/null/undefined)
78
+ num.format(val, digit?: { max?, min? }): string // toLocaleString. min 부족분은 0 패딩
78
79
  ```
79
80
 
80
- ## bytes — Uint8Array
81
+ ## `bytes` — Uint8Array
81
82
 
82
- ```typescript
83
- bytes.concat([a, b, ...])
84
- bytes.toHex(u8) / bytes.fromHex(hex) // 소문자 hex. 홀수 길이/잘못된 문자 시 ArgumentError
85
- bytes.toBase64(u8) / bytes.fromBase64(b64) // 표준 base64 (+/, = 패딩). 공백 자동 제거
83
+ ```ts
84
+ bytes.concat(arrs: Bytes[]): Bytes
85
+ bytes.toHex(b): string // 소문자
86
+ bytes.fromHex(hex): Bytes // 홀수 길이/무효 문자 ArgumentError
87
+ bytes.toBase64(b): string
88
+ bytes.fromBase64(s): Bytes // 공백·패딩 정규화. 무효 문자/길이 → ArgumentError
86
89
  ```
87
90
 
88
- ## path — POSIX 경로 (브라우저용)
91
+ ## `path` — POSIX 경로 (브라우저용, `/` 만 지원)
89
92
 
90
- ```typescript
91
- path.join(...segs) // 슬래시만 지원, 백슬래시 X
92
- path.basename(p, ext?)
93
- path.extname(p) // 숨김 파일(".gitignore")은 문자열
93
+ ```ts
94
+ path.join(...segments): string // 슬래시 정규화
95
+ path.basename(filePath, ext?): string // ext 제거 옵션
96
+ path.extname(filePath): string // 숨김파일(.gitignore)은 "" (Node 동일)
94
97
  ```
95
98
 
96
- ## json — 커스텀 타입 지원 JSON
99
+ ## `json` — 커스텀 타입 지원 JSON
97
100
 
98
- ```typescript
99
- json.stringify(obj, { space?, replacer?, redactBytes? })
100
- // Date/DateTime/DateOnly/Time/Uuid/Set/Map/Error/Uint8Array { __type__, data } 로.
101
- // redactBytes=true Uint8Array 내용을 "__hidden__"로 (parse 복원 불가).
102
- // 순환 참조 TypeError. 전역 prototype 미수정 (Worker 안전).
103
- json.parse<T>(str) // __type__ 마커 복원. 모든 null → undefined (simplysm null-free 규칙).
104
- // 에러 시 SdError. DEV 환경에서만 메시지에 전체 JSON 포함.
101
+ ```ts
102
+ json.stringify(obj, options?: {
103
+ space?: number | string,
104
+ replacer?: (key, value) => unknown,
105
+ redactBytes?: boolean, // Uint8Array "__hidden__" (로깅용, parse throw)
106
+ }): string
107
+ json.parse<T>(str): T
105
108
  ```
109
+ - 사전 변환으로 `{ __type__, data }` 태그 객체 생성: Date/DateTime/DateOnly/Time/Uuid/Set/Map/Error(cause·code·detail 포함)/Uint8Array(hex). `Date.prototype.toJSON` 미수정 → Worker 안전.
110
+ - 순환 참조 → `TypeError`.
111
+ - `parse`는 `nullToUndefined` 적용 (모든 JSON `null` → `undefined`, simplysm null-free 규칙).
112
+ - 파싱 실패 시 `__DEV__` 환경에서는 메시지에 전체 JSON 포함, 아니면 길이만.
106
113
 
107
- ## xml — fast-xml-parser 래퍼
114
+ ## `xml`XML (fast-xml-parser 래퍼)
108
115
 
109
- ```typescript
110
- xml.parse(str, { stripTagPrefix? })
111
- // 결과: 속성은 `$` 객체, 텍스트는 `_` 키, 자식 요소는 배열 (루트 제외).
112
- // stripTagPrefix=true 면 "ns:tag" → "tag" (속성은 유지).
113
- xml.stringify(obj, options?) // fast-xml-parser XmlBuilderOptions
116
+ ```ts
117
+ xml.parse(str, options?: { stripTagPrefix?: boolean }): unknown
118
+ xml.stringify(obj, options?: XmlBuilderOptions): string
114
119
  ```
120
+ - 속성은 `$` 그룹, 텍스트 노드는 `_` key. 1단계 깊이 미만은 단일 객체, 이상은 array.
121
+ - `stripTagPrefix`: `"ns:tag"`에서 접두사 제거 (속성은 유지).
115
122
 
116
- ## wait — 대기
123
+ ## `wait`타이밍
117
124
 
118
- ```typescript
119
- await wait.time(ms)
120
- await wait.until(() => cond, intervalMs=100, maxCount?) // maxCount 초과 시 TimeoutError
125
+ ```ts
126
+ wait.time(ms): Promise<void> // setTimeout Promise화
127
+ wait.until(forwarder, ms = 100, maxCount?): Promise<void>
128
+ // forwarder가 true 반환할 때까지 ms 간격 폴링. maxCount 초과 시 TimeoutError(count)
121
129
  ```
122
130
 
123
- ## transfer — Worker 전송
131
+ ## `transfer` — Worker 직렬화
124
132
 
125
- `structuredClone` 미지원 타입 처리. Date/DateTime/DateOnly/Time/Uuid/RegExp/Error(cause/code/detail)/Uint8Array/Map/Set/Array/Object.
133
+ ```ts
134
+ transfer.encode(obj): { result: unknown; transferList: ArrayBuffer[] }
135
+ transfer.decode(obj): unknown
136
+ ```
137
+ - `worker.postMessage(result, transferList)` 패턴. Uint8Array는 zero-copy 전송, SharedArrayBuffer는 transferList 제외.
138
+ - 지원: Date/DateTime/DateOnly/Time/Uuid/RegExp, Error(cause·code·detail), Array/Map/Set/일반 객체. 그 외 TypedArray는 일반 객체로 처리됨.
139
+ - 순환 참조 → `TypeError("순환 참조 감지됨: <path>")`. 같은 객체 다중 참조는 캐시 재사용.
140
+
141
+ ## `err` — 에러 메시지
126
142
 
127
- ```typescript
128
- const { result, transferList } = transfer.encode(data);
129
- worker.postMessage(result, transferList); // Uint8Array.buffer 가 transferList 에 zero-copy
130
- const decoded = transfer.decode(event.data);
143
+ ```ts
144
+ err.message(err: unknown): string // Error.message 또는 String(err)
131
145
  ```
132
146
 
133
- 순환 참조 시 `TypeError("순환 참조 감지됨: <path>")`. 같은 객체 다중 참조는 인코딩 결과 캐싱.
147
+ ## `dt` 날짜·시간 포맷 내부 헬퍼
148
+
149
+ ```ts
150
+ dt.format(formatStr, args: { year?, month?, day?, hour?, minute?, second?, millisecond?, timezoneOffsetMinutes? }): string
151
+ dt.normalizeMonth(year, month, day): { year, month, day } // 월 오버플로 + 일수 보정
152
+ dt.convert12To24(rawHour, isPM): number // 12 AM=0, 12 PM=12
153
+ ```
154
+ - format 토큰: `yyyy yy`, `MM M`, `ddd`(요일 한글), `dd d`, `tt`(AM/PM), `hh h`(12시간), `HH H`(24시간), `mm m`, `ss s`, `fff ff f`(밀리초), `zzz zz z`(타임존 ±HH:mm/±HH/±H). 긴 토큰 우선.
134
155
 
135
- ## err에러 메시지
156
+ ## `primitive`PrimitiveType 런타임
136
157
 
137
- ```typescript
138
- err.message(unknownErr) // Error .message, 아니면 String(err)
158
+ ```ts
159
+ primitive.typeStr(value): PrimitiveTypeStr // "string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes"
139
160
  ```
161
+ - 미지원 타입 → `ArgumentError`.
140
162
 
141
- ## dtdate-format 저수준
163
+ ## 직접 export 템플릿 태그
142
164
 
143
- `DateTime/DateOnly/Time#toFormatString` 내부에서 사용. 직접 사용 드묾.
165
+ `js`, `ts`, `html`, `tsql`, `mysql`, `pgsql` — 모두 같은 동작 (IDE 코드 하이라이팅용). 들여쓰기 정규화: 앞뒤 빈 줄 제거 + 모든 줄에서 공통 최소 들여쓰기만큼 dedent.
144
166
 
145
- ```typescript
146
- dt.format(formatStr, { year?, month?, day?, hour?, minute?, second?, millisecond?, timezoneOffsetMinutes? })
147
- // 토큰은 types.md 의 "date-format 토큰" 참조
148
- dt.normalizeMonth(year, month, day) // 월 1-12 정규화 + 일 클램프
149
- dt.convert12To24(rawHour, isPM) // 12시간 → 24시간
167
+ ```ts
168
+ const sql = mysql`
169
+ SELECT * FROM users
170
+ LIMIT 10
171
+ `;
150
172
  ```
151
173
 
152
- ## primitive런타임 타입 추론
174
+ ## 직접 export `ZipArchive` (`@zip.js/zip.js` 래퍼)
153
175
 
154
- ```typescript
155
- primitive.typeStr(value)
156
- // string/number/boolean/DateTime/DateOnly/Time/Uuid/Bytes 하나 반환.
157
- // 미지원 타입은 ArgumentError.
176
+ ```ts
177
+ class ZipArchive {
178
+ constructor(data?: Blob | Bytes) // 없으면 아카이브
179
+ extractAll(progressCb?: (p: { fileName, totalSize, extractedSize }) => void): Promise<Map<string, Bytes>>
180
+ get(fileName): Promise<Bytes | undefined> // 캐싱됨
181
+ exists(fileName): Promise<boolean>
182
+ write(fileName, bytes): void // 캐시에만 저장
183
+ compress(): Promise<Bytes> // 캐시된 모든 파일을 ZIP으로 (extractAll 호출 → 전체 메모리 적재)
184
+ close(): Promise<void> // reader 닫고 캐시 비움
185
+ }
186
+ interface ZipArchiveProgress { fileName, totalSize, extractedSize }
158
187
  ```
188
+ - 같은 파일 재추출 방지를 위해 내부 `_cache: Map<filename, Bytes>` 사용.
189
+ - 대용량 ZIP 의 `compress()` 는 메모리 주의 (스트리밍 X).
@@ -1,21 +1,12 @@
1
1
  ## @simplysm/core-node
2
2
 
3
- Node.js 전용 유틸·기능 묶음. 파일 IO/glob, 경로 변환, 자식 프로세스 spawn, 파일 시스템 감시, consola 리포터 셋업, worker_threads 타입 안전 래퍼.
3
+ Node.js 환경 전용 파일시스템·자식 프로세스·경로·파일감시·로깅·Worker 유틸 묶음.
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
7
- - **`fsx` 네임스페이스** — 파일/디렉토리 존재 확인·생성·복사·삭제·읽기·쓰기·JSON·glob·재귀 유틸. 자세히: [fsx.md](./fsx.md)
8
- - **`pathx` 네임스페이스** — POSIX 경로 변환, 하위 경로 판정, 디렉토리 치환, target 필터링. 자세히: [pathx.md](./pathx.md)
9
- - **`cpx` 네임스페이스** 시스템 인코딩 감지 + 자식 프로세스 spawn/spawnSync (인코딩 자동 디코딩, exitCode 기반 reject). 자세히: [cpx.md](./cpx.md)
10
- - **`FsWatcher`** — chokidar 기반 디바운스/이벤트 병합 + Windows EPERM 자동 복구 파일 감시. 자세히: [fs-watcher.md](./fs-watcher.md)
11
- - **consola 셋업** Node 진입점에서 로깅 환경(콘솔/파일) 구성. 자세히: [consola.md](./consola.md)
12
- - **`setupConsola`** — 진입점 1회 호출. dev/prod·`SD_DEBUG`·`cli` 조합으로 리포터 자동 구성.
13
- - **`PrettyReporter`** — 색상·아이콘·tag·stack/cause 정리 포함 콘솔 출력 리포터.
14
- - **`createFileReporter`** — `<cwd>/.logs/app.<날짜>.log` JSON 회전 + 보존 기간 정리.
15
- - **`withMaxLevel`** — 기존 리포터에 level 상한 필터 씌우기 (dev 콘솔에서 debug 가리기 등).
16
- - **Worker 래퍼** — worker_threads 위 타입 안전 RPC (메서드 호출 + 이벤트 + 워커 stdout 메인 전달). 자세히: [worker.md](./worker.md)
17
- - **`Worker.create`** — 메인 측. 워커 파일을 띄우고 메서드 직접 호출 가능한 Proxy 반환.
18
- - **`createWorker`** — 워커 측. `methods` 등록 + `send(event, data)` 로 이벤트 발행. `export default` 필수.
19
- - **`WorkerProxy<TModule>`** — `Worker.create` 반환 타입. 워커 모듈 타입 추론에 사용.
20
- - **`WorkerModule`** — 워커 default export 의 구조 인터페이스 (`__methods`/`__events`). 직접 구현 불필요.
21
- - **`PromisifyMethods<T>`** — 메서드 반환을 `Promise<Awaited<R>>` 로 감싸는 매핑 타입. `WorkerProxy` 내부 사용.
7
+ - **`fsx`** — 파일/디렉토리 존재확인·생성·복사·삭제·읽기/쓰기(텍스트/바이너리/JSON)·glob·부모 디렉토리 탐색이 필요할 때. 자세히: [fsx.md](./fsx.md)
8
+ - **`pathx`** — POSIX 슬래시 경로 변환, 부모-자식 경로 판정, cwd 기준 타겟 필터링이 필요할 때. 자세히: [pathx.md](./pathx.md)
9
+ - **`cpx`** — 자식 프로세스 spawn(비동기/동기), 시스템 인코딩 자동 디코딩, 실패 reject 옵션. 자세히: [cpx.md](./cpx.md)
10
+ - **`FsWatcher`** — 디렉토리/glob 변경 감시 + 디바운싱 + 이벤트 병합 + EPERM 자동 재시작. 자세히: [fs-watcher.md](./fs-watcher.md)
11
+ - **`setupConsola` / `PrettyReporter` / `createFileReporter`** CLI/서버 로깅 일괄 설정, 컬러 콘솔 + JSONL 파일 회전. 자세히: [consola.md](./consola.md)
12
+ - **`Worker` / `createWorker`** — 타입 안전 worker_threads 래퍼. 메인에서 `Worker.create()`, 워커에서 `createWorker()`. 자세히: [worker.md](./worker.md)
@@ -1,64 +1,59 @@
1
- ## @simplysm/core-node — consola
1
+ ## @simplysm/core-node — consola 설정
2
2
 
3
- `consola` 전역 인스턴스 셋업 + 컬러 콘솔 리포터 + JSON 파일 회전 리포터. Node 앱(서버·CLI) 진입점에서 `setupConsola()` 1회 호출하면 환경에 맞게 reporter 가 구성됨.
3
+ 전역 `consola` 인스턴스에 reporter 일괄 부착하는 헬퍼와 reporter 구현체.
4
4
 
5
5
  ### setupConsola
6
6
 
7
7
  ```ts
8
- interface SetupConsolaOptions { cli?: boolean; }
9
- setupConsola(opts?: SetupConsolaOptions): void;
8
+ setupConsola(opts?: { cli?: boolean }): void
10
9
  ```
11
10
 
12
- - `cli: true` 또는 `env.DEV` truthy dev 모드.
13
- - `SD_DEBUG` truthy: `PrettyReporter` 하나만, debug 까지.
14
- - 아니면: `createFileReporter()` + `withMaxLevel(new PrettyReporter(), LogLevels.info)` — 파일에는 debug 까지, 콘솔에는 info 이상만.
15
- - 그 외 (prod): `createFileReporter()` 하나만, debug 까지.
11
+ 전역 `consola.level = LogLevels.debug` 설정 환경에 따라 reporter 구성:
16
12
 
17
- 모든 경로에서 `consola.level = LogLevels.debug`.
13
+ - `opts.cli !== true` && `env("DEV")` falsy → **프로덕션**: `createFileReporter()` 만 (JSONL 파일, 콘솔 X).
14
+ - 그 외 → **개발**:
15
+ - `env("SD_DEBUG")` truthy → `PrettyReporter()` 만 (debug 포함 콘솔).
16
+ - 그 외 → `createFileReporter()`(debug 포함 파일) + `PrettyReporter()`(info 이상만 콘솔, `withMaxLevel` 적용).
18
17
 
19
- ### PrettyReporter
18
+ `opts.cli`: CLI 프로세스(`sd-cli` 등) 여부. true 면 파일 출력 없이 콘솔만 사용하도록 분기.
19
+
20
+ ### withMaxLevel
20
21
 
21
22
  ```ts
22
- class PrettyReporter implements ConsolaReporter {}
23
+ withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter
23
24
  ```
24
25
 
25
- - 색상 자동 감지: `NO_COLOR` 있으면 off, `FORCE_COLOR` 있으면 on, TTY on, win32 면 on.
26
- - 타입별 아이콘/색 (info=cyan, success/ready=green, warn=yellow, error/fatal/fail=red, start=magenta, debug=gear, trace=arrow).
27
- - `logObj.type === "box"` 면 `> tag`, `> title`, `> 각 줄` 형태로 박스 렌더.
28
- - `logObj.tag !== ""` 면 `[tag]` 가 회색으로 prefix.
29
- - level < 2 (error/fatal/warn) 는 `stderr`, 그 외 `stdout` 으로 출력. 추가로 badge 처리(앞뒤 빈 줄).
30
- - args 중 `Error.stack` 가진 객체는 재귀적으로 cause 체인까지 풀어서 stack 정리 (cwd 제거, `file://` 제거, 들여쓰기).
31
- - `type === "trace"` 면 즉시 stack 첨부.
26
+ 기존 reporter 감싸 `logObj.level > maxLevel` 항목을 버린다. consola `LogLevels` 낮을수록 심각(0=fatal/error, 1=warn, 2=log, 3=info, 4=debug, 5=trace).
32
27
 
33
- ### createFileReporter
28
+ ### PrettyReporter
34
29
 
35
- ```ts
36
- interface FileReporterOptions {
37
- maxSize?: number; // default 20MB
38
- maxDays?: number; // default 14
39
- }
40
- createFileReporter(options?: FileReporterOptions): ConsolaReporter;
41
- ```
30
+ `class PrettyReporter implements ConsolaReporter` — ANSI 컬러 콘솔 출력.
42
31
 
43
- - 출력 디렉토리: `<cwd>/.logs` (자동 mkdir).
44
- - 파일명: `app.<YYYY-MM-DD>.log`크기 초과 `app.<YYYY-MM-DD>.<n>.log` 로 순번.
45
- - 로그 1줄 = JSON: `{ time(ISO), level, tag?, err?: {message,stack}, msg? }`. `Error` arg `err` 필드로, 외는 모두 `String` 공백 조인되어 `msg`.
46
- - 날짜 바뀌면 회전 + 그날 write 에서 `maxDays` 이전 `app.YYYY-MM-DD.*.log` 정리.
32
+ - 색상 활성화: `env("NO_COLOR")` 있으면 비활성, `env("FORCE_COLOR")` 있으면 강제, TTY 또는 win32 면 활성.
33
+ - level<2 (fatal/error/warn)`stderr`, `stdout`.
34
+ - `type === "box"` 박스 포맷, `type === "trace"` 스택 첨부, level<2 또는 `badge: true` 위아래 줄.
35
+ - `Error` args message + cwd prefix 제거된 스택 + `cause` 재귀 indent.
36
+ - `ctx.options.formatOptions.date: true` → 시각 `HH:mm:ss.SSS` 부착.
47
37
 
48
- ### withMaxLevel
38
+ ### createFileReporter
49
39
 
50
40
  ```ts
51
- withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter;
41
+ createFileReporter(options?: { maxSize?: number; maxDays?: number }): ConsolaReporter
52
42
  ```
53
43
 
54
- `logObj.level > maxLevel` 항목을 drop. `setupConsola` dev 콘솔 리포터에 `LogLevels.info` 적용.
44
+ - `maxSize` (기본 20MB): 파일 1개 최대 크기. 초과 `app.<YYYY-MM-DD>.<seq>.log` 시퀀스로 회전.
45
+ - `maxDays` (기본 14): cutoff 일자 이전 로그 파일 자동 삭제. 매일 첫 로그 시 1회 실행.
46
+ - 출력 위치: `${process.cwd()}/.logs/app.<YYYY-MM-DD>.log` (필요시 `.<seq>` 부착).
47
+ - 라인 형식: `{ time, level, tag?, err?: {message,stack}, msg? }` JSONL. `Error` arg 는 `err` 필드로 분리, 나머지는 `msg` 로 공백 연결.
48
+ - 날짜 바뀌면 자동 로테이트. `.logs` 디렉토리는 첫 쓰기 시 생성.
55
49
 
56
- ### 사용
50
+ ### 예
57
51
 
58
52
  ```ts
59
- import { setupConsola } from "@simplysm/core-node";
53
+ import { setupConsola, PrettyReporter, createFileReporter } from "@simplysm/core-node";
54
+ import consola from "consola";
55
+ setupConsola({ cli: true });
60
56
 
61
- setupConsola({ cli: true }); // 진입점 1회
62
- consola.info("server started");
63
- consola.withTag("db").debug({ sql, params });
57
+ // 커스텀
58
+ consola.options.reporters = [new PrettyReporter(), createFileReporter({ maxDays: 30 })];
64
59
  ```
@@ -1,52 +1,44 @@
1
1
  ## @simplysm/core-node — cpx
2
2
 
3
- `import { cpx } from "@simplysm/core-node"` 자식 프로세스 spawn + 시스템 인코딩 감지. Windows `chcp` 코드 페이지 또는 POSIX `LANG/LC_ALL` 의 charset 부분을 보고 stdout/stderr 적절히 디코딩.
3
+ `import { cpx } from "@simplysm/core-node"`. 자식 프로세스 spawn 래퍼. stdout/stderr 시스템 인코딩으로 자동 디코딩, 실패 stderr 포함 에러 throw.
4
4
 
5
5
  ### 인코딩 감지
6
6
 
7
- ```ts
8
- cpx.getSystemEncoding(): string // 최초 1회 감지모듈 캐시
9
- cpx.resetEncodingCache(): void
10
- cpx.codePageToEncoding(codePage: number): string // 65001→utf-8, 949→euc-kr, 932→shift-jis, 936→gbk, 950big5, 1252/1251/1250/874→windows-* / utf-8
11
- ```
12
-
13
- 감지 실패 시 `utf-8` fallback.
7
+ - `getSystemEncoding(): string` — Windows 는 `chcp` 결과 코드 페이지를, POSIX 는 `LANG`/`LC_ALL` 의 `.<enc>` 부분을 파싱. 실패 시 `"utf-8"`. **결과는 모듈 캐시**.
8
+ - `resetEncodingCache(): void` 캐시 초기화. 환경 변경 강제 재감지 시.
9
+ - `codePageToEncoding(codePage): string` — Windows 코드 페이지 번호→IANA 인코딩명(65001/949/932/936/950/1252/1251/1250/874 지원, 그 외 `"utf-8"`).
10
+ - `decodeBytes(raw: Uint8Array, systemEncoding?: string): string` — UTF-8 fatal decode 시도실패 지정 인코딩으로 fallback.
14
11
 
15
- ### 디코딩
12
+ ### spawn
16
13
 
17
14
  ```ts
18
- cpx.decodeBytes(raw: Uint8Array, systemEncoding?: string): string
19
- // utf-8 이면 바로 디코딩.
20
- // 그 외 인코딩이면 먼저 utf-8 strict 시도 → 실패 시 systemEncoding 으로 fallback.
15
+ spawn(cmd, args, options?): SpawnProcess
16
+ spawnSync(cmd, args, options?): SpawnResult
21
17
  ```
22
18
 
23
- ### spawn / spawnSync
19
+ - `options`: Node `SpawnOptions`/`SpawnSyncOptions` + `reject?: boolean`.
20
+ - `stdio` 기본 `"pipe"`. pipe 인 스트림만 캡처되며 비-pipe 스트림은 결과 문자열이 빈 문자열.
21
+ - `env`: `process.env` 와 병합되어 자식에 전달.
22
+ - `reject` (기본 `true`): exitCode≠0 일 때 `false` 면 결과 객체 반환, `true` 면 throw. 에러 메시지는 `Command failed (exit N): <cmd> <args>` + stderr/stdout 마지막 4000자.
23
+ - `SpawnResult`: `{ stdout: string; stderr: string; exitCode: number }`. 종료 코드가 없고 시그널만 있으면 exitCode=1.
24
24
 
25
- ```ts
26
- interface SpawnResult { stdout: string; stderr: string; exitCode: number; }
25
+ ### SpawnProcess
27
26
 
28
- cpx.spawn(cmd, args, opts?: SpawnOptions & { reject?: boolean }): SpawnProcess
29
- cpx.spawnSync(cmd, args, opts?: SpawnSyncOptions & { reject?: boolean }): SpawnResult
30
- ```
27
+ `spawn()` 반환값. `PromiseLike<SpawnResult>`.
31
28
 
32
- - 기본 `stdio: "pipe"`. `process.env` `opts.env` 머지해 자식에 전달.
33
- - stdout/stderr pipe 면 모아서 `decodeBytes` 문자열화.
34
- - `exitCode !== 0` 이고 `reject !== false` 면 throw (`Command failed: <cmd> <args>`).
35
- - `cpx.spawn` 은 `SpawnProcess` 반환 (PromiseLike + `pid` + `kill(signal?)`).
29
+ - `pid: number | undefined` 자식 PID.
30
+ - `then / catch` `SpawnResult` resolve 또는 에러 reject.
31
+ - `kill(signal?): boolean` 자식 프로세스에 시그널 전송.
36
32
 
37
- ```ts
38
- const r = await cpx.spawn("git", ["status", "--porcelain"]);
39
- console.log(r.stdout);
33
+ ### resolveStdioPipe
40
34
 
41
- const proc = cpx.spawn("pnpm", ["watch"], { reject: false });
42
- process.on("SIGINT", () => proc.kill());
43
- const { exitCode } = await proc;
44
- ```
35
+ - `resolveStdioPipe(stdio): { stdout: boolean; stderr: boolean }` — `SpawnOptions["stdio"]` 스트림에 대해 pipe 인지 판정. 배열·`"pipe"`·`undefined` 모두 처리.
45
36
 
46
- ### resolveStdioPipe
37
+ ###
47
38
 
48
39
  ```ts
49
- cpx.resolveStdioPipe(stdio): { stdout: boolean; stderr: boolean }
40
+ import { cpx } from "@simplysm/core-node";
41
+ const { stdout } = await cpx.spawn("git", ["rev-parse", "HEAD"]);
42
+ const proc = cpx.spawn("node", ["server.js"], { reject: false });
43
+ proc.kill("SIGTERM");
50
44
  ```
51
-
52
- stdio 값(`"pipe"`, `"inherit"`, 배열, `undefined`)에서 stdout/stderr 각각 pipe 여부 판정. spawn 자체가 내부 사용하므로 사용자 호출 거의 없음.
@@ -1,53 +1,42 @@
1
1
  ## @simplysm/core-node — FsWatcher
2
2
 
3
- chokidar 래퍼. 짧은 시간 내 이벤트를 병합해서 콜백 1회만 호출하고, Windows EPERM (감시 디렉토리 사라짐 등) 발생 watcher 자동 재시작. 모듈 로드 시 `fs.FSWatcher.prototype.emit` 을 한 번 패치해서, listener 없는 인스턴스의 orphan `error` 이벤트로 인한 uncaughtException 도 swallow.
3
+ Chokidar 기반 디렉토리/글로브 변경 감시. 짧은 시간 내 이벤트를 병합하여 콜백 1회로 묶고, Windows EPERM 시 자동 재시작.
4
4
 
5
- ### 시그니처
5
+ ### 타입
6
6
 
7
- ```ts
8
- type FsWatcherEvent = "add" | "addDir" | "change" | "unlink" | "unlinkDir";
7
+ - `FsWatcherEvent = "add" | "addDir" | "change" | "unlink" | "unlinkDir"` — chokidar 원본 이벤트 그대로. 디렉토리/파일 add·unlink 구분.
8
+ - `FsWatcherChangeInfo = { event: FsWatcherEvent; path: PosixPath }` 콜백 인자 1건. `path` 는 POSIX 슬래시 정규화.
9
+
10
+ ### 생성
9
11
 
10
- interface FsWatcherChangeInfo {
11
- event: FsWatcherEvent;
12
- path: PosixPath; // 항상 슬래시 경로
13
- }
12
+ ```ts
13
+ FsWatcher.watch(paths: string[], options?: ChokidarOptions): Promise<FsWatcher>
14
+ ```
14
15
 
15
- class FsWatcher {
16
- static watch(paths: string[], options?: chokidar.ChokidarOptions): Promise<FsWatcher>;
16
+ - `paths`: 감시 대상. 디렉토리 절대 경로 또는 glob 패턴 혼용 가능. glob 메타문자 이전까지를 실제 감시 base 로 추출 후 chokidar 에 전달.
17
+ - `options`: chokidar `ChokidarOptions`. `ignoreInitial` 은 내부적으로 항상 `true` 로 override 되지만, 사용자가 `false` 로 명시한 경우 첫 `onChange` 콜백을 **빈 배열**로 1회 호출(초기 트리거 신호용, 실제 초기 목록은 X).
18
+ - `ready` 대기 후 resolve. 초기화 에러 시 close 후 reject.
17
19
 
18
- onChange(
19
- opt: { delay?: number },
20
- cb: (changes: FsWatcherChangeInfo[]) => void | Promise<void>,
21
- ): this;
20
+ ### 메서드
22
21
 
23
- close(): Promise<void>;
24
- }
25
- ```
22
+ - `onChange(opt: { delay?: number }, cb: (changes: FsWatcherChangeInfo[]) => void | Promise<void>): this`
23
+ - `opt.delay`: 디바운스 ms. 마지막 이벤트로부터 이 시간 후 콜백 1회.
24
+ - 같은 파일에 대한 연속 이벤트 자동 병합: `add+change=add`, `add+unlink`=상쇄(제외), `addDir+unlinkDir`=상쇄, `unlink+add=add`, `unlink+change=change`, `unlinkDir+addDir=addDir`. 룩업 미스는 현재 이벤트로 덮어쓰기.
25
+ - 콜백은 감시 base 가 아닌 **`paths` 원본 glob 에 매칭되는 경로만** 통과 (minimatch, dot 포함).
26
+ - 다중 호출 가능. 각 호출마다 독립 debounce 큐 + handler 등록.
27
+ - `close(): Promise<void>` — debounce 큐 dispose + chokidar close.
26
28
 
27
- ### 동작 디테일
29
+ ### 자동 복구
28
30
 
29
- - `paths` 원소에서 glob 메타문자(`* ? { [ ]`) 이전 부분을 `chokidar.watch` 대상 디렉토리로 추출하고, 실제 변경 알림은 원래 패턴에 대해 `minimatch(path, p, { dot: true })` 또는 `minimatch(path, p + "/**")` 매칭만 통과.
30
- - 항상 `ignoreInitial: true` 로 chokidar 호출. 사용자가 `options.ignoreInitial: false` 를 주면 콜백을 빈 배열로 1회 호출 (실제 파일 목록은 포함하지 않음 이벤트 병합 모델과의 충돌 방지).
31
- - `onChange` 의 `delay` 로 `DebounceQueue` 생성. 윈도우 내 같은 파일의 이벤트는 lookup 테이블로 병합:
32
- - `add+change` → `add`
33
- - `add+unlink` → 제거 (생성 직후 삭제 상쇄)
34
- - `addDir+unlinkDir` → 제거
35
- - `unlink+add` → `add`
36
- - `unlink+change` → `change`
37
- - `unlinkDir+addDir` → `addDir`
38
- - 미정의 조합은 뒤 이벤트로 덮어쓰기.
39
- - `error` 의 code 가 `EPERM` 이면 watcher 종료 → 1초 대기 → 재생성 → 등록된 모든 핸들러 재부착 → ready 대기. 최대 3회 시도 후 포기 (에러 로그만).
31
+ - 감시 디렉토리 소실로 EPERM 발생 최대 3회 / 1초 간격으로 watcher 재생성·핸들러 재등록. 성공 retry count 리셋, 초과 중단(error 로그).
32
+ - native `fs.FSWatcher.prototype.emit` 를 모듈 로드 1회 패치: listener 0 + `error` 이벤트는 swallow (orphan error 인한 프로세스 종료 방지).
40
33
 
41
- ### 사용
34
+ ### 예
42
35
 
43
36
  ```ts
44
- const watcher = await FsWatcher.watch(["src/**/*.ts", "tests/**/*.ts"]);
45
- watcher.onChange({ delay: 300 }, (changes) => {
46
- for (const { event, path } of changes) {
47
- consola.info(`${event}: ${path}`);
48
- }
37
+ const w = await FsWatcher.watch(["src/**/*.ts"]);
38
+ w.onChange({ delay: 300 }, async (changes) => {
39
+ for (const { path, event } of changes) console.log(event, path);
49
40
  });
50
-
51
- // 종료
52
- await watcher.close();
41
+ // await w.close();
53
42
  ```