@simplysm/sd-claude 14.0.97 → 14.0.99

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 (77) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -16
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +81 -153
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +179 -205
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +71 -57
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +49 -109
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +58 -86
  7. package/claude/references/sd-simplysm14/apis/angular/kanban.md +32 -40
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +38 -52
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +86 -110
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -86
  11. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +82 -74
  12. package/claude/references/sd-simplysm14/apis/angular/sheet.md +56 -80
  13. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +15 -15
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +21 -21
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +79 -53
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +9 -11
  17. package/claude/references/sd-simplysm14/apis/core-browser/README.md +15 -15
  18. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +20 -20
  19. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +18 -18
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +20 -49
  21. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +66 -55
  22. package/claude/references/sd-simplysm14/apis/core-common/collection-ext.md +83 -56
  23. package/claude/references/sd-simplysm14/apis/core-common/errors.md +32 -21
  24. package/claude/references/sd-simplysm14/apis/core-common/obj.md +57 -39
  25. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +36 -30
  26. package/claude/references/sd-simplysm14/apis/core-common/value-types.md +69 -41
  27. package/claude/references/sd-simplysm14/apis/core-node/README.md +4 -4
  28. package/claude/references/sd-simplysm14/apis/core-node/consola.md +15 -13
  29. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +11 -7
  30. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +8 -8
  31. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +29 -20
  32. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +14 -6
  33. package/claude/references/sd-simplysm14/apis/core-node/worker.md +3 -3
  34. package/claude/references/sd-simplysm14/apis/excel/README.md +3 -3
  35. package/claude/references/sd-simplysm14/apis/excel/cell.md +32 -32
  36. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +23 -24
  37. package/claude/references/sd-simplysm14/apis/excel/style.md +24 -30
  38. package/claude/references/sd-simplysm14/apis/excel/utils.md +20 -23
  39. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +60 -71
  40. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +36 -36
  41. package/claude/references/sd-simplysm14/apis/lint/README.md +7 -9
  42. package/claude/references/sd-simplysm14/apis/lint/recommended.md +59 -37
  43. package/claude/references/sd-simplysm14/apis/lint/rules.md +81 -74
  44. package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -6
  45. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +112 -78
  46. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +131 -75
  47. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +126 -82
  48. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +170 -113
  49. package/claude/references/sd-simplysm14/apis/orm-common/types.md +102 -48
  50. package/claude/references/sd-simplysm14/apis/orm-node/README.md +12 -13
  51. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +3 -3
  52. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +5 -5
  53. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +67 -65
  54. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +130 -123
  55. package/claude/references/sd-simplysm14/apis/service-client/README.md +63 -63
  56. package/claude/references/sd-simplysm14/apis/service-client/orm.md +22 -22
  57. package/claude/references/sd-simplysm14/apis/service-client/transport.md +30 -26
  58. package/claude/references/sd-simplysm14/apis/service-common/README.md +8 -8
  59. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +13 -6
  60. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +1 -1
  61. package/claude/references/sd-simplysm14/apis/service-server/README.md +43 -47
  62. package/claude/references/sd-simplysm14/apis/service-server/built-in-services.md +35 -0
  63. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +20 -19
  64. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +23 -25
  65. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +9 -9
  66. package/claude/references/sd-simplysm14/apis/storage/README.md +26 -26
  67. package/claude/references/sd-simplysm14/manuals/client-component.md +9 -1
  68. package/claude/references/sd-simplysm14/manuals/client-crud.md +1 -1
  69. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -0
  70. package/claude/references/sd-simplysm14/manuals/client-service.md +1 -0
  71. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +1 -0
  72. package/claude/references/sd-simplysm14/manuals/client-ssg.md +1 -0
  73. package/claude/sd-system-prompt.md +11 -26
  74. package/claude/skills/sd-docs/references/subagent-prompt.md +4 -3
  75. package/claude/skills/sd-spec/SKILL.md +87 -18
  76. package/claude/skills/sd-spec/references/format.md +2 -2
  77. package/package.json +1 -1
@@ -1,31 +1,28 @@
1
- # @simplysm/core-common — 날짜·시간 값 타입
1
+ # @simplysm/core-common — value-types
2
2
 
3
- 불변(immutable) 날짜/시간/식별자 값 타입 `DateTime`·`DateOnly`·`Time`·`Uuid`. 모두 set/add 계열이 **새 인스턴스를 반환**하며 로컬 타임존 기준으로 동작. ORM 컬럼·폼 입력·직렬화 전반에서 함께 다뤄짐.
3
+ 불변(immutable) 날짜/시간/식별자 값 타입 `DateTime`·`DateOnly`·`Time`·`Uuid`. set/add 계열은 모두 **새 인스턴스를 반환**하고, 날짜/시간은 로컬 타임존 기준으로 동작. ORM 컬럼·폼 입력·직렬화 전반에서 함께 다뤄짐. `import { DateTime, DateOnly, Time, Uuid } from "@simplysm/core-common"`.
4
+
5
+ 값을 문자열로 변환하는 C# 스타일 포맷 토큰(`toFormatString`/`dt.format` 공용)은 맨 아래 표 참조.
4
6
 
5
7
  ## DateTime
6
8
 
7
- 밀리초 정밀도 날짜+시간. 내부에 `readonly date: Date` 보유.
9
+ JavaScript `Date` 래핑. 밀리초 정밀도, 로컬 타임존. 내부 `date: Date` 를 읽기전용으로 보유.
8
10
 
9
11
  생성자:
12
+
10
13
  - `new DateTime()` — 현재 시각.
11
- - `new DateTime(year, month, day, hour?, minute?, second?, millisecond?)` — month 는 1-12(내부에서 0-기준 변환). 시·분·초·밀리초 생략 시 0.
14
+ - `new DateTime(year, month, day, hour?, minute?, second?, millisecond?)` — month 는 1-12(내부에서 0-11 변환). 시/분/초/밀리초 생략 시 0.
12
15
  - `new DateTime(tick: number)` — epoch 밀리초.
13
16
  - `new DateTime(date: Date)` — Date 복제.
17
+ - `static parse(str): DateTime` — 문자열 파싱. `Date.parse` 가 먼저 시도되고(ISO 8601 등), 실패 시 `yyyy-MM-dd HH:mm:ss[.fff]`, `yyyy-MM-dd AM|PM ...`, `yyyy-MM-dd 오전|오후 ...`, `yyyyMMddHHmmss` 순으로 매칭. 모두 실패하면 `ArgumentError`.
14
18
 
15
- `static parse(str): DateTime` 다음 형식 인식, 실패 `ArgumentError`: `yyyy-MM-dd HH:mm:ss(.fff)`, `yyyyMMddHHmmss`, `yyyy-MM-dd AM/PM HH:mm:ss`, `yyyy-MM-dd 오전/오후 HH:mm:ss`, ISO 8601(`Date.parse` 경유).
19
+ 게터(읽기전용): `year`, `month`(1-12), `day`, `hour`, `minute`, `second`, `millisecond`, `tick`(epoch ms), `dayOfWeek`(0=일~6=토), `timezoneOffsetMinutes`(UTC 대비 분, KST +540), `isValid`(유효 Date 여부).
16
20
 
17
- 게터(읽기 전용):
18
- - `year/month/day/hour/minute/second/millisecond` — 각 구성요소(month 는 1-12).
19
- - `tick` — epoch 밀리초.
20
- - `dayOfWeek` — 요일(일=0 ~ 토=6).
21
- - `timezoneOffsetMinutes` — 로컬 오프셋(분, KST=+540).
22
- - `isValid` — 내부 Date 가 유효하면 true.
21
+ 불변 변환( 인스턴스): `setYear/setMonth/setDay/setHour/setMinute/setSecond/setMillisecond`. `setYear`/`setMonth` 는 대상 월 일수를 넘는 일을 말일로 보정(1/31→2/28). `setMonth` 는 1-12 범위 밖 월을 연도로 이월. `setDay` 는 범위 밖 일을 JS Date 규칙대로 다음/이전 월로 이월.
23
22
 
24
- 불변 변환(새 인스턴스):
25
- - `setYear/setMonth/setDay/setHour/setMinute/setSecond/setMillisecond(n)` — 해당 구성요소만 교체. `setYear`/`setMonth` 는 대상 월 일수를 넘으면 말일로 보정, `setDay` 는 JS Date 규칙대로 월 넘김.
26
- - `addYears/addMonths/addDays/addHours/addMinutes/addSeconds/addMilliseconds(n)` — 더하기(연/월/일은 set 경유 보정, 시 이하는 tick 가산).
27
- - `toFormatString(formatStr)` — C# 스타일 토큰 포맷(`dt.format` 위임, 토큰 목록은 README dt 섹션 참조).
28
- - `toString()` — `"yyyy-MM-ddTHH:mm:ss.fffzzz"`.
23
+ 산술(새 인스턴스): `addYears/addMonths/addDays`(달력 기준, `setYear`/`setMonth`/`setDay` 경유라 말일 보정 적용), `addHours/addMinutes/addSeconds/addMilliseconds`(tick 기준 절대 가산).
24
+
25
+ 포맷: `toFormatString(formatStr): string`(아래 토큰 표), `toString()`=`yyyy-MM-ddTHH:mm:ss.fffzzz`.
29
26
 
30
27
  ```ts
31
28
  const dt = DateTime.parse("2025-01-15 10:30:00");
@@ -34,53 +31,84 @@ dt.addDays(1).toFormatString("yyyy-MM-dd"); // "2025-01-16"
34
31
 
35
32
  ## DateOnly
36
33
 
37
- 시간 없는 날짜(`yyyy-MM-dd`). `readonly date: Date`(시각 0).
34
+ 시간 없는 날짜(`yyyy-MM-dd`). 내부 `date: Date` 는 자정으로 정규화.
35
+
36
+ 생성자: `new DateOnly()`(오늘), `(year, month, day)`, `(tick)`, `(date)` — 모두 날짜 부분만 추출.
38
37
 
39
- 생성자: `new DateOnly()`(오늘), `(year, month, day)`, `(tick)`, `(date)` 모두 시각을 0 으로 정규화.
38
+ - `static parse(str): DateOnly` `yyyy-MM-dd`/`yyyyMMdd` 타임존 무관하게 문자열에서 직접 추출. 그 외(ISO 8601 등) `Date.parse` 로컬 타임존 변환 적용. 실패 시 `ArgumentError`. 서버/클라이언트 타임존이 다르면 `yyyy-MM-dd` 형식 권장.
40
39
 
41
- `static parse(str)` `yyyy-MM-dd`·`yyyyMMdd` 는 타임존 무관하게 문자열에서 직접 추출, ISO 8601 은 UTC 해석 후 로컬로 변환(DST 지역은 대상 날짜 오프셋 사용). 실패 `ArgumentError`. 서버/클라이언트 타임존이 다르면 `yyyy-MM-dd` 권장.
40
+ 게터: `isValid`, `year`, `month`(1-12), `day`, `tick`, `dayOfWeek`.
42
41
 
43
- 게터: `isValid`, `year/month/day`, `tick`, `dayOfWeek`.
42
+ 불변 변환/산술: `setYear/setMonth/setDay`, `addYears/addMonths/addDays`(DateTime 과 동일 말일·이월 규칙).
44
43
 
45
- 불변 변환: `setYear/setMonth/setDay`, `addYears/addMonths/addDays`(DateTime 동일 보정 규칙), `toFormatString`, `toString()`(`"yyyy-MM-dd"`).
44
+ 주차 계산 모두 `weekStartDay`(0=일~6=토, 기본 1=월요일) `minDaysInFirstWeek`(1~7, 기본 4=ISO 8601) 인자를 받음:
46
45
 
47
- 주차 계산(ISO 8601 기본: 월요일 시작, 최소 4일). 공통 옵션: `weekStartDay`(0=일~6=토, 기본 1), `minDaysInFirstWeek`(1-7, 기본 4):
48
- - `getBaseYearMonthSeqForWeekSeq(weekStartDay?, minDaysInFirstWeek?): { year; monthSeq }` — 이 날짜가 속한 주의 기준 연·월.
49
- - `getWeekSeqStartDate(weekStartDay?, minDaysInFirstWeek?): DateOnly` — 속한 주의 시작 날짜.
50
- - `getWeekSeqOfYear(weekStartDay?, minDaysInFirstWeek?): { year; weekSeq }` — 기준 주차.
51
- - `getWeekSeqOfMonth(weekStartDay?, minDaysInFirstWeek?): { year; monthSeq; weekSeq }` — 월 기준 주차.
52
- - `static getDateByYearWeekSeq(arg: { year; month?; weekSeq }, weekStartDay?, minDaysInFirstWeek?): DateOnly` — 연(+선택 월)·주차로 그 주 시작 날짜 역산.
46
+ - `getBaseYearMonthSeqForWeekSeq(weekStartDay?, minDaysInFirstWeek?): { year; monthSeq }` 날짜가 속한 주의 기준 연·월( 경계 처리 포함).
47
+ - `getWeekSeqStartDate(weekStartDay?, minDaysInFirstWeek?): DateOnly` — 이 날짜가 속한 주의 시작 날짜.
48
+ - `getWeekSeqOfYear(weekStartDay?, minDaysInFirstWeek?): { year; weekSeq }` — 기준 몇째 주.
49
+ - `getWeekSeqOfMonth(weekStartDay?, minDaysInFirstWeek?): { year; monthSeq; weekSeq }` — 기준 몇째 주.
50
+ - `static getDateByYearWeekSeq(arg: { year; month?; weekSeq }, weekStartDay?, minDaysInFirstWeek?): DateOnly` — 연(+선택 )·주차로 해당 주 시작 날짜 역산.
51
+
52
+ 포맷: `toFormatString(formatStr)`, `toString()`=`yyyy-MM-dd`.
53
53
 
54
54
  ```ts
55
- new DateOnly(2025, 1, 6).getWeekSeqOfYear(); // { year: 2025, weekSeq: 2 }
56
- DateOnly.getDateByYearWeekSeq({ year: 2025, weekSeq: 2 }); // 2025-01-06
55
+ new DateOnly(2025, 1, 6).getWeekSeqOfYear(); // { year: 2025, weekSeq: 2 }
57
56
  ```
58
57
 
59
58
  ## Time
60
59
 
61
- 날짜 없는 시간(`HH:mm:ss.fff`). 내부 tick 0 이상 하루 미만으로 정규화(24h 순환, 음수는 양수로 보정).
60
+ 날짜 없는 시간(`HH:mm:ss.fff`). 내부 tick(ms)으로 24시간(`86400000ms`) 모듈로 보관 24시간 초과/음수는 자동 정규화.
61
+
62
+ 생성자: `new Time()`(현재 시각의 시간 부분), `(hour, minute, second?, millisecond?)`, `(tick)`, `(date)`(시간 부분만).
63
+
64
+ - `static parse(str): Time` — `HH:mm:ss[.fff]`, `AM|PM HH:mm:ss[.fff]`, ISO 8601(`...T...` 의 시간 부분) 매칭. 실패 시 `ArgumentError`.
62
65
 
63
- 생성자: `new Time()`(현재 시각의 시간부), `(hour, minute, second?, millisecond?)`, `(tick)`, `(date)`(Date 의 시간부만).
66
+ 게터: `hour`, `minute`, `second`, `millisecond`, `tick`, `isValid`.
64
67
 
65
- `static parse(str)` `HH:mm:ss(.fff)`, `AM/PM HH:mm:ss`, ISO 8601 의 시간부. 실패 시 `ArgumentError`.
68
+ 불변 변환: `setHour/setMinute/setSecond/setMillisecond`.
66
69
 
67
- 게터: `hour/minute/second/millisecond`, `tick`, `isValid`.
70
+ 산술(24시간 순환): `addHours/addMinutes/addSeconds/addMilliseconds` — 결과 tick 을 24시간으로 모듈로(음수면 +24h).
68
71
 
69
- 불변 변환: `setHour/setMinute/setSecond/setMillisecond`, `addHours/addMinutes/addSeconds/addMilliseconds`(모두 24시간 순환), `toFormatString`, `toString()`(`"HH:mm:ss.fff"`).
72
+ 포맷: `toFormatString(formatStr)`, `toString()`=`HH:mm:ss.fff`.
73
+
74
+ ```ts
75
+ Time.parse("23:30:00").addHours(2).toString(); // "01:30:00.000"
76
+ ```
70
77
 
71
78
  ## Uuid
72
79
 
73
- UUID v4 래퍼. 내부에 정규화된 문자열 보유. `crypto.getRandomValues` 기반 난수.
80
+ UUID v4 객체. 내부 문자열을 정규식(`8-4-4-4-12` hex)으로 검증.
74
81
 
75
- - `static generate(): Uuid` — 암호학적으로 안전한 v4.
76
- - `static fromBytes(bytes: Bytes): Uuid` — 16바이트 배열에서 생성. 길이가 16 아니면 `ArgumentError`.
77
- - `new Uuid(uuid: string)` — `8-4-4-4-12` 형식 검증, 불일치 시 `ArgumentError`.
78
- - `toString(): string` — 표준 문자열.
82
+ - `static generate(): Uuid` — `crypto.getRandomValues` 기반 암호학적 안전 UUID v4 생성.
83
+ - `static fromBytes(bytes: Bytes): Uuid` — 16바이트 배열로 생성. 길이 16 이면 `ArgumentError`.
84
+ - `new Uuid(uuid: string)` — 문자열로 생성. 형식 불일치 시 `ArgumentError`.
85
+ - `toString(): string` — UUID 문자열.
79
86
  - `toBytes(): Bytes` — 16바이트 `Uint8Array`.
80
87
 
81
88
  ```ts
82
89
  const id = Uuid.generate();
83
- id.toString(); // "550e8400-e29b-41d4-a716-446655440000" 형태
90
+ id.toString(); // "550e8400-e29b-41d4-a716-446655440000"
84
91
  ```
85
92
 
86
- 주의: 모든 타입은 불변이므로 `set/add` 결과를 변수에 다시 받아야 함. `obj.clone`/`obj.equal`/`json` 직렬화가 이들 타입을 인지해 tick/문자열 기준으로 복제·비교·복원함.
93
+ ## 포맷 토큰 (toFormatString / dt.format 공용)
94
+
95
+ `DateTime`/`DateOnly`/`Time` 의 `toFormatString` 은 내부적으로 `dt.format(formatString, args)` 를 호출한다(`import { dt } from "@simplysm/core-common"` 로 직접도 사용 가능). 전달되지 않은 구성요소의 토큰은 치환되지 않고 그대로 남음. C# 스타일 토큰:
96
+
97
+ | 토큰 | 의미 | 예 |
98
+ | ---- | ---- | -- |
99
+ | `yyyy` / `yy` | 4자리 / 2자리 연도 | 2024 / 24 |
100
+ | `MM` / `M` | 0채움 월 / 월 | 01~12 / 1~12 |
101
+ | `ddd` | 요일(한글) | 일·월·화·수·목·금·토 |
102
+ | `dd` / `d` | 0채움 일 / 일 | 01~31 / 1~31 |
103
+ | `tt` | 오전/오후 | AM / PM |
104
+ | `hh` / `h` | 0채움 12시간 / 12시간 | 01~12 / 1~12 |
105
+ | `HH` / `H` | 0채움 24시간 / 24시간 | 00~23 / 0~23 |
106
+ | `mm` / `m` | 0채움 분 / 분 | 00~59 / 0~59 |
107
+ | `ss` / `s` | 0채움 초 / 초 | 00~59 / 0~59 |
108
+ | `fff` / `ff` / `f` | 밀리초 3/2/1자리 | 000~999 |
109
+ | `zzz` / `zz` / `z` | 타임존 오프셋 ±HH:mm / ±HH / ±H | +09:00 / +09 / +9 |
110
+
111
+ `dt` 네임스페이스의 보조 함수:
112
+
113
+ - `dt.normalizeMonth(year, month, day): { year; month; day }` — 1-12 범위 밖 월을 연도로 이월하고, 대상 월 일수를 넘는 일은 말일로 보정. `DtNormalizedMonth` 가 반환 타입.
114
+ - `dt.convert12To24(rawHour: number, isPM: boolean): number` — 12시간제(1-12)+오전/오후를 24시간제(0-23)로(12 AM→0, 12 PM→12).
@@ -1,16 +1,16 @@
1
1
  # @simplysm/core-node
2
2
 
3
- Node 전용 기반 유틸·기능 모음. `@simplysm/core-common` 위에 Node API(`fs`/`path`/`child_process`/`worker_threads`/`chokidar`/`consola`)를 얹은 계층이라 브라우저에서는 사용 불가.
3
+ `@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** — 파일/디렉토리 존재 확인·생성·삭제·복사·읽기/쓰기(텍스트·바이너리·JSON)·stat·glob·빈 디렉토리 정리·부모 방향 탐색을 동기/비동기 쌍으로 다룰 때. 실패는 모두 `SdError(원인, 경로)` 로 감싸 throw. 자세히: [fsx.md](./fsx.md)
9
+ - **fsx** — 파일/디렉토리 존재 확인·생성(재귀)·삭제(재시도)·복사(필터)·읽기/쓰기(텍스트·바이너리·JSON)·stat·glob·빈 디렉토리 정리·부모 방향 탐색을 동기/비동기 쌍으로 다룰 때. 실패는 모두 `SdError(원인, 경로)` 로 감싸 throw. 자세히: [fsx.md](./fsx.md)
10
10
  - **pathx** — 경로를 POSIX(슬래시)로 정규화(`PosixPath` 브랜드)하거나, 하위 경로 판정·디렉토리 치환·확장자 제거 basename·타겟 필터링 같은 경로 문자열 가공이 필요할 때. 자세히: [pathx.md](./pathx.md)
11
11
  - **cpx** — 외부 명령을 자식 프로세스로 실행해(`spawn`/`spawnSync`) stdout/stderr 를 OS 인코딩(Windows 코드페이지·POSIX LANG)으로 디코딩해 받을 때. exitCode≠0 자동 throw. 자세히: [cpx.md](./cpx.md)
12
- - **FsWatcher / FsWatcherEvent / FsWatcherChangeInfo** — glob 경로를 chokidar 로 감시하며 짧은 시간 내 이벤트를 병합해 콜백 한 번으로 받을 때(watch 빌드 등). 자세히: [fs-watcher.md](./fs-watcher.md)
13
- - **setupConsola / PrettyReporter / createFileReporter / FileReporterOptions / SetupConsolaOptions / withMaxLevel** — Node 진입점(서버·CLI)에서 consola 전역 로거의 콘솔/파일 출력 형식을 환경별로 1회 셋업하거나 reporter 를 커스텀할 때. 자세히: [consola.md](./consola.md)
12
+ - **FsWatcher / FsWatcherEvent / FsWatcherChangeInfo** — glob 경로를 chokidar 로 감시하며 짧은 시간 내 이벤트를 병합해 콜백 한 번으로 받을 때(watch 빌드 등). Windows EPERM 자동 복구. 자세히: [fs-watcher.md](./fs-watcher.md)
13
+ - **setupConsola / PrettyReporter / createFileReporter / FileReporterOptions / SetupConsolaOptions / withMaxLevel** — Node 진입점(서버·CLI)에서 consola 전역 로거의 콘솔/파일 출력 형식을 환경별로 1회 셋업하거나 reporter 를 커스텀할 때. 로깅 코드 자체는 `@simplysm/core-common` 의 `createLogger(tag)` 로 작성하고, 이 군은 진입점 셋업만 담당. 자세히: [consola.md](./consola.md)
14
14
  - **Worker / createWorker / WorkerProxy / WorkerModule / PromisifyMethods / WorkerRequest / WorkerResponse** — worker_threads 를 타입 안전한 메서드 호출·이벤트·로그 전달 프록시로 쓸 때. 워커 측은 `createWorker`, 메인 측은 `Worker.create`. 자세히: [worker.md](./worker.md)
15
15
 
16
16
  위 6개 군은 사용 시점·컨텍스트가 분리되고 시그니처 분량이 커 각각 별도 `.md` 로 분할됨. README 인라인 군 없음.
@@ -1,18 +1,18 @@
1
1
  # @simplysm/core-node — consola 로깅 설정
2
2
 
3
- `consola` 글로벌 로거의 reporter 를 환경별로 구성하는 셋업과 reporter 구현 (`packages/core-node/src/features/consola/*`). 로그 출력 자체는 `@simplysm/core-common` 의 `createLogger(tag)` 로 하고(직접 `console.*`·`consola.withTag` 금지), 출력 채널·형식만 여기서 셋업한다. 콘솔용 `PrettyReporter`(색·아이콘·tag·날짜·에러 스택·box)와 일자별 rotate 되는 `createFileReporter` 를 조합한다.
3
+ `consola` 글로벌 로거의 reporter 를 환경별로 구성하는 셋업과 reporter 구현 (`packages/core-node/src/features/consola/*`). 로그 출력 **자체**는 `@simplysm/core-common` 의 `createLogger(tag)` 로 하고(직접 `console.*`·`consola.withTag` 금지), 출력 채널·형식만 여기서 셋업한다. 콘솔용 `PrettyReporter`(색·아이콘·tag·날짜·에러 스택·box)와 일자별 rotate 되는 `createFileReporter` 를 조합한다.
4
4
 
5
- **Node 진입점(서버·CLI) 에서 1회 `setupConsola()` 호출** 이 일반 사용. Browser·Capacitor 진입점에서는 호출 금지(Node 전용 API) — 그쪽은 consola 기본 reporter 가 브라우저 콘솔로 출력한다.
5
+ **Node 진입점(서버·CLI)에서 1회 `setupConsola()` 호출** 이 일반 사용. Browser·Capacitor 진입점에서는 호출 금지(Node 전용 API) — 그쪽은 consola 기본 reporter 가 브라우저 콘솔로 출력한다.
6
6
 
7
7
  ## setupConsola
8
8
 
9
9
  - `setupConsola(opts?: SetupConsolaOptions): void` — 환경 변수(`DEV`, `SD_DEBUG`)에 따라 `consola.level` 과 `reporters` 를 설정. 모든 분기에서 level 은 `debug` 까지 포함.
10
- - `opts.cli?: boolean` — CLI 모드 여부. true 면 prod 분기를 건너뛰고 항상 콘솔 출력 경로로 감.
10
+ - `opts.cli?: boolean` — CLI 모드 여부. `true` 면 prod 분기를 건너뛰고 항상 콘솔 출력 경로로 감.
11
11
  - 분기:
12
12
  - `cli` 아니고 `DEV` 아님(prod) → 파일 reporter 만(`createFileReporter()`). 콘솔 출력 없음, debug 까지 파일 기록.
13
13
  - `SD_DEBUG` 참(dev+디버그) → `PrettyReporter` 만, debug 까지 콘솔 출력.
14
14
  - 그 외(dev / cli) → 파일 reporter + `info` 까지만 콘솔 출력하는 PrettyReporter(`withMaxLevel(..., LogLevels.info)`). 파일에는 debug 전부, 콘솔에는 info 이하만.
15
- - `DEV`/`SD_DEBUG` 는 `parseBoolEnv` 로 해석되는 boolean 환경값(`@simplysm/core-common`).
15
+ - `DEV`/`SD_DEBUG` 는 `@simplysm/core-common` 의 `parseBoolEnv` 로 해석되는 boolean 환경값.
16
16
 
17
17
  ```ts
18
18
  import { setupConsola } from "@simplysm/core-node";
@@ -21,20 +21,22 @@ setupConsola({ cli: true }); // 진입점에서 1회
21
21
 
22
22
  ## withMaxLevel
23
23
 
24
- - `withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter` — reporter 를 감싸 `logObj.level > maxLevel` 인 로그를 버리는 필터 래퍼. "콘솔에는 정보성만, 파일에는 전부" 같은 분리에 사용. consola `LogLevels` 숫자 기준(작을수록 심각: error 0, warn 1, info 3 등) — maxLevel 보다 큰(=덜 심각한) 로그가 잘림.
24
+ - `withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter` — reporter 를 감싸 `logObj.level > maxLevel` 인 로그를 버리는 필터 래퍼. "콘솔에는 정보성만, 파일에는 전부" 같은 분리에 사용.
25
+ - `reporter: ConsolaReporter` — 감쌀 원본 reporter.
26
+ - `maxLevel: number` — 통과시킬 최대 level. consola `LogLevels` 는 숫자가 작을수록 심각(error 0, warn 1, info 3 등)하므로, maxLevel 보다 **큰**(=덜 심각한) 로그가 잘린다.
25
27
 
26
28
  ## PrettyReporter
27
29
 
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://` 접두를 제거.
30
+ - `class PrettyReporter implements ConsolaReporter` — 색·아이콘·tag·날짜·에러 스택·box 를 직접 포맷하는 콘솔 reporter. level<2(error/warn)는 stderr, 그 외는 stdout 으로 출력. 색 지원은 `NO_COLOR`(끔)/`FORCE_COLOR`(켬)/TTY/win32 순으로 자동 감지.
31
+ - `log(logObj, ctx): void` — consola 가 호출하는 reporter 인터페이스. 한 줄(또는 멀티라인) 포맷 후 스트림에 기록. error 의 `cause` 체인을 들여쓰기로 펼치고, 스택에서 cwd/`file://` 접두를 제거한다.
30
32
  - `formatPlain(logObj, formatOptions?): string` — 색·날짜·뱃지 여백 **없이** 평문으로 포맷(trim). 아이콘·tag·객체 inspect·스택 표현은 콘솔과 동일하게 재사용. 파일 reporter 등이 콘솔과 같은 본문을 얻기 위한 진입점.
31
33
  - `formatOptions?: Partial<FormatOpts>` — 콘솔과 동일한 `ctx.options.formatOptions`(예: 객체 펼침 `compact`)를 넘기면 출력이 콘솔과 일치.
32
34
 
33
35
  ## createFileReporter
34
36
 
35
- - `createFileReporter(options?: FileReporterOptions): ConsolaReporter` — `<cwd>/.logs/app.<YYYY-MM-DD>.log` 에 기록하는 reporter 생성. 본문은 `PrettyReporter.formatPlain` 으로 콘솔과 동일하게, 앞에 `타임스탬프 [TYPE]` 접두를 붙임. 날짜 변경 또는 크기 초과 시 rotate, 일자가 바뀌는 첫 기록 시 오래된 파일 정리.
36
- - `options.maxSize?: number` — 파일 1 최대 바이트. 초과 시 `app.<date>.<seq>.log` 로 분할. 기본 20MB(`20 * 1024 * 1024`).
37
- - `options.maxDays?: number` — 보관 일수. cutoff(오늘 − maxDays) 이전 날짜 파일 삭제. 기본 14.
37
+ - `createFileReporter(options?: FileReporterOptions): ConsolaReporter` — `<cwd>/.logs/app.<YYYY-MM-DD>.log` 에 기록하는 reporter 생성. 본문은 `PrettyReporter.formatPlain` 으로 콘솔과 동일하게, 앞에 `타임스탬프 [TYPE]` 접두를 붙인다. 날짜 변경 또는 크기 초과 시 rotate, 일자가 바뀌는 첫 기록 시 오래된 파일을 정리.
38
+ - `options.maxSize?: number` — 파일 1개의 최대 바이트. 초과 시 `app.<date>.<seq>.log` 로 분할. 기본 20MB(`20 * 1024 * 1024`).
39
+ - `options.maxDays?: number` — 보관 일수. cutoff(오늘 − maxDays) 이전 날짜 파일을 삭제. 기본 14.
38
40
 
39
41
  ```ts
40
42
  import { createFileReporter } from "@simplysm/core-node";
@@ -44,8 +46,8 @@ consola.options.reporters = [createFileReporter({ maxSize: 5 * 1024 * 1024, maxD
44
46
 
45
47
  ## FileReporterOptions / SetupConsolaOptions
46
48
 
47
- - `interface FileReporterOptions { maxSize?: number; maxDays?: number }` — createFileReporter 옵션 타입.
49
+ - `interface FileReporterOptions { maxSize?: number; maxDays?: number }` — `createFileReporter` 옵션 타입.
48
50
  - `maxSize?: number` — 로그 파일 1개의 최대 바이트(기본 20MB). 초과 시 seq 파일로 분할.
49
51
  - `maxDays?: number` — 로그 보관 일수(기본 14). 초과 날짜 파일 삭제.
50
- - `interface SetupConsolaOptions { cli?: boolean }` — setupConsola 옵션 타입.
51
- - `cli?: boolean` — CLI 모드 여부. true 면 prod 라도 콘솔 출력 경로 사용.
52
+ - `interface SetupConsolaOptions { cli?: boolean }` — `setupConsola` 옵션 타입.
53
+ - `cli?: boolean` — CLI 모드 여부. `true` 면 prod 라도 콘솔 출력 경로 사용.
@@ -1,6 +1,6 @@
1
1
  # @simplysm/core-node — cpx
2
2
 
3
- `export * as cpx` 네임스페이스 (`packages/core-node/src/utils/cp.ts`). 자식 프로세스 실행 + 출력 OS 인코딩 디코딩. `cpx.spawn(...)` 형태로 호출. 출력은 OS 코드페이지(Windows `chcp`, POSIX `LANG`/`LC_ALL`) 감지해 디코딩하므로 한글 등 비-UTF8 콘솔 출력도 깨지지 않음.
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,14 +9,15 @@
9
9
  - `cmd: string` — 실행 명령.
10
10
  - `args: string[]` — 인자 배열.
11
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 를 검사하려는 경우).
12
+ - `env` — 전달 env. 항상 `process.env` 와 병합되며 전달분이 덮어쓴다.
13
+ - `stdio` — 기본 `"pipe"`. pipe 인 스트림만 캡처되어 결과 문자열에 담긴다. `"inherit"` 등이면 해당 스트림은 빈 문자열.
14
+ - `reject?: boolean` — exitCode 가 0 이 아닐 때 동작. 기본(미지정/`true`): 실패 메시지로 reject(spawn) 또는 throw(spawnSync). `false`: 0 이 아니어도 정상 반환(직접 `exitCode` 를 검사하려는 경우).
15
15
 
16
16
  실패 메시지 형식: `Command failed (exit <code>): <cmd> <args>` 뒤에 stderr(없으면 stdout) 마지막 4000자.
17
17
 
18
18
  ```ts
19
19
  import { cpx } from "@simplysm/core-node";
20
+
20
21
  const { stdout, exitCode } = await cpx.spawn("git", ["status", "--short"]);
21
22
  const r = cpx.spawnSync("node", ["-v"], { reject: false });
22
23
  if (r.exitCode !== 0) { /* 직접 처리 */ }
@@ -30,7 +31,7 @@ if (r.exitCode !== 0) { /* 직접 처리 */ }
30
31
 
31
32
  ## SpawnProcess
32
33
 
33
- `spawn` 반환 타입. Promise 처럼 쓰면서 프로세스 제어를 함께 제공(`implements PromiseLike<SpawnResult>`).
34
+ `spawn` 반환 타입(`implements PromiseLike<SpawnResult>`). Promise 처럼 쓰면서 프로세스 제어를 함께 제공.
34
35
 
35
36
  - `pid: number | undefined` — 자식 프로세스 PID(생성 전이면 undefined).
36
37
  - `then(onfulfilled?, onrejected?)` / `catch(onrejected?)` — `SpawnResult` 로 resolve 되는 thenable(그래서 `await` 가능).
@@ -44,8 +45,11 @@ const result = await proc;
44
45
 
45
46
  ## 인코딩 유틸
46
47
 
47
- - `getSystemEncoding(): string` — OS 콘솔 인코딩 감지(결과 캐시됨). Windows 는 `chcp` 코드페이지, POSIX 는 `LANG`/`LC_ALL` 의 `.` 뒤 인코딩(`utf8`→`utf-8` 정규화). 감지 실패 시 `"utf-8"` fallback. spawn 출력 디코딩에 내부적으로 사용.
48
+ - `getSystemEncoding(): string` — OS 콘솔 인코딩 감지(결과 캐시됨). Windows 는 `chcp` 코드페이지, POSIX 는 `LANG`/`LC_ALL` 의 `.` 뒤 인코딩(`utf8`→`utf-8` 정규화). 감지 실패 시 `"utf-8"` fallback. spawn 출력 디코딩에 내부 사용.
48
49
  - `resetEncodingCache(): void` — `getSystemEncoding` 캐시 초기화. 런타임 중 코드페이지가 바뀐 경우 재감지를 강제.
49
50
  - `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"`.
50
51
  - `resolveStdioPipe(stdio): { stdout: boolean; stderr: boolean }` — stdio 옵션에서 stdout/stderr 가 pipe 인지 판정. 배열이면 index 1/2 가 `"pipe"` 인지, 단일값이면 `"pipe"` 또는 미지정(null)일 때 둘 다 pipe. 캡처 여부 사전 판단용.
51
- - `decodeBytes(raw: Uint8Array, systemEncoding?: string): string` — 바이트열을 인코딩으로 디코딩. `systemEncoding` 미지정 시 `getSystemEncoding()` 사용. 인코딩이 utf-8 이 아니어도 먼저 UTF-8(fatal) 디코딩을 시도해 성공하면 UTF-8 로, 실패 시 지정 인코딩으로 디코딩(UTF-8/레거시 혼재 출력 대응).
52
+ - `stdio: SpawnOptions["stdio"]` — 판정할 stdio 설정.
53
+ - `decodeBytes(raw: Uint8Array, systemEncoding?: string): string` — 바이트열을 인코딩으로 디코딩.
54
+ - `raw: Uint8Array` — 디코딩할 바이트열.
55
+ - `systemEncoding?: string` — 인코딩명. 미지정 시 `getSystemEncoding()` 사용. 인코딩이 utf-8 이 아니어도 먼저 UTF-8(fatal) 디코딩을 시도해 성공하면 UTF-8 로, 실패 시 지정 인코딩으로 디코딩(UTF-8/레거시 혼재 출력 대응).
@@ -1,12 +1,12 @@
1
1
  # @simplysm/core-node — FsWatcher
2
2
 
3
- chokidar 기반 파일 감시 래퍼 (`packages/core-node/src/features/fs-watcher.ts`). watch 빌드처럼 "여러 변경을 모아 한 번에 처리"해야 할 때 사용. 짧은 시간 내 이벤트를 디바운스+병합해 콜백을 한 번만 호출하고, Windows 의 EPERM(감시 디렉토리 소실) 발생 시 watcher 를 자동 재시작한다. 모듈 로드 시 native FSWatcher prototype 의 orphan `error` emit swallow 하는 가드를 1회 설치(close race 인한 프로세스 종료 방지).
3
+ chokidar 기반 파일 감시 래퍼 (`packages/core-node/src/features/fs-watcher.ts`). watch 빌드처럼 "여러 변경을 모아 한 번에 처리"해야 할 때 사용. 짧은 시간 내 이벤트를 디바운스+병합해 콜백을 한 번만 호출하고, Windows 의 EPERM(감시 디렉토리 소실) 발생 시 watcher 를 자동 재시작한다. 모듈 로드 시 native FSWatcher prototype 의 orphan `error` emit(close race 로 listener 없는 인스턴스)을 swallow 하는 가드를 1회 설치해 프로세스 강제 종료를 막는다.
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 재필터(패턴 자체 + `패턴/**` 양쪽을 `dot: true` 로 매칭).
9
- - `options?: ChokidarOptions` — chokidar 옵션. `persistent` 기본 true. `ignoreInitial` 은 chokidar 에는 내부적으로 항상 true 로 강제(초기 스캔 이벤트 무시).
8
+ - `paths: string[]` — 감시할 경로/glob 패턴 배열. 내부적으로 각 패턴에서 glob 메타문자(`* ? { [ ]`) 이전의 base 디렉토리를 추출해 chokidar 에 등록하고, 콜백 단계에서 원본 패턴으로 minimatch 재필터(패턴 자체 + `패턴/**` 양쪽을 `dot: true` 로 매칭).
9
+ - `options?: ChokidarOptions` — chokidar 옵션. `persistent` 기본 true. 단, `ignoreInitial` 은 chokidar 호출 내부적으로 항상 true 로 강제(초기 스캔 이벤트 무시).
10
10
  - `options.ignoreInitial: false` 지정 시 → `onChange` 의 첫 콜백이 **빈 배열 `[]`** 로 1회 호출됨(실제 초기 파일 목록은 담기지 않음 — 이벤트 병합과의 충돌 방지). "초기 1회 전체 빌드 후 변경 감시" 패턴 트리거용.
11
11
 
12
12
  ```ts
@@ -16,10 +16,10 @@ const watcher = await FsWatcher.watch(["src/**/*.ts"]);
16
16
 
17
17
  ## onChange
18
18
 
19
- - `onChange(opt, cb): this` — 디바운스된 변경 콜백 등록. 체이닝 가능(this 반환). 여러 번 호출해 핸들러 다중 등록 가능.
20
- - `opt.delay?: number` — 디바운스 지연(ms). 이 시간 내 들어온 이벤트를 모아 한 번 호출(`DebounceQueue` 사용). 생략 시 DebounceQueue 기본값.
19
+ - `onChange(opt, cb): this` — 디바운스된 변경 콜백 등록. 체이닝 가능(this 반환). 여러 번 호출해 핸들러를 다중 등록 가능.
20
+ - `opt.delay?: number` — 디바운스 지연(ms). 이 시간 내 들어온 이벤트를 모아 한 번 호출(`DebounceQueue` 사용). 생략 시 `DebounceQueue` 기본값.
21
21
  - `cb: (changeInfos: FsWatcherChangeInfo[]) => void | Promise<void>` — 병합된 변경 목록 콜백. async 가능.
22
- - 이벤트 병합: 같은 파일에 대해 `add+change→add`, `add+unlink→상쇄(콜백 제외)`, `addDir+unlinkDir→상쇄`, `unlink+add→add`, `unlink+change→change`, `unlinkDir+addDir→addDir`. 그 외 조합은 최신 이벤트로 덮어씀. → 생성 직후 삭제된 임시파일 등은 콜백에 나타남.
22
+ - 이벤트 병합(같은 파일 기준): `add+change→add`, `add+unlink→상쇄(콜백 제외)`, `addDir+unlinkDir→상쇄`, `unlink+add→add`, `unlink+change→change`, `unlinkDir+addDir→addDir`. 그 외 조합은 최신 이벤트로 덮어쓴다. → 생성 직후 삭제된 임시파일 등은 콜백에 나타나지 않음.
23
23
 
24
24
  ```ts
25
25
  watcher.onChange({ delay: 300 }, (changes) => {
@@ -29,7 +29,7 @@ watcher.onChange({ delay: 300 }, (changes) => {
29
29
 
30
30
  ## close
31
31
 
32
- - `close(): Promise<void>` — 디바운스 큐를 dispose 한 뒤 chokidar watcher 종료. 감시 해제 시 반드시 호출.
32
+ - `close(): Promise<void>` — 디바운스 큐를 모두 dispose 한 뒤 chokidar watcher 종료. 감시 해제 시 반드시 호출.
33
33
 
34
34
  ## FsWatcherChangeInfo / FsWatcherEvent
35
35
 
@@ -39,4 +39,4 @@ watcher.onChange({ delay: 300 }, (changes) => {
39
39
 
40
40
  ## EPERM 자동 복구 동작
41
41
 
42
- EPERM 감지 시 최대 3회(1000ms 간격) watcher 재생성을 시도하며 등록된 핸들러를 다시 부착한다. 성공 시 `success` 로그, 한도 초과 시 `error` 로그 후 중단. 로거 태그는 `sd-fs-watcher`(consola). 재시도는 자동 복구이므로 호출측에서 별도 처리 불필요.
42
+ EPERM 감지 시 최대 3회(1000ms 간격) watcher 재생성을 시도하며 등록된 모든 핸들러를 다시 부착한다. 성공 시 `success` 로그, 한도 초과 시 `error` 로그 후 중단. 로거 태그는 `sd-fs-watcher`(consola). 재시도는 자동 복구이므로 호출측에서 별도 처리 불필요.
@@ -1,53 +1,56 @@
1
1
  # @simplysm/core-node — fsx
2
2
 
3
- `export * as fsx` 네임스페이스 (`packages/core-node/src/utils/fs.ts`). Node `fs` 를 감싼 파일시스템 IO. 대부분 동기(`...Sync`)/비동기 쌍을 제공하며, 실패 시 모두 `SdError(err, targetPath)` 로 원인+경로를 붙여 throw 한다. 항상 `fsx.<fn>(...)` 형태로 호출.
3
+ `export * as fsx` 네임스페이스 (`packages/core-node/src/utils/fs.ts`). Node `fs` 를 감싼 파일시스템 IO 군. 대부분 동기(`...Sync`)/비동기 쌍을 제공하며, 존재 확인 계열을 제외한 모든 함수는 실패 시 `SdError(err, targetPath)` 로 원인+경로를 묶어 throw 한다. 항상 `fsx.<fn>(...)` 형태로 호출.
4
4
 
5
5
  ## 존재 확인
6
6
 
7
- - `existsSync(targetPath: string): boolean` — 파일/디렉토리 존재 여부(동기). 내부적으로 `fs.existsSync`.
8
- - `exists(targetPath: string): Promise<boolean>` — 동일(비동기). `fs.access` 실패를 catch 해 false 반환(throw 안 함).
7
+ - `existsSync(targetPath: string): boolean` — 파일/디렉토리 존재 여부(동기, `fs.existsSync`).
8
+ - `exists(targetPath: string): Promise<boolean>` — 동일(비동기). `fs.promises.access` 던지면 catch 해 `false` 반환(throw 안 함).
9
+ - `targetPath: string` — 확인할 경로.
9
10
 
10
11
  ## 디렉토리 생성
11
12
 
12
- - `mkdirSync(targetPath: string): void` / `mkdir(targetPath: string): Promise<void>` — 재귀 생성(`recursive: true`). 중간 경로가 없어도 전부 만든다. 실패 시 `SdError`.
13
+ - `mkdirSync(targetPath: string): void` / `mkdir(targetPath: string): Promise<void>` — 재귀 생성(`recursive: true`). 중간 경로가 없어도 전부 만들고, 이미 존재해도 에러 없음. 실패 시 `SdError`.
14
+ - `targetPath: string` — 생성할 디렉토리 경로.
13
15
 
14
16
  ## 삭제
15
17
 
16
- - `rmSync(targetPath: string): void` — 재귀+force 삭제. **재시도 없이 즉시 실패**. 파일 잠금 등 일시 오류가 예상되면 비동기 `rm` 을 쓸 것.
18
+ - `rmSync(targetPath: string): void` — 재귀+force 삭제(`recursive: true, force: true`). **재시도 없이 즉시 실패**. 파일 잠금 등 일시 오류가 예상되면 비동기 `rm` 을 쓸 것.
17
19
  - `rm(targetPath: string): Promise<void>` — 재귀+force 삭제. 일시 오류에 대해 **500ms 간격 최대 6회 재시도**(`retryDelay: 500, maxRetries: 6`). Windows 파일 잠금 회피용.
20
+ - `targetPath: string` — 삭제할 경로. 없는 경로도 force 라 에러 없이 처리.
18
21
 
19
22
  ## 복사
20
23
 
21
- - `copySync(sourcePath, targetPath, filter?): void` / `copy(sourcePath, targetPath, filter?): Promise<void>` — 파일/디렉토리 복사. async 는 하위 항목을 `parallelAsync` 로 병렬 복사.
22
- - `sourcePath: string` — 원본. **존재하지 않으면 아무 것도 하고 반환**(throw 안 함).
23
- - `targetPath: string` — 대상. 원본이 디렉토리면 대상 디렉토리를 만들고 하위를 재귀 복사(glob `*`, `dot: true` 숨김 포함), 파일이면 상위 디렉토리 생성 후 복사.
24
- - `filter?: (absolutePath: string) => boolean` — 복사 여부 결정. 각 하위 항목의 **절대 경로**가 전달되며 true=복사, false=제외. **최상위 sourcePath 자신은 필터 대상 아님**. 디렉토리에 false 면 그 디렉토리와 모든 내용을 건너뜀. 빌드 산출물 특정 파일만 복사할 때 사용.
25
- - 파일 복사 실패 시 500ms 대기(sync 는 busy-wait, async 는 setTimeout) 최대 7회(i=0..6) 시도 후에도 실패하면 `SdError`.
24
+ - `copySync(sourcePath, targetPath, filter?): void` / `copy(sourcePath, targetPath, filter?): Promise<void>` — 파일/디렉토리 복사. 비동기 `copy` 디렉토리 하위 항목을 `parallelAsync` 로 병렬 복사.
25
+ - `sourcePath: string` — 원본. **존재하지 않으면 아무 작업 없이 반환**(throw 안 함).
26
+ - `targetPath: string` — 대상. 원본이 디렉토리면 대상 디렉토리를 만들고 하위를 재귀 복사(내부 glob `*`, `dot: true` 숨김 파일 포함), 파일이면 상위 디렉토리 생성 후 복사.
27
+ - `filter?: (absolutePath: string) => boolean` — 복사 여부 결정. 각 하위 항목의 **절대 경로**가 전달되며 `true`=복사, `false`=제외. **최상위 sourcePath 자신은 필터 대상 아님**. 디렉토리에 `false` 면 그 디렉토리와 모든 내용을 통째로 건너뜀. 모든 하위 항목(직·간접)에 재귀 적용.
28
+ - 파일 복사 실패 시 500ms 대기(sync 는 busy-wait, async 는 `setTimeout`) 후 재시도, 최대 7회(`i=0..6`) 시도해도 실패하면 `SdError`.
26
29
 
27
- ## 읽기
30
+ ## 파일 읽기
28
31
 
29
32
  - `readSync(targetPath: string): string` / `read(...): Promise<string>` — UTF-8 문자열로 읽음.
30
33
  - `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 프리뷰(+`...(truncated)`)를 첨부.
34
+ - `readJsonSync<TData = unknown>(targetPath): TData` / `readJson<TData = unknown>(...): Promise<TData>` — 읽어 `@simplysm/core-common` 의 `json.parse` 로 역직렬화(Date 등 특수타입 복원). 파싱 실패 시 `SdError` 메시지에 경로와 내용 프리뷰(앞 500자, 초과 시 `...(truncated)`)를 첨부.
32
35
  - 제네릭 `TData` — 파싱 결과 타입. 호출부에서 기대 타입을 지정해 반환 타입을 좁힘.
33
36
 
34
- ## 쓰기
37
+ ## 파일 쓰기
35
38
 
36
39
  - `writeSync(targetPath, data): void` / `write(targetPath, data): Promise<void>` — **상위 디렉토리 자동 생성** 후 기록(`flush: true` 로 디스크 플러시까지 보장).
37
40
  - `data: string | Uint8Array` — 텍스트 또는 바이너리.
38
41
  - `writeJsonSync(targetPath, data, options?): void` / `writeJson(targetPath, data, options?): Promise<void>` — `json.stringify` 로 직렬화 후 write.
39
42
  - `data: unknown` — 직렬화 대상.
40
- - `options?.replacer?: (this, key, value) => unknown` — JSON.stringify 의 replacer 와 동일 역할. 특정 키 값을 변환·제외할 때.
41
- - `options?.space?: string | number` — 들여쓰기. 사람이 읽을 파일이면 `2` 같은 값 지정, 생략 시 압축.
43
+ - `options?.replacer?: (this, key: string | undefined, value: unknown) => unknown` — `json.stringify` 의 replacer. 특정 키 값을 변환·제외할 때.
44
+ - `options?.space?: string | number` — 들여쓰기 폭. 사람이 읽을 파일이면 `2` 같은 값 지정, 생략 시 압축.
42
45
 
43
46
  ## 디렉토리 읽기
44
47
 
45
- - `readdirSync(targetPath: string): string[]` / `readdir(...): Promise<string[]>` — 디렉토리 직계 항목명(전체 경로 아닌 이름만) 배열.
48
+ - `readdirSync(targetPath: string): string[]` / `readdir(...): Promise<string[]>` — 디렉토리 직계 항목의 **이름**(전체 경로가 아님) 배열.
46
49
 
47
50
  ## 파일 정보
48
51
 
49
52
  - `statSync(targetPath): fs.Stats` / `stat(...): Promise<fs.Stats>` — 정보 조회. **심볼릭 링크를 따라감**(링크 대상의 정보).
50
- - `lstatSync(targetPath): fs.Stats` / `lstat(...): Promise<fs.Stats>` — 정보 조회. **심볼릭 링크를 따라가지 않음**(링크 자체 정보). 링크/실파일을 구분해야 하면 lstat.
53
+ - `lstatSync(targetPath): fs.Stats` / `lstat(...): Promise<fs.Stats>` — 정보 조회. **심볼릭 링크를 따라가지 않음**(링크 자체 정보). 링크/실파일을 구분해야 하면 lstat. 반환 `fs.Stats` 의 `isDirectory()`/`isFile()` 로 종류 판별.
51
54
 
52
55
  ## Glob
53
56
 
@@ -57,17 +60,23 @@
57
60
 
58
61
  ## 유틸리티
59
62
 
60
- - `clearEmptyDirectory(dirPath: string): Promise<void>` — `dirPath` 하위를 재귀 순회하며 **빈 디렉토리만** 삭제. 하위가 모두 비어 상위도 비게 되면 상위도 삭제. 파일이 하나라도 있으면 그 디렉토리는 보존. 존재하지 않으면 즉시 반환.
61
- - `findAllParentChildPathsSync(childGlob, fromPath, rootPath?): string[]` / `findAllParentChildPaths(...): Promise<string[]>` — `fromPath` 에서 루트 방향으로 부모 디렉토리를 거슬러 올라가며 각 디렉토리에서 `childGlob` 매칭 파일을 모아 평탄화 반환. 상위 디렉토리들의 설정 파일(예: 각 단계 `package.json`) 수집에 사용.
63
+ - `clearEmptyDirectory(dirPath: string): Promise<void>` — `dirPath` 하위를 재귀 순회하며 **빈 디렉토리만** 삭제. 하위가 모두 비어 상위까지 비게 되면 상위도 삭제. 파일이 하나라도 있으면 그 디렉토리는 보존. 존재하지 않으면 즉시 반환.
64
+ - `findAllParentChildPathsSync(childGlob, fromPath, rootPath?): string[]` / `findAllParentChildPaths(...): Promise<string[]>` — `fromPath` 에서 루트 방향으로 부모 디렉토리를 거슬러 올라가며 각 디렉토리에서 `childGlob` 매칭 파일을 모아 평탄화 반환. 상위 디렉토리들의 설정 파일(각 단계 `package.json`) 수집용.
62
65
  - `childGlob: string` — 각 디렉토리에서 검색할 glob.
63
66
  - `fromPath: string` — 탐색 시작 경로.
64
- - `rootPath?: string` — 탐색 중단 경로. 생략 시 파일시스템 루트까지. **주의: fromPath 는 rootPath 의 하위여야 함**, 아니면 루트까지 올라간다.
67
+ - `rootPath?: string` — 탐색 중단 경로. 생략 시 파일시스템 루트까지. **주의: fromPath 는 rootPath 의 하위여야 함**, 아니면 경계가 매칭되지 않아 루트까지 올라간다.
65
68
 
66
69
  ## 사용 예
67
70
 
68
71
  ```ts
69
72
  import { fsx } from "@simplysm/core-node";
73
+
70
74
  await fsx.writeJson("dist/meta.json", { builtAt: new Date() }, { space: 2 });
71
75
  await fsx.copy("src", "dist", (p) => !p.endsWith(".ts")); // .ts 제외 복사
72
76
  const pkgs = await fsx.findAllParentChildPaths("package.json", process.cwd());
73
77
  ```
78
+
79
+ ## 주의사항
80
+
81
+ - 존재 확인(`exists`/`existsSync`)을 제외한 모든 함수는 실패 시 `SdError(원인, 경로)` 로 throw 한다 — silent skip 하지 않으므로 호출부에서 try/catch 또는 상위 전파를 설계할 것.
82
+ - `copy`/`copySync` 는 원본 부재를 정상 흐름(no-op)으로 처리하므로, 원본 필수 여부는 호출부에서 별도 검증해야 한다.
@@ -4,23 +4,26 @@
4
4
 
5
5
  ## PosixPath (브랜드 타입)
6
6
 
7
- - `type PosixPath = string & { [POSIX]: never }` — POSIX 슬래시 경로임을 나타내는 브랜드 타입. `posix()`/`posixResolve()` 로만 생성 가능(심볼 키라 외부에서 캐스팅 없이는 못 만듦). 일반 `string` 을 PosixPath 자리에 직접 넣을 수 없어, 정규화를 강제하는 타입 가드 역할.
7
+ - `type PosixPath = string & { [POSIX]: never }` — POSIX 슬래시 경로임을 나타내는 브랜드 타입. `POSIX` 가 모듈 내부 심볼이라 `posix()`/`posixResolve()` 로만 생성 가능(외부에서 캐스팅 없이는 못 만듦). 일반 `string` 을 `PosixPath` 자리에 직접 넣을 수 없어 정규화를 강제하는 타입 가드 역할.
8
8
 
9
9
  ## 정규화
10
10
 
11
- - `posix(p: string): PosixPath` — 백슬래시 → 슬래시 치환만 수행. **결합·resolve 안 함**. 예: `posix("C:\\Users\\test")` → `"C:/Users/test"`. 이미 절대/상대 경로인 문자열을 POSIX 표기로만 바꿀 때.
12
- - `posixResolve(...args: string[]): PosixPath` — 인자들을 `path.resolve` 로 절대 경로 결합 후 슬래시로 변환. 예: `posixResolve("/base", "sub", "f.txt")` → `"/base/sub/f.txt"`, 상대경로 단독이면 cwd 기준 절대화. 경로 결합+정규화를 동시에 할 때.
11
+ - `posix(p: string): PosixPath` — 백슬래시 → 슬래시 치환만 수행. **결합·resolve 안 함**. 예: `posix("C:\\Users\\test")` → `"C:/Users/test"`. 이미 절대/상대인 경로 문자열을 POSIX 표기로만 바꿀 때.
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
+ - `filePath: string` — 원본 파일 경로.
18
+ - `fromDirectory: string` — 치환할 기준 디렉토리.
19
+ - `toDirectory: string` — 새 기준 디렉토리.
20
+ - `basenameWithoutExt(filePath: string): string` — 마지막 확장자 1단계만 제거한 basename. 예: `"file.txt"` → `"file"`, `"a/file.spec.ts"` → `"file.spec"`(마지막 `.ts` 만 제거).
18
21
 
19
22
  ## 판정·필터링
20
23
 
21
- - `isChildPath(childPath, parentPath): boolean` — `childPath` 가 `parentPath` 의 하위인지. 양쪽을 `posixResolve` 로 정규화 후 비교. **동일 경로면 false**(자기 자신은 하위 아님). 경계 오탐 방지를 위해 parent 끝에 `/` 를 붙여 startsWith 비교.
24
+ - `isChildPath(childPath, parentPath): boolean` — `childPath` 가 `parentPath` 의 하위인지. 양쪽을 `posixResolve` 로 정규화 후 비교. **동일 경로면 false**(자기 자신은 하위 아님). 경계 오탐 방지를 위해 parent 끝에 `/` 를 붙여 `startsWith` 비교.
22
25
  - `filterByTargets(files, targets, cwd): string[]` — 파일 목록을 타겟 경로 하위만 남김.
23
- - `files: string[]` — 필터링할 파일. **cwd 하위 절대 경로여야 함**(외부 경로는 `../` 상대경로로 변환되어 매칭 실패 가능).
26
+ - `files: string[]` — 필터링할 파일. **cwd 하위의 절대 경로여야 함**(cwd 외부 경로는 `../` 상대경로로 변환되어 매칭 실패 가능).
24
27
  - `targets: string[]` — 대상 경로(cwd 기준 상대, POSIX 권장). 각 파일의 cwd-상대경로가 target 과 같거나 `target + "/"` 로 시작하면 통과.
25
28
  - `cwd: string` — 기준 작업 디렉토리(절대 경로).
26
29
  - **`targets` 가 비면 `files` 를 그대로 반환**(필터 미적용). CLI 의 `-t <패키지>` 같은 부분 빌드 대상 한정에 사용.
@@ -29,7 +32,12 @@
29
32
 
30
33
  ```ts
31
34
  import { pathx } from "@simplysm/core-node";
35
+
32
36
  const out = pathx.changeFileDirectory(srcFile, "/proj/src", "/proj/dist");
33
37
  if (pathx.isChildPath(out, "/proj")) { /* ... */ }
34
38
  const targeted = pathx.filterByTargets(allFiles, ["src", "tests"], process.cwd());
35
39
  ```
40
+
41
+ ## 주의사항
42
+
43
+ - `changeFileDirectory` 는 `filePath` 가 `fromDirectory` 내부가 아니면(동일 경로 제외) `ArgumentError` 로 throw 하므로, 경계 밖 경로를 넘길 가능성이 있으면 `isChildPath` 로 먼저 확인할 것.
@@ -1,6 +1,6 @@
1
1
  # @simplysm/core-node — worker
2
2
 
3
- `worker_threads` 를 타입 안전하게 쓰기 위한 래퍼 (`packages/core-node/src/worker/*`). 워커 파일에서 `createWorker(methods)` 로 메서드 묶음을 만들어 `export default` 하고, 메인에서 `Worker.create<typeof import("./worker")>(path)` 로 프록시를 만들어 `await worker.method(...)` 처럼 호출. 메시지 직렬화는 `@simplysm/core-common` 의 `transfer`(Date 등 특수타입·transferList 지원)를 사용한다. 개발(`.ts`)·프로덕션(`.js`) 양쪽을 자동 분기한다.
3
+ `worker_threads` 를 타입 안전하게 쓰기 위한 래퍼 (`packages/core-node/src/worker/*`). 워커 파일에서 `createWorker(methods)` 로 메서드 묶음을 만들어 `export default` 하고, 메인에서 `Worker.create<typeof import("./worker")>(path)` 로 프록시를 만들어 `await worker.method(...)` 처럼 호출한다. 메시지 직렬화는 `@simplysm/core-common` 의 `transfer`(Date 등 특수타입·transferList 지원)를 사용. 개발(`.ts`)·프로덕션(`.js`) 양쪽을 자동 분기한다.
4
4
 
5
5
  ## createWorker (워커 스레드 측)
6
6
 
@@ -25,7 +25,7 @@ export default sender;
25
25
 
26
26
  - `Worker.create<TModule extends WorkerModule>(filePath, opt?): WorkerProxy<TModule>` — 워커 스레드를 띄우고 메서드 프록시를 반환. 메서드 호출은 메시지로 전달되어 결과가 Promise 로 resolve/reject 된다. 워커 stdout/stderr 는 메인 프로세스로 파이프되며, 워커 비정상 종료(exit code≠0)·error 시 대기 중인 모든 호출이 reject 된다. 로거 태그 `sd-worker`.
27
27
  - `filePath: string` — 워커 파일 경로. `file://` URL 또는 절대 경로. 확장자가 `.ts` 면 dev 모드로 `lib/worker-dev-proxy.js`(tsx 로 TS 동적 로드)를 통해 실행, `.js` 면 직접 실행.
28
- - `opt?: Omit<WorkerRawOptions, "stdout" | "stderr">` — worker_threads 옵션(stdout/stderr 는 내부 고정이라 제외). `env` 는 `process.env` 와 병합 전달, `argv` 는 dev 모드에서 워커 경로 뒤에 이어 붙음.
28
+ - `opt?: Omit<WorkerRawOptions, "stdout" | "stderr">` — worker_threads 옵션(stdout/stderr 는 내부 고정이라 제외). `env` 는 `process.env` 와 병합되어 전달, `argv` 는 dev 모드에서 워커 경로 뒤에 이어 붙는다.
29
29
 
30
30
  ```ts
31
31
  // main.ts (메인 측)
@@ -47,7 +47,7 @@ await worker.terminate();
47
47
 
48
48
  ## 타입
49
49
 
50
- - `interface WorkerModule { default: { __methods: Record<string, (...args: any[]) => unknown>; __events: Record<string, unknown> } }` — `Worker.create` 의 제네릭 제약. `typeof import("./worker")` 가 이 구조를 만족(=`createWorker` 반환을 default export).
50
+ - `interface WorkerModule { default: { __methods: Record<string, (...args: any[]) => unknown>; __events: Record<string, unknown> } }` — `Worker.create` 의 제네릭 제약. `typeof import("./worker")` 가 이 구조를 만족(=`createWorker` 반환을 default export)해야 한다.
51
51
  - `type PromisifyMethods<TMethods>` — 각 메서드 반환을 `Promise<Awaited<R>>` 로 바꾸는 매핑 타입. 함수가 아닌 멤버는 `never`.
52
52
  - `type WorkerProxy<TModule>` — 위 프록시 타입(Promise화 메서드 + on/off/terminate).
53
53
  - `interface WorkerRequest { id: string; method: string; params: unknown[] }` — 내부 요청 메시지.
@@ -8,7 +8,7 @@ OOXML(.xlsx) 워크북을 ZIP 단위 lazy-load 로 읽고 쓰는 라이브러리
8
8
  - **ExcelCell / ExcelRow / ExcelCol** — 개별 셀의 값·수식·병합·스타일을 읽고 쓰거나, 행/열 단위로 셀을 순회하고 열 너비를 줄 때. 자세히: [cell.md](./cell.md)
9
9
  - **셀 스타일 (ExcelStyleOptions / ExcelFont)** — 셀(`cell.setStyle`)이나 워크북 default(`wb.setDefaultStyle`)의 배경·테두리·정렬·숫자형식·폰트를 지정할 때. 자세히: [style.md](./style.md)
10
10
  - **조건부 서식 (ExcelConditionalRule / ExcelConditionalRuleStyle)** — 셀/범위에 값 비교·텍스트 매칭·수식 기반 native CF 규칙을 추가할 때. 자세히: [conditional-format.md](./conditional-format.md)
11
- - **ExcelWrapper** — Zod 스키마로 헤더 매핑·타입 변환·유효성 검사를 자동화해 레코드 배열 ↔ 엑셀을 변환할 때. 자세히: [wrapper.md](./wrapper.md)
11
+ - **ExcelWrapper** — Zod 스키마로 헤더 매핑·타입 변환·유효성 검사를 자동화해 레코드 배열 ↔ 엑셀을 변환할 때(엑셀 다운로드/업로드). 자세히: [wrapper.md](./wrapper.md)
12
12
  - **ExcelUtils** — 셀 주소(A1 ↔ 좌표) 변환, 엑셀 날짜 시리얼 ↔ 타임스탬프 변환, 숫자형식 코드/ID/이름 상호 변환이 필요할 때. 자세히: [utils.md](./utils.md)
13
13
  - **값/주소/형식 타입** — 셀 값 유니온·숫자형식 프리셋·셀 타입·주소 좌표·정렬/테두리/밑줄 enum 을 시그니처에서 참조할 때. 아래 인라인 섹션.
14
14
  - **OOXML XML-shape 타입** — `ExcelXml*` / `Excel*Data` 류 내부 직렬화 타입. 아래 인라인 섹션.
@@ -18,8 +18,8 @@ OOXML(.xlsx) 워크북을 ZIP 단위 lazy-load 로 읽고 쓰는 라이브러리
18
18
  `./types` 가 노출하는 사용자 대면 값/형식 타입. 셀 값을 다루거나 메서드 시그니처를 해석할 때 참조.
19
19
 
20
20
  - `ExcelValueType` = `number | string | DateOnly | DateTime | Time | boolean | undefined` — 셀이 가질 수 있는 값 유니온. `getValue()` 반환·`setValue()` 인자 타입. `undefined` = "값 없음"(읽기 시 빈 셀, 쓰기 시 셀 삭제)이므로 결측을 끝까지 보존한다. `DateOnly`/`DateTime`/`Time` 은 `@simplysm/core-common` 타입이며 셀에 시리얼 숫자 + 날짜 numFmt 로 저장된다.
21
- - `ExcelNumberFormat` = `"number" | "string" | "DateOnly" | "DateTime" | "Time"` — 숫자형식 프리셋 이름. `"number"` = 일반 수치(numFmtId 0), `"string"` = 텍스트 형식(numFmtId 49), `"DateOnly"`(14)/`"DateTime"`(22)/`"Time"`(18) = 날짜/시간 시리얼 해석·표시. `ExcelStyleOptions.numberFormat` 와 `ExcelUtils` 변환의 공용 단위. 날짜 셀은 값으로 직접 `DateOnly`/`Time` 을 넣으면 numFmt 가 자동 부여되므로 따로 지정할 필요가 적다.
22
- - `ExcelCellType` = `"s" | "b" | "str" | "n" | "inlineStr" | "e"` — OOXML 셀 `t` 속성. `"s"` = SharedString 인덱스 참조, `"b"` = boolean(`"1"`/`"0"`), `"str"` = 수식 결과 문자열, `"n"` = 숫자, `"inlineStr"` = 인라인 서식 텍스트, `"e"` = 에러(읽기 시 throw). 보통 `getValue`/`setValue` 가 자동 매핑하므로, 외부 생성 파일의 셀 타입을 직접 분기 검사할 때만 참조. 숫자/날짜 셀은 타입 코드가 `null` 이고 스타일 numFmt 로 구분된다.
21
+ - `ExcelNumberFormat` = `"number" | "string" | "DateOnly" | "DateTime" | "Time"` — 숫자형식 프리셋 이름. `"number"` = 일반 수치(numFmtId 0), `"string"` = 텍스트 형식(numFmtId 49), `"DateOnly"`(14)/`"DateTime"`(22)/`"Time"`(18) = 날짜/시간 시리얼 해석·표시. `ExcelStyleOptions.numberFormat` 와 `ExcelUtils` 변환의 공용 단위. 날짜 셀은 값으로 직접 `DateOnly`/`DateTime`/`Time` 을 넣으면 numFmt 가 자동 부여되므로 따로 지정할 필요가 적다.
22
+ - `ExcelCellType` = `"s" | "b" | "str" | "n" | "inlineStr" | "e"` — OOXML 셀 `t` 속성. `"s"` = SharedString 인덱스 참조, `"b"` = boolean(`"1"`/`"0"`), `"str"` = 수식 결과 문자열, `"n"` = 숫자, `"inlineStr"` = 인라인 서식 텍스트, `"e"` = 에러(읽기 시 throw). 보통 `getValue`/`setValue` 가 자동 매핑하므로 외부 생성 파일의 셀 타입을 직접 분기할 때만 참조. 숫자/날짜 셀은 타입 코드가 `null` 이고 스타일 numFmt 로 구분된다.
23
23
  - `ExcelAddressPoint` = `{ r: number; c: number }` — 0 기반 행(`r`)·열(`c`) 좌표. 셀 단일 위치 단위. `cell(r, c)` 인덱스와 동일 0 기반.
24
24
  - `ExcelAddressRangePoint` = `{ s: ExcelAddressPoint; e: ExcelAddressPoint }` — 범위 좌표. `s` = 시작(좌상단), `e` = 끝(우하단, inclusive). `getRange()` 반환 타입이며 시트 순회 루프 경계로 쓴다.
25
25
  - `ExcelBorderPosition` = `"left" | "right" | "top" | "bottom"` — 테두리 적용 변. `ExcelStyleOptions.border` 배열 원소. 4변 전부면 4개를 배열에 모두 넣음.