@simplysm/sd-claude 14.0.88 → 14.0.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -17
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +39 -43
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +174 -80
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +41 -50
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +60 -26
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +109 -37
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +61 -44
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +39 -31
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +73 -85
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -39
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +55 -30
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +71 -67
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +82 -72
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +35 -36
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +71 -43
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +38 -30
  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 +39 -38
  21. package/claude/references/sd-simplysm14/apis/core-common/README.md +95 -103
  22. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +59 -54
  23. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +86 -0
  24. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +57 -66
  25. package/claude/references/sd-simplysm14/apis/core-common/errors.md +86 -0
  26. package/claude/references/sd-simplysm14/apis/core-common/obj.md +60 -42
  27. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +55 -0
  28. package/claude/references/sd-simplysm14/apis/core-node/README.md +10 -8
  29. package/claude/references/sd-simplysm14/apis/core-node/consola.md +29 -32
  30. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +34 -22
  31. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +29 -25
  32. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +40 -53
  33. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +22 -29
  34. package/claude/references/sd-simplysm14/apis/core-node/worker.md +31 -31
  35. package/claude/references/sd-simplysm14/apis/excel/README.md +26 -26
  36. package/claude/references/sd-simplysm14/apis/excel/cell.md +37 -29
  37. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +29 -15
  38. package/claude/references/sd-simplysm14/apis/excel/style.md +33 -27
  39. package/claude/references/sd-simplysm14/apis/excel/utils.md +29 -19
  40. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +78 -55
  41. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +42 -45
  42. package/claude/references/sd-simplysm14/apis/lint/README.md +27 -21
  43. package/claude/references/sd-simplysm14/apis/lint/rules.md +89 -49
  44. package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -62
  45. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +149 -67
  46. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +111 -99
  47. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +115 -72
  48. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +134 -92
  49. package/claude/references/sd-simplysm14/apis/orm-common/types.md +67 -52
  50. package/claude/references/sd-simplysm14/apis/orm-node/README.md +63 -26
  51. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +51 -40
  52. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +10 -12
  53. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +92 -45
  54. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +226 -108
  55. package/claude/references/sd-simplysm14/apis/service-client/README.md +90 -88
  56. package/claude/references/sd-simplysm14/apis/service-client/orm.md +37 -29
  57. package/claude/references/sd-simplysm14/apis/service-client/transport.md +45 -20
  58. package/claude/references/sd-simplysm14/apis/service-common/README.md +89 -40
  59. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +126 -34
  60. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +109 -54
  61. package/claude/references/sd-simplysm14/apis/service-server/README.md +70 -66
  62. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +47 -47
  63. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +71 -34
  64. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +31 -32
  65. package/claude/references/sd-simplysm14/apis/storage/README.md +34 -28
  66. package/claude/references/sd-simplysm14/manuals/client-app-structure.md +142 -140
  67. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -1
  68. package/claude/references/sd-simplysm14/manuals/client-service.md +19 -7
  69. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +2 -2
  70. package/claude/references/sd-simplysm14/manuals/client-system-log.md +11 -3
  71. package/claude/references/sd-simplysm14/manuals/data-log.md +0 -1
  72. package/claude/references/sd-simplysm14/manuals/orm.md +16 -0
  73. package/claude/rules/sd-design-rules.md +10 -0
  74. package/claude/skills/sd-docs/SKILL.md +58 -46
  75. package/claude/skills/sd-docs/references/{doc-rules.md → subagent-prompt.md} +103 -103
  76. package/claude/skills/sd-impl/SKILL.md +1 -1
  77. package/claude/skills/sd-spec/SKILL.md +858 -858
  78. package/claude/skills/sd-spec/references/example-spec.md +26 -36
  79. package/package.json +1 -1
  80. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +0 -47
  81. package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +0 -29
  82. package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
  83. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
  84. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
  85. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
  86. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
  87. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
  88. package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
  89. package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
  90. package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
  91. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
  92. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
  93. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
  94. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
  95. package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
  96. package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
  97. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
  98. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
  99. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
  100. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
  101. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
  102. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
  103. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
  104. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
  105. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
  106. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
  107. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
  108. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
  109. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
  110. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
  111. package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
  112. package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
  113. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
  114. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
  115. package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
  116. package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
  117. package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
  118. package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
  119. package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
  120. package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
  121. package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
  122. package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
  123. package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
  124. package/claude/skills/sd-review/evals/golden.jsonl +0 -2
  125. package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
  126. package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
  127. package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
  128. package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
  129. package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
  130. package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
  131. package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
  132. package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
  133. package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
  134. package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
  135. package/claude/skills/sd-use/evals/golden.jsonl +0 -6
@@ -1,95 +1,86 @@
1
- # @simplysm/core-common — 날짜·시간
1
+ # @simplysm/core-common — 날짜·시간 값 타입
2
2
 
3
- 날짜/시간 값을 다룰 때 함께 읽히는 묶음. 불변 클래스 `DateTime`/`DateOnly`/`Time` 포맷 문자열을 다루는 `dt` 네임스페이스. 클래스 모두 로컬 타임존 기준으로 동작하며, 변환/산술 메서드는 원본을 바꾸지 않고 새 인스턴스를 반환한다.
3
+ 날짜·시간을 **불변(immutable)** 값으로 다룰 때 함께 읽히는 묶음. JS `Date` 대신 사용. `set*`·`add*` 메서드는 모두 인스턴스를 반환하고 원본을 바꾸지 않음. 모두 로컬 타임존 기준으로 동작. ORM 컬럼 타입(`DateTime`/`DateOnly`/`Time`)·JSON/Worker 직렬화에서 1급 지원됨.
4
+
5
+ 세 클래스 공통:
6
+ - `parse(str)` 정적 메서드로 문자열 파싱(미지원 형식이면 ArgumentError).
7
+ - `tick` getter — 내부 밀리초 값. `equal`·정렬·복제의 동등성 기준.
8
+ - `isValid` getter — 유효 값 여부.
9
+ - `toFormatString(formatStr)` / `toString()` — 포맷 문자열 변환(아래 `dt` 네임스페이스의 포맷 토큰 사용).
4
10
 
5
11
  ## DateTime
6
12
 
7
- 날짜+시간(밀리초 정밀도) 불변 클래스. 내부에 `readonly date: Date` 보유.
13
+ 날짜+시간(밀리초 정밀도) 불변 클래스.
8
14
 
9
- 생성자:
15
+ 생성자 오버로드:
10
16
  - `new DateTime()` — 현재 시각.
11
- - `new DateTime(year, month, day, hour?, minute?, second?, millisecond?)` — `month` 는 1~12(내부에서 0-base 변환). 생략한 시/분/초/밀리초는 0.
12
- - `new DateTime(tick: number)` — epoch 밀리초.
13
- - `new DateTime(date: Date)` — Date 복사(원본과 분리).
14
-
15
- - `DateTime.parse(str): DateTime`문자열 파싱. 지원: ISO 8601, `yyyy-MM-dd HH:mm:ss(.fff)`, `yyyyMMddHHmmss`, `yyyy-MM-dd AM/PM HH:mm:ss`, 한국어 `오전/오후`. 실패 `ArgumentError` throw.
16
-
17
- 읽기 전용 getter:
18
- - `year`/`month`(1~12)/`day`/`hour`/`minute`/`second`/`millisecond` — 각 구성요소.
19
- - `tick: number` epoch 밀리초. 두 시점 비교·차이 계산에 사용.
20
- - `dayOfWeek: number` — 요일(일=0 ~ 토=6).
21
- - `timezoneOffsetMinutes: number` — UTC 대비 오프셋 (KST=+540). `Date.getTimezoneOffset()` 부호 반대.
22
- - `isValid: boolean` — 유효한 날짜인지. 잘못된 tick 으로 만든 인스턴스 검증에 사용.
23
-
24
- 변환 메서드(새 인스턴스 반환):
25
- - `setYear/setMonth/setHour/setMinute/setSecond/setMillisecond(n)` 해당 구성요소만 교체. `setMonth` 는 범위 밖 월을 연도로 넘기고, 대상 월 일수 초과 시 말일로 보정(1/31 → setMonth(2) → 2/28·29).
26
- - `setDay(n)` 일 교체. 월 범위를 벗어나는 일은 JS Date 동작대로 다음/이전 월로 넘어감(1월 day=32 → 2/1).
27
-
28
- 산술 메서드(새 인스턴스 반환):
29
- - `addYears/addMonths(n)` — `setYear/setMonth` 경유라 말일 보정 규칙을 따름.
30
- - `addDays/addHours/addMinutes/addSeconds/addMilliseconds(n)` — 음수 가능. 시 이하는 tick 기반 가산이라 DST 경계를 그대로 통과.
31
-
32
- 포맷:
33
- - `toFormatString(formatStr): string` — 포맷 문자열로 변환(아래 `dt.format` 토큰 참조).
34
- - `toString(): string` — `yyyy-MM-ddTHH:mm:ss.fffzzz` 형식.
35
-
36
- ```typescript
37
- const d = DateTime.parse("2025-01-15 10:30:00");
38
- d.addDays(1).toFormatString("yyyy-MM-dd (ddd)"); // "2025-01-16 (목)"
17
+ - `new DateTime(year, month, day, hour?, minute?, second?, millisecond?)` — month 는 1~12(내부에서 0-base 변환). 시·분·초·밀리초 생략 0.
18
+ - `new DateTime(tick)` — 밀리초 tick.
19
+ - `new DateTime(date)` — JS Date 복제.
20
+
21
+ - `DateTime.parse(str)`: DateTime — 지원 형식: `yyyy-MM-dd HH:mm:ss[.fff]`, `yyyyMMddHHmmss`, `yyyy-MM-dd AM/PM HH:mm:ss`, 한국어 `yyyy-MM-dd 오전/오후 HH:mm:ss`, ISO 8601.
22
+
23
+ getter: `year`·`month`(1~12)·`day`·`hour`·`minute`·`second`·`millisecond`·`tick`·`dayOfWeek`(일~토=0~6)·`timezoneOffsetMinutes`(UTC 대비 분, KST=+540)·`isValid`·`date`(내부 Date, 읽기 전용).
24
+
25
+ 불변 변환(새 인스턴스): `setYear`·`setMonth`·`setDay`·`setHour`·`setMinute`·`setSecond`·`setMillisecond`. 산술(새 인스턴스): `addYears`·`addMonths`·`addDays`·`addHours`·`addMinutes`·`addSeconds`·`addMilliseconds`.
26
+
27
+ - `setMonth(month)` / `addMonths`대상 월의 일수가 현재 일보다 적으면 그 달 마지막 일로 보정(1/31 → setMonth(2) 2/28|29). 범위 밖 월은 연도로 캐리.
28
+ - `setDay(day)` — 범위 일은 JS Date 규칙대로 다음/이전 월로 넘어감.
29
+
30
+ ```ts
31
+ import { DateTime } from "@simplysm/core-common";
32
+ const at = DateTime.parse("2025-01-15 10:30:00");
33
+ at.addDays(3).toFormatString("yyyy-MM-dd HH:mm"); // "2025-01-18 10:30"
39
34
  ```
40
35
 
41
36
  ## DateOnly
42
37
 
43
- 시간 제외 날짜만(`yyyy-MM-dd`) 불변 클래스. 주차(week) 계산 메서드 포함.
44
-
45
- 생성자: `new DateOnly()`(오늘) / `(year, month, day)` / `(tick)` / `(date)` — 모두 시간 부분을 버리고 자정으로 정규화.
38
+ 시간 없는 날짜(yyyy-MM-dd) 불변 클래스.
46
39
 
47
- - `DateOnly.parse(str): DateOnly` `yyyy-MM-dd`/`yyyyMMdd`(타임존 무관, 문자열 직접 추출) 또는 ISO 8601(UTC 해석 후 로컬 변환). 서버·클라 타임존이 다르면 `yyyy-MM-dd` 권장. 실패 시 `ArgumentError`.
40
+ 생성자: `new DateOnly()`(오늘) / `(year, month, day)` / `(tick)` / `(date)`.
48
41
 
49
- getter: `isValid`/`year`/`month`(1~12)/`day`/`tick`/`dayOfWeek`(일=0~토=6).
42
+ - `DateOnly.parse(str)`: → DateOnly — `yyyy-MM-dd`·`yyyyMMdd`(타임존 무관, 문자열에서 직접 추출)·ISO 8601(UTC 해석 후 로컬 변환). 서버/클라 타임존이 다르면 `yyyy-MM-dd` 형식 권장.
50
43
 
51
- 변환/산술: `setYear/setMonth/setDay`, `addYears/addMonths/addDays` — DateTime 과 동일한 말일·월 넘김 규칙.
44
+ getter: `year`·`month`·`day`·`tick`·`dayOfWeek`·`isValid`·`date`. 불변 변환: `setYear`·`setMonth`·`setDay`(DateTime 과 동일한 월말/캐리 보정). 산술: `addYears`·`addMonths`·`addDays`.
52
45
 
53
- 주차 계산 공통 인자 `weekStartDay`(주 시작 요일, 0=일~6=토, 기본 1=월)·`minDaysInFirstWeek`(첫 주로 인정할 최소 일수 1~7, 기본 4=ISO 8601 표준):
54
- - `getBaseYearMonthSeqForWeekSeq(weekStartDay?, minDaysInFirstWeek?): { year, monthSeq }` — 날짜가 속한 주의 기준 연·월. 월 경계 주를 이전/다음 달 중 어디로 귀속할지 판정.
55
- - `getWeekSeqStartDate(weekStartDay?, minDaysInFirstWeek?): DateOnly` — 날짜가 속한 주의 시작일.
56
- - `getWeekSeqOfYear(weekStartDay?, minDaysInFirstWeek?): { year, weekSeq }` 기준 주차 번호.
57
- - `getWeekSeqOfMonth(weekStartDay?, minDaysInFirstWeek?): { year, monthSeq, weekSeq }` — 기준 주차 번호.
58
- - `DateOnly.getDateByYearWeekSeq(arg, weekStartDay?, minDaysInFirstWeek?): DateOnly` — `arg: { year, month?, weekSeq }` 로 해당 주의 시작일 역산. `month` 생략 시 연 단위 주차.
46
+ 주차 계산(ISO 8601 기본: weekStartDay=1 월요일, minDaysInFirstWeek=4):
47
+ - `getWeekSeqOfYear(weekStartDay?, minDaysInFirstWeek?)`: → `{ year, weekSeq }` — 해당 연도 주차 번호.
48
+ - `getWeekSeqOfMonth(weekStartDay?, minDaysInFirstWeek?)`: `{ year, monthSeq, weekSeq }` 해당 주차 번호.
49
+ - `getWeekSeqStartDate(weekStartDay?, minDaysInFirstWeek?)`: DateOnly 날짜가 속한 주의 시작일.
50
+ - `getBaseYearMonthSeqForWeekSeq(weekStartDay?, minDaysInFirstWeek?)`: → `{ year, monthSeq }` — 주차 귀속 기준 연·월(주가 걸친 경우 어느 달로 셈할지).
51
+ - `DateOnly.getDateByYearWeekSeq(arg, weekStartDay?, minDaysInFirstWeek?)`: DateOnly — `arg = { year, month?, weekSeq }` 로 주의 시작일을 역산.
59
52
 
60
- 포맷: `toFormatString(formatStr)`, `toString()` → `yyyy-MM-dd`.
53
+ 옵션 풀이:
54
+ - weekStartDay: 0~6 — 주 시작 요일. 0=일요일(미국식), 1=월요일(ISO, 기본). 달력 표시 기준에 맞춤.
55
+ - minDaysInFirstWeek: 1~7 — 첫 주로 인정할 최소 일수. 4=ISO(주의 과반), 1=시작일 포함 즉시 1주차(미국식).
61
56
 
62
- ```typescript
63
- new DateOnly(2025, 1, 15).getWeekSeqOfMonth(); // { year: 2025, monthSeq: 1, weekSeq: 3 }
57
+ ```ts
58
+ import { DateOnly } from "@simplysm/core-common";
59
+ new DateOnly(2025, 1, 6).getWeekSeqOfYear(); // { year: 2025, weekSeq: 2 }
64
60
  ```
65
61
 
66
62
  ## Time
67
63
 
68
- 날짜 제외 시간만(`HH:mm:ss.fff`) 불변 클래스. 24시간을 넘거나 음수인 값은 자동으로 0~24시 범위로 순환 정규화됨.
69
-
70
- 생성자: `new Time()`(현재 시각의 시간부) / `(hour, minute, second?, millisecond?)` / `(tick)`(하루 내 밀리초) / `(date)`(Date 의 시간부만).
71
-
72
- - `Time.parse(str): Time` — `HH:mm:ss(.fff)`, `AM/PM HH:mm:ss`, ISO 8601(시간부만 추출) 지원. 실패 시 `ArgumentError`.
64
+ 날짜 없는 시간(HH:mm:ss.fff) 불변 클래스. 24시간 초과·음수 tick 자동으로 0~24h 범위로 순환 정규화됨.
73
65
 
74
- getter: `hour`/`minute`/`second`/`millisecond`/`tick`(하루 밀리초)/`isValid`.
66
+ 생성자: `new Time()`(현재 시각) / `(hour, minute, second?, millisecond?)` / `(tick)` / `(date)`(Date 시간부만).
75
67
 
76
- 변환: `setHour/setMinute/setSecond/setMillisecond(n)`.
77
- 산술: `addHours/addMinutes/addSeconds/addMilliseconds(n)` — 24시간 순환(23:30 + 1h → 00:30).
68
+ - `Time.parse(str)`: → Time — `HH:mm:ss[.fff]`·`AM/PM HH:mm:ss`·ISO 8601(시간부만 추출).
78
69
 
79
- 포맷: `toFormatString(formatStr)`, `toString()` → `HH:mm:ss.fff`.
70
+ getter: `hour`·`minute`·`second`·`millisecond`·`tick`·`isValid`. 불변 변환: `setHour`·`setMinute`·`setSecond`·`setMillisecond`. 산술: `addHours`·`addMinutes`·`addSeconds`·`addMilliseconds` (모두 24시간 순환 — 23:30 에 +1h 00:30).
80
71
 
81
- ## dt (네임스페이스)
72
+ ## dt 네임스페이스 (날짜/시간 포맷)
82
73
 
83
- `import { dt } from "@simplysm/core-common"`. 세 클래스의 `toFormatString` 내부 구현이자, 직접 호출도 가능한 포맷터.
74
+ `toFormatString` 내부적으로 쓰는 포맷 토큰 정의. 직접 호출도 가능.
84
75
 
85
- - `dt.format(formatString, args): string` — `args: { year?, month?, day?, hour?, minute?, second?, millisecond?, timezoneOffsetMinutes? }` 중 제공된 구성요소만 치환(미제공 토큰은 원문 유지). 요일(`ddd`)은 year·month·day 가 모두 있을 때만 계산.
86
- - `dt.normalizeMonth(year, month, day): { year, month, day }` — 1~12 범위 밖 월을 연도로 넘기고 말일 보정.
87
- - `dt.convert12To24(rawHour, isPM): number` — 12시간제(1~12)+오전/오후 → 24시간제(0~23). 12 AM=0, 12 PM=12.
88
- - 타입 `dt.DtNormalizedMonth = { year, month, day }`.
76
+ - `dt.format(formatString, args)`: string — `args = { year?, month?, day?, hour?, minute?, second?, millisecond?, timezoneOffsetMinutes? }` 중 주어진 구성요소만 치환. C# 스타일 토큰 사용.
77
+ - `dt.normalizeMonth(year, month, day)`: → `{ year, month, day }` — 범위 밖 월을 연도로 캐리하고 일을 월말로 보정. `set*Month` 구현 기반.
78
+ - `dt.convert12To24(rawHour, isPM)`: number — 12시간제(1~12)+오전/오후 → 24시간제(0~23). 12 AM=0, 12 PM=12.
79
+ - 타입 `dt.DtNormalizedMonth` = `{ year; month; day }`.
89
80
 
90
- 포맷 토큰(C# 호환): `yyyy`(4자리 연)·`yy`(2자리), `MM`/`M`(0채움/일반 월), `ddd`(요일 한글 일~토), `dd`/`d`(일), `tt`(AM/PM), `hh`/`h`(12시간), `HH`/`H`(24시간), `mm`/`m`(분), `ss`/`s`(초), `fff`/`ff`/`f`(밀리초 3/2/1자리), `zzz`(±HH:mm)·`zz`(±HH)·`z`(±H) 타임존 오프셋.
81
+ 포맷 토큰: `yyyy`/`yy`(연), `MM`/`M`(월), `ddd`(요일 일~토)/`dd`/`d`(일), `tt`(AM/PM), `hh`/`h`(12시간), `HH`/`H`(24시간), `mm`/`m`(분), `ss`/`s`(초), `fff`/`ff`/`f`(밀리초), `zzz`(±HH:mm)/`zz`(±HH)/`z`(±H, 타임존 오프셋).
91
82
 
92
- ```typescript
93
- dt.format("yyyy-MM-dd tt h:mm", { year: 2024, month: 3, day: 15, hour: 14, minute: 30 });
94
- // "2024-03-15 PM 2:30"
83
+ ```ts
84
+ import { dt } from "@simplysm/core-common";
85
+ dt.format("yyyy-MM-dd (ddd)", { year: 2024, month: 3, day: 15 }); // "2024-03-15 (금)"
95
86
  ```
@@ -0,0 +1,86 @@
1
+ # @simplysm/core-common — 에러 클래스
2
+
3
+ `throw` 를 던지거나, 에러 원인을 체인으로 감싸거나, catch 에서 `instanceof` 로 분기할 때 함께 읽히는 묶음. 모두 `SdError` 를 상속하므로 `instanceof SdError` 로 한꺼번에 잡을 수 있음.
4
+
5
+ ## SdError
6
+
7
+ ```ts
8
+ class SdError extends Error {
9
+ override cause?: Error;
10
+ constructor(cause: Error, ...messages: string[]); // 원인 에러를 감싸기
11
+ constructor(...messages: string[]); // 메시지만으로 생성
12
+ }
13
+ ```
14
+
15
+ ES2024 `cause` 를 활용한 트리형 에러. 메시지는 **역순으로** `" => "` 로 결합됨(상위 메시지가 앞).
16
+
17
+ - cause: Error — 첫 인자가 Error 면 원인 에러로 보존(`this.cause`). 원인 에러의 stack 이 현재 stack 뒤에 `---- cause stack ----` 로 이어 붙음. 하위 호출에서 받은 에러를 상위 문맥으로 감쌀 때.
18
+ - ...messages: string[] — 문맥 메시지들. `new SdError(err, "API 호출 실패", "사용자 로드 실패")` → `"사용자 로드 실패 => API 호출 실패 => 원본 메시지"`. null/undefined 메시지는 제외됨.
19
+ - `name` 은 `"SdError"`. V8(Node·Chrome)에서 `captureStackTrace` 로 생성자 프레임 제거.
20
+
21
+ ```ts
22
+ import { SdError } from "@simplysm/core-common";
23
+ try {
24
+ await fetch(url);
25
+ } catch (err) {
26
+ throw new SdError(err, "API 호출 실패", "사용자 로드 실패");
27
+ }
28
+ ```
29
+
30
+ 주의: 첫 인자가 Error 가 아니면(문자열·기타) cause 없이 메시지로만 취급됨. `new SdError("잘못된 상태", "처리 불가")` → `"처리 불가 => 잘못된 상태"`.
31
+
32
+ ## ArgumentError
33
+
34
+ ```ts
35
+ class ArgumentError extends SdError {
36
+ constructor(argObj: Record<string, unknown>);
37
+ constructor(message: string, argObj: Record<string, unknown>);
38
+ }
39
+ ```
40
+
41
+ 유효하지 않은 인자를 받았을 때 던지는 에러. 디버깅을 위해 인자 객체를 **YAML 형식**으로 메시지에 붙임. `name` 은 `"ArgumentError"`.
42
+
43
+ - argObj: Record<string, unknown> — 메시지에 YAML 로 직렬화해 포함할 인자값들. 어떤 입력이 문제였는지 드러낼 때.
44
+ - message: string — 커스텀 머리말. 생략 시 `"잘못된 인자입니다."` 사용.
45
+
46
+ ```ts
47
+ import { ArgumentError } from "@simplysm/core-common";
48
+ throw new ArgumentError("유효하지 않은 UUID 형식입니다.", { uuid });
49
+ // 메시지: "유효하지 않은 UUID 형식입니다.\n\nuuid: ..."
50
+ ```
51
+
52
+ 이 패키지 내부 검증(Uuid·bytes·obj 체인 등)에서 이미 광범위하게 throw 하므로, 유효성 위반은 직접 처리하지 말고 그대로 전파하는 편이 일관적.
53
+
54
+ ## NotImplementedError
55
+
56
+ ```ts
57
+ class NotImplementedError extends SdError {
58
+ constructor(message?: string);
59
+ }
60
+ ```
61
+
62
+ 아직 구현되지 않은 기능이 호출됐을 때. 메시지는 `"미구현"` 또는 `"미구현: <message>"`. `name` 은 `"NotImplementedError"`. 추상 메서드 스텁, 미구현 분기에 사용.
63
+
64
+ - message?: string — 무엇이 미구현인지 추가 설명. 예: `throw new NotImplementedError(\`타입 ${type} 처리\`)`.
65
+
66
+ ## TimeoutError
67
+
68
+ ```ts
69
+ class TimeoutError extends SdError {
70
+ constructor(count?: number, message?: string);
71
+ }
72
+ ```
73
+
74
+ 대기 시간 초과 에러. 메시지는 `"대기 시간 초과"` + (count 있으면 `(N회 시도)`) + (message 있으면 `: <message>`). `name` 은 `"TimeoutError"`.
75
+
76
+ - count?: number — 시도 횟수. `wait.until(...)` 이 최대 시도 초과 시 자동으로 이 에러를 throw(시도 횟수를 넣어).
77
+ - message?: string — 무엇을 기다리다 초과했는지 추가 설명.
78
+
79
+ ```ts
80
+ import { TimeoutError, wait } from "@simplysm/core-common";
81
+ try {
82
+ await wait.until(() => isReady, 100, 50);
83
+ } catch (err) {
84
+ if (err instanceof TimeoutError) { /* 타임아웃 처리 */ }
85
+ }
86
+ ```
@@ -1,53 +1,71 @@
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"` 접근하는 객체 조작 유틸. 깊은 복사/비교/병합, 체인 경로 접근, key 변환을 때 함께 읽힘. 깊은 연산은 커스텀 값 타입(DateTime·DateOnly·Time·Uuid·Uint8ArrayDate·RegExp·Map·Set·Error 를 인지하고 순환 참조를 처리함.
4
+
5
+ ## clone
6
+
7
+ - `obj.clone(source)`: 동일 타입 — 깊은 복사. 순환 참조 지원. Date/DateTime/DateOnly/Time/Uuid/Uint8Array/RegExp/Array/Map/Set/Error(cause·커스텀 속성 포함) 일반 객체(프로토타입 체인 유지)를 복제.
8
+ - 주의: 함수·Symbol 은 복사 안 되고 참조 유지. WeakMap/WeakSet 미지원( 객체화). getter/setter 현재 값으로 평가되어 복사(접근자 자체는 복사 안 됨).
9
+
10
+ ## equal
11
+
12
+ - `obj.equal(source, target, options?)`: → boolean — 깊은 동등성 비교. Date/날짜타입(tick)/Uuid(문자열)/RegExp/Array/Map/Set/일반 객체를 인지. null/undefined 인 속성은 비교에서 제외(없는 것으로 취급).
13
+
14
+ 옵션(`EqualOptions`):
15
+ - topLevelIncludes?: string[]비교할 key 화이트리스트(최상위 객체 속성에만 적용). 일부 필드만 같으면 OK 때.
16
+ - topLevelExcludes?: string[]비교에서 key(최상위만). `updatedAt` 같은 변동 필드 무시할 때.
17
+ - ignoreArrayIndex?: booleantrue 배열 순서 무시(같은 다중집합인지). O(). `[1,2,3]==[3,2,1]`.
18
+ - shallow?: boolean true 면 1단계만 참조 비교. 대용량에서 성능 우선일 때.
19
+ - 주의: include/exclude 는 객체 속성 key 에만 적용. Map 의 key 는 항상 전부 비교됨.
20
+
21
+ ## merge / merge3
22
+
23
+ - `obj.merge(source, target, opt?)`: → `Source & Target` 깊은 병합(원본 불변, 새 객체 반환). target 값으로 source 덮어쓰되 객체/Map 재귀 병합. 날짜타입·Uuid·Uint8Array 는 통째로 교체.
24
+ - opt.arrayProcess?: `"replace" | "concat"` 배열 처리. `"replace"`(기본)=target 배열로 교체, `"concat"`=합치고 Set 으로 중복 제거(객체는 참조 비교).
25
+ - opt.useDelTargetNull?: boolean true target 값이 null 일 때 해당 key 를 결과에서 삭제. 패치에서 "필드 제거"를 표현할 때.
26
+ - `obj.merge3(source, origin, target, optionsObj?)`: → `{ conflict, result }` 3-way 병합(공통 조상 origin 기준). source 만 바뀌면 source, target 만 바뀌면 target, 둘 다 같으면 그 값, 셋 다 다르면 conflict=true(origin 유지). optionsObj 는 key 별 비교 옵션(`Merge3KeyOptions`: keys/excludes/ignoreArrayIndex). 동시 편집 충돌 감지에 사용.
27
+
28
+ ```ts
29
+ import { obj } from "@simplysm/core-common";
30
+ const merged = obj.merge(base, patch, { arrayProcess: "concat", useDelTargetNull: true });
31
+ const { conflict, result } = obj.merge3(mine, origin, theirs);
32
+ ```
33
+
34
+ ## omit / pick
35
+
36
+ - `obj.omit(item, omitKeys)`: → `Omit<T,K>` — 지정 key 제외한 새 객체.
37
+ - `obj.omitByFilter(item, omitKeyFn)`: → T — `omitKeyFn(key)` 가 true 인 key 제외(예: `_` 로 시작하는 내부 속성).
38
+ - `obj.pick(item, pickKeys)`: → `Pick<T,K>` — 지정 key 만 남긴 새 객체.
29
39
 
30
40
  ## 체인 경로 접근
31
41
 
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)` — 경로로 값 삭제. 중간 경로가 없으면 조용히 반환.
42
+ 문자열 경로(`"a.b[0].c"`)로 중첩접근. `?`·`!`·따옴표는 무시됨.
36
43
 
37
- ## 정리 변환 (@mutates 표기는 원본 수정)
44
+ - `obj.getChainValue(o, chain)` / `getChainValue(o, chain, true)`: → unknown — 경로 값 조회. 세 번째 `true` 면 중간 null/undefined 를 만나도 에러 없이 undefined.
45
+ - `obj.getChainValueByDepth(o, key, depth, optional?)`: → 값 — 같은 key 로 depth 단계 하강(예: `parent` 를 2번). depth<1 이면 ArgumentError. optional 처리는 위와 동일.
46
+ - `obj.setChainValue(o, chain, value)`: → void — 경로에 값 설정(중간 객체 자동 생성). 빈 chain 이면 ArgumentError.
47
+ - `obj.deleteChainValue(o, chain)`: → void — 경로 값 삭제(중간 경로 없으면 조용히 반환).
38
48
 
39
- - `obj.clearUndefined(target)` undefined/null 값 key 삭제(원본 수정).
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 } } }`.
49
+ ## 정리 유틸 (@mutates)
43
50
 
44
- ## 타입 유틸리티
51
+ - `obj.clearUndefined(o)`: → T — null/undefined 값 key 를 원본에서 삭제.
52
+ - `obj.clear(o)`: → 빈 객체 — 모든 key 삭제.
53
+ - `obj.nullToUndefined(o)`: → `T | undefined` — null 을 undefined 로 재귀 변환(순환 안전). 날짜타입·Uuid 는 통과. simplysm 의 null-free 규칙 적용에 사용.
54
+ - `obj.unflatten(flatObj)`: → 중첩 객체 — `{ "a.b.c": 1 }` → `{ a: { b: { c: 1 } } }`.
45
55
 
46
- - `obj.UndefToOptional<T>` `undefined` 포함한 속성을 optional(`?`)로. 예: `{ b: string | undefined }` → `{ b?: string | undefined }`.
47
- - `obj.OptionalToUndef<T>` — optional 속성을 `필수 + undefined` 유니온으로(역변환).
56
+ ## 타입 안전 Object.* 변환
48
57
 
49
- ```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);
58
+ - `obj.keys(o)`: → `(keyof T)[]` — 타입 안전 `Object.keys`.
59
+ - `obj.entries(o)`: `Entries<T>` 타입 안전 `Object.entries`(튜플 타입 보존).
60
+ - `obj.fromEntries(entryPairs)`: 객체 타입 안전 `Object.fromEntries`.
61
+ - `obj.map(o, fn)`: → 새 객체 — 각 엔트리를 `fn(key, value) => [newKey | null, newValue]` 로 변환. newKey 가 null 이면 기존 key 유지(값만 변환). key+값 동시 변환 가능.
62
+
63
+ ```ts
64
+ obj.map(colors, (key, rgb) => [`${key}Light`, `rgb(${rgb})`]);
53
65
  ```
66
+
67
+ ## 함께 export 되는 타입 유틸
68
+
69
+ - `UndefToOptional<T>` — `undefined` 를 포함한 속성을 optional(`?`)로 변환.
70
+ - `OptionalToUndef<T>` — optional 속성을 `필수 + undefined` union 으로 변환.
71
+ - 옵션 타입: `EqualOptions`·`MergeOptions`·`Merge3KeyOptions`.
@@ -0,0 +1,55 @@
1
+ # @simplysm/core-common — 직렬화 / Worker 전송
2
+
3
+ 커스텀 타입(DateTime·DateOnly·Time·Uuid·Map·Set·Error·Uint8Array 등)을 포함한 데이터를 JSON·XML·바이트·Worker 메시지로 주고받을 때 함께 읽히는 묶음. JSON·transfer 는 `__type__` 태그 방식으로 표준 직렬화가 잃어버리는 타입을 복원함.
4
+
5
+ ## json 네임스페이스
6
+
7
+ - `json.stringify(obj, options?)`: → string — 커스텀 타입 보존 JSON 직렬화. Date/DateTime/DateOnly/Time/Uuid/Set/Map/Error/Uint8Array 를 `{ __type__, data }` 형태로 변환. 전역 프로토타입을 건드리지 않아 Worker 환경에서도 안전. 순환 참조면 TypeError.
8
+ - options.space?: `string | number` — 들여쓰기(숫자=공백 수, 문자열=들여쓰기 문자).
9
+ - options.replacer?: `(key, value) => unknown` — 기본 타입 변환 **전에** 호출되는 커스텀 변환.
10
+ - options.redactBytes?: boolean — true 면 Uint8Array 내용을 `"__hidden__"` 로 대체(로깅용). 이 결과는 `json.parse` 로 복원 불가.
11
+ - `json.parse<T>(json)`: → T — `json.stringify` 결과 역직렬화. `__type__` 태그를 보고 원래 타입 복원. **모든 JSON null 은 undefined 로 변환**됨(null-free 규칙). `redactBytes` 로 가려진 Uint8Array 를 만나면 SdError. 파싱 실패 시 SdError(개발 모드 `env("DEV")` 이면 전체 JSON, 운영이면 길이만 메시지에 포함).
12
+ - 주의: 사용자 데이터에 `{ __type__: "Date"|..., data: ... }` 형태가 우연히 들어 있으면 의도치 않게 타입으로 변환될 수 있음.
13
+
14
+ ```ts
15
+ import { json } from "@simplysm/core-common";
16
+ const text = json.stringify({ at: new DateTime(), ids: new Set([1, 2]) });
17
+ const back = json.parse<{ at: DateTime; ids: Set<number> }>(text); // 타입 복원
18
+ ```
19
+
20
+ ## transfer 네임스페이스 (Worker 전송)
21
+
22
+ `structuredClone` 이 못 다루는 커스텀 타입을 Worker 로 보내기 위한 인코딩/디코딩. `postMessage` 의 transferList(zero-copy)와 연동.
23
+
24
+ - `transfer.encode(obj)`: → `{ result, transferList }` — 직렬화 가능한 형태로 변환. Uint8Array 의 ArrayBuffer 를 transferList 에 모아 zero-copy 전송 준비(SharedArrayBuffer 는 제외). Date/DateTime/DateOnly/Time/Uuid/RegExp/Error(cause·code·detail 포함)는 `__type__` 태그로 변환, Array/Map/Set/일반 객체는 재귀. 같은 객체 재참조는 캐시 재사용. **순환 참조면 TypeError(경로 포함)**.
25
+ - `transfer.decode(obj)`: → unknown — Worker 수신 데이터를 커스텀 타입으로 복원(encode 의 역).
26
+
27
+ ```ts
28
+ import { transfer } from "@simplysm/core-common";
29
+ const { result, transferList } = transfer.encode(data);
30
+ worker.postMessage(result, transferList);
31
+ // worker 측: const decoded = transfer.decode(event.data);
32
+ ```
33
+
34
+ json 과의 차이: transfer 는 날짜를 tick(숫자)로 인코딩하고 RegExp 를 지원하며 Uint8Array 를 변환 없이 transferList 로 넘김(메모리 효율). 문자열 산출물이 필요하면 json, Worker 간 객체 전송이면 transfer.
35
+
36
+ ## bytes 네임스페이스 (Uint8Array 인코딩)
37
+
38
+ - `bytes.concat(arrays)`: → Bytes — 여러 Uint8Array 결합.
39
+ - `bytes.toHex(b)`: → string — 소문자 hex 문자열.
40
+ - `bytes.fromHex(hex)`: → Bytes — hex(대소문자 허용)→바이트. 홀수 길이·비 hex 문자면 ArgumentError.
41
+ - `bytes.toBase64(b)`: → string — Base64 인코딩(표준 패딩 `=`).
42
+ - `bytes.fromBase64(b64)`: → Bytes — Base64 디코딩. 공백·패딩 정규화 후 비 base64 문자·잘못된 길이면 ArgumentError.
43
+
44
+ ## xml 네임스페이스
45
+
46
+ `fast-xml-parser` 래퍼. 속성은 `$` 객체, 텍스트 노드는 `_` key, 자식 요소는 배열로 표현.
47
+
48
+ - `xml.parse(str, options?)`: → unknown — XML→객체. `options.stripTagPrefix?: boolean` true 면 태그의 네임스페이스 접두사(`ns:tag`→`tag`) 제거(속성 접두사는 유지).
49
+ - `xml.stringify(obj, options?)`: → string — 객체→XML. options 는 `fast-xml-parser` 의 `XmlBuilderOptions`(선택).
50
+
51
+ ```ts
52
+ import { xml } from "@simplysm/core-common";
53
+ xml.parse('<root id="1"><item>hello</item></root>');
54
+ // { root: { $: { id: "1" }, item: [{ _: "hello" }] } }
55
+ ```
@@ -1,14 +1,16 @@
1
1
  # @simplysm/core-node
2
2
 
3
- Node.js 환경 전용 유틸리티 모음 파일시스템·자식 프로세스·경로 조작, 파일 감시, consola 로깅 설정, worker_threads 래퍼.
3
+ Node.js 런타임 전용 유틸·기능 모음. `@simplysm/core-common` 위에 Node API(`fs`/`path`/`child_process`/`worker_threads`/`chokidar`/`consola`)를 얹은 계층이므로 브라우저에서는 사용 불가.
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** — 파일/디렉토리 존재 확인·생성·삭제·복사·읽기/쓰기(텍스트·바이너리·JSON)·stat·glob·부모 탐색을 동기/비동기 쌍으로 다룰 때. 모든 오류를 `SdError(원인, 경로)` 감싸 throw. 자세히: [fsx.md](./fsx.md)
10
+ - **pathx** — 경로를 POSIX(슬래시)로 정규화하거나, 하위경로 판정·디렉토리 치환·확장자 제거 basename·타겟 필터링 같은 경로 문자열 가공이 필요할 때. 자세히: [pathx.md](./pathx.md)
11
+ - **cpx** — 외부 명령을 실행해 stdout/stderr OS 인코딩으로 디코딩해 받을 때(`spawn`/`spawnSync`), 또는 OS 코드페이지 인코딩 감지가 필요할 때. exitCode≠0 자동 throw. 자세히: [cpx.md](./cpx.md)
12
+ - **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)
14
+ - **Worker / createWorker / WorkerProxy / WorkerModule / PromisifyMethods / WorkerRequest / WorkerResponse** — worker_threads 를 타입 안전한 메서드 호출·이벤트·로그 전달 프록시로 때. 자세히: [worker.md](./worker.md)
15
+
16
+ 위 6개 군은 사용 시점·컨텍스트가 분리되어 각각 별도 `.md` 로 분할됨. README 인라인 군 없음.
@@ -1,51 +1,48 @@
1
1
  # @simplysm/core-node — consola 로깅 설정
2
2
 
3
- 전역 `consola` 로거의 level/reporter 를 환경(prod/dev/cli) 맞춰 구성한다. 콘솔용 pretty 출력과 일자별 회전 파일 로그(콘솔과 동일한 평문, 색만 제거)제공한다.
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
- ## setupConsola
5
+ **Node 진입점(서버·CLI) 에서 1회 `setupConsola()` 호출** 이 일반 사용. Browser·Capacitor 진입점에서는 호출 금지(Node 전용 API) — 그쪽은 consola 기본 reporter 가 브라우저 콘솔로 출력한다.
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 }`.
7
+ ## setupConsola
10
8
 
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 이하까지만.
9
+ - `setupConsola(opts?: SetupConsolaOptions): void` — 환경 변수(`DEV`, `SD_DEBUG`)에 따라 `consola.level` 과 `reporters` 를 설정. 모든 분기에서 level 은 `debug` 까지 포함.
10
+ - `opts.cli?: boolean` CLI 모드 여부. true prod 분기를 건너뛰고 항상 콘솔 출력 경로로 감.
11
+ - 분기:
12
+ - `cli` 아니고 `DEV` 아님(prod) 파일 reporter 만(`createFileReporter()`). 콘솔 출력 없음, debug 까지 파일 기록.
13
+ - `SD_DEBUG` 참(dev+디버그) → `PrettyReporter` 만, debug 까지 콘솔 출력.
14
+ - 그 외(dev / cli) → 파일 reporter + `info` 까지만 콘솔 출력하는 PrettyReporter(`withMaxLevel(..., LogLevels.info)`). 파일에는 debug 전부, 콘솔에는 info 이하만.
15
+ - `DEV`/`SD_DEBUG` 는 `parseBoolEnv` 로 해석되는 boolean 환경값(`@simplysm/core-common`).
15
16
 
16
17
  ```ts
17
- setupConsola({ cli: true });
18
- import consola from "consola";
19
- consola.info("started");
18
+ import { setupConsola } from "@simplysm/core-node";
19
+ setupConsola({ cli: true }); // 진입점에서 1회
20
20
  ```
21
21
 
22
- ## PrettyReporter
22
+ ## withMaxLevel
23
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`) 등이 콘솔과 일치.
24
+ - `withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter` — reporter 감싸 `logObj.level > maxLevel` 인 로그를 버리는 필터 래퍼. "콘솔에는 정보성만, 파일에는 전부" 같은 분리에 사용. consola `LogLevels` 숫자 기준(작을수록 심각: error 0, warn 1, info 3 등) maxLevel 보다 큰(=덜 심각한) 로그가 잘림.
30
25
 
31
- ## createFileReporter
32
-
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 }`.
26
+ ## PrettyReporter
37
27
 
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 는 메시지+스택으로 기록됨.
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
+ - `formatPlain(logObj, formatOptions?): string` — 색·날짜·뱃지 여백 **없이** 평문으로 포맷(trim). 아이콘·tag·객체 inspect·스택 표현은 콘솔과 동일하게 재사용. 파일 reporter 등이 콘솔과 같은 본문을 얻기 위한 진입점.
31
+ - `formatOptions?: Partial<FormatOpts>` — 콘솔과 동일한 `ctx.options.formatOptions`(예: 객체 펼침 `compact`)를 넘기면 출력이 콘솔과 일치.
39
32
 
40
- ## withMaxLevel
33
+ ## createFileReporter
41
34
 
42
- - `withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter` — 리포터 래핑. `logObj.level > maxLevel` 로그는 위임하지 않고 버림. 특정 리포터에 최대 상세도 상한을 사용(예: 콘솔엔 info 까지만).
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.
43
38
 
44
39
  ```ts
45
- const reporter = withMaxLevel(new PrettyReporter(), LogLevels.info);
40
+ import { createFileReporter } from "@simplysm/core-node";
41
+ import consola from "consola";
42
+ consola.options.reporters = [createFileReporter({ maxSize: 5 * 1024 * 1024, maxDays: 7 })];
46
43
  ```
47
44
 
48
- ## 주의사항
45
+ ## FileReporterOptions / SetupConsolaOptions
49
46
 
50
- - prod 에서는 콘솔 출력이 없고 `.logs` 파일로만 남음운영 로그 확인은 파일 기준.
51
- - 파일 리포터는 `process.cwd()` 기준 `.logs` 기록하므로 작업 디렉토리에 의존.
47
+ - `interface FileReporterOptions { maxSize?: number; maxDays?: number }` createFileReporter 옵션 타입.
48
+ - `interface SetupConsolaOptions { cli?: boolean }` setupConsola 옵션 타입.