@simplysm/sd-claude 14.0.88 → 14.0.89
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 +17 -17
- package/claude/references/sd-simplysm14/apis/angular/README.md +27 -53
- package/claude/references/sd-simplysm14/apis/angular/controls.md +37 -105
- package/claude/references/sd-simplysm14/apis/angular/crud.md +46 -43
- package/claude/references/sd-simplysm14/apis/angular/directives.md +22 -32
- package/claude/references/sd-simplysm14/apis/angular/features.md +40 -55
- package/claude/references/sd-simplysm14/apis/angular/infra.md +40 -40
- package/claude/references/sd-simplysm14/apis/angular/layout.md +25 -53
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +70 -82
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +44 -39
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +21 -36
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +52 -65
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +65 -70
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +33 -35
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +7 -7
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +29 -29
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +45 -50
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +42 -55
- package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +62 -0
- package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +13 -12
- package/claude/references/sd-simplysm14/apis/core-common/README.md +222 -98
- package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +102 -53
- package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +128 -0
- package/claude/references/sd-simplysm14/apis/core-common/datetime.md +98 -64
- package/claude/references/sd-simplysm14/apis/core-common/errors.md +91 -0
- package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +34 -28
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +104 -40
- package/claude/references/sd-simplysm14/apis/core-node/README.md +11 -8
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +23 -31
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +33 -22
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +28 -25
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +39 -53
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +26 -29
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +27 -29
- package/claude/references/sd-simplysm14/apis/excel/README.md +14 -14
- package/claude/references/sd-simplysm14/apis/lint/README.md +27 -21
- package/claude/references/sd-simplysm14/apis/lint/rules.md +89 -49
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +5 -59
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +98 -67
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +107 -92
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +99 -65
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +83 -98
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +62 -52
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +62 -25
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +27 -27
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +12 -15
- package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +92 -45
- package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +226 -108
- package/claude/references/sd-simplysm14/apis/service-client/README.md +84 -86
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +14 -11
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +33 -10
- package/claude/references/sd-simplysm14/apis/service-common/README.md +37 -23
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +9 -9
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +13 -13
- package/claude/references/sd-simplysm14/apis/service-server/README.md +81 -65
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +32 -35
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +44 -33
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +34 -45
- package/claude/references/sd-simplysm14/apis/storage/README.md +24 -18
- package/claude/skills/sd-demo/SKILL.md +6 -0
- package/claude/skills/sd-impl/SKILL.md +4 -7
- package/claude/skills/sd-spec/SKILL.md +31 -858
- package/claude/skills/sd-spec/references/spec-authoring.md +519 -0
- package/claude/workflows/sd-docs.js +84 -0
- package/package.json +1 -1
- package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +0 -29
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
- package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
- package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
- package/claude/skills/sd-docs/SKILL.md +0 -46
- package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
- package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
- package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
- package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
- package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
- package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
- package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
- package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
- package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
- package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
- package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
- package/claude/skills/sd-review/evals/golden.jsonl +0 -2
- package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
- package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
- package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
- package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
- package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
- package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
- package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
- package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
- package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
- package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
- package/claude/skills/sd-use/evals/golden.jsonl +0 -6
- /package/claude/{skills/sd-docs/references/doc-rules.md → workflows/sd-docs.rules.md} +0 -0
|
@@ -1,47 +1,53 @@
|
|
|
1
|
-
# @simplysm/core-common — 직렬화 (json /
|
|
1
|
+
# @simplysm/core-common — JSON / Worker 직렬화 (json / transfer)
|
|
2
2
|
|
|
3
|
-
커스텀 타입을 보존하며 직렬화/역직렬화할 때 함께 읽히는 묶음. `json`(문자열 ↔ 객체)
|
|
3
|
+
커스텀 타입을 보존하며 직렬화/역직렬화할 때 함께 읽히는 묶음. `json`(문자열 ↔ 객체)과 `transfer`(Web Worker 메시지 ↔ 객체). 두 모듈 모두 `Date`/`DateTime`/`DateOnly`/`Time`/`Uuid`/`Set`/`Map`/`Error`/`Uint8Array` 를 `{ __type__, data }` 태그 객체로 변환해 왕복 보존한다. (XML 직렬화 `xml` 네임스페이스는 README 의 "xml" 인라인 섹션 참조.)
|
|
4
4
|
|
|
5
|
-
## json
|
|
5
|
+
## json
|
|
6
6
|
|
|
7
|
-
`import { json } from "@simplysm/core-common"`.
|
|
8
|
-
|
|
9
|
-
- `json.stringify(obj, options?): string` — 커스텀 타입(Date/DateTime/DateOnly/Time/Uuid/Set/Map/Error/Uint8Array)을 `{ __type__, data }` 로 변환 후 직렬화. `options`:
|
|
10
|
-
- `space?: string | number` — 들여쓰기(숫자=공백 수, 문자열=들여쓰기 문자열).
|
|
11
|
-
- `replacer?: (key, value) => unknown` — 기본 타입 변환 **전에** 호출되는 커스텀 변환기.
|
|
12
|
-
- `redactBytes?: boolean` — true 면 `Uint8Array` 내용을 `"__hidden__"` 로 대체(로깅용). 이 결과는 `json.parse` 로 복원 불가.
|
|
13
|
-
- 순환 참조는 `TypeError`, `toJSON` 메서드가 있으면 호출(위 커스텀 타입은 예외), `undefined` 속성은 제외.
|
|
14
|
-
- `json.parse<T>(json): T` — `__type__`/`data` 태그를 보고 커스텀 타입 복원. 모든 JSON `null` 을 `undefined` 로 변환(simplysm null-free 규칙). 사용자 데이터에 우연히 `{ __type__, data }` 형태가 있으면 오변환 위험. `redactBytes` 로 가려진 바이트를 만나면 `SdError`. 파싱 실패 시 `SdError`(환경변수 `DEV` 가 truthy 면 전체 JSON, 아니면 길이만 메시지에 포함).
|
|
7
|
+
`import { json } from "@simplysm/core-common"`.
|
|
15
8
|
|
|
16
9
|
```typescript
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
json.stringify(obj: unknown, options?: {
|
|
11
|
+
space?: string | number;
|
|
12
|
+
replacer?: (key: string | undefined, value: unknown) => unknown;
|
|
13
|
+
redactBytes?: boolean;
|
|
14
|
+
}): string;
|
|
15
|
+
json.parse<TResult = unknown>(json: string): TResult;
|
|
19
16
|
```
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`
|
|
18
|
+
- `stringify(obj, options)` — 커스텀 타입을 태그 객체로 변환 후 `JSON.stringify`. 전역 프로토타입을 건드리지 않아 Worker 환경에서 안전.
|
|
19
|
+
- `space` — 들여쓰기(숫자=공백 수, 문자열=들여쓰기 문자).
|
|
20
|
+
- `replacer(key, value)` — 기본 타입 변환 **전에** 호출되는 커스텀 변환 훅.
|
|
21
|
+
- `redactBytes: true` — `Uint8Array` 내용을 `"__hidden__"` 으로 대체(로깅용). 이 결과는 `parse` 로 복원 불가(복원 시도 시 `SdError`).
|
|
22
|
+
- 순환 참조가 있으면 `TypeError`. `toJSON` 메서드가 있는 객체는 그 결과를 사용(Date/DateTime 등 커스텀 타입은 사전 변환 처리됨).
|
|
23
|
+
- `parse(json)` — 태그 객체를 원래 타입으로 복원. 복원 후 **모든 JSON null 을 undefined 로 변환**(simplysm null-free 규칙, `obj.nullToUndefined` 사용). 파싱 실패 시 `SdError`로 감싸 throw — `env("DEV")` 가 truthy 면 전체 JSON 문자열을, 아니면 길이만 메시지에 포함.
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
- `xml.stringify(obj, options?): string` — 객체 → XML. `options` 는 fast-xml-parser 의 `XmlBuilderOptions`(기본값을 덮어쓸 때만 사용).
|
|
25
|
+
주의: 사용자 데이터에 우연히 `{ __type__: "Date"|..., data: ... }` 형태가 있으면 의도치 않게 타입으로 복원될 수 있음.
|
|
27
26
|
|
|
28
27
|
```typescript
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const s = json.stringify({ at: new DateTime(), id: Uuid.generate() }, { space: 2 });
|
|
29
|
+
const o = json.parse<{ at: DateTime; id: Uuid }>(s); // 타입 복원, null→undefined
|
|
31
30
|
```
|
|
32
31
|
|
|
33
|
-
## transfer
|
|
32
|
+
## transfer
|
|
34
33
|
|
|
35
|
-
`import { transfer } from "@simplysm/core-common"`.
|
|
34
|
+
`import { transfer } from "@simplysm/core-common"`. `structuredClone` 이 다루지 못하는 커스텀 타입을 Worker 로 보내기 위한 인코딩/디코딩.
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
```typescript
|
|
37
|
+
transfer.encode(obj: unknown): { result: unknown; transferList: ArrayBuffer[] };
|
|
38
|
+
transfer.decode(obj: unknown): unknown;
|
|
39
|
+
```
|
|
39
40
|
|
|
40
|
-
`
|
|
41
|
+
- `encode(obj)` — 커스텀 타입을 태그 객체로 변환한 `result` 와, zero-copy 전송 대상 `transferList`(Uint8Array 의 ArrayBuffer)를 반환. `postMessage(result, transferList)` 에 그대로 전달.
|
|
42
|
+
- `Uint8Array` 는 태그하지 않고 그대로 두되 그 buffer 를 `transferList` 에 추가(`SharedArrayBuffer` 는 제외 — 이미 공유 메모리).
|
|
43
|
+
- `Error` 는 `name`/`message`/`stack` 과 존재 시 `code`/`detail`/`cause` 까지 재귀 인코딩.
|
|
44
|
+
- 순환 참조 시 경로 정보를 담아 `TypeError` throw. 같은 객체가 여러 번 참조되면 캐싱된 인코딩 재사용.
|
|
45
|
+
- `decode(obj)` — Worker 에서 받은 데이터의 태그 객체를 원래 타입으로 복원(`Date`/`DateTime`/`DateOnly`/`Time`/`Uuid`/`RegExp`/`Error`, Array/Map/Set/객체 재귀). `Uint8Array` 는 그대로 통과.
|
|
41
46
|
|
|
42
47
|
```typescript
|
|
43
|
-
|
|
48
|
+
// 송신
|
|
49
|
+
const { result, transferList } = transfer.encode(data);
|
|
44
50
|
worker.postMessage(result, transferList);
|
|
45
|
-
// 수신
|
|
46
|
-
const
|
|
51
|
+
// 수신
|
|
52
|
+
const decoded = transfer.decode(event.data);
|
|
47
53
|
```
|
|
@@ -1,53 +1,117 @@
|
|
|
1
1
|
# @simplysm/core-common — obj 네임스페이스
|
|
2
2
|
|
|
3
|
-
`import { obj } from "@simplysm/core-common"`. 객체/컬렉션의 깊은
|
|
4
|
-
|
|
5
|
-
## clone
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- `
|
|
27
|
-
- `
|
|
28
|
-
- `
|
|
3
|
+
`import { obj } from "@simplysm/core-common"`. 객체/컬렉션의 깊은 복사·동등성 비교·병합·체인 경로 접근·키 변환을 다룰 때 함께 읽힌다. `clone`/`equal`/`merge` 는 커스텀 타입(`DateTime`/`DateOnly`/`Time`/`Uuid`/`Date`/`RegExp`/`Uint8Array`)과 `Map`/`Set`/`Array`/`Error` 를 인지한다. 타입 유틸리티(`Type`/`DeepPartial`)는 entry 직노출, 나머지는 `obj.*` 로 접근.
|
|
4
|
+
|
|
5
|
+
## clone
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
obj.clone<T>(source: T): T; // 깊은 복사 (순환 참조 지원)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- 순환 참조를 WeakMap 으로 추적해 안전 복사. `Date`/`DateTime`/`DateOnly`/`Time`/`Uuid`/`RegExp`/`Uint8Array`/`Array`/`Map`/`Set`/`Error` 를 각 타입으로 재구성하고 프로토타입 체인 유지.
|
|
12
|
+
- 함수·Symbol 은 복사되지 않고 참조 유지. WeakMap/WeakSet 미지원(빈 객체화). getter/setter 는 현재 값으로 평가되어 복사.
|
|
13
|
+
|
|
14
|
+
## equal
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
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
|
+
```
|
|
25
|
+
|
|
26
|
+
- `topLevelIncludes`/`topLevelExcludes` — 최상위 객체 속성 key 에만 적용(중첩·Map key 에는 미적용). 특정 필드만/제외하고 비교할 때(`{ topLevelExcludes: ["updatedAt"] }`).
|
|
27
|
+
- `ignoreArrayIndex: true` — 순서 무시 집합 비교(`[1,2,3]≈[3,2,1]`). O(n²) 비용.
|
|
28
|
+
- `shallow: true` — 한 단계만 `===` 참조 비교. 대용량 비교 시 비용 절감용.
|
|
29
|
+
- null/undefined 속성은 비교에서 동등하게 다뤄짐(둘 다 결측이면 키 수에서 제외).
|
|
30
|
+
|
|
31
|
+
## merge / merge3
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
interface MergeOptions {
|
|
35
|
+
arrayProcess?: "replace" | "concat"; // 기본 "replace"
|
|
36
|
+
useDelTargetNull?: boolean; // target=null 인 key 삭제
|
|
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` 비교 옵션.
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
obj.merge({ a: 1, list: [1] }, { list: [2] }, { arrayProcess: "concat" }); // { a: 1, list: [1, 2] }
|
|
49
|
+
const { conflict, result } = obj.merge3({ a: 1, b: 2 }, { a: 1, b: 1 }, { a: 2, b: 1 }); // false, { a: 2, b: 2 }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## omit / pick
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
obj.omit<T, K>(item: T, omitKeys: K[]): Omit<T, K>;
|
|
56
|
+
obj.omitByFilter<T>(item: T, omitKeyFn: (key: keyof T) => boolean): T; // @internal
|
|
57
|
+
obj.pick<T, K>(item: T, pickKeys: K[]): Pick<T, K>;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- `omit`/`pick` — 지정 key 를 제외/선택한 새 객체. `omitByFilter` 는 key 판정 함수로 제외(예: `_` 접두사 내부 필드 제거).
|
|
29
61
|
|
|
30
62
|
## 체인 경로 접근
|
|
31
63
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
64
|
+
```typescript
|
|
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`.
|
|
75
|
+
|
|
76
|
+
## 결측 정리 / 평탄화 (@internal·@mutates)
|
|
36
77
|
|
|
37
|
-
|
|
78
|
+
```typescript
|
|
79
|
+
obj.clearUndefined<T>(obj: T): T; // null/undefined key 삭제 @mutates
|
|
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
|
+
```
|
|
38
84
|
|
|
39
|
-
- `
|
|
40
|
-
- `
|
|
41
|
-
|
|
42
|
-
|
|
85
|
+
- `clearUndefined`/`clear`/`nullToUndefined` 는 원본을 직접 수정. `nullToUndefined` 는 커스텀 값 타입(Date/DateTime 등)은 보존하고 순환 참조를 WeakSet 으로 방어. simplysm 의 null-free 규칙(JSON 역직렬화 등)에서 사용.
|
|
86
|
+
- `unflatten({ "a.b.c": 1 })` → `{ a: { b: { c: 1 } } }`.
|
|
87
|
+
|
|
88
|
+
## 타입 안전 키 순회
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
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
|
+
```
|
|
96
|
+
|
|
97
|
+
- `keys`/`entries`/`fromEntries` — `Object.*` 의 타입 보존 래퍼.
|
|
98
|
+
- `obj.map(obj, fn)` — 각 엔트리를 `[newKey|null, newValue]` 로 변환한 새 객체. newKey 가 `null` 이면 기존 key 유지(값만 변환). 키·값 동시 변환에 사용.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
obj.map({ r: "255,0,0" }, (k, v) => [null, `rgb(${v})`]); // { r: "rgb(255,0,0)" }
|
|
102
|
+
```
|
|
43
103
|
|
|
44
104
|
## 타입 유틸리티
|
|
45
105
|
|
|
46
|
-
|
|
47
|
-
- `obj.OptionalToUndef<T>` — optional 속성을 `필수 + undefined` 유니온으로(역변환).
|
|
106
|
+
entry 에서 직접 노출되는 타입(`obj.*` 아님).
|
|
48
107
|
|
|
49
108
|
```typescript
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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)
|
|
53
113
|
```
|
|
114
|
+
|
|
115
|
+
- `Type<T>` — 클래스 생성자를 값으로 받을 때(DI·팩토리·`Array.ofType`). `new ctor()` 가능.
|
|
116
|
+
- `DeepPartial<T>` — 원시/값 타입은 유지하고 객체·배열만 재귀 optional. 부분 패치 입력 타입에 사용.
|
|
117
|
+
- `UndefToOptional`/`OptionalToUndef` — `b: string | undefined` ↔ `b?: string` 양방향 변환. API 경계에서 optional 표기 정합 맞출 때.
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
# @simplysm/core-node
|
|
2
2
|
|
|
3
|
-
Node.js
|
|
3
|
+
Node.js 런타임 전용 유틸리티·기능 모음. 파일시스템 IO(`fsx`)·경로 가공(`pathx`)·자식 프로세스(`cpx`) 네임스페이스, 파일 감시(`FsWatcher`), consola 로그 reporter 셋업, worker_threads 타입 안전 래퍼를 제공한다.
|
|
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
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **FsWatcher** —
|
|
13
|
-
- **
|
|
14
|
-
- **Worker / createWorker** — worker_threads 를 타입 안전한 메서드
|
|
9
|
+
- **fsx** — 파일/디렉토리 읽기·쓰기·복사·삭제·glob·stat·JSON IO 를 sync/async 쌍으로 다룰 때. 모든 오류를 `SdError(원인, 경로)` 로 감싸 던진다. 자세히: [fsx.md](./fsx.md)
|
|
10
|
+
- **pathx** — 경로를 POSIX(슬래시)로 정규화하거나, 하위경로 판정·디렉토리 치환·타겟 필터링 같은 경로 가공이 필요할 때. 자세히: [pathx.md](./pathx.md)
|
|
11
|
+
- **cpx** — 외부 명령을 실행해 stdout/stderr 를 시스템 인코딩으로 디코딩해 받을 때(`spawn`/`spawnSync`), 또는 OS 코드페이지 인코딩 감지가 필요할 때. 자세히: [cpx.md](./cpx.md)
|
|
12
|
+
- **FsWatcher / FsWatcherEvent / FsWatcherChangeInfo** — glob 경로를 chokidar 로 감시하며 짧은 시간 내 이벤트를 병합해 콜백 한 번으로 받을 때(watch 빌드 등). 자세히: [fs-watcher.md](./fs-watcher.md)
|
|
13
|
+
- **setupConsola / PrettyReporter / createFileReporter / withMaxLevel** — 앱 진입점에서 consola 전역 로거의 콘솔/파일 출력 형식을 환경별로 셋업할 때. 자세히: [consola.md](./consola.md)
|
|
14
|
+
- **Worker / createWorker / WorkerProxy / WorkerModule / PromisifyMethods / WorkerRequest / WorkerResponse** — worker_threads 를 타입 안전한 메서드 호출·이벤트·로그 전달 프록시로 쓸 때. 자세히: [worker.md](./worker.md)
|
|
15
|
+
|
|
16
|
+
> 위 6개 군은 사용 시점·컨텍스트가 분리되어 각각 별도 `.md` 로 분할됨. README 인라인 군 없음.
|
|
17
|
+
</content>
|
|
@@ -1,51 +1,43 @@
|
|
|
1
1
|
# @simplysm/core-node — consola 로깅 설정
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`consola` 글로벌 로거의 reporter 를 환경별로 구성하는 셋업과 reporter 구현. 앱 진입점에서 1회 `setupConsola()` 호출이 일반적 사용. 콘솔용 `PrettyReporter`(색·아이콘·tag·날짜·에러 스택·box) 와 일자별 rotate 되는 `createFileReporter` 를 조합한다.
|
|
4
4
|
|
|
5
5
|
## setupConsola
|
|
6
6
|
|
|
7
|
-
- `setupConsola(opts?: SetupConsolaOptions): void` —
|
|
8
|
-
- `opts.cli?: boolean` —
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- **dev** (그 외): `createFileReporter()` + `withMaxLevel(new PrettyReporter(), info)` — 파일엔 debug 까지, 콘솔엔 info 이하까지만.
|
|
7
|
+
- `setupConsola(opts?: SetupConsolaOptions): void` — 환경 변수(`DEV`, `SD_DEBUG`)에 따라 `consola.level` 과 `reporters` 를 설정. 모든 경우 level 은 `debug` 포함.
|
|
8
|
+
- `opts.cli?: boolean` — CLI 모드 여부.
|
|
9
|
+
- 분기:
|
|
10
|
+
- `cli` 아니고 `DEV` 아님(prod) → 파일 reporter 만(`createFileReporter()`). 콘솔 출력 없음, debug 까지 파일 기록.
|
|
11
|
+
- `SD_DEBUG` 참(dev+디버그) → `PrettyReporter` 만, debug 까지 콘솔 출력.
|
|
12
|
+
- 그 외(dev) → 파일 reporter + `info` 까지만 콘솔 출력하는 PrettyReporter(`withMaxLevel(..., LogLevels.info)`). 파일에는 debug 전부, 콘솔에는 info 이하만.
|
|
13
|
+
- `env`/`parseBoolEnv` 는 `@simplysm/core-common` 사용. `DEV`/`SD_DEBUG` 는 boolean 환경값.
|
|
15
14
|
|
|
16
15
|
```ts
|
|
17
16
|
setupConsola({ cli: true });
|
|
18
|
-
import consola from "consola";
|
|
19
|
-
consola.info("started");
|
|
20
17
|
```
|
|
21
18
|
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
- `class PrettyReporter implements ConsolaReporter` — 컬러·아이콘·태그·스택 정리 콘솔 리포터. `new PrettyReporter()` 로 사용.
|
|
25
|
-
- level < 2(error/warn)는 stderr, 그 외는 stdout 으로 출력.
|
|
26
|
-
- 컬러 지원은 `NO_COLOR`(있으면 끔) → `FORCE_COLOR`(있으면 켬) → TTY → Windows 순으로 자동 판정.
|
|
27
|
-
- Error 인자는 메시지 + 정리된 스택(cwd 접두 제거, `file://` 제거)으로 출력하고, `cause` 체인을 들여쓰기로 재귀 출력.
|
|
28
|
-
- `box`/`trace` 타입 로그는 별도 포맷(box 는 ` > ` 접두, trace 는 스택 부착).
|
|
29
|
-
- `formatPlain(logObj, formatOptions?): string` — 색·날짜·뱃지 여백 없이 한 엔트리를 평문(멀티라인 가능)으로 포맷. 파일 리포터 등에서 콘솔과 동일한 표현(아이콘·tag·객체 inspect·스택)을 재사용하기 위한 진입점. `formatOptions` 에 `ctx.options.formatOptions` 를 넘기면 객체 펼침(`compact`) 등이 콘솔과 일치.
|
|
19
|
+
## withMaxLevel
|
|
30
20
|
|
|
31
|
-
|
|
21
|
+
- `withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter` — reporter 를 감싸 `logObj.level > maxLevel` 인 로그를 버리는 필터 래퍼. 콘솔에는 정보성만, 파일에는 전부 같은 패턴에 사용. consola LogLevels 숫자(작을수록 심각: error 0, warn 1, info 3 등) 기준.
|
|
32
22
|
|
|
33
|
-
|
|
34
|
-
- `options.maxSize?: number` — 파일 회전 임계 바이트. 기본 20MB(`20 * 1024 * 1024`).
|
|
35
|
-
- `options.maxDays?: number` — 로그 보존 일수. 초과 일자 파일은 일자가 바뀔 때 정리됨. 기본 14.
|
|
36
|
-
- `FileReporterOptions` — `{ maxSize?: number; maxDays?: number }`.
|
|
23
|
+
## PrettyReporter
|
|
37
24
|
|
|
38
|
-
|
|
25
|
+
- `class PrettyReporter implements ConsolaReporter` — 색·아이콘·tag·날짜·에러 스택·box 를 직접 포맷하는 콘솔 reporter. level<2(error/warn) 는 stderr, 그 외 stdout 으로 출력. 색 지원은 `NO_COLOR`/`FORCE_COLOR`/TTY/win32 로 자동 감지.
|
|
26
|
+
- `log(logObj, ctx): void` — consola 가 호출하는 reporter 인터페이스. 한 줄(또는 멀티라인) 포맷 후 스트림에 기록. error 의 `cause` 체인을 들여쓰기로 펼치고, 스택에서 cwd/`file://` 접두 제거.
|
|
27
|
+
- `formatPlain(logObj, formatOptions?): string` — 색·날짜·뱃지 여백 **없이** 평문으로 포맷(trim). 아이콘·tag·객체 inspect·스택 표현은 콘솔과 동일하게 재사용. 파일 reporter 등이 콘솔과 같은 본문을 얻기 위한 진입점.
|
|
28
|
+
- `formatOptions?` — 콘솔과 동일한 `ctx.options.formatOptions`(예: 객체 펼침 `compact`)를 넘기면 출력이 콘솔과 일치.
|
|
39
29
|
|
|
40
|
-
##
|
|
30
|
+
## createFileReporter
|
|
41
31
|
|
|
42
|
-
- `
|
|
32
|
+
- `createFileReporter(options?: FileReporterOptions): ConsolaReporter` — `<cwd>/.logs/app.<YYYY-MM-DD>.log` 에 기록하는 reporter 생성. 본문은 `PrettyReporter.formatPlain` 으로 콘솔과 동일하게, 앞에 `타임스탬프 [TYPE]` 접두를 붙임. 날짜 변경 또는 크기 초과 시 rotate, 일자 바뀔 때 오래된 파일 정리.
|
|
33
|
+
- `options.maxSize?: number` — 파일 1개 최대 바이트. 초과 시 `app.<date>.<seq>.log` 로 분할. 기본 20MB(`20 * 1024 * 1024`).
|
|
34
|
+
- `options.maxDays?: number` — 보관 일수. cutoff(오늘 - maxDays) 이전 날짜 파일 삭제. 기본 14.
|
|
43
35
|
|
|
44
36
|
```ts
|
|
45
|
-
|
|
37
|
+
consola.options.reporters = [createFileReporter({ maxSize: 5 * 1024 * 1024, maxDays: 7 })];
|
|
46
38
|
```
|
|
47
39
|
|
|
48
|
-
##
|
|
40
|
+
## FileReporterOptions / SetupConsolaOptions
|
|
49
41
|
|
|
50
|
-
-
|
|
51
|
-
-
|
|
42
|
+
- `interface FileReporterOptions { maxSize?: number; maxDays?: number }` — 위 createFileReporter 옵션 타입.
|
|
43
|
+
- `interface SetupConsolaOptions { cli?: boolean }` — 위 setupConsola 옵션 타입.
|
|
@@ -1,39 +1,50 @@
|
|
|
1
1
|
# @simplysm/core-node — cpx
|
|
2
2
|
|
|
3
|
-
`
|
|
3
|
+
`export * as cpx` 네임스페이스. 자식 프로세스 실행 + 출력 OS 인코딩 디코딩. `cpx.spawn(...)` 형태로 호출. 출력은 OS 코드페이지(Windows `chcp`, POSIX `LANG`/`LC_ALL`)를 감지해 디코딩하므로 한글 등 비-UTF8 콘솔 출력도 깨지지 않음.
|
|
4
4
|
|
|
5
5
|
## spawn / spawnSync
|
|
6
6
|
|
|
7
|
-
- `spawn(cmd
|
|
8
|
-
- `spawnSync(cmd
|
|
9
|
-
- `
|
|
10
|
-
- `
|
|
11
|
-
- `options
|
|
12
|
-
|
|
7
|
+
- `spawn(cmd, args, options?): SpawnProcess` — 자식 프로세스를 비동기 실행. 반환값은 `await` 가능하며(`PromiseLike<SpawnResult>`) 동시에 `pid`/`kill` 접근 가능.
|
|
8
|
+
- `spawnSync(cmd, args, options?): SpawnResult` — 동기 실행. 즉시 결과 반환.
|
|
9
|
+
- `cmd: string` — 실행 명령.
|
|
10
|
+
- `args: string[]` — 인자 배열.
|
|
11
|
+
- `options` — Node `SpawnOptions`(spawn) / `SpawnSyncOptions`(spawnSync) + `reject?: boolean`.
|
|
12
|
+
- `env` — 전달 env. 기본적으로 `process.env` 와 병합됨(전달분 우선).
|
|
13
|
+
- `stdio` — 기본 `"pipe"`. pipe 인 스트림만 캡처되어 결과 문자열에 담김. `"inherit"` 등이면 해당 스트림은 빈 문자열.
|
|
14
|
+
- `reject?: boolean` — exitCode 가 0 이 아닐 때 동작. 기본(미지정/true): 실패 메시지로 reject(spawn) 또는 throw(spawnSync). `false`: 0 아니어도 정상 반환(직접 exitCode 검사하려는 경우).
|
|
13
15
|
|
|
14
16
|
```ts
|
|
15
|
-
const { stdout } = await cpx.spawn("git", ["
|
|
17
|
+
const { stdout, exitCode } = await cpx.spawn("git", ["status", "--short"]);
|
|
16
18
|
const r = cpx.spawnSync("node", ["-v"], { reject: false });
|
|
17
19
|
if (r.exitCode !== 0) { /* 직접 처리 */ }
|
|
18
20
|
```
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
실패 메시지 형식: `Command failed (exit <code>): <cmd> <args>` 뒤에 stderr(없으면 stdout) 마지막 4000자.
|
|
23
|
+
|
|
24
|
+
## SpawnResult
|
|
25
|
+
|
|
26
|
+
- `{ stdout: string; stderr: string; exitCode: number }` — 실행 결과.
|
|
27
|
+
- `stdout`/`stderr` — pipe 로 캡처된 출력(OS 인코딩 디코딩 적용). 비-pipe 면 `""`.
|
|
28
|
+
- `exitCode` — 종료 코드. 시그널로 종료됐고 코드가 없으면 1, 정상이면 0.
|
|
29
|
+
|
|
30
|
+
## SpawnProcess
|
|
31
|
+
|
|
32
|
+
`spawn` 반환 타입. Promise 처럼 쓰면서 프로세스 제어를 함께 제공.
|
|
33
|
+
|
|
34
|
+
- `pid: number | undefined` — 자식 프로세스 PID.
|
|
35
|
+
- `then(...)` / `catch(...)` — `SpawnResult` 로 resolve 되는 thenable(그래서 `await` 가능).
|
|
36
|
+
- `kill(signal?: NodeJS.Signals | number): boolean` — 프로세스에 시그널 전송. 타임아웃·취소 시 사용.
|
|
22
37
|
|
|
23
38
|
```ts
|
|
24
39
|
const proc = cpx.spawn("long-task", []);
|
|
25
|
-
proc.kill("SIGTERM");
|
|
40
|
+
setTimeout(() => proc.kill("SIGTERM"), 5000);
|
|
41
|
+
const result = await proc;
|
|
26
42
|
```
|
|
27
43
|
|
|
28
|
-
## 인코딩
|
|
29
|
-
|
|
30
|
-
- `getSystemEncoding(): string` — OS 시스템 인코딩 감지(Windows: `chcp` 코드페이지, POSIX: `LANG`/`LC_ALL` 의 `.` 뒤 부분). 결과를 캐시. 감지 실패 시 `"utf-8"`. spawn 결과 디코딩에 내부적으로 사용.
|
|
31
|
-
- `resetEncodingCache(): void` — 위 캐시 무효화(코드페이지 변경 후 재감지 / 테스트 용).
|
|
32
|
-
- `codePageToEncoding(codePage: number): string` — Windows 코드페이지 번호 → 인코딩명(예: 65001→utf-8, 949→euc-kr, 932→shift-jis). 매핑 없으면 `"utf-8"`.
|
|
33
|
-
- `decodeBytes(raw: Uint8Array, systemEncoding?: string): string` — 바이트 디코딩. 인코딩이 utf-8 이면 그대로 디코딩, 아니면 utf-8(fatal) 시도 후 실패 시 시스템 인코딩으로 폴백. `systemEncoding` 미지정 시 `getSystemEncoding()` 사용.
|
|
34
|
-
- `resolveStdioPipe(stdio): { stdout: boolean; stderr: boolean }` — `stdio` 옵션에서 stdout/stderr 가 pipe 인지 판정. 배열이면 인덱스 1/2 가 `"pipe"` 인지 검사, 단일 값/`null` 이면 둘 다 동일 판정. 캡처 여부 결정에 사용.
|
|
35
|
-
|
|
36
|
-
## 주의사항
|
|
44
|
+
## 인코딩 유틸
|
|
37
45
|
|
|
38
|
-
-
|
|
39
|
-
-
|
|
46
|
+
- `getSystemEncoding(): string` — OS 콘솔 인코딩 감지(캐시됨). Windows 는 `chcp` 코드페이지, POSIX 는 `LANG`/`LC_ALL` 의 `.` 뒤 인코딩. 감지 실패 시 `"utf-8"` fallback. spawn 출력 디코딩에 내부적으로 사용.
|
|
47
|
+
- `resetEncodingCache(): void` — `getSystemEncoding` 캐시 초기화. 런타임 중 코드페이지가 바뀐 경우 재감지 강제.
|
|
48
|
+
- `codePageToEncoding(codePage: number): string` — Windows 코드페이지 숫자 → 인코딩명. 매핑 예: 65001→utf-8, 949→euc-kr, 932→shift-jis, 936→gbk, 950→big5, 1252/1251/1250→windows-125x, 874→windows-874. 미지정 코드페이지는 `"utf-8"`.
|
|
49
|
+
- `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()` 사용. 비-utf-8 인코딩이어도 먼저 UTF-8(fatal) 디코딩을 시도해 성공하면 UTF-8 로, 실패 시 지정 인코딩으로 디코딩(UTF-8/레거시 혼재 출력 대응).
|
|
@@ -1,38 +1,41 @@
|
|
|
1
1
|
# @simplysm/core-node — FsWatcher
|
|
2
2
|
|
|
3
|
-
chokidar 기반 파일 감시 래퍼. 짧은 시간 내
|
|
3
|
+
chokidar 기반 파일 감시 래퍼. watch 빌드처럼 "여러 변경을 모아 한 번에 처리"해야 할 때 사용. 짧은 시간 내 이벤트를 디바운스+병합해 콜백을 한 번만 호출하고, Windows 의 EPERM(감시 디렉토리 소실) 발생 시 watcher 를 자동 재시작한다. 모듈 로드 시 native FSWatcher prototype 의 orphan `error` emit 를 swallow 하는 가드를 1회 설치(프로세스 종료 방지).
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## FsWatcher.watch (정적 진입점)
|
|
6
6
|
|
|
7
|
-
- `
|
|
8
|
-
- `
|
|
9
|
-
- `options
|
|
10
|
-
- `
|
|
11
|
-
- `opt.delay?: number` — 디바운스 ms. 마지막 이벤트 후 이 시간만큼 잠잠하면 누적 변경을 한 번에 전달.
|
|
12
|
-
- `cb: (changeInfos: FsWatcherChangeInfo[]) => void | Promise<void>` — 병합된 변경 목록으로 호출.
|
|
13
|
-
- `close(): Promise<void>` — 디바운스 큐를 dispose 한 뒤 chokidar 종료.
|
|
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 로 강제(초기 스캔 이벤트 무시).
|
|
10
|
+
- `options.ignoreInitial: false` 지정 시 → `onChange` 의 첫 콜백이 **빈 배열 `[]`** 로 1회 호출됨(실제 초기 파일 목록은 담기지 않음 — 이벤트 병합과의 충돌 방지). "초기 1회 전체 빌드 후 변경 감시" 패턴 트리거용.
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- `FsWatcherEvent` — `"add" | "addDir" | "change" | "unlink" | "unlinkDir"`. 파일/디렉토리의 생성("add"/"addDir")·변경("change")·삭제("unlink"/"unlinkDir") 구분.
|
|
19
|
-
|
|
20
|
-
## 이벤트 병합 규칙
|
|
12
|
+
```ts
|
|
13
|
+
const watcher = await FsWatcher.watch(["src/**/*.ts"]);
|
|
14
|
+
```
|
|
21
15
|
|
|
22
|
-
|
|
16
|
+
## onChange
|
|
23
17
|
|
|
24
|
-
|
|
18
|
+
- `onChange(opt, cb): this` — 디바운스된 변경 콜백 등록. 체이닝 가능(this 반환). 여러 번 호출해 핸들러 다중 등록 가능.
|
|
19
|
+
- `opt.delay?: number` — 디바운스 지연(ms). 이 시간 내 들어온 이벤트를 모아 한 번 호출(`DebounceQueue` 사용).
|
|
20
|
+
- `cb: (changeInfos: FsWatcherChangeInfo[]) => void | Promise<void>` — 병합된 변경 목록 콜백. async 가능.
|
|
21
|
+
- 이벤트 병합: 같은 파일에 대해 `add+change→add`, `add+unlink→삭제(상쇄)`, `addDir+unlinkDir→상쇄`, `unlink+add→add`, `unlink+change→change`, `unlinkDir+addDir→addDir`. 그 외 조합은 최신 이벤트로 덮어씀. → 생성 직후 삭제된 임시파일 등은 콜백에 안 나타남.
|
|
25
22
|
|
|
26
23
|
```ts
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
for (const { event, path } of changes) console.log(`${event}: ${path}`);
|
|
24
|
+
watcher.onChange({ delay: 300 }, (changes) => {
|
|
25
|
+
for (const { path, event } of changes) console.log(`${event}: ${path}`);
|
|
30
26
|
});
|
|
31
|
-
await watcher.close();
|
|
32
27
|
```
|
|
33
28
|
|
|
34
|
-
##
|
|
29
|
+
## close
|
|
30
|
+
|
|
31
|
+
- `close(): Promise<void>` — 디바운스 큐 dispose 후 chokidar watcher 종료. 감시 해제 시 반드시 호출.
|
|
32
|
+
|
|
33
|
+
## FsWatcherChangeInfo / FsWatcherEvent
|
|
34
|
+
|
|
35
|
+
- `interface FsWatcherChangeInfo { event: FsWatcherEvent; path: PosixPath }` — 변경 1건. `path` 는 슬래시 정규화된 PosixPath.
|
|
36
|
+
- `type FsWatcherEvent = "add" | "addDir" | "change" | "unlink" | "unlinkDir"` — 변경 종류.
|
|
37
|
+
- `add` — 파일 생성. `addDir` — 디렉토리 생성. `change` — 파일 내용 변경. `unlink` — 파일 삭제. `unlinkDir` — 디렉토리 삭제.
|
|
38
|
+
|
|
39
|
+
## EPERM 자동 복구 동작
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
- EPERM 자동 재시작은 최대 3회(1초 간격). 초과 시 error 로그 후 중단하므로 이후 변경이 감지되지 않을 수 있음.
|
|
38
|
-
- 모듈 로드 시 native `fs.FSWatcher.prototype.emit` 에 가드를 설치해, listener 없는 orphan `error` 이벤트로 인한 프로세스 종료를 방지함(부수효과 — import 만으로 1회 적용).
|
|
41
|
+
EPERM 감지 시 최대 3회(1000ms 간격) watcher 재생성을 시도하며 기존 핸들러를 다시 부착한다. 성공 시 `success` 로그, 한도 초과 시 `error` 로그 후 중단. 로거 태그는 `sd-fs-watcher`(consola). 재시도는 자동 복구이므로 호출측에서 별도 처리 불필요.
|