@simplysm/sd-claude 14.0.89 → 14.0.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude/references/sd-simplysm14/README.md +16 -17
- package/claude/references/sd-simplysm14/apis/angular/README.md +52 -30
- package/claude/references/sd-simplysm14/apis/angular/controls.md +200 -38
- package/claude/references/sd-simplysm14/apis/angular/crud.md +41 -53
- package/claude/references/sd-simplysm14/apis/angular/directives.md +66 -22
- package/claude/references/sd-simplysm14/apis/angular/features.md +127 -40
- package/claude/references/sd-simplysm14/apis/angular/infra.md +60 -43
- package/claude/references/sd-simplysm14/apis/angular/layout.md +56 -20
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +74 -74
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +50 -40
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +55 -15
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +59 -42
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +77 -62
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +8 -7
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +71 -43
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +22 -14
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +19 -19
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +17 -17
- package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +28 -28
- package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +37 -37
- package/claude/references/sd-simplysm14/apis/core-common/README.md +87 -219
- package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +54 -98
- package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +57 -99
- package/claude/references/sd-simplysm14/apis/core-common/datetime.md +60 -103
- package/claude/references/sd-simplysm14/apis/core-common/errors.md +42 -47
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +42 -88
- package/claude/references/sd-simplysm14/apis/core-common/serialization.md +55 -0
- package/claude/references/sd-simplysm14/apis/core-node/README.md +6 -7
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +17 -12
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +14 -13
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +9 -8
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +14 -13
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +4 -8
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +14 -12
- package/claude/references/sd-simplysm14/apis/excel/README.md +22 -22
- package/claude/references/sd-simplysm14/apis/excel/cell.md +37 -29
- package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +29 -15
- package/claude/references/sd-simplysm14/apis/excel/style.md +33 -27
- package/claude/references/sd-simplysm14/apis/excel/utils.md +29 -19
- package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +78 -55
- package/claude/references/sd-simplysm14/apis/excel/wrapper.md +42 -45
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -8
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +118 -67
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +83 -86
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +102 -93
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +138 -81
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +49 -44
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +42 -42
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +44 -33
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +11 -10
- package/claude/references/sd-simplysm14/apis/service-client/README.md +56 -52
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +33 -28
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +23 -21
- package/claude/references/sd-simplysm14/apis/service-common/README.md +83 -48
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +126 -34
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +109 -54
- package/claude/references/sd-simplysm14/apis/service-server/README.md +69 -81
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +46 -43
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +63 -37
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +40 -30
- package/claude/references/sd-simplysm14/apis/storage/README.md +17 -17
- package/claude/references/sd-simplysm14/manuals/client-app-structure.md +142 -140
- package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -1
- package/claude/references/sd-simplysm14/manuals/client-service.md +19 -7
- package/claude/references/sd-simplysm14/manuals/client-shared-data.md +2 -2
- package/claude/references/sd-simplysm14/manuals/client-system-log.md +11 -3
- package/claude/references/sd-simplysm14/manuals/data-log.md +0 -1
- package/claude/references/sd-simplysm14/manuals/orm.md +16 -0
- package/claude/rules/sd-design-rules.md +10 -0
- package/claude/skills/sd-demo/SKILL.md +0 -6
- package/claude/skills/sd-docs/SKILL.md +58 -0
- package/claude/{workflows/sd-docs.rules.md → skills/sd-docs/references/subagent-prompt.md} +103 -103
- package/claude/skills/sd-impl/SKILL.md +7 -4
- package/claude/skills/sd-spec/SKILL.md +842 -15
- package/claude/skills/sd-spec/references/example-spec.md +26 -36
- package/package.json +1 -1
- package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +0 -53
- package/claude/skills/sd-spec/references/spec-authoring.md +0 -519
- package/claude/workflows/sd-docs.js +0 -84
|
@@ -1,117 +1,71 @@
|
|
|
1
1
|
# @simplysm/core-common — obj 네임스페이스
|
|
2
2
|
|
|
3
|
-
`import { obj } from "@simplysm/core-common"
|
|
3
|
+
`import { obj } from "@simplysm/core-common"` 로 접근하는 객체 조작 유틸. 깊은 복사/비교/병합, 체인 경로 접근, key 변환을 할 때 함께 읽힘. 깊은 연산은 커스텀 값 타입(DateTime·DateOnly·Time·Uuid·Uint8Array)·Date·RegExp·Map·Set·Error 를 인지하고 순환 참조를 처리함.
|
|
4
4
|
|
|
5
5
|
## clone
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
- 순환 참조를 WeakMap 으로 추적해 안전 복사. `Date`/`DateTime`/`DateOnly`/`Time`/`Uuid`/`RegExp`/`Uint8Array`/`Array`/`Map`/`Set`/`Error` 를 각 타입으로 재구성하고 프로토타입 체인 유지.
|
|
12
|
-
- 함수·Symbol 은 복사되지 않고 참조 유지. WeakMap/WeakSet 미지원(빈 객체화). getter/setter 는 현재 값으로 평가되어 복사.
|
|
7
|
+
- `obj.clone(source)`: → 동일 타입 — 깊은 복사. 순환 참조 지원. Date/DateTime/DateOnly/Time/Uuid/Uint8Array/RegExp/Array/Map/Set/Error(cause·커스텀 속성 포함) 및 일반 객체(프로토타입 체인 유지)를 복제.
|
|
8
|
+
- 주의: 함수·Symbol 은 복사 안 되고 참조 유지. WeakMap/WeakSet 미지원(빈 객체화). getter/setter 는 현재 값으로 평가되어 복사(접근자 자체는 복사 안 됨).
|
|
13
9
|
|
|
14
10
|
## equal
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
interface EqualOptions {
|
|
18
|
-
topLevelIncludes?: string[]; // 비교할 key (최상위만)
|
|
19
|
-
topLevelExcludes?: string[]; // 제외할 key (최상위만)
|
|
20
|
-
ignoreArrayIndex?: boolean; // 배열 순서 무시 (true면 O(n²))
|
|
21
|
-
shallow?: boolean; // 1단계 참조 비교
|
|
22
|
-
}
|
|
23
|
-
obj.equal(source: unknown, target: unknown, options?: EqualOptions): boolean;
|
|
24
|
-
```
|
|
12
|
+
- `obj.equal(source, target, options?)`: → boolean — 깊은 동등성 비교. Date/날짜타입(tick)/Uuid(문자열)/RegExp/Array/Map/Set/일반 객체를 인지. null/undefined 인 속성은 비교에서 제외(없는 것으로 취급).
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
14
|
+
옵션(`EqualOptions`):
|
|
15
|
+
- topLevelIncludes?: string[] — 비교할 key 화이트리스트(최상위 객체 속성에만 적용). 일부 필드만 같으면 OK 로 볼 때.
|
|
16
|
+
- topLevelExcludes?: string[] — 비교에서 뺄 key(최상위만). `updatedAt` 같은 변동 필드 무시할 때.
|
|
17
|
+
- ignoreArrayIndex?: boolean — true 면 배열 순서 무시(같은 다중집합인지). O(n²). `[1,2,3]==[3,2,1]`.
|
|
18
|
+
- shallow?: boolean — true 면 1단계만 참조 비교. 대용량에서 성능 우선일 때.
|
|
19
|
+
- 주의: include/exclude 는 객체 속성 key 에만 적용. Map 의 key 는 항상 전부 비교됨.
|
|
30
20
|
|
|
31
21
|
## merge / merge3
|
|
32
22
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
obj.merge<S, T>(source: S, target: T, opt?: MergeOptions): S & T; // 불변, 새 객체 반환
|
|
39
|
-
|
|
40
|
-
interface Merge3KeyOptions { keys?: string[]; excludes?: string[]; ignoreArrayIndex?: boolean; }
|
|
41
|
-
obj.merge3<S, O, T>(source: S, origin: O, target: T, optionsObj?: Record<string, Merge3KeyOptions>): { conflict: boolean; result: O & S & T };
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
- `merge(source, target, opt)` — source 위에 target 을 깊은 병합한 새 객체. `arrayProcess: "replace"`(기본) 면 배열을 target 으로 교체, `"concat"` 이면 Set 으로 중복 제거 병합. `useDelTargetNull: true` 면 target 의 null 값 key 를 결과에서 삭제. 타입이 다르면 target 우선.
|
|
45
|
-
- `merge3(source, origin, target, optionsObj)` — 공통 조상 `origin` 기준 3-way 병합. 한쪽만 바뀌면 그 값 채택, 양쪽이 같으면 그 값, 셋 다 다르면 `conflict: true`(origin 유지). `optionsObj` 는 key 별 `equal` 비교 옵션.
|
|
23
|
+
- `obj.merge(source, target, opt?)`: → `Source & Target` — 깊은 병합(원본 불변, 새 객체 반환). target 값으로 source 를 덮어쓰되 객체/Map 은 재귀 병합. 날짜타입·Uuid·Uint8Array 는 통째로 교체.
|
|
24
|
+
- opt.arrayProcess?: `"replace" | "concat"` — 배열 처리. `"replace"`(기본)=target 배열로 교체, `"concat"`=합치고 Set 으로 중복 제거(객체는 참조 비교).
|
|
25
|
+
- opt.useDelTargetNull?: boolean — true 면 target 값이 null 일 때 해당 key 를 결과에서 삭제. 패치에서 "필드 제거"를 표현할 때.
|
|
26
|
+
- `obj.merge3(source, origin, target, optionsObj?)`: → `{ conflict, result }` — 3-way 병합(공통 조상 origin 기준). source 만 바뀌면 source, target 만 바뀌면 target, 둘 다 같으면 그 값, 셋 다 다르면 conflict=true(origin 유지). optionsObj 는 key 별 비교 옵션(`Merge3KeyOptions`: keys/excludes/ignoreArrayIndex). 동시 편집 충돌 감지에 사용.
|
|
46
27
|
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
const
|
|
28
|
+
```ts
|
|
29
|
+
import { obj } from "@simplysm/core-common";
|
|
30
|
+
const merged = obj.merge(base, patch, { arrayProcess: "concat", useDelTargetNull: true });
|
|
31
|
+
const { conflict, result } = obj.merge3(mine, origin, theirs);
|
|
50
32
|
```
|
|
51
33
|
|
|
52
34
|
## omit / pick
|
|
53
35
|
|
|
54
|
-
|
|
55
|
-
obj.
|
|
56
|
-
obj.
|
|
57
|
-
obj.pick<T, K>(item: T, pickKeys: K[]): Pick<T, K>;
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
- `omit`/`pick` — 지정 key 를 제외/선택한 새 객체. `omitByFilter` 는 key 판정 함수로 제외(예: `_` 접두사 내부 필드 제거).
|
|
36
|
+
- `obj.omit(item, omitKeys)`: → `Omit<T,K>` — 지정 key 제외한 새 객체.
|
|
37
|
+
- `obj.omitByFilter(item, omitKeyFn)`: → T — `omitKeyFn(key)` 가 true 인 key 제외(예: `_` 로 시작하는 내부 속성).
|
|
38
|
+
- `obj.pick(item, pickKeys)`: → `Pick<T,K>` — 지정 key 만 남긴 새 객체.
|
|
61
39
|
|
|
62
40
|
## 체인 경로 접근
|
|
63
41
|
|
|
64
|
-
|
|
65
|
-
obj.getChainValue(obj: unknown, chain: string): unknown;
|
|
66
|
-
obj.getChainValue(obj: unknown, chain: string, optional: true): unknown | undefined;
|
|
67
|
-
obj.getChainValueByDepth<T, K>(obj: T, key: K, depth: number, optional?: true): T[K] | undefined;
|
|
68
|
-
obj.setChainValue(obj: unknown, chain: string, value: unknown): void;
|
|
69
|
-
obj.deleteChainValue(obj: unknown, chain: string): void;
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
- `getChainValue(obj, "a.b[0].c")` — 점/대괄호 경로로 중첩 값 조회. 3번째 `optional: true` 면 중간 null/undefined 를 만나도 throw 없이 `undefined`.
|
|
73
|
-
- `getChainValueByDepth(obj, key, depth)` — 같은 key 로 depth 회 하강(예: `parent` 를 2번). `depth < 1` 이면 `ArgumentError`. `optional: true` 로 안전 하강.
|
|
74
|
-
- `setChainValue` — 경로 따라 내려가며 없는 중간 객체는 `{}` 로 생성 후 설정. `deleteChainValue` — 마지막 key 삭제(중간 경로 없으면 조용히 반환). chain 이 비면 `ArgumentError`.
|
|
42
|
+
문자열 경로(`"a.b[0].c"`)로 중첩 값 접근. `?`·`!`·따옴표는 무시됨.
|
|
75
43
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
obj.
|
|
80
|
-
obj.clear<T>(obj: T): Record<string, never>; // 모든 key 삭제 @mutates
|
|
81
|
-
obj.nullToUndefined<T>(obj: T): T | undefined; // null → undefined 재귀 @mutates
|
|
82
|
-
obj.unflatten(flatObj: Record<string, unknown>): Record<string, unknown>; // "a.b.c" key → 중첩
|
|
83
|
-
```
|
|
44
|
+
- `obj.getChainValue(o, chain)` / `getChainValue(o, chain, true)`: → unknown — 경로 값 조회. 세 번째 `true` 면 중간 null/undefined 를 만나도 에러 없이 undefined.
|
|
45
|
+
- `obj.getChainValueByDepth(o, key, depth, optional?)`: → 값 — 같은 key 로 depth 단계 하강(예: `parent` 를 2번). depth<1 이면 ArgumentError. optional 처리는 위와 동일.
|
|
46
|
+
- `obj.setChainValue(o, chain, value)`: → void — 경로에 값 설정(중간 객체 자동 생성). 빈 chain 이면 ArgumentError.
|
|
47
|
+
- `obj.deleteChainValue(o, chain)`: → void — 경로 값 삭제(중간 경로 없으면 조용히 반환).
|
|
84
48
|
|
|
85
|
-
|
|
86
|
-
- `unflatten({ "a.b.c": 1 })` → `{ a: { b: { c: 1 } } }`.
|
|
49
|
+
## 정리 유틸 (@mutates)
|
|
87
50
|
|
|
88
|
-
|
|
51
|
+
- `obj.clearUndefined(o)`: → T — null/undefined 값 key 를 원본에서 삭제.
|
|
52
|
+
- `obj.clear(o)`: → 빈 객체 — 모든 key 삭제.
|
|
53
|
+
- `obj.nullToUndefined(o)`: → `T | undefined` — null 을 undefined 로 재귀 변환(순환 안전). 날짜타입·Uuid 는 통과. simplysm 의 null-free 규칙 적용에 사용.
|
|
54
|
+
- `obj.unflatten(flatObj)`: → 중첩 객체 — `{ "a.b.c": 1 }` → `{ a: { b: { c: 1 } } }`.
|
|
89
55
|
|
|
90
|
-
|
|
91
|
-
obj.keys<T>(obj: T): (keyof T)[];
|
|
92
|
-
obj.entries<T>(obj: T): [keyof T, T[keyof T]][];
|
|
93
|
-
obj.fromEntries<T extends [string, unknown]>(entryPairs: T[]): { [K in T[0]]: T[1] };
|
|
94
|
-
obj.map<S, NK extends string, NV>(obj: S, fn: (key, value) => [NK | null, NV]): Record<...>;
|
|
95
|
-
```
|
|
56
|
+
## 타입 안전 Object.* 와 변환
|
|
96
57
|
|
|
97
|
-
- `keys
|
|
98
|
-
- `obj.
|
|
58
|
+
- `obj.keys(o)`: → `(keyof T)[]` — 타입 안전 `Object.keys`.
|
|
59
|
+
- `obj.entries(o)`: → `Entries<T>` — 타입 안전 `Object.entries`(튜플 타입 보존).
|
|
60
|
+
- `obj.fromEntries(entryPairs)`: → 객체 — 타입 안전 `Object.fromEntries`.
|
|
61
|
+
- `obj.map(o, fn)`: → 새 객체 — 각 엔트리를 `fn(key, value) => [newKey | null, newValue]` 로 변환. newKey 가 null 이면 기존 key 유지(값만 변환). key+값 동시 변환 가능.
|
|
99
62
|
|
|
100
|
-
```
|
|
101
|
-
obj.map(
|
|
63
|
+
```ts
|
|
64
|
+
obj.map(colors, (key, rgb) => [`${key}Light`, `rgb(${rgb})`]);
|
|
102
65
|
```
|
|
103
66
|
|
|
104
|
-
## 타입
|
|
105
|
-
|
|
106
|
-
entry 에서 직접 노출되는 타입(`obj.*` 아님).
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
type Type<TInstance> = { new (...args: unknown[]): TInstance } & Function; // 생성자 타입 (common.types)
|
|
110
|
-
type DeepPartial<TObject>; // 모든 속성 재귀 optional (common.types)
|
|
111
|
-
type UndefToOptional<TObject>; // undefined 포함 속성을 optional 로 (obj.ts)
|
|
112
|
-
type OptionalToUndef<TObject>; // optional 속성을 필수 + undefined 유니온으로 (obj.ts)
|
|
113
|
-
```
|
|
67
|
+
## 함께 export 되는 타입 유틸
|
|
114
68
|
|
|
115
|
-
- `
|
|
116
|
-
- `
|
|
117
|
-
-
|
|
69
|
+
- `UndefToOptional<T>` — `undefined` 를 포함한 속성을 optional(`?`)로 변환.
|
|
70
|
+
- `OptionalToUndef<T>` — optional 속성을 `필수 + undefined` union 으로 변환.
|
|
71
|
+
- 옵션 타입: `EqualOptions`·`MergeOptions`·`Merge3KeyOptions`.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# @simplysm/core-common — 직렬화 / Worker 전송
|
|
2
|
+
|
|
3
|
+
커스텀 타입(DateTime·DateOnly·Time·Uuid·Map·Set·Error·Uint8Array 등)을 포함한 데이터를 JSON·XML·바이트·Worker 메시지로 주고받을 때 함께 읽히는 묶음. JSON·transfer 는 `__type__` 태그 방식으로 표준 직렬화가 잃어버리는 타입을 복원함.
|
|
4
|
+
|
|
5
|
+
## json 네임스페이스
|
|
6
|
+
|
|
7
|
+
- `json.stringify(obj, options?)`: → string — 커스텀 타입 보존 JSON 직렬화. Date/DateTime/DateOnly/Time/Uuid/Set/Map/Error/Uint8Array 를 `{ __type__, data }` 형태로 변환. 전역 프로토타입을 건드리지 않아 Worker 환경에서도 안전. 순환 참조면 TypeError.
|
|
8
|
+
- options.space?: `string | number` — 들여쓰기(숫자=공백 수, 문자열=들여쓰기 문자).
|
|
9
|
+
- options.replacer?: `(key, value) => unknown` — 기본 타입 변환 **전에** 호출되는 커스텀 변환.
|
|
10
|
+
- options.redactBytes?: boolean — true 면 Uint8Array 내용을 `"__hidden__"` 로 대체(로깅용). 이 결과는 `json.parse` 로 복원 불가.
|
|
11
|
+
- `json.parse<T>(json)`: → T — `json.stringify` 결과 역직렬화. `__type__` 태그를 보고 원래 타입 복원. **모든 JSON null 은 undefined 로 변환**됨(null-free 규칙). `redactBytes` 로 가려진 Uint8Array 를 만나면 SdError. 파싱 실패 시 SdError(개발 모드 `env("DEV")` 이면 전체 JSON, 운영이면 길이만 메시지에 포함).
|
|
12
|
+
- 주의: 사용자 데이터에 `{ __type__: "Date"|..., data: ... }` 형태가 우연히 들어 있으면 의도치 않게 타입으로 변환될 수 있음.
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { json } from "@simplysm/core-common";
|
|
16
|
+
const text = json.stringify({ at: new DateTime(), ids: new Set([1, 2]) });
|
|
17
|
+
const back = json.parse<{ at: DateTime; ids: Set<number> }>(text); // 타입 복원
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## transfer 네임스페이스 (Worker 전송)
|
|
21
|
+
|
|
22
|
+
`structuredClone` 이 못 다루는 커스텀 타입을 Worker 로 보내기 위한 인코딩/디코딩. `postMessage` 의 transferList(zero-copy)와 연동.
|
|
23
|
+
|
|
24
|
+
- `transfer.encode(obj)`: → `{ result, transferList }` — 직렬화 가능한 형태로 변환. Uint8Array 의 ArrayBuffer 를 transferList 에 모아 zero-copy 전송 준비(SharedArrayBuffer 는 제외). Date/DateTime/DateOnly/Time/Uuid/RegExp/Error(cause·code·detail 포함)는 `__type__` 태그로 변환, Array/Map/Set/일반 객체는 재귀. 같은 객체 재참조는 캐시 재사용. **순환 참조면 TypeError(경로 포함)**.
|
|
25
|
+
- `transfer.decode(obj)`: → unknown — Worker 수신 데이터를 커스텀 타입으로 복원(encode 의 역).
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { transfer } from "@simplysm/core-common";
|
|
29
|
+
const { result, transferList } = transfer.encode(data);
|
|
30
|
+
worker.postMessage(result, transferList);
|
|
31
|
+
// worker 측: const decoded = transfer.decode(event.data);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
json 과의 차이: transfer 는 날짜를 tick(숫자)로 인코딩하고 RegExp 를 지원하며 Uint8Array 를 변환 없이 transferList 로 넘김(메모리 효율). 문자열 산출물이 필요하면 json, Worker 간 객체 전송이면 transfer.
|
|
35
|
+
|
|
36
|
+
## bytes 네임스페이스 (Uint8Array 인코딩)
|
|
37
|
+
|
|
38
|
+
- `bytes.concat(arrays)`: → Bytes — 여러 Uint8Array 결합.
|
|
39
|
+
- `bytes.toHex(b)`: → string — 소문자 hex 문자열.
|
|
40
|
+
- `bytes.fromHex(hex)`: → Bytes — hex(대소문자 허용)→바이트. 홀수 길이·비 hex 문자면 ArgumentError.
|
|
41
|
+
- `bytes.toBase64(b)`: → string — Base64 인코딩(표준 패딩 `=`).
|
|
42
|
+
- `bytes.fromBase64(b64)`: → Bytes — Base64 디코딩. 공백·패딩 정규화 후 비 base64 문자·잘못된 길이면 ArgumentError.
|
|
43
|
+
|
|
44
|
+
## xml 네임스페이스
|
|
45
|
+
|
|
46
|
+
`fast-xml-parser` 래퍼. 속성은 `$` 객체, 텍스트 노드는 `_` key, 자식 요소는 배열로 표현.
|
|
47
|
+
|
|
48
|
+
- `xml.parse(str, options?)`: → unknown — XML→객체. `options.stripTagPrefix?: boolean` true 면 태그의 네임스페이스 접두사(`ns:tag`→`tag`) 제거(속성 접두사는 유지).
|
|
49
|
+
- `xml.stringify(obj, options?)`: → string — 객체→XML. options 는 `fast-xml-parser` 의 `XmlBuilderOptions`(선택).
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { xml } from "@simplysm/core-common";
|
|
53
|
+
xml.parse('<root id="1"><item>hello</item></root>');
|
|
54
|
+
// { root: { $: { id: "1" }, item: [{ _: "hello" }] } }
|
|
55
|
+
```
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
# @simplysm/core-node
|
|
2
2
|
|
|
3
|
-
Node.js 런타임 전용
|
|
3
|
+
Node.js 런타임 전용 유틸·기능 모음. `@simplysm/core-common` 위에 Node API(`fs`/`path`/`child_process`/`worker_threads`/`chokidar`/`consola`)를 얹은 계층이므로 브라우저에서는 사용 불가.
|
|
4
4
|
|
|
5
5
|
`cpx`/`fsx`/`pathx` 는 `export * as` 네임스페이스로 노출되므로 항상 접두사로 호출한다 (`import { fsx } from "@simplysm/core-node"` → `fsx.read(...)`). 나머지(FsWatcher, consola 셋업, worker)는 named export.
|
|
6
6
|
|
|
7
7
|
## 사용 트리거 인덱스
|
|
8
8
|
|
|
9
|
-
- **fsx** — 파일/디렉토리
|
|
10
|
-
- **pathx** — 경로를 POSIX(슬래시)로 정규화하거나, 하위경로 판정·디렉토리
|
|
11
|
-
- **cpx** — 외부 명령을 실행해 stdout/stderr 를
|
|
9
|
+
- **fsx** — 파일/디렉토리 존재 확인·생성·삭제·복사·읽기/쓰기(텍스트·바이너리·JSON)·stat·glob·부모 탐색을 동기/비동기 쌍으로 다룰 때. 모든 오류를 `SdError(원인, 경로)` 로 감싸 throw. 자세히: [fsx.md](./fsx.md)
|
|
10
|
+
- **pathx** — 경로를 POSIX(슬래시)로 정규화하거나, 하위경로 판정·디렉토리 치환·확장자 제거 basename·타겟 필터링 같은 경로 문자열 가공이 필요할 때. 자세히: [pathx.md](./pathx.md)
|
|
11
|
+
- **cpx** — 외부 명령을 실행해 stdout/stderr 를 OS 인코딩으로 디코딩해 받을 때(`spawn`/`spawnSync`), 또는 OS 코드페이지 인코딩 감지가 필요할 때. exitCode≠0 자동 throw. 자세히: [cpx.md](./cpx.md)
|
|
12
12
|
- **FsWatcher / FsWatcherEvent / FsWatcherChangeInfo** — glob 경로를 chokidar 로 감시하며 짧은 시간 내 이벤트를 병합해 콜백 한 번으로 받을 때(watch 빌드 등). 자세히: [fs-watcher.md](./fs-watcher.md)
|
|
13
|
-
- **setupConsola / PrettyReporter / createFileReporter / withMaxLevel** —
|
|
13
|
+
- **setupConsola / PrettyReporter / createFileReporter / FileReporterOptions / SetupConsolaOptions / withMaxLevel** — Node 진입점(서버·CLI)에서 consola 전역 로거의 콘솔/파일 출력 형식을 환경별로 1회 셋업하거나 reporter 를 커스텀할 때. 자세히: [consola.md](./consola.md)
|
|
14
14
|
- **Worker / createWorker / WorkerProxy / WorkerModule / PromisifyMethods / WorkerRequest / WorkerResponse** — worker_threads 를 타입 안전한 메서드 호출·이벤트·로그 전달 프록시로 쓸 때. 자세히: [worker.md](./worker.md)
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
</content>
|
|
16
|
+
위 6개 군은 사용 시점·컨텍스트가 분리되어 각각 별도 `.md` 로 분할됨. README 인라인 군 없음.
|
|
@@ -1,39 +1,44 @@
|
|
|
1
1
|
# @simplysm/core-node — consola 로깅 설정
|
|
2
2
|
|
|
3
|
-
`consola` 글로벌 로거의 reporter 를 환경별로 구성하는 셋업과 reporter
|
|
3
|
+
`consola` 글로벌 로거의 reporter 를 환경별로 구성하는 셋업과 reporter 구현 (`packages/core-node/src/features/consola/*`). 로그 출력 자체는 `@simplysm/core-common` 의 `createLogger(tag)` 로 하고(직접 `console.*`·`consola.withTag` 금지), 출력 채널·형식만 여기서 셋업한다. 콘솔용 `PrettyReporter`(색·아이콘·tag·날짜·에러 스택·box)와 일자별 rotate 되는 `createFileReporter` 를 조합한다.
|
|
4
|
+
|
|
5
|
+
**Node 진입점(서버·CLI) 에서 1회 `setupConsola()` 호출** 이 일반 사용. Browser·Capacitor 진입점에서는 호출 금지(Node 전용 API) — 그쪽은 consola 기본 reporter 가 브라우저 콘솔로 출력한다.
|
|
4
6
|
|
|
5
7
|
## setupConsola
|
|
6
8
|
|
|
7
|
-
- `setupConsola(opts?: SetupConsolaOptions): void` — 환경 변수(`DEV`, `SD_DEBUG`)에 따라 `consola.level` 과 `reporters` 를 설정. 모든
|
|
8
|
-
- `opts.cli?: boolean` — CLI 모드 여부.
|
|
9
|
+
- `setupConsola(opts?: SetupConsolaOptions): void` — 환경 변수(`DEV`, `SD_DEBUG`)에 따라 `consola.level` 과 `reporters` 를 설정. 모든 분기에서 level 은 `debug` 까지 포함.
|
|
10
|
+
- `opts.cli?: boolean` — CLI 모드 여부. true 면 prod 분기를 건너뛰고 항상 콘솔 출력 경로로 감.
|
|
9
11
|
- 분기:
|
|
10
12
|
- `cli` 아니고 `DEV` 아님(prod) → 파일 reporter 만(`createFileReporter()`). 콘솔 출력 없음, debug 까지 파일 기록.
|
|
11
13
|
- `SD_DEBUG` 참(dev+디버그) → `PrettyReporter` 만, debug 까지 콘솔 출력.
|
|
12
|
-
- 그 외(dev) → 파일 reporter + `info` 까지만 콘솔 출력하는 PrettyReporter(`withMaxLevel(..., LogLevels.info)`). 파일에는 debug 전부, 콘솔에는 info 이하만.
|
|
13
|
-
- `
|
|
14
|
+
- 그 외(dev / cli) → 파일 reporter + `info` 까지만 콘솔 출력하는 PrettyReporter(`withMaxLevel(..., LogLevels.info)`). 파일에는 debug 전부, 콘솔에는 info 이하만.
|
|
15
|
+
- `DEV`/`SD_DEBUG` 는 `parseBoolEnv` 로 해석되는 boolean 환경값(`@simplysm/core-common`).
|
|
14
16
|
|
|
15
17
|
```ts
|
|
16
|
-
|
|
18
|
+
import { setupConsola } from "@simplysm/core-node";
|
|
19
|
+
setupConsola({ cli: true }); // 진입점에서 1회
|
|
17
20
|
```
|
|
18
21
|
|
|
19
22
|
## withMaxLevel
|
|
20
23
|
|
|
21
|
-
- `withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter` — reporter 를 감싸 `logObj.level > maxLevel` 인 로그를 버리는 필터 래퍼. 콘솔에는 정보성만, 파일에는 전부 같은
|
|
24
|
+
- `withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter` — reporter 를 감싸 `logObj.level > maxLevel` 인 로그를 버리는 필터 래퍼. "콘솔에는 정보성만, 파일에는 전부" 같은 분리에 사용. consola `LogLevels` 숫자 기준(작을수록 심각: error 0, warn 1, info 3 등) — maxLevel 보다 큰(=덜 심각한) 로그가 잘림.
|
|
22
25
|
|
|
23
26
|
## PrettyReporter
|
|
24
27
|
|
|
25
|
-
- `class PrettyReporter implements ConsolaReporter` — 색·아이콘·tag·날짜·에러 스택·box 를 직접 포맷하는 콘솔 reporter. level<2(error/warn)
|
|
26
|
-
- `log(logObj, ctx): void` — consola 가 호출하는 reporter 인터페이스. 한 줄(또는 멀티라인) 포맷 후 스트림에 기록. error 의 `cause` 체인을 들여쓰기로 펼치고, 스택에서 cwd/`file://`
|
|
28
|
+
- `class PrettyReporter implements ConsolaReporter` — 색·아이콘·tag·날짜·에러 스택·box 를 직접 포맷하는 콘솔 reporter. level<2(error/warn)는 stderr, 그 외 stdout 으로 출력. 색 지원은 `NO_COLOR`(끔)/`FORCE_COLOR`(켬)/TTY/win32 순으로 자동 감지.
|
|
29
|
+
- `log(logObj, ctx): void` — consola 가 호출하는 reporter 인터페이스. 한 줄(또는 멀티라인) 포맷 후 스트림에 기록. error 의 `cause` 체인을 들여쓰기로 펼치고, 스택에서 cwd/`file://` 접두를 제거.
|
|
27
30
|
- `formatPlain(logObj, formatOptions?): string` — 색·날짜·뱃지 여백 **없이** 평문으로 포맷(trim). 아이콘·tag·객체 inspect·스택 표현은 콘솔과 동일하게 재사용. 파일 reporter 등이 콘솔과 같은 본문을 얻기 위한 진입점.
|
|
28
|
-
- `formatOptions
|
|
31
|
+
- `formatOptions?: Partial<FormatOpts>` — 콘솔과 동일한 `ctx.options.formatOptions`(예: 객체 펼침 `compact`)를 넘기면 출력이 콘솔과 일치.
|
|
29
32
|
|
|
30
33
|
## createFileReporter
|
|
31
34
|
|
|
32
|
-
- `createFileReporter(options?: FileReporterOptions): ConsolaReporter` — `<cwd>/.logs/app.<YYYY-MM-DD>.log` 에 기록하는 reporter 생성. 본문은 `PrettyReporter.formatPlain` 으로 콘솔과 동일하게, 앞에 `타임스탬프 [TYPE]` 접두를 붙임. 날짜 변경 또는 크기 초과 시 rotate,
|
|
35
|
+
- `createFileReporter(options?: FileReporterOptions): ConsolaReporter` — `<cwd>/.logs/app.<YYYY-MM-DD>.log` 에 기록하는 reporter 생성. 본문은 `PrettyReporter.formatPlain` 으로 콘솔과 동일하게, 앞에 `타임스탬프 [TYPE]` 접두를 붙임. 날짜 변경 또는 크기 초과 시 rotate, 일자가 바뀌는 첫 기록 시 오래된 파일 정리.
|
|
33
36
|
- `options.maxSize?: number` — 파일 1개 최대 바이트. 초과 시 `app.<date>.<seq>.log` 로 분할. 기본 20MB(`20 * 1024 * 1024`).
|
|
34
|
-
- `options.maxDays?: number` — 보관 일수. cutoff(오늘
|
|
37
|
+
- `options.maxDays?: number` — 보관 일수. cutoff(오늘 − maxDays) 이전 날짜 파일 삭제. 기본 14.
|
|
35
38
|
|
|
36
39
|
```ts
|
|
40
|
+
import { createFileReporter } from "@simplysm/core-node";
|
|
41
|
+
import consola from "consola";
|
|
37
42
|
consola.options.reporters = [createFileReporter({ maxSize: 5 * 1024 * 1024, maxDays: 7 })];
|
|
38
43
|
```
|
|
39
44
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @simplysm/core-node — cpx
|
|
2
2
|
|
|
3
|
-
`export * as cpx`
|
|
3
|
+
`export * as cpx` 네임스페이스 (`packages/core-node/src/utils/cp.ts`). 자식 프로세스 실행 + 출력 OS 인코딩 디코딩. `cpx.spawn(...)` 형태로 호출. 출력은 OS 코드페이지(Windows `chcp`, POSIX `LANG`/`LC_ALL`)를 감지해 디코딩하므로 한글 등 비-UTF8 콘솔 출력도 깨지지 않음.
|
|
4
4
|
|
|
5
5
|
## spawn / spawnSync
|
|
6
6
|
|
|
@@ -9,29 +9,30 @@
|
|
|
9
9
|
- `cmd: string` — 실행 명령.
|
|
10
10
|
- `args: string[]` — 인자 배열.
|
|
11
11
|
- `options` — Node `SpawnOptions`(spawn) / `SpawnSyncOptions`(spawnSync) + `reject?: boolean`.
|
|
12
|
-
- `env` — 전달 env.
|
|
12
|
+
- `env` — 전달 env. 항상 `process.env` 와 병합됨(전달분이 덮어씀).
|
|
13
13
|
- `stdio` — 기본 `"pipe"`. pipe 인 스트림만 캡처되어 결과 문자열에 담김. `"inherit"` 등이면 해당 스트림은 빈 문자열.
|
|
14
|
-
- `reject?: boolean` — exitCode 가 0 이 아닐 때 동작. 기본(미지정/true): 실패 메시지로 reject(spawn) 또는 throw(spawnSync). `false`: 0 아니어도 정상 반환(직접 exitCode 검사하려는 경우).
|
|
14
|
+
- `reject?: boolean` — exitCode 가 0 이 아닐 때 동작. 기본(미지정/true): 실패 메시지로 reject(spawn) 또는 throw(spawnSync). `false`: 0 이 아니어도 정상 반환(직접 exitCode 를 검사하려는 경우).
|
|
15
|
+
|
|
16
|
+
실패 메시지 형식: `Command failed (exit <code>): <cmd> <args>` 뒤에 stderr(없으면 stdout) 마지막 4000자.
|
|
15
17
|
|
|
16
18
|
```ts
|
|
19
|
+
import { cpx } from "@simplysm/core-node";
|
|
17
20
|
const { stdout, exitCode } = await cpx.spawn("git", ["status", "--short"]);
|
|
18
21
|
const r = cpx.spawnSync("node", ["-v"], { reject: false });
|
|
19
22
|
if (r.exitCode !== 0) { /* 직접 처리 */ }
|
|
20
23
|
```
|
|
21
24
|
|
|
22
|
-
실패 메시지 형식: `Command failed (exit <code>): <cmd> <args>` 뒤에 stderr(없으면 stdout) 마지막 4000자.
|
|
23
|
-
|
|
24
25
|
## SpawnResult
|
|
25
26
|
|
|
26
|
-
- `{ stdout: string; stderr: string; exitCode: number }` — 실행 결과.
|
|
27
|
-
- `stdout
|
|
28
|
-
- `exitCode` — 종료 코드.
|
|
27
|
+
- `interface SpawnResult { stdout: string; stderr: string; exitCode: number }` — 실행 결과.
|
|
28
|
+
- `stdout` / `stderr` — pipe 로 캡처된 출력(OS 인코딩 디코딩 적용). 비-pipe 스트림이면 `""`.
|
|
29
|
+
- `exitCode` — 종료 코드. 코드 없이 시그널로 종료됐으면 1, 정상이면 0.
|
|
29
30
|
|
|
30
31
|
## SpawnProcess
|
|
31
32
|
|
|
32
33
|
`spawn` 반환 타입. Promise 처럼 쓰면서 프로세스 제어를 함께 제공.
|
|
33
34
|
|
|
34
|
-
- `pid: number | undefined` — 자식 프로세스 PID.
|
|
35
|
+
- `pid: number | undefined` — 자식 프로세스 PID(생성 전이면 undefined).
|
|
35
36
|
- `then(...)` / `catch(...)` — `SpawnResult` 로 resolve 되는 thenable(그래서 `await` 가능).
|
|
36
37
|
- `kill(signal?: NodeJS.Signals | number): boolean` — 프로세스에 시그널 전송. 타임아웃·취소 시 사용.
|
|
37
38
|
|
|
@@ -43,8 +44,8 @@ const result = await proc;
|
|
|
43
44
|
|
|
44
45
|
## 인코딩 유틸
|
|
45
46
|
|
|
46
|
-
- `getSystemEncoding(): string` — OS 콘솔 인코딩 감지(캐시됨). Windows 는 `chcp` 코드페이지, POSIX 는 `LANG`/`LC_ALL` 의 `.` 뒤
|
|
47
|
-
- `resetEncodingCache(): void` — `getSystemEncoding` 캐시 초기화. 런타임 중 코드페이지가 바뀐 경우
|
|
48
|
-
- `codePageToEncoding(codePage: number): string` — Windows 코드페이지 숫자 → 인코딩명.
|
|
47
|
+
- `getSystemEncoding(): string` — OS 콘솔 인코딩 감지(결과 캐시됨). Windows 는 `chcp` 코드페이지, POSIX 는 `LANG`/`LC_ALL` 의 `.` 뒤 인코딩(`utf8`→`utf-8` 정규화). 감지 실패 시 `"utf-8"` fallback. spawn 출력 디코딩에 내부적으로 사용.
|
|
48
|
+
- `resetEncodingCache(): void` — `getSystemEncoding` 캐시 초기화. 런타임 중 코드페이지가 바뀐 경우 재감지를 강제.
|
|
49
|
+
- `codePageToEncoding(codePage: number): string` — Windows 코드페이지 숫자 → 인코딩명. 매핑: 65001→utf-8, 949→euc-kr, 932→shift-jis, 936→gbk, 950→big5, 1252→windows-1252, 1251→windows-1251, 1250→windows-1250, 874→windows-874. 미등록 코드페이지는 `"utf-8"`.
|
|
49
50
|
- `resolveStdioPipe(stdio): { stdout: boolean; stderr: boolean }` — stdio 옵션에서 stdout/stderr 가 pipe 인지 판정. 배열이면 index 1/2 가 `"pipe"` 인지, 단일값이면 `"pipe"` 또는 미지정(null)일 때 둘 다 pipe. 캡처 여부 사전 판단용.
|
|
50
|
-
- `decodeBytes(raw: Uint8Array, systemEncoding?: string): string` — 바이트열을 인코딩으로 디코딩. `systemEncoding` 미지정 시 `getSystemEncoding()` 사용.
|
|
51
|
+
- `decodeBytes(raw: Uint8Array, systemEncoding?: string): string` — 바이트열을 인코딩으로 디코딩. `systemEncoding` 미지정 시 `getSystemEncoding()` 사용. 인코딩이 utf-8 이 아니어도 먼저 UTF-8(fatal) 디코딩을 시도해 성공하면 UTF-8 로, 실패 시 지정 인코딩으로 디코딩(UTF-8/레거시 혼재 출력 대응).
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
# @simplysm/core-node — FsWatcher
|
|
2
2
|
|
|
3
|
-
chokidar 기반 파일 감시
|
|
3
|
+
chokidar 기반 파일 감시 래퍼 (`packages/core-node/src/features/fs-watcher.ts`). watch 빌드처럼 "여러 변경을 모아 한 번에 처리"해야 할 때 사용. 짧은 시간 내 이벤트를 디바운스+병합해 콜백을 한 번만 호출하고, Windows 의 EPERM(감시 디렉토리 소실) 발생 시 watcher 를 자동 재시작한다. 모듈 로드 시 native FSWatcher prototype 의 orphan `error` emit 를 swallow 하는 가드를 1회 설치(close 중 race 로 인한 프로세스 종료 방지).
|
|
4
4
|
|
|
5
5
|
## FsWatcher.watch (정적 진입점)
|
|
6
6
|
|
|
7
7
|
- `static watch(paths: string[], options?: chokidar.ChokidarOptions): Promise<FsWatcher>` — 감시 시작. ready 될 때까지 대기 후 인스턴스 반환. ready 전 에러 시 watcher 를 close 하고 throw.
|
|
8
|
-
- `paths: string[]` — 감시할 경로/glob 패턴 배열. 내부적으로 각 패턴의 glob 메타문자 이전 base 디렉토리를 추출해 chokidar 에 등록하고, 콜백 단계에서 원본 패턴으로 minimatch
|
|
9
|
-
- `options?: ChokidarOptions` — chokidar 옵션. `persistent` 기본 true. `ignoreInitial` 은 내부적으로 항상 true 로 강제(초기 스캔 이벤트 무시).
|
|
8
|
+
- `paths: string[]` — 감시할 경로/glob 패턴 배열. 내부적으로 각 패턴의 glob 메타문자 이전 base 디렉토리를 추출해 chokidar 에 등록하고, 콜백 단계에서 원본 패턴으로 minimatch 재필터(패턴 자체 + `패턴/**` 양쪽 매칭).
|
|
9
|
+
- `options?: ChokidarOptions` — chokidar 옵션. `persistent` 기본 true. `ignoreInitial` 은 chokidar 에는 내부적으로 항상 true 로 강제(초기 스캔 이벤트 무시).
|
|
10
10
|
- `options.ignoreInitial: false` 지정 시 → `onChange` 의 첫 콜백이 **빈 배열 `[]`** 로 1회 호출됨(실제 초기 파일 목록은 담기지 않음 — 이벤트 병합과의 충돌 방지). "초기 1회 전체 빌드 후 변경 감시" 패턴 트리거용.
|
|
11
11
|
|
|
12
12
|
```ts
|
|
13
|
+
import { FsWatcher } from "@simplysm/core-node";
|
|
13
14
|
const watcher = await FsWatcher.watch(["src/**/*.ts"]);
|
|
14
15
|
```
|
|
15
16
|
|
|
16
17
|
## onChange
|
|
17
18
|
|
|
18
19
|
- `onChange(opt, cb): this` — 디바운스된 변경 콜백 등록. 체이닝 가능(this 반환). 여러 번 호출해 핸들러 다중 등록 가능.
|
|
19
|
-
- `opt.delay?: number` — 디바운스 지연(ms). 이 시간 내 들어온 이벤트를 모아 한 번 호출(`DebounceQueue` 사용).
|
|
20
|
+
- `opt.delay?: number` — 디바운스 지연(ms). 이 시간 내 들어온 이벤트를 모아 한 번 호출(`DebounceQueue` 사용). 생략 시 DebounceQueue 기본값.
|
|
20
21
|
- `cb: (changeInfos: FsWatcherChangeInfo[]) => void | Promise<void>` — 병합된 변경 목록 콜백. async 가능.
|
|
21
|
-
- 이벤트 병합: 같은 파일에 대해 `add+change→add`, `add+unlink
|
|
22
|
+
- 이벤트 병합: 같은 파일에 대해 `add+change→add`, `add+unlink→상쇄(콜백 제외)`, `addDir+unlinkDir→상쇄`, `unlink+add→add`, `unlink+change→change`, `unlinkDir+addDir→addDir`. 그 외 조합은 최신 이벤트로 덮어씀. → 생성 직후 삭제된 임시파일 등은 콜백에 안 나타남.
|
|
22
23
|
|
|
23
24
|
```ts
|
|
24
25
|
watcher.onChange({ delay: 300 }, (changes) => {
|
|
@@ -28,14 +29,14 @@ watcher.onChange({ delay: 300 }, (changes) => {
|
|
|
28
29
|
|
|
29
30
|
## close
|
|
30
31
|
|
|
31
|
-
- `close(): Promise<void>` — 디바운스
|
|
32
|
+
- `close(): Promise<void>` — 디바운스 큐를 dispose 한 뒤 chokidar watcher 종료. 감시 해제 시 반드시 호출.
|
|
32
33
|
|
|
33
34
|
## FsWatcherChangeInfo / FsWatcherEvent
|
|
34
35
|
|
|
35
|
-
- `interface FsWatcherChangeInfo { event: FsWatcherEvent; path: PosixPath }` — 변경 1건. `path` 는 슬래시 정규화된 PosixPath.
|
|
36
|
+
- `interface FsWatcherChangeInfo { event: FsWatcherEvent; path: PosixPath }` — 변경 1건. `path` 는 슬래시 정규화된 `PosixPath`(pathx 의 브랜드 타입).
|
|
36
37
|
- `type FsWatcherEvent = "add" | "addDir" | "change" | "unlink" | "unlinkDir"` — 변경 종류.
|
|
37
38
|
- `add` — 파일 생성. `addDir` — 디렉토리 생성. `change` — 파일 내용 변경. `unlink` — 파일 삭제. `unlinkDir` — 디렉토리 삭제.
|
|
38
39
|
|
|
39
40
|
## EPERM 자동 복구 동작
|
|
40
41
|
|
|
41
|
-
EPERM 감지 시 최대 3회(1000ms 간격) watcher 재생성을 시도하며
|
|
42
|
+
EPERM 감지 시 최대 3회(1000ms 간격) watcher 재생성을 시도하며 등록된 핸들러를 다시 부착한다. 성공 시 `success` 로그, 한도 초과 시 `error` 로그 후 중단. 로거 태그는 `sd-fs-watcher`(consola). 재시도는 자동 복구이므로 호출측에서 별도 처리 불필요.
|
|
@@ -4,43 +4,45 @@
|
|
|
4
4
|
|
|
5
5
|
## 존재 확인
|
|
6
6
|
|
|
7
|
-
- `existsSync(targetPath: string): boolean`
|
|
7
|
+
- `existsSync(targetPath: string): boolean` — 파일/디렉토리 존재 여부(동기). 내부적으로 `fs.existsSync`.
|
|
8
|
+
- `exists(targetPath: string): Promise<boolean>` — 동일(비동기). `fs.access` 실패를 catch 해 false 반환(throw 안 함).
|
|
8
9
|
|
|
9
10
|
## 디렉토리 생성
|
|
10
11
|
|
|
11
|
-
- `mkdirSync(targetPath: string): void` / `mkdir(targetPath: string): Promise<void>` — 재귀 생성(`recursive: true`). 중간 경로가 없어도 전부 만든다.
|
|
12
|
+
- `mkdirSync(targetPath: string): void` / `mkdir(targetPath: string): Promise<void>` — 재귀 생성(`recursive: true`). 중간 경로가 없어도 전부 만든다. 실패 시 `SdError`.
|
|
12
13
|
|
|
13
14
|
## 삭제
|
|
14
15
|
|
|
15
16
|
- `rmSync(targetPath: string): void` — 재귀+force 삭제. **재시도 없이 즉시 실패**. 파일 잠금 등 일시 오류가 예상되면 비동기 `rm` 을 쓸 것.
|
|
16
|
-
- `rm(targetPath: string): Promise<void>` — 재귀+force 삭제. 일시 오류에 대해 **500ms 간격 최대 6회 재시도**(`retryDelay: 500, maxRetries: 6`).
|
|
17
|
+
- `rm(targetPath: string): Promise<void>` — 재귀+force 삭제. 일시 오류에 대해 **500ms 간격 최대 6회 재시도**(`retryDelay: 500, maxRetries: 6`). Windows 파일 잠금 회피용.
|
|
17
18
|
|
|
18
19
|
## 복사
|
|
19
20
|
|
|
20
21
|
- `copySync(sourcePath, targetPath, filter?): void` / `copy(sourcePath, targetPath, filter?): Promise<void>` — 파일/디렉토리 복사. async 는 하위 항목을 `parallelAsync` 로 병렬 복사.
|
|
21
22
|
- `sourcePath: string` — 원본. **존재하지 않으면 아무 것도 안 하고 반환**(throw 안 함).
|
|
22
|
-
- `targetPath: string` — 대상. 디렉토리면 대상 디렉토리를 만들고 하위를 재귀
|
|
23
|
+
- `targetPath: string` — 대상. 원본이 디렉토리면 대상 디렉토리를 만들고 하위를 재귀 복사(glob `*`, `dot: true` 로 숨김 포함), 파일이면 상위 디렉토리 생성 후 복사.
|
|
23
24
|
- `filter?: (absolutePath: string) => boolean` — 복사 여부 결정. 각 하위 항목의 **절대 경로**가 전달되며 true=복사, false=제외. **최상위 sourcePath 자신은 필터 대상 아님**. 디렉토리에 false 면 그 디렉토리와 모든 내용을 건너뜀. 빌드 산출물 중 특정 파일만 복사할 때 사용.
|
|
24
|
-
- 파일 복사 실패 시 500ms busy-wait
|
|
25
|
+
- 파일 복사 실패 시 500ms 대기(sync 는 busy-wait, async 는 setTimeout)로 최대 7회 시도 후에도 실패하면 `SdError`.
|
|
25
26
|
|
|
26
27
|
## 읽기
|
|
27
28
|
|
|
28
29
|
- `readSync(targetPath: string): string` / `read(...): Promise<string>` — UTF-8 문자열로 읽음.
|
|
29
|
-
- `readBytesSync(targetPath: string): Uint8Array` / `readBytes(...): Promise<Uint8Array>` —
|
|
30
|
-
- `readJsonSync<TData = unknown>(targetPath): TData` / `readJson<TData = unknown>(...): Promise<TData>` — 읽어 `json.parse`(`@simplysm/core-common`, Date 등 특수타입 복원)
|
|
30
|
+
- `readBytesSync(targetPath: string): Uint8Array` / `readBytes(...): Promise<Uint8Array>` — 바이너리(`Uint8Array`)로 읽음.
|
|
31
|
+
- `readJsonSync<TData = unknown>(targetPath): TData` / `readJson<TData = unknown>(...): Promise<TData>` — 읽어 `json.parse`(`@simplysm/core-common`, Date 등 특수타입 복원)로 파싱. 파싱 실패 시 SdError 메시지에 내용 앞 500자 프리뷰를 첨부.
|
|
32
|
+
- 제네릭 `TData` — 파싱 결과 타입. 호출부에서 기대 타입을 지정해 반환 타입을 좁힘.
|
|
31
33
|
|
|
32
34
|
## 쓰기
|
|
33
35
|
|
|
34
|
-
- `writeSync(targetPath, data): void` / `write(targetPath, data): Promise<void>` — **상위 디렉토리 자동 생성** 후 기록(`flush: true`).
|
|
36
|
+
- `writeSync(targetPath, data): void` / `write(targetPath, data): Promise<void>` — **상위 디렉토리 자동 생성** 후 기록(`flush: true` 로 디스크 플러시까지 보장).
|
|
35
37
|
- `data: string | Uint8Array` — 텍스트 또는 바이너리.
|
|
36
38
|
- `writeJsonSync(targetPath, data, options?): void` / `writeJson(targetPath, data, options?): Promise<void>` — `json.stringify` 로 직렬화 후 write.
|
|
37
39
|
- `data: unknown` — 직렬화 대상.
|
|
38
40
|
- `options?.replacer?: (this, key, value) => unknown` — JSON.stringify 의 replacer 와 동일 역할. 특정 키 값을 변환·제외할 때.
|
|
39
|
-
- `options?.space?: string | number` — 들여쓰기. 사람이 읽을 파일이면 `2` 같은 값
|
|
41
|
+
- `options?.space?: string | number` — 들여쓰기. 사람이 읽을 파일이면 `2` 같은 값 지정, 생략 시 압축.
|
|
40
42
|
|
|
41
43
|
## 디렉토리 읽기
|
|
42
44
|
|
|
43
|
-
- `readdirSync(targetPath: string): string[]` / `readdir(...): Promise<string[]>` — 디렉토리 직계 항목명(경로
|
|
45
|
+
- `readdirSync(targetPath: string): string[]` / `readdir(...): Promise<string[]>` — 디렉토리 직계 항목명(전체 경로 아닌 이름만) 배열.
|
|
44
46
|
|
|
45
47
|
## 파일 정보
|
|
46
48
|
|
|
@@ -51,12 +53,12 @@
|
|
|
51
53
|
|
|
52
54
|
- `globSync(pattern: string, options?: GlobOptions): string[]` / `glob(...): Promise<string[]>` — glob 검색. 입력 패턴의 `\` 를 `/` 로 치환해 매칭하고, 결과는 모두 `path.resolve` 로 **절대 경로**화해 반환.
|
|
53
55
|
- `pattern: string` — glob 패턴(예: `"src/**/*.ts"`).
|
|
54
|
-
- `options?: GlobOptions` — `glob` 패키지 옵션 그대로(`dot`, `cwd`, `ignore` 등). 숨김 파일 포함하려면 `{ dot: true }`.
|
|
56
|
+
- `options?: GlobOptions` — `glob` 패키지 옵션 그대로(`dot`, `cwd`, `ignore` 등). 숨김 파일 포함하려면 `{ dot: true }`. 생략 시 빈 객체.
|
|
55
57
|
|
|
56
58
|
## 유틸리티
|
|
57
59
|
|
|
58
60
|
- `clearEmptyDirectory(dirPath: string): Promise<void>` — `dirPath` 하위를 재귀 순회하며 **빈 디렉토리만** 삭제. 하위가 모두 비어 상위도 비게 되면 상위도 삭제. 파일이 하나라도 있으면 그 디렉토리는 보존. 존재하지 않으면 즉시 반환.
|
|
59
|
-
- `findAllParentChildPathsSync(childGlob, fromPath, rootPath?): string[]` / `findAllParentChildPaths(...): Promise<string[]>` — `fromPath` 에서 루트 방향으로 부모 디렉토리를 거슬러 올라가며 각 디렉토리에서 `childGlob` 매칭 파일을 모아 평탄화 반환. 상위
|
|
61
|
+
- `findAllParentChildPathsSync(childGlob, fromPath, rootPath?): string[]` / `findAllParentChildPaths(...): Promise<string[]>` — `fromPath` 에서 루트 방향으로 부모 디렉토리를 거슬러 올라가며 각 디렉토리에서 `childGlob` 매칭 파일을 모아 평탄화 반환. 상위 디렉토리들의 설정 파일(예: 각 단계 `package.json`) 수집에 사용.
|
|
60
62
|
- `childGlob: string` — 각 디렉토리에서 검색할 glob.
|
|
61
63
|
- `fromPath: string` — 탐색 시작 경로.
|
|
62
64
|
- `rootPath?: string` — 탐색 중단 경로. 생략 시 파일시스템 루트까지. **주의: fromPath 는 rootPath 의 하위여야 함**, 아니면 루트까지 올라간다.
|
|
@@ -69,4 +71,3 @@ await fsx.writeJson("dist/meta.json", { builtAt: new Date() }, { space: 2 });
|
|
|
69
71
|
await fsx.copy("src", "dist", (p) => !p.endsWith(".ts")); // .ts 제외 복사
|
|
70
72
|
const pkgs = await fsx.findAllParentChildPaths("package.json", process.cwd());
|
|
71
73
|
```
|
|
72
|
-
</content>
|
|
@@ -4,30 +4,27 @@
|
|
|
4
4
|
|
|
5
5
|
## PosixPath (브랜드 타입)
|
|
6
6
|
|
|
7
|
-
- `type PosixPath = string & { [POSIX]: never }` — POSIX 슬래시 경로임을 나타내는 브랜드 타입. `posix()`/`posixResolve()` 로만 생성
|
|
7
|
+
- `type PosixPath = string & { [POSIX]: never }` — POSIX 슬래시 경로임을 나타내는 브랜드 타입. `posix()`/`posixResolve()` 로만 생성 가능(심볼 키라 외부에서 캐스팅 없이는 못 만듦). 일반 `string` 을 PosixPath 자리에 직접 넣을 수 없어, 정규화를 강제하는 타입 가드 역할.
|
|
8
8
|
|
|
9
9
|
## 정규화
|
|
10
10
|
|
|
11
|
-
- `posix(p: string): PosixPath` — 백슬래시 → 슬래시 치환만 수행. **결합·resolve 안 함**. 예: `posix("C:\\Users\\test")` → `"C:/Users/test"`. 이미
|
|
11
|
+
- `posix(p: string): PosixPath` — 백슬래시 → 슬래시 치환만 수행. **결합·resolve 안 함**. 예: `posix("C:\\Users\\test")` → `"C:/Users/test"`. 이미 절대/상대 경로인 문자열을 POSIX 표기로만 바꿀 때.
|
|
12
12
|
- `posixResolve(...args: string[]): PosixPath` — 인자들을 `path.resolve` 로 절대 경로 결합 후 슬래시로 변환. 예: `posixResolve("/base", "sub", "f.txt")` → `"/base/sub/f.txt"`, 상대경로 단독이면 cwd 기준 절대화. 경로 결합+정규화를 동시에 할 때.
|
|
13
13
|
|
|
14
14
|
## 경로 가공
|
|
15
15
|
|
|
16
16
|
- `changeFileDirectory(filePath, fromDirectory, toDirectory): string` — `filePath` 의 디렉토리 prefix 를 `fromDirectory` → `toDirectory` 로 치환(상대 위치 유지). 예: `("/a/b/c.txt", "/a", "/x")` → `"/x/b/c.txt"`. `filePath === fromDirectory` 면 `toDirectory` 반환. **filePath 가 fromDirectory 내부가 아니면 `ArgumentError` throw**. src→dist 같은 출력 경로 산출에 사용.
|
|
17
|
+
- `basenameWithoutExt(filePath: string): string` — 확장자 1단계만 제거한 basename. 예: `"file.txt"` → `"file"`, `"a/file.spec.ts"` → `"file.spec"`(마지막 확장자만 제거).
|
|
17
18
|
|
|
18
19
|
## 판정·필터링
|
|
19
20
|
|
|
20
|
-
- `isChildPath(childPath, parentPath): boolean` — `childPath` 가 `parentPath` 의 하위인지. 양쪽을 `posixResolve` 로 정규화 후 비교. **동일 경로면 false**(자기 자신은 하위 아님). 경계 오탐 방지를 위해 parent 끝에 `/` 를 붙여 비교.
|
|
21
|
+
- `isChildPath(childPath, parentPath): boolean` — `childPath` 가 `parentPath` 의 하위인지. 양쪽을 `posixResolve` 로 정규화 후 비교. **동일 경로면 false**(자기 자신은 하위 아님). 경계 오탐 방지를 위해 parent 끝에 `/` 를 붙여 startsWith 비교.
|
|
21
22
|
- `filterByTargets(files, targets, cwd): string[]` — 파일 목록을 타겟 경로 하위만 남김.
|
|
22
23
|
- `files: string[]` — 필터링할 파일. **cwd 하위 절대 경로여야 함**(외부 경로는 `../` 상대경로로 변환되어 매칭 실패 가능).
|
|
23
24
|
- `targets: string[]` — 대상 경로(cwd 기준 상대, POSIX 권장). 각 파일의 cwd-상대경로가 target 과 같거나 `target + "/"` 로 시작하면 통과.
|
|
24
25
|
- `cwd: string` — 기준 작업 디렉토리(절대 경로).
|
|
25
26
|
- **`targets` 가 비면 `files` 를 그대로 반환**(필터 미적용). CLI 의 `-t <패키지>` 같은 부분 빌드 대상 한정에 사용.
|
|
26
27
|
|
|
27
|
-
## 파일명
|
|
28
|
-
|
|
29
|
-
- `basenameWithoutExt(filePath: string): string` — 확장자 1단계만 제거한 basename. 예: `"file.txt"` → `"file"`, `"a/file.spec.ts"` → `"file.spec"`(마지막 확장자만 제거).
|
|
30
|
-
|
|
31
28
|
## 사용 예
|
|
32
29
|
|
|
33
30
|
```ts
|
|
@@ -36,4 +33,3 @@ const out = pathx.changeFileDirectory(srcFile, "/proj/src", "/proj/dist");
|
|
|
36
33
|
if (pathx.isChildPath(out, "/proj")) { /* ... */ }
|
|
37
34
|
const targeted = pathx.filterByTargets(allFiles, ["src", "tests"], process.cwd());
|
|
38
35
|
```
|
|
39
|
-
</content>
|