@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.
- package/claude/output-styles/sd-tone.md +128 -0
- package/claude/references/sd-simplysm14/apis/angular/README.md +28 -89
- package/claude/references/sd-simplysm14/apis/angular/app-structure.md +75 -32
- package/claude/references/sd-simplysm14/apis/angular/buttons.md +65 -29
- package/claude/references/sd-simplysm14/apis/angular/crud.md +86 -21
- package/claude/references/sd-simplysm14/apis/angular/forms.md +168 -42
- package/claude/references/sd-simplysm14/apis/angular/infrastructure.md +200 -49
- package/claude/references/sd-simplysm14/apis/angular/kanban.md +64 -20
- package/claude/references/sd-simplysm14/apis/angular/layout.md +75 -30
- package/claude/references/sd-simplysm14/apis/angular/modal.md +92 -40
- package/claude/references/sd-simplysm14/apis/angular/routing.md +86 -25
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +72 -41
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +113 -21
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +108 -33
- package/claude/references/sd-simplysm14/apis/angular/toast.md +81 -30
- package/claude/references/sd-simplysm14/apis/angular/visual.md +140 -32
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +46 -43
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +59 -48
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +17 -7
- package/claude/references/sd-simplysm14/apis/core-common/README.md +43 -116
- package/claude/references/sd-simplysm14/apis/core-common/extensions.md +74 -109
- package/claude/references/sd-simplysm14/apis/core-common/features.md +40 -35
- package/claude/references/sd-simplysm14/apis/core-common/types.md +80 -106
- package/claude/references/sd-simplysm14/apis/core-common/utils.md +142 -111
- package/claude/references/sd-simplysm14/apis/core-node/README.md +7 -16
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +33 -38
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +25 -33
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +27 -38
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +32 -60
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +14 -45
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +35 -81
- package/claude/references/sd-simplysm14/apis/excel/README.md +178 -80
- package/claude/references/sd-simplysm14/apis/lint/README.md +5 -0
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +1 -1
- package/claude/references/sd-simplysm14/apis/sd-claude/README.md +28 -5
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +1 -1
- package/claude/references/sd-simplysm14/apis/service-client/README.md +57 -50
- package/claude/references/sd-simplysm14/apis/service-server/README.md +8 -15
- package/claude/references/sd-simplysm14/apis/service-server/auth.md +24 -16
- package/claude/references/sd-simplysm14/apis/service-server/builtin-services.md +55 -31
- package/claude/references/sd-simplysm14/apis/service-server/define-service.md +28 -44
- package/claude/references/sd-simplysm14/apis/service-server/internals.md +59 -18
- package/claude/references/sd-simplysm14/apis/service-server/server.md +37 -46
- package/claude/references/sd-simplysm14/manuals/client-component.md +3 -1
- package/claude/references/sd-simplysm14/manuals/logging.md +9 -8
- package/claude/rules/sd-base-rules.md +380 -217
- package/claude/settings.json +1 -0
- package/claude/skills/sd-commit/SKILL.md +31 -8
- package/claude/skills/sd-docs/SKILL.md +15 -10
- package/claude/skills/sd-docs/references/subagent-prompt.md +26 -8
- package/claude/skills/sd-impl/SKILL.md +1 -1
- package/claude/skills/sd-skill/references/skill-authoring.md +1 -1
- package/claude/skills/sd-spec/SKILL.md +22 -13
- package/claude/skills/sd-spec/references/spec-authoring.md +1 -1
- package/claude/skills/sd-unpack/SKILL.md +150 -26
- package/claude/skills/sd-unpack/scripts/handlers/__pycache__/_common.cpython-314.pyc +0 -0
- package/claude/skills/sd-unpack/scripts/handlers/__pycache__/eml_handler.cpython-314.pyc +0 -0
- package/claude/skills/sd-unpack/scripts/handlers/__pycache__/office_com.cpython-314.pyc +0 -0
- package/claude/skills/sd-unpack/scripts/handlers/__pycache__/pdf_handler.cpython-314.pyc +0 -0
- package/claude/skills/sd-unpack/scripts/handlers/_common.py +17 -2
- package/claude/skills/sd-unpack/scripts/handlers/eml_handler.py +100 -24
- package/claude/skills/sd-unpack/scripts/handlers/msg_handler.py +140 -27
- package/claude/skills/sd-unpack/scripts/handlers/office_com.py +698 -107
- package/claude/skills/sd-unpack/scripts/handlers/office_worker.py +34 -26
- package/claude/skills/sd-unpack/scripts/handlers/pdf_handler.py +130 -8
- package/package.json +1 -1
|
@@ -1,158 +1,189 @@
|
|
|
1
1
|
# @simplysm/core-common — utils
|
|
2
2
|
|
|
3
|
-
네임스페이스 import
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
obj.
|
|
36
|
-
obj.
|
|
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
|
-
|
|
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
|
-
|
|
45
|
-
obj.
|
|
46
|
-
obj.
|
|
47
|
-
obj.
|
|
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
|
-
|
|
54
|
-
|
|
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
|
-
```
|
|
61
|
-
str.getKoreanSuffix(text, "을"|"은"|"이"|"와"|"랑"|"로"|"라")
|
|
62
|
-
|
|
63
|
-
str.replaceFullWidth(
|
|
64
|
-
str.toPascalCase(s) / toCamelCase
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
str.
|
|
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
|
-
```
|
|
73
|
-
num.parseInt(text)
|
|
74
|
-
num.parseFloat(text)
|
|
75
|
-
num.parseRoundedInt(text) //
|
|
76
|
-
num.isNullOrEmpty(
|
|
77
|
-
num.format(
|
|
73
|
+
```ts
|
|
74
|
+
num.parseInt(text): number | undefined // 숫자 외 문자 제거. 선행 - 만 음수, 중간 - 제거. "010-1234-5678"→1012345678
|
|
75
|
+
num.parseFloat(text): number | undefined
|
|
76
|
+
num.parseRoundedInt(text): number | undefined // parseFloat 후 Math.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
|
-
```
|
|
83
|
-
bytes.concat([
|
|
84
|
-
bytes.toHex(
|
|
85
|
-
bytes.
|
|
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
|
-
```
|
|
91
|
-
path.join(...
|
|
92
|
-
path.basename(
|
|
93
|
-
path.extname(
|
|
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
|
-
```
|
|
99
|
-
json.stringify(obj, {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
```
|
|
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
|
-
```
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
128
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
156
|
+
## `primitive` — PrimitiveType 런타임
|
|
136
157
|
|
|
137
|
-
```
|
|
138
|
-
|
|
158
|
+
```ts
|
|
159
|
+
primitive.typeStr(value): PrimitiveTypeStr // 값 → "string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes"
|
|
139
160
|
```
|
|
161
|
+
- 미지원 타입 → `ArgumentError`.
|
|
140
162
|
|
|
141
|
-
##
|
|
163
|
+
## 직접 export — 템플릿 태그
|
|
142
164
|
|
|
143
|
-
`
|
|
165
|
+
`js`, `ts`, `html`, `tsql`, `mysql`, `pgsql` — 모두 같은 동작 (IDE 코드 하이라이팅용). 들여쓰기 정규화: 앞뒤 빈 줄 제거 + 모든 줄에서 공통 최소 들여쓰기만큼 dedent.
|
|
144
166
|
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
167
|
+
```ts
|
|
168
|
+
const sql = mysql`
|
|
169
|
+
SELECT * FROM users
|
|
170
|
+
LIMIT 10
|
|
171
|
+
`;
|
|
150
172
|
```
|
|
151
173
|
|
|
152
|
-
##
|
|
174
|
+
## 직접 export — `ZipArchive` (`@zip.js/zip.js` 래퍼)
|
|
153
175
|
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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 전용
|
|
3
|
+
Node.js 환경 전용 파일시스템·자식 프로세스·경로·파일감시·로깅·Worker 유틸 묶음.
|
|
4
4
|
|
|
5
5
|
## 사용 트리거 인덱스
|
|
6
6
|
|
|
7
|
-
- **`fsx
|
|
8
|
-
- **`pathx
|
|
9
|
-
- **`cpx
|
|
10
|
-
- **`FsWatcher`** —
|
|
11
|
-
-
|
|
12
|
-
|
|
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`
|
|
3
|
+
전역 `consola` 인스턴스에 reporter 를 일괄 부착하는 헬퍼와 reporter 구현체.
|
|
4
4
|
|
|
5
5
|
### setupConsola
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
|
-
|
|
9
|
-
setupConsola(opts?: SetupConsolaOptions): void;
|
|
8
|
+
setupConsola(opts?: { cli?: boolean }): void
|
|
10
9
|
```
|
|
11
10
|
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
18
|
+
`opts.cli`: CLI 프로세스(`sd-cli` 등) 여부. true 면 파일 출력 없이 콘솔만 사용하도록 분기.
|
|
19
|
+
|
|
20
|
+
### withMaxLevel
|
|
20
21
|
|
|
21
22
|
```ts
|
|
22
|
-
|
|
23
|
+
withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
|
|
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
|
-
###
|
|
28
|
+
### PrettyReporter
|
|
34
29
|
|
|
35
|
-
|
|
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
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
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
|
-
###
|
|
38
|
+
### createFileReporter
|
|
49
39
|
|
|
50
40
|
```ts
|
|
51
|
-
|
|
41
|
+
createFileReporter(options?: { maxSize?: number; maxDays?: number }): ConsolaReporter
|
|
52
42
|
```
|
|
53
43
|
|
|
54
|
-
|
|
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
|
-
|
|
62
|
-
consola.
|
|
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"
|
|
3
|
+
`import { cpx } from "@simplysm/core-node"`. 자식 프로세스 spawn 래퍼. stdout/stderr 를 시스템 인코딩으로 자동 디코딩, 실패 시 stderr 포함 에러 throw.
|
|
4
4
|
|
|
5
5
|
### 인코딩 감지
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
// 그 외 인코딩이면 먼저 utf-8 strict 시도 → 실패 시 systemEncoding 으로 fallback.
|
|
15
|
+
spawn(cmd, args, options?): SpawnProcess
|
|
16
|
+
spawnSync(cmd, args, options?): SpawnResult
|
|
21
17
|
```
|
|
22
18
|
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
interface SpawnResult { stdout: string; stderr: string; exitCode: number; }
|
|
25
|
+
### SpawnProcess
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
cpx.spawnSync(cmd, args, opts?: SpawnSyncOptions & { reject?: boolean }): SpawnResult
|
|
30
|
-
```
|
|
27
|
+
`spawn()` 반환값. `PromiseLike<SpawnResult>`.
|
|
31
28
|
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
- `
|
|
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
|
-
|
|
38
|
-
const r = await cpx.spawn("git", ["status", "--porcelain"]);
|
|
39
|
-
console.log(r.stdout);
|
|
33
|
+
### resolveStdioPipe
|
|
40
34
|
|
|
41
|
-
|
|
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
|
-
###
|
|
37
|
+
### 예
|
|
47
38
|
|
|
48
39
|
```ts
|
|
49
|
-
|
|
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
|
-
|
|
3
|
+
Chokidar 기반 디렉토리/글로브 변경 감시. 짧은 시간 내 이벤트를 병합하여 콜백 1회로 묶고, Windows EPERM 시 자동 재시작.
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### 타입
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
- `FsWatcherEvent = "add" | "addDir" | "change" | "unlink" | "unlinkDir"` — chokidar 원본 이벤트 그대로. 디렉토리/파일 add·unlink 구분.
|
|
8
|
+
- `FsWatcherChangeInfo = { event: FsWatcherEvent; path: PosixPath }` — 콜백 인자 1건. `path` 는 POSIX 슬래시 정규화.
|
|
9
|
+
|
|
10
|
+
### 생성
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
12
|
+
```ts
|
|
13
|
+
FsWatcher.watch(paths: string[], options?: ChokidarOptions): Promise<FsWatcher>
|
|
14
|
+
```
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
opt: { delay?: number },
|
|
20
|
-
cb: (changes: FsWatcherChangeInfo[]) => void | Promise<void>,
|
|
21
|
-
): this;
|
|
20
|
+
### 메서드
|
|
22
21
|
|
|
23
|
-
|
|
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
|
-
-
|
|
30
|
-
-
|
|
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
|
|
45
|
-
|
|
46
|
-
for (const {
|
|
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
|
```
|