@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.
Files changed (122) hide show
  1. package/claude/references/sd-simplysm14/README.md +17 -17
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +27 -53
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +37 -105
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +46 -43
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +22 -32
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +40 -55
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +40 -40
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +25 -53
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +70 -82
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +44 -39
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +21 -36
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +52 -65
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +65 -70
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +33 -35
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +7 -7
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +29 -29
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +45 -50
  18. package/claude/references/sd-simplysm14/apis/core-browser/README.md +42 -55
  19. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +62 -0
  20. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +13 -12
  21. package/claude/references/sd-simplysm14/apis/core-common/README.md +222 -98
  22. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +102 -53
  23. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +128 -0
  24. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +98 -64
  25. package/claude/references/sd-simplysm14/apis/core-common/errors.md +91 -0
  26. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +34 -28
  27. package/claude/references/sd-simplysm14/apis/core-common/obj.md +104 -40
  28. package/claude/references/sd-simplysm14/apis/core-node/README.md +11 -8
  29. package/claude/references/sd-simplysm14/apis/core-node/consola.md +23 -31
  30. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +33 -22
  31. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +28 -25
  32. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +39 -53
  33. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +26 -29
  34. package/claude/references/sd-simplysm14/apis/core-node/worker.md +27 -29
  35. package/claude/references/sd-simplysm14/apis/excel/README.md +14 -14
  36. package/claude/references/sd-simplysm14/apis/lint/README.md +27 -21
  37. package/claude/references/sd-simplysm14/apis/lint/rules.md +89 -49
  38. package/claude/references/sd-simplysm14/apis/orm-common/README.md +5 -59
  39. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +98 -67
  40. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +107 -92
  41. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +99 -65
  42. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +83 -98
  43. package/claude/references/sd-simplysm14/apis/orm-common/types.md +62 -52
  44. package/claude/references/sd-simplysm14/apis/orm-node/README.md +62 -25
  45. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +27 -27
  46. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +12 -15
  47. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +92 -45
  48. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +226 -108
  49. package/claude/references/sd-simplysm14/apis/service-client/README.md +84 -86
  50. package/claude/references/sd-simplysm14/apis/service-client/orm.md +14 -11
  51. package/claude/references/sd-simplysm14/apis/service-client/transport.md +33 -10
  52. package/claude/references/sd-simplysm14/apis/service-common/README.md +37 -23
  53. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +9 -9
  54. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +13 -13
  55. package/claude/references/sd-simplysm14/apis/service-server/README.md +81 -65
  56. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +32 -35
  57. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +44 -33
  58. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +34 -45
  59. package/claude/references/sd-simplysm14/apis/storage/README.md +24 -18
  60. package/claude/skills/sd-demo/SKILL.md +6 -0
  61. package/claude/skills/sd-impl/SKILL.md +4 -7
  62. package/claude/skills/sd-spec/SKILL.md +31 -858
  63. package/claude/skills/sd-spec/references/spec-authoring.md +519 -0
  64. package/claude/workflows/sd-docs.js +84 -0
  65. package/package.json +1 -1
  66. package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +0 -29
  67. package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
  68. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
  69. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
  70. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
  71. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
  72. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
  73. package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
  74. package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
  75. package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
  76. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
  77. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
  78. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
  79. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
  80. package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
  81. package/claude/skills/sd-docs/SKILL.md +0 -46
  82. package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
  83. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
  84. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
  85. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
  86. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
  87. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
  88. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
  89. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
  90. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
  91. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
  92. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
  93. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
  94. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
  95. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
  96. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
  97. package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
  98. package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
  99. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
  100. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
  101. package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
  102. package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
  103. package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
  104. package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
  105. package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
  106. package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
  107. package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
  108. package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
  109. package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
  110. package/claude/skills/sd-review/evals/golden.jsonl +0 -2
  111. package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
  112. package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
  113. package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
  114. package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
  115. package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
  116. package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
  117. package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
  118. package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
  119. package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
  120. package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
  121. package/claude/skills/sd-use/evals/golden.jsonl +0 -6
  122. /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 / xml / transfer)
1
+ # @simplysm/core-common — JSON / Worker 직렬화 (json / transfer)
2
2
 
3
- 커스텀 타입을 보존하며 직렬화/역직렬화할 때 함께 읽히는 묶음. `json`(문자열 ↔ 객체), `xml`(XML ↔ 객체), `transfer`(Web Worker 전송용). 모듈 모두 `DateTime`/`DateOnly`/`Time`/`Uuid`/`Set`/`Map`/`Error`/`Uint8Array` 등을 `__type__` 태그 객체로 변환해 왕복 보존한다.
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"`. 전역 프로토타입을 건드리지 않아 Worker 환경 안전.
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
- const text = json.stringify({ at: new DateTime(), bin: new Uint8Array([1, 2]) }, { space: 2 });
18
- const back = json.parse<{ at: DateTime; bin: Uint8Array }>(text); // 타입 복원됨
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
- ## xml 네임스페이스
22
-
23
- `import { xml } from "@simplysm/core-common"`. `fast-xml-parser` 기반.
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
- - `xml.parse(str, options?): unknown` — XML → 객체. 속성은 `$` 객체로, 텍스트 노드는 `_` key 로, 자식 요소는 배열로 변환(루트 제외). `options.stripTagPrefix?: boolean` 태그의 네임스페이스 접두사(`ns:tag`)를 제거(속성 접두사는 유지).
26
- - `xml.stringify(obj, options?): string` — 객체 → XML. `options` 는 fast-xml-parser 의 `XmlBuilderOptions`(기본값을 덮어쓸 때만 사용).
25
+ 주의: 사용자 데이터에 우연히 `{ __type__: "Date"|..., data: ... }` 형태가 있으면 의도치 않게 타입으로 복원될 있음.
27
26
 
28
27
  ```typescript
29
- xml.parse('<root id="1"><item>hi</item></root>');
30
- // { root: { $: { id: "1" }, item: [{ _: "hi" }] } }
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"`. Web Worker 간 데이터 전송용. `structuredClone` 이 다루는 커스텀 타입을 처리하고, `Uint8Array` 버퍼를 zero-copy 전송 목록에 담는다.
34
+ `import { transfer } from "@simplysm/core-common"`. `structuredClone` 이 다루지 못하는 커스텀 타입을 Worker 보내기 위한 인코딩/디코딩.
36
35
 
37
- - `transfer.encode(obj): { result, transferList }` — 커스텀 타입을 `{ __type__, data }` 로 변환한 `result` 와 transfer 대상 `ArrayBuffer[]`(transferList) 반환. `worker.postMessage(result, transferList)` 형태로 사용. 순환 참조 시 경로 포함 `TypeError`. 같은 객체 다중 참조는 인코딩 결과 캐시 재사용. `SharedArrayBuffer` 는 transferList 에 넣지 않음.
38
- - `transfer.decode(obj): unknown` 수신한 태그 객체를 커스텀 타입으로 복원(Date/DateTime/DateOnly/Time/Uuid/RegExp/Error, Map/Set/Array/객체 재귀). `Uint8Array` 는 그대로.
36
+ ```typescript
37
+ transfer.encode(obj: unknown): { result: unknown; transferList: ArrayBuffer[] };
38
+ transfer.decode(obj: unknown): unknown;
39
+ ```
39
40
 
40
- `json` 차이: transfer 날짜류를 tick(숫자)으로 저장하고 `RegExp` 지원하며 `Uint8Array`직렬화하지 않고 버퍼째 넘긴다. JSON 문자열이 아니라 구조화 클론 가능한 객체를 만든다.
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
- const { result, transferList } = transfer.encode(payload);
48
+ // 송신
49
+ const { result, transferList } = transfer.encode(data);
44
50
  worker.postMessage(result, transferList);
45
- // 수신
46
- const data = transfer.decode(event.data);
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"`. 객체/컬렉션의 깊은 복사·비교·병합·경로 접근·키 변환을 다룰 때 함께 읽힌다. clone/equal/merge 는 `DateTime`/`DateOnly`/`Time`/`Uuid`/`Uint8Array`/`Date`/`RegExp` 커스텀 타입과 `Map`/`Set`/`Array`/`Error` 를 인지한다.
4
-
5
- ## clone / equal / merge / merge3
6
-
7
- - `obj.clone(source): T` — 깊은 복사. 순환 참조 지원, 프로토타입 체인 유지, 위 커스텀 타입 보존, `Error`(cause·커스텀 속성 포함) 복사. 단 함수·Symbol 은 참조 유지, WeakMap/WeakSet 은 빈 객체화, getter/setter 는 현재 값으로 평가됨.
8
- - `obj.equal(source, target, options?): boolean` 깊은 동등 비교. `options`:
9
- - `topLevelIncludes?: string[]` — 비교할 key 만 한정(최상위 객체 속성에만). 예: id·name 만 비교.
10
- - `topLevelExcludes?: string[]` — 비교 제외 key(최상위). 예: updatedAt 무시.
11
- - `ignoreArrayIndex?: boolean` 배열 순서 무시(집합 동치 비교). true O(n²).
12
- - `shallow?: boolean` 1단계 참조 비교. 대용량에서 성능용.
13
- - null/undefined 인 속성은 비교에서 제외됨(키 개수 계산에서도 빠짐). Map key 에는 include/exclude 미적용.
14
- - `obj.merge(source, target, opt?): TSource & TMergeTarget` — source 기반으로 target 을 깊은 병합한 새 객체(원본 불변). `opt`:
15
- - `arrayProcess?: "replace"|"concat"` — 배열을 target 으로 교체(기본) 또는 Set 으로 합집합(중복 제거, 객체는 참조 비교).
16
- - `useDelTargetNull?: boolean` — target 값이 `null` 이면 결과에서 해당 key 삭제(undefined 는 항상 source 유지). 타입이 다르면 target 으로 덮어씀.
17
- - `obj.merge3(source, origin, target, optionsObj?): { conflict, result }` — 공통 조상 `origin` 기준 3-way 병합. 한쪽만 바뀌면 그 값, 양쪽 동일하면 그 값, 셋 다 다르면 `conflict:true`(origin 값 유지). `optionsObj: Record<key, { keys?, excludes?, ignoreArrayIndex? }>` 로 key 별 `equal` 옵션 지정.
18
- - 옵션 타입: `obj.EqualOptions`, `obj.MergeOptions`, `obj.Merge3KeyOptions`.
19
-
20
- ## 부분 선택 / 변환
21
-
22
- - `obj.omit(item, omitKeys)` — 지정 key 제외한 새 객체. 반환 `Omit<T,K>`.
23
- - `obj.omitByFilter(item, omitKeyFn)` `omitKeyFn(key)===true` 인 key 제외(예: `_` 접두 내부 속성 숨김).
24
- - `obj.pick(item, pickKeys)` — 지정 key 만 선택. 반환 `Pick<T,K>`.
25
- - `obj.keys(obj)` — 타입 안전 `Object.keys`. 반환 `(keyof T)[]`.
26
- - `obj.entries(obj)` — 타입 안전 `Object.entries`. 반환 `[K, T[K]][]`.
27
- - `obj.fromEntries(entryPairs)` — 타입 안전 `Object.fromEntries`.
28
- - `obj.map(obj, fn)` — 엔트리를 `fn(key, value) => [newKey|null, newValue]` 변환한 새 객체. `newKey` 가 `null` 이면 원래 key 유지(값만 변환).
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
- - `obj.getChainValue(target, chain, optional?)` — `"a.b[0].c"` 경로로 값 조회. `optional:true` 면 중간 null/undefined 에서 에러 없이 `undefined`.
33
- - `obj.getChainValueByDepth(target, key, depth, optional?)` — 같은 key 로 `depth` 단계 하강(예: `parent` 로 2단계). `depth<1` 이면 `ArgumentError`.
34
- - `obj.setChainValue(target, chain, value)` — 경로로 값 설정(중간 객체 자동 생성). chain 이면 `ArgumentError`.
35
- - `obj.deleteChainValue(target, chain)` 경로로 삭제. 중간 경로가 없으면 조용히 반환.
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
- ## 정리 변환 (@mutates 표기는 원본 수정)
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
- - `obj.clearUndefined(target)` undefined/nullkey 삭제(원본 수정).
40
- - `obj.clear(target)` 모든 key 삭제(원본 수정).
41
- - `obj.nullToUndefined(target)` — `null` → `undefined` 재귀 변환(원본 수정, 순환 참조 안전). 커스텀 날짜/Uuid 타입은 그대로 둠. simplysm 의 null-free 규칙용.
42
- - `obj.unflatten(flatObj)` `{ "a.b.c": 1 }` → `{ a: { b: { c: 1 } } }`.
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
- - `obj.UndefToOptional<T>` `undefined` 를 포함한 속성을 optional(`?`)로. 예: `{ b: string | undefined }` → `{ b?: string | undefined }`.
47
- - `obj.OptionalToUndef<T>` — optional 속성을 `필수 + undefined` 유니온으로(역변환).
106
+ entry 에서 직접 노출되는 타입(`obj.*` 아님).
48
107
 
49
108
  ```typescript
50
- const next = obj.merge(prev, patch, { arrayProcess: "concat", useDelTargetNull: true });
51
- if (!obj.equal(a, b, { topLevelExcludes: ["updatedAt"] })) save();
52
- const city = obj.getChainValue(user, "profile.address.city", true);
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 환경 전용 유틸리티 모음 파일시스템·자식 프로세스·경로 조작, 파일 감시, consola 로깅 설정, worker_threads 래퍼.
3
+ Node.js 런타임 전용 유틸리티·기능 모음. 파일시스템 IO(`fsx`)·경로 가공(`pathx`)·자식 프로세스(`cpx`) 네임스페이스, 파일 감시(`FsWatcher`), consola 로그 reporter 셋업, worker_threads 타입 안전 래퍼를 제공한다.
4
4
 
5
- 엔트리는 `cpx`/`fsx`/`pathx` 개의 네임스페이스 객체(`export * as`)와 fs-watcher·consola·worker 심볼을 재노출한다. 네임스페이스는 `import { fsx } from "@simplysm/core-node"` `fsx.read(...)` 형태로 호출한다.
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** — 파일/디렉토리 존재 확인·생성·삭제·복사·읽기·쓰기(JSON 포함)·glob 검색·부모 디렉토리 탐색이 필요할 때. 네임스페이스 `import { fsx }`. 자세히: [fsx.md](./fsx.md)
10
- - **cpx** — 자식 프로세스를 spawn stdout/stderr 시스템 인코딩으로 디코딩 수집하거나 OS 코드페이지 인코딩을 감지할 때. 네임스페이스 `import { cpx }`. 자세히: [cpx.md](./cpx.md)
11
- - **pathx** — 경로를 POSIX 슬래시로 정규화·resolve, 하위 경로 판정, 디렉토리 치환, 대상 목록 기준 파일 필터링이 필요할 때. 네임스페이스 `import { pathx }`. 자세히: [pathx.md](./pathx.md)
12
- - **FsWatcher** — chokidar 기반으로 파일 변경을 감시하며 짧은 시간 내 이벤트를 병합해 콜백을번만 호출하고 싶을 때. 자세히: [fs-watcher.md](./fs-watcher.md)
13
- - **consola 설정**앱/CLI consola 전역 reporter(콘솔 pretty 출력·`.logs` 파일 로테이션)를 환경(dev/prod)에 맞춰 구성할 때. `setupConsola`·`PrettyReporter`·`createFileReporter`·`withMaxLevel`. 자세히: [consola.md](./consola.md)
14
- - **Worker / createWorker** — worker_threads 를 타입 안전한 메서드 프록시 + 이벤트로 래핑해 별도 스레드에서 함수를 실행할 때. 자세히: [worker.md](./worker.md)
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
- 전역 `consola` 로거의 level/reporter 를 환경(prod/dev/cli) 맞춰 구성한다. 콘솔용 pretty 출력과 일자별 회전 파일 로그(콘솔과 동일한 평문, 색만 제거)제공한다.
3
+ `consola` 글로벌 로거의 reporter 를 환경별로 구성하는 셋업과 reporter 구현. 앱 진입점에서 1회 `setupConsola()` 호출이 일반적 사용. 콘솔용 `PrettyReporter`(색·아이콘·tag·날짜·에러 스택·box) 일자별 rotate 되는 `createFileReporter`조합한다.
4
4
 
5
5
  ## setupConsola
6
6
 
7
- - `setupConsola(opts?: SetupConsolaOptions): void` — 전역 `consola` `level`/`options.reporters` 를 환경에 맞게 설정. 앱·CLI 부트스트랩 시 1회 호출. level 은 모든 분기에서 `debug` 포함.
8
- - `opts.cli?: boolean` — true 면 CLI 모드로 취급해 dev 분기를 사용(prod 파일 전용 분기를 건너뜀).
9
- - `SetupConsolaOptions` — `{ cli?: boolean }`.
10
-
11
- 환경별 동작:
12
- - **prod** (`cli` 아님 + `DEV` env truthy 아님): `createFileReporter()` 콘솔 출력 없이 파일에만 debug 포함 기록.
13
- - **dev + `SD_DEBUG` truthy**: `PrettyReporter` 콘솔에 debug 포함 출력.
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
- ## PrettyReporter
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
- ## createFileReporter
21
+ - `withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter` — reporter 를 감싸 `logObj.level > maxLevel` 인 로그를 버리는 필터 래퍼. 콘솔에는 정보성만, 파일에는 전부 같은 패턴에 사용. consola LogLevels 숫자(작을수록 심각: error 0, warn 1, info 3 등) 기준.
32
22
 
33
- - `createFileReporter(options?: FileReporterOptions): ConsolaReporter` — `<cwd>/.logs/app.<YYYY-MM-DD>.log` 에 평문 라인으로 기록하는 리포터 생성. 일자 변경·크기 초과 시 회전(`app.<date>.<seq>.log`).
34
- - `options.maxSize?: number` — 파일 회전 임계 바이트. 기본 20MB(`20 * 1024 * 1024`).
35
- - `options.maxDays?: number` — 로그 보존 일수. 초과 일자 파일은 일자가 바뀔 때 정리됨. 기본 14.
36
- - `FileReporterOptions` — `{ maxSize?: number; maxDays?: number }`.
23
+ ## PrettyReporter
37
24
 
38
- 라인 포맷: `<로컬시각> [<LEVEL>] <PrettyReporter 평문>` — 예 `2026-06-02 14:23:01.123 [ERROR] [api] 메시지 { id: 1 }`. 타임스탬프는 로컬 `YYYY-MM-DD HH:mm:ss.SSS`, `[<LEVEL>]` 은 `logObj.type` 대문자(`INFO`/`ERROR`/`WARN`/`DEBUG` grep 필터용). 본문은 `PrettyReporter.formatPlain` 으로 색만 제거하고 콘솔과 동일하게 생성되어 객체·배열은 `util` inspect 펼쳐지고 Error 는 메시지+스택으로 기록됨.
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
- ## withMaxLevel
30
+ ## createFileReporter
41
31
 
42
- - `withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter` — 리포터 래핑. `logObj.level > maxLevel` 로그는 위임하지 않고 버림. 특정 리포터에 최대 상세도 상한을 사용(예: 콘솔엔 info 까지만).
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
- const reporter = withMaxLevel(new PrettyReporter(), LogLevels.info);
37
+ consola.options.reporters = [createFileReporter({ maxSize: 5 * 1024 * 1024, maxDays: 7 })];
46
38
  ```
47
39
 
48
- ## 주의사항
40
+ ## FileReporterOptions / SetupConsolaOptions
49
41
 
50
- - prod 에서는 콘솔 출력이 없고 `.logs` 파일로만 남음운영 로그 확인은 파일 기준.
51
- - 파일 리포터는 `process.cwd()` 기준 `.logs` 기록하므로 작업 디렉토리에 의존.
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
- `import { cpx } from "@simplysm/core-node"`. 자식 프로세스 실행 네임스페이스. stdout/stderr OS 시스템 인코딩(Windows 코드페이지 / POSIX `LANG`)으로 디코딩 수집하고, 0 아닌 종료 코드는 기본적으로 에러로 throw 한다.
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: string, args: string[], options?): SpawnProcess` — 비동기 실행. `await` 하면 `SpawnResult` 반환. 기본 `stdio: "pipe"`.
8
- - `spawnSync(cmd: string, args: string[], options?): SpawnResult` — 동기 실행, 결과 즉시 반환.
9
- - `options` — Node `SpawnOptions`/`SpawnSyncOptions` + `reject?: boolean`.
10
- - `options.reject?: boolean` — `false` 면 0 이 아닌 종료 코드라도 throw 하지 않고 `SpawnResult` 를 반환(종료 코드를 호출자가 직접 검사할 때). 미지정/`true` 면 실패 시 cmd·args·exitCode·출력 일부(최대 4000자)를 담은 메시지로 throw.
11
- - `options.env` — `process.env` 위에 머지되어 자식에 전달(전체 교체 아님).
12
- - `options.stdio` — `"pipe"`(또는 미지정)일 때만 stdout/stderr 캡처. `"inherit"` 등이면 결과 문자열은 빈 값.
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", ["rev-parse", "HEAD"]);
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
- - `SpawnResult` `{ stdout: string; stderr: string; exitCode: number }`. 시그널 종료 시 exitCode 는 1 로 채워짐.
21
- - `SpawnProcess` — `spawn` 반환값. `PromiseLike<SpawnResult>`(then/catch 가능) + `get pid(): number | undefined` + `kill(signal?: NodeJS.Signals | number): boolean`. 완료 전 프로세스를 제어할 때 사용.
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
- - 실패 throw 기본 silent skip 금지 원칙과 일치. 종료 코드를 직접 다루려면 명시적으로 `reject: false`.
39
- - 인코딩 디코딩 비용을 피하려면 `stdio: "inherit"` 캡처를 끄면 됨(단 결과 문자열은 값).
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 기반 파일 감시 래퍼. 짧은 시간 내 다발한 이벤트를 디바운스+병합해 콜백을 한 번만 호출하고, Windows EPERM 발생 시 watcher 를 자동 재시작한다. glob 패턴 watch지원한다.
3
+ chokidar 기반 파일 감시 래퍼. watch 빌드처럼 "여러 변경을 모아 한 번에 처리"해야 할 때 사용. 짧은 시간 내 이벤트를 디바운스+병합해 콜백을 한 번만 호출하고, Windows EPERM(감시 디렉토리 소실) 발생 시 watcher 를 자동 재시작한다. 모듈 로드 native FSWatcher prototype 의 orphan `error` emit swallow 하는 가드를 1회 설치(프로세스 종료 방지).
4
4
 
5
- ## API
5
+ ## FsWatcher.watch (정적 진입점)
6
6
 
7
- - `FsWatcher.watch(paths: string[], options?: chokidar.ChokidarOptions): Promise<FsWatcher>` — 정적 팩토리. ready 까지 대기 후 인스턴스 반환. ready 전 에러가 나면 close 하고 throw. `paths` 는 glob 패턴 허용(메타문자 이전 base 디렉토리를 추출해 chokidar 에 등록하고, 실제 매칭은 minimatch 로 수행).
8
- - `options` — chokidar `ChokidarOptions`. 내부에서 `ignoreInitial` 항상 `true` 강제(초기 스캔 이벤트 무시).
9
- - `options.ignoreInitial: false` — 호출자가 명시하면 onChange 콜백이 **빈 배열로 1회 선호출**됨(실제 초기 파일 목록은 전달하지 않음 이벤트 병합 충돌 방지). 초기 1회 전체 빌드 트리거 용도.
10
- - `onChange(opt: { delay?: number }, cb): this` — 변경 콜백 등록(체이닝 가능).
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
- - `FsWatcherChangeInfo` — `{ event: FsWatcherEvent; path: PosixPath }`. path 는 POSIX 슬래시.
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
- 같은 경로의 연속 이벤트는 디바운스 윈도 안에서 병합된다: `add+change`→`add`, `add+unlink`→제거(상쇄), `addDir+unlinkDir`→제거, `unlink+add`→`add`, `unlink+change`→`change`, `unlinkDir+addDir`→`addDir`. 그 외 조합은 최신 이벤트로 덮어씀. 생성 직후 삭제 등 상쇄된 변경은 콜백에 나타나지 않음.
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
- const watcher = await FsWatcher.watch(["src/**/*.ts"], { ignoreInitial: false });
28
- watcher.onChange({ delay: 300 }, async (changes) => {
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
- - `ignoreInitial` 다른 값으로 줘도 chokidar 에는 `true` 강제됨 초기 목록이 필요하면 `false` 배열 선호출 신호를 받아 호출자가 직접 스캔.
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). 재시도는 자동 복구이므로 호출측에서 별도 처리 불필요.