@simplysm/sd-claude 14.0.75 → 14.0.77

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 (66) hide show
  1. package/claude/output-styles/sd-tone.md +128 -0
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +28 -89
  3. package/claude/references/sd-simplysm14/apis/angular/app-structure.md +75 -32
  4. package/claude/references/sd-simplysm14/apis/angular/buttons.md +65 -29
  5. package/claude/references/sd-simplysm14/apis/angular/crud.md +86 -21
  6. package/claude/references/sd-simplysm14/apis/angular/forms.md +168 -42
  7. package/claude/references/sd-simplysm14/apis/angular/infrastructure.md +200 -49
  8. package/claude/references/sd-simplysm14/apis/angular/kanban.md +64 -20
  9. package/claude/references/sd-simplysm14/apis/angular/layout.md +75 -30
  10. package/claude/references/sd-simplysm14/apis/angular/modal.md +92 -40
  11. package/claude/references/sd-simplysm14/apis/angular/routing.md +86 -25
  12. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +72 -41
  13. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +113 -21
  14. package/claude/references/sd-simplysm14/apis/angular/sheet.md +108 -33
  15. package/claude/references/sd-simplysm14/apis/angular/toast.md +81 -30
  16. package/claude/references/sd-simplysm14/apis/angular/visual.md +140 -32
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +46 -43
  18. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +59 -48
  19. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +17 -7
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +43 -116
  21. package/claude/references/sd-simplysm14/apis/core-common/extensions.md +74 -109
  22. package/claude/references/sd-simplysm14/apis/core-common/features.md +40 -35
  23. package/claude/references/sd-simplysm14/apis/core-common/types.md +80 -106
  24. package/claude/references/sd-simplysm14/apis/core-common/utils.md +142 -111
  25. package/claude/references/sd-simplysm14/apis/core-node/README.md +7 -16
  26. package/claude/references/sd-simplysm14/apis/core-node/consola.md +33 -38
  27. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +25 -33
  28. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +27 -38
  29. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +32 -60
  30. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +14 -45
  31. package/claude/references/sd-simplysm14/apis/core-node/worker.md +35 -81
  32. package/claude/references/sd-simplysm14/apis/excel/README.md +178 -80
  33. package/claude/references/sd-simplysm14/apis/lint/README.md +5 -0
  34. package/claude/references/sd-simplysm14/apis/orm-node/README.md +1 -1
  35. package/claude/references/sd-simplysm14/apis/sd-claude/README.md +28 -5
  36. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +1 -1
  37. package/claude/references/sd-simplysm14/apis/service-client/README.md +57 -50
  38. package/claude/references/sd-simplysm14/apis/service-server/README.md +8 -15
  39. package/claude/references/sd-simplysm14/apis/service-server/auth.md +24 -16
  40. package/claude/references/sd-simplysm14/apis/service-server/builtin-services.md +55 -31
  41. package/claude/references/sd-simplysm14/apis/service-server/define-service.md +28 -44
  42. package/claude/references/sd-simplysm14/apis/service-server/internals.md +59 -18
  43. package/claude/references/sd-simplysm14/apis/service-server/server.md +37 -46
  44. package/claude/references/sd-simplysm14/manuals/client-component.md +3 -1
  45. package/claude/references/sd-simplysm14/manuals/logging.md +9 -8
  46. package/claude/rules/sd-base-rules.md +380 -217
  47. package/claude/settings.json +1 -0
  48. package/claude/skills/sd-commit/SKILL.md +31 -8
  49. package/claude/skills/sd-docs/SKILL.md +15 -10
  50. package/claude/skills/sd-docs/references/subagent-prompt.md +26 -8
  51. package/claude/skills/sd-impl/SKILL.md +1 -1
  52. package/claude/skills/sd-skill/references/skill-authoring.md +1 -1
  53. package/claude/skills/sd-spec/SKILL.md +22 -13
  54. package/claude/skills/sd-spec/references/spec-authoring.md +1 -1
  55. package/claude/skills/sd-unpack/SKILL.md +150 -26
  56. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/_common.cpython-314.pyc +0 -0
  57. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/eml_handler.cpython-314.pyc +0 -0
  58. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/office_com.cpython-314.pyc +0 -0
  59. package/claude/skills/sd-unpack/scripts/handlers/__pycache__/pdf_handler.cpython-314.pyc +0 -0
  60. package/claude/skills/sd-unpack/scripts/handlers/_common.py +17 -2
  61. package/claude/skills/sd-unpack/scripts/handlers/eml_handler.py +100 -24
  62. package/claude/skills/sd-unpack/scripts/handlers/msg_handler.py +140 -27
  63. package/claude/skills/sd-unpack/scripts/handlers/office_com.py +698 -107
  64. package/claude/skills/sd-unpack/scripts/handlers/office_worker.py +34 -26
  65. package/claude/skills/sd-unpack/scripts/handlers/pdf_handler.py +130 -8
  66. package/package.json +1 -1
@@ -1,123 +1,88 @@
1
1
  # @simplysm/core-common — extensions
2
2
 
3
- `index.ts` import 되면 자동으로 `Array.prototype`, `Set.prototype`, `Map.prototype` 메서드를 정의 (`enumerable: false`).
4
-
5
- ## Array (Readonly — 새 배열/값 반환)
6
-
7
- ### 조회
8
-
9
- ```typescript
10
- arr.single(pred?) // 0 or 1개. 2개 이상이면 ArgumentError. 없으면 undefined.
11
- arr.first(pred?) // pred find, 없으면 [0]
12
- arr.last(pred?) // pred 역방향 find, 없으면 [length-1]
13
- arr.filterExists() // null/undefined 제거 → NonNullable<T>[]
14
- arr.ofType("string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes")
15
- arr.ofType(SomeClass) // instanceof 또는 constructor 일치
16
- ```
17
-
18
- ### 비동기
19
-
20
- ```typescript
21
- await arr.filterAsync(asyncPred) // 순차
22
- await arr.mapAsync(asyncSelector) // 순차
23
- await arr.mapManyAsync(asyncSelector) // 순차 + 평탄화
24
- await arr.parallelAsync(fn) // Promise.all (하나 reject 시 전체 reject)
25
- ```
26
-
27
- ### 매핑·평탄화
28
-
29
- ```typescript
30
- arr.mapMany() // 중첩 배열 1단계 flatten + filterExists
31
- arr.mapMany(selector) // 매핑 flatten
3
+ `@simplysm/core-common` import 자동 적용. 글로벌 `Array`/`ReadonlyArray`/`Map`/`Set` 인터페이스에 ambient declare.
4
+
5
+ ## ReadonlyArray<T> (비파괴)
6
+
7
+ ```ts
8
+ single(predicate?): T | undefined // 0~1개만 통과. 2개+ → ArgumentError
9
+ first(predicate?): T | undefined // 첫 요소 (predicate 시 find와 동일)
10
+ last(predicate?): T | undefined // 마지막 요소
11
+ filterAsync(predicate: async): Promise<T[]> // 비동기 필터 (순차)
12
+ filterExists(): NonNullable<T>[] // null/undefined 제거 + 타입 좁힘
13
+ ofType(type: PrimitiveTypeStr | Type<N>): N[] // 원시 타입명 또는 생성자로 필터
14
+ mapAsync(selector: async): Promise<R[]> // 비동기 매핑 (순차)
15
+ mapMany(selector?): R[] // flat + filterExists
16
+ mapManyAsync(selector?: async): Promise<R[]>
17
+ parallelAsync(fn: async): Promise<R[]> // Promise.all (하나 reject 시 전체 reject)
18
+
19
+ groupBy(keySelector, valueSelector?): { key, values }[]
20
+ // 원시 key: O(n) Map 최적화. 객체 key: equal() 비교로 O(n²)
21
+ toMap(keySelector, valueSelector?): Map<K, V> // 중복 key → ArgumentError
22
+ toMapAsync(keySelector: async, valueSelector?: async): Promise<Map>
23
+ toArrayMap(keySelector, valueSelector?): Map<K, V[]> // 중복 허용, array로 누적 (O(n))
24
+ toSetMap(keySelector, valueSelector?): Map<K, Set<V>>
25
+ toMapValues(keySelector, valueSelector(items)): Map<K, V> // value는 그룹 전체로 계산
26
+ toObject(keySelector: → string, valueSelector?): Record<string, V>
27
+ toTree(keyProp, parentKey): TreeArray<T>[] // 평면 → 트리 (parentKey null이면 root)
28
+
29
+ distinct(options?: bool | { matchAddress?, keyFn? }): T[]
30
+ // matchAddress=true: Set 참조 비교. keyFn: 커스텀 키 O(n). 없이 객체: O(n²) equal
31
+ orderBy(selector?): T[] // 오름차순 (불변, 새 array)
32
+ orderByDesc(selector?): T[]
33
+
34
+ diffs(target, options?: { keys?, excludes? }): ArrayDiffsResult<T, P>[]
35
+ // 전체 일치 우선, 없으면 keys 일치(update 후보). 결과: INSERT|DELETE|UPDATE
36
+ oneWayDiffs(orgItems: T[] | Map<TKey, T>, keyPropNameOrGetValFn, options?):
37
+ ArrayOneWayDiffResult<T>[] // type: 'create'|'update'|'same'
38
+ merge(target, options?): (T | P | T&P)[] // diffs 후 update는 obj.merge, insert는 append
39
+
40
+ sum(selector?): number // 숫자 아니면 ArgumentError
41
+ min(selector?): number | string | undefined // 숫자/문자열만
42
+ max(selector?): number | string | undefined
43
+ shuffle(): T[] // Fisher-Yates, 새 array
32
44
  ```
33
45
 
34
- ### 변환
46
+ ## Array<T> (파괴, @mutates)
35
47
 
36
- ```typescript
37
- arr.groupBy(keyFn) // [{ key, values }, ...] (객체 key 시 O(n²), 원시 key 는 O(n))
38
- arr.groupBy(keyFn, valueFn)
39
- arr.toMap(keyFn) / toMap(keyFn, valFn) // 중복 key 시 ArgumentError
40
- await arr.toMapAsync(...)
41
- arr.toArrayMap(keyFn[, valFn]) // Map<K, V[]> (원시 key 권장 — O(n))
42
- arr.toSetMap(keyFn[, valFn]) // Map<K, Set<V>>
43
- arr.toMapValues(keyFn, (items) => aggregated)
44
- arr.toObject(strKeyFn[, valFn]) // 중복 key 시 ArgumentError
45
- arr.toTree("id", "parentId") // 평면 → 트리 ({...item, children: []}), O(n).
46
- // parentKey 가 null/undefined 면 루트.
48
+ ```ts
49
+ distinctThis(options?) // distinct를 in-place로
50
+ orderByThis(selector?) // Array.prototype.sort 위임 (in-place)
51
+ orderByDescThis(selector?)
52
+ insert(index, ...items) // splice 삽입, this 반환
53
+ remove(item | selector) // 역순 순회 splice 제거
54
+ toggle(item) // 있으면 remove, 없으면 push
55
+ clear() // 전체 비우기
47
56
  ```
48
57
 
49
- ### 중복·정렬
50
-
51
- ```typescript
52
- arr.distinct() // 깊은 equal 비교 (객체 array 는 O(n²))
53
- arr.distinct(true) // matchAddress: 참조 비교 (Set 기반, O(n))
54
- arr.distinct({ matchAddress?, keyFn? }) // keyFn 권장 — O(n)
55
- arr.orderBy(selector?) // 오름차순. string/number/DateTime/DateOnly/Time/undefined 지원
56
- arr.orderByDesc(selector?)
57
- arr.shuffle() // Fisher-Yates
58
+ ## 익스포트 타입
59
+
60
+ ```ts
61
+ type ArrayDiffsResult<TOrig, TOther> =
62
+ | { source: undefined; target: TOther } // INSERT
63
+ | { source: TOrig; target: undefined } // DELETE
64
+ | { source: TOrig; target: TOther } // UPDATE
65
+ type ArrayOneWayDiffResult<T> =
66
+ | { type: 'create'; item: T; orgItem: undefined }
67
+ | { type: 'update'; item: T; orgItem: T }
68
+ | { type: 'same'; item: T; orgItem: T }
69
+ type TreeArray<TNode> = TNode & { children: TreeArray<TNode>[] }
70
+ type ComparableType = string | number | boolean | DateTime | DateOnly | Time | undefined
58
71
  ```
59
72
 
60
- ### 비교·병합
61
-
62
- ```typescript
63
- arr.diffs(target) // INSERT/DELETE/UPDATE 결과
64
- arr.diffs(target, { keys: ["id"], excludes? }) // key 기반 매칭 (Map 인덱싱 O(n+m))
65
- arr.diffs(target, { excludes: [...] })
66
- // 결과 타입:
67
- // { source: undefined, target: T } // INSERT
68
- // { source: T, target: undefined } // DELETE
69
- // { source: T, target: T } // UPDATE
73
+ ## Map<K, V>
70
74
 
71
- arr.oneWayDiffs(orgItems | Map, keyPropOrFn, {
72
- includeSame?, excludes?, includes?,
73
- })
74
- // 결과: { type: "create"|"update"|"same", item, orgItem }
75
-
76
- arr.merge(target[, { keys?, excludes? }]) // diffs 후 source 기반에 target 병합·신규 push
75
+ ```ts
76
+ getOrCreate(key, newValue: V): V
77
+ getOrCreate(key, newValueFn: () => V): V // 함수면 팩토리로 호출하여 lazy 생성
78
+ update(key, updateFn: (v: V | undefined) => V): void // key 없어도 fn 호출
77
79
  ```
80
+ - **주의**: `Map<K, () => void>` 같이 V가 함수 타입이면 두 번째 인자가 팩토리로 인식됨. 함수 값을 저장하려면 `getOrCreate(k, () => myFn)`.
78
81
 
79
- ### 집계
82
+ ## Set<T>
80
83
 
81
- ```typescript
82
- arr.sum(selector?) // 숫자 아니면 ArgumentError. 빈 배열 → 0.
83
- arr.min(selector?) / max(selector?) // string|number. 배열 → undefined
84
+ ```ts
85
+ adds(...values: T[]): this // 다중 add
86
+ toggle(value, addOrDel?: "add" | "del"): this
87
+ // 인자 없으면 자동 토글, "add"=강제 추가, "del"=강제 삭제
84
88
  ```
85
-
86
- ### TreeArray 타입
87
-
88
- ```typescript
89
- type TreeArray<T> = T & { children: TreeArray<T>[] };
90
- ```
91
-
92
- ## Array (Mutable — 원본 변경, `@mutates`)
93
-
94
- ```typescript
95
- arr.distinctThis(options?) // 원본에서 중복 제거 (역순 splice, O(n))
96
- arr.orderByThis(selector?) / orderByDescThis(selector?)
97
- arr.insert(index, ...items)
98
- arr.remove(itemOrSelector) // 일치 항목 모두 제거. 역순 순회.
99
- arr.toggle(item) // 있으면 remove, 없으면 push
100
- arr.clear()
101
- ```
102
-
103
- 모두 `this` 반환 (체이닝 가능, `clear/distinctThis/orderBy*This` 는 변경된 자기 자신).
104
-
105
- ## Set
106
-
107
- ```typescript
108
- set.adds(...values) // 다중 add, this 반환
109
- set.toggle(value) // 자동 토글
110
- set.toggle(value, "add" | "del") // 강제 추가/제거
111
- ```
112
-
113
- ## Map
114
-
115
- ```typescript
116
- map.getOrCreate(key, defaultValue) // 없으면 set 후 반환
117
- map.getOrCreate(key, () => expensiveCompute()) // 팩토리 (값이 함수이면 항상 호출됨 — 함수 값을 저장하려면 `() => fn` 으로 한 번 더 감쌀 것)
118
- map.update(key, (v|undefined) => newV) // 없는 key 도 호출됨 (카운터·배열 push 패턴)
119
- ```
120
-
121
- ## ComparableType
122
-
123
- `orderBy*` 의 selector 반환 타입: `string | number | boolean | DateTime | DateOnly | Time | undefined`.
@@ -1,46 +1,51 @@
1
1
  # @simplysm/core-common — features
2
2
 
3
- 비동기 큐와 타입 안전 이벤트 이미터.
4
-
5
- ## EventEmitter\<TEvents\>
6
-
7
- 브라우저·Node 공용 (내부 `EventTarget`). 타입 안전.
8
-
9
- ```typescript
10
- interface MyEvents { data: string; error: Error; done: void; }
11
- class MyEmitter extends EventEmitter<MyEvents> {}
12
-
13
- const e = new MyEmitter();
14
- e.on("data", (d) => ...); // d: string
15
- e.emit("data", "hello");
16
- e.emit("done"); // void 이벤트는 인자 없이
17
- e.off("data", handler);
18
- e.listenerCount("data"); // 등록된
19
- e.dispose(); // 모든 리스너 제거
3
+ ## EventEmitter<TEvents>
4
+
5
+ EventTarget 기반의 타입 안전 이벤트 이미터. 브라우저·Node 공통.
6
+
7
+ ```ts
8
+ class EventEmitter<TEvents extends { [K in keyof TEvents]: unknown } = Record<string, unknown>> {
9
+ on<E extends keyof TEvents & string>(type: E, listener: (data: TEvents[E]) => void): void
10
+ off<E>(type: E, listener: ...): void
11
+ emit<E>(type: E, ...args: TEvents[E] extends void ? [] : [data: TEvents[E]]): void
12
+ listenerCount(type): number
13
+ dispose(): void // 모든 리스너 제거
14
+ }
15
+ ```
16
+ - `TEvents`: `{ eventName: dataType }` 맵. `void` 타입이면 `emit("done")` 인자 생략.
17
+ - 같은 `(type, listener)` 쌍 중복 등록 시 무시.
18
+ - 내부적으로 `CustomEvent.detail`로 데이터 전송. listener는 wrapper로 감싸져 등록되며, listenerMap이 원본 ↔ wrapper 매핑 보관.
19
+
20
+ ```ts
21
+ interface MyEvents { data: string; done: void }
22
+ class M extends EventEmitter<MyEvents> {}
23
+ const m = new M();
24
+ m.on("data", s => ...);
25
+ m.emit("data", "hi");
26
+ m.emit("done");
20
27
  ```
21
28
 
22
- 같은 이벤트에 같은 리스너 중복 등록은 무시.
23
-
24
- ## DebounceQueue extends EventEmitter\<{ error: SdError }\>
29
+ ## DebounceQueue extends EventEmitter<{ error: SdError }>
25
30
 
26
- 연속 호출 마지막 작업만 실행.
31
+ 마지막 요청만 실행. 짧은 시간 내 다중 호출 마지막 1건만 처리.
27
32
 
28
- ```typescript
29
- const q = new DebounceQueue(300); // 300ms 지연 (생략 시 다음 이벤트 루프)
30
- q.run(fn); // 이전 대기 함수 교체
31
- q.on("error", (e) => ...); // 작업 throw 시. 리스너 없으면 logger.error
32
- q.dispose(); // 타이머·대기 함수 정리
33
+ ```ts
34
+ new DebounceQueue(delay?: number) // ms. 생략 시 다음 이벤트 루프 (setTimeout(_, undefined))
35
+ run(fn: () => void | Promise<void>): void
36
+ dispose(): void // 타이머·pending 정리
33
37
  ```
38
+ - `delay` ms 지난 뒤 가장 최근 `fn` 실행. 실행 중 도착한 추가 `run()`은 디바운스 지연 없이 현재 실행 직후 즉시 처리 (요청 누락 방지).
39
+ - fn throw 시 `SdError`로 감싸 `"error"` 이벤트 발행. 리스너 없으면 `consola` 로그.
34
40
 
35
- 실행 도중 들어온 요청은 지연 없이 실행 완료 직후 즉시 처리 (놓침 방지).
36
-
37
- ## SerialQueue extends EventEmitter\<{ error: SdError }\>
41
+ ## SerialQueue extends EventEmitter<{ error: SdError }>
38
42
 
39
- 순차 실행, 작업 사이 간격 옵션.
43
+ 큐에 추가된 함수들을 순차 실행.
40
44
 
41
- ```typescript
42
- const q = new SerialQueue(0); // gap ms (기본 0)
43
- q.run(asyncFn); // 큐에 추가, 자동 실행
44
- q.on("error", ...); // 에러는 다음 작업에 영향 X (계속 실행)
45
- q.dispose(); // 대기 큐 비움 (현재 작업은 완료됨)
45
+ ```ts
46
+ new SerialQueue(gap: number = 0) // 작업 사이 ms 간격
47
+ run(fn: () => void | Promise<void>): void
48
+ dispose(): void // 대기 비우기 (실행 중은 완료됨)
46
49
  ```
50
+ - 하나 완료 후 다음 시작. 에러 발생해도 후속 작업 계속 실행. throw는 `SdError` 감싸서 `"error"` 이벤트 (리스너 없으면 `consola.error`).
51
+ - `gap>0` 이면 작업 간 `wait.time(gap)` 대기.
@@ -1,114 +1,88 @@
1
1
  # @simplysm/core-common — types
2
2
 
3
- 날짜·시간·UUID·만료 캐시 타입.
4
-
5
- ## DateTime (불변)
6
-
7
- 밀리초 정밀도, 로컬 타임존. 모든 변환/산술 메서드는 새 인스턴스 반환.
3
+ ## Uuid
8
4
 
9
- ```typescript
10
- new DateTime() // 현재
11
- new DateTime(year, month, day, h?, m?, s?, ms?) // month 는 1-12
12
- new DateTime(tick) // ms epoch
13
- new DateTime(date) // Date 복사
14
- DateTime.parse(str) // "yyyy-MM-dd HH:mm:ss" / "yyyyMMddHHmmss" /
15
- // "yyyy-MM-dd AM|PM HH:mm:ss" / "오전|오후" / ISO 8601
16
- // 실패 시 ArgumentError
5
+ ```ts
6
+ class Uuid {
7
+ static generate(): Uuid // crypto.getRandomValues 기반 UUID v4
8
+ static fromBytes(bytes: Bytes): Uuid // 16바이트 Uint8Array → Uuid (길이≠16이면 ArgumentError)
9
+ constructor(uuid: string) // 형식 검증 (실패 시 ArgumentError)
10
+ toString(): string
11
+ toBytes(): Bytes
12
+ }
17
13
  ```
18
-
19
- Getters: `year/month/day/hour/minute/second/millisecond/tick/dayOfWeek/timezoneOffsetMinutes/isValid` (month 1-12, dayOfWeek 0-6 일~토).
20
-
21
- 변환: `setYear/setMonth/setDay/setHour/setMinute/setSecond/setMillisecond` 인스턴스. `setMonth` 는 대상 월 일수 초과 시 마지막 일로 클램프. `setDay` 는 JS Date 동작에 따라 월 경계 자동 조정.
22
-
23
- 산술: `addYears/addMonths/addDays/addHours/addMinutes/addSeconds/addMilliseconds`.
24
-
25
- 포맷: `toFormatString(formatStr)` ([date-format 토큰](#date-format-토큰)). `toString()` = `"yyyy-MM-ddTHH:mm:ss.fffzzz"`.
26
-
27
- ## DateOnly (불변)
28
-
29
- 날짜만(yyyy-MM-dd), 로컬 타임존.
30
-
31
- ```typescript
32
- new DateOnly() // 오늘
33
- new DateOnly(year, month, day)
34
- new DateOnly(tick) / new DateOnly(date)
35
- DateOnly.parse(str) // "yyyy-MM-dd" / "yyyyMMdd" (타임존 무관) /
36
- // ISO 8601 (UTC → 로컬 변환). 실패 시 ArgumentError
14
+ - 정규식 `^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i` 로 검증.
15
+ - v4 비트: byte[6]은 0x40 마스크, byte[8]은 0x80 마스크 적용.
16
+
17
+ ## DateTime (불변, 로컬 타임존, ms 정밀도)
18
+
19
+ ```ts
20
+ new DateTime() // 현재
21
+ new DateTime(year, month, day, h?, m?, s?, ms?)
22
+ new DateTime(tick: number)
23
+ new DateTime(date: Date)
24
+ static parse(str): DateTime // 'yyyy-MM-dd HH:mm:ss[.fff]', 'yyyyMMddHHmmss',
25
+ // 'yyyy-MM-dd AM/PM HH:mm:ss', '오전/오후', ISO 8601
37
26
  ```
38
-
39
- Getters: `year/month/day/tick/dayOfWeek/isValid`.
40
- 변환·산술: `setYear/setMonth/setDay`, `addYears/addMonths/addDays`.
41
- 포맷: `toFormatString`, `toString()` = `"yyyy-MM-dd"`.
42
-
43
- 주차 API (ISO 8601 기본: 월요일 시작, 첫 주 최소 4일):
44
-
45
- ```typescript
46
- d.getWeekSeqOfYear(weekStartDay=1, minDaysInFirstWeek=4) // { year, weekSeq }
47
- d.getWeekSeqOfMonth(...) // { year, monthSeq, weekSeq }
48
- d.getWeekSeqStartDate(...) // DateOnly
49
- d.getBaseYearMonthSeqForWeekSeq(...) // { year, monthSeq }
50
- DateOnly.getDateByYearWeekSeq({ year, month?, weekSeq }, ...) // 해당 주 시작일
27
+ - 읽기 getter: `year`, `month` (1-12), `day`, `hour`, `minute`, `second`, `millisecond`, `tick`, `dayOfWeek` (0=일~6=토), `timezoneOffsetMinutes`, `isValid`, `date` (내부 Date 복사 X — readonly 참조).
28
+ - 변환: `setYear/Month/Day/Hour/Minute/Second/Millisecond(n)` → 새 인스턴스. `setMonth`는 일수 초과 시 해당 월 말일로 보정 (예: 1월 31일 → 2월 28/29일).
29
+ - 산술: `addYears/Months/Days` (캘린더 기반), `addHours/Minutes/Seconds/Milliseconds` (tick 가산).
30
+ - 포맷: `toFormatString(fmt)` `dt.format` 형식 문자열. `toString()` = `"yyyy-MM-ddTHH:mm:ss.fffzzz"`.
31
+ - 파싱 실패 시 `ArgumentError`.
32
+
33
+ ## DateOnly (불변, 시간 제외)
34
+
35
+ ```ts
36
+ new DateOnly() | new DateOnly(y,m,d) | new DateOnly(tick) | new DateOnly(date)
37
+ static parse(str): DateOnly // 'yyyy-MM-dd', 'yyyyMMdd' (타임존 무관),
38
+ // ISO 8601 (UTC→로컬 변환)
39
+ static getDateByYearWeekSeq(
40
+ { year, month?, weekSeq },
41
+ weekStartDay = 1, minDaysInFirstWeek = 4,
42
+ ): DateOnly // 지정 주차의 시작 날짜
51
43
  ```
52
-
53
- ## Time (불변)
54
-
55
- 시간만(HH:mm:ss.fff). 24h 순환 음수/24h+ 자동 정규화.
56
-
57
- ```typescript
58
- new Time() // 현재 시각의 시간 부분
59
- new Time(hour, minute, second?, ms?)
60
- new Time(tick) / new Time(date)
61
- Time.parse(str) // "HH:mm:ss[.fff]" / "AM|PM HH:mm:ss[.fff]" / ISO 8601 시간 부분
44
+ - getter: `year`, `month`, `day`, `tick`, `dayOfWeek`, `isValid`.
45
+ - `setYear/Month/Day`, `addYears/Months/Days` 동일 패턴 (월 보정 동일).
46
+ - 주차 API (인스턴스):
47
+ - `getBaseYearMonthSeqForWeekSeq(weekStartDay=1, minDaysInFirstWeek=4)`: 날짜가 속한 주의 기준 연/월.
48
+ - `getWeekSeqStartDate(...)`: 이 날짜가 속한 주의 시작 날짜.
49
+ - `getWeekSeqOfYear(...)`: `{ year, weekSeq }`.
50
+ - `getWeekSeqOfMonth(...)`: `{ year, monthSeq, weekSeq }`.
51
+ - `weekStartDay`: 시작 요일 (0=일~6=토). 기본 1=월.
52
+ - `minDaysInFirstWeek`: 첫 주 최소 일수 (1~7). 4=ISO 8601, 1=미국식.
53
+ - `toFormatString(fmt)` / `toString()` = `"yyyy-MM-dd"`.
54
+
55
+ ## Time (불변, 24시간 순환)
56
+
57
+ ```ts
58
+ new Time() | new Time(h, m, s?, ms?) | new Time(tick) | new Time(date)
59
+ static parse(str): Time // 'HH:mm:ss[.fff]', 'AM/PM HH:mm:ss', ISO 8601
62
60
  ```
63
-
64
- Getters/Setters/Add 메서드는 DateTime 시간부와 유사. `addHours/Minutes/Seconds/Milliseconds` 는 24h 순환.
65
-
66
- ## Uuid
67
-
68
- UUID v4, `crypto.getRandomValues` 기반.
69
-
70
- ```typescript
71
- Uuid.generate() // 새 v4
72
- new Uuid("xxxxxxxx-...") // 형식 검증, 실패 시 ArgumentError
73
- Uuid.fromBytes(bytes16) // 16바이트 → Uuid (길이 ≠ 16 시 ArgumentError)
74
- u.toString() // "xxxxxxxx-xxxx-..."
75
- u.toBytes() // 16바이트 Uint8Array
61
+ - 24시간을 초과/음수인 tick 입력은 자동으로 `% MS_PER_DAY` 정규화.
62
+ - getter: `hour`, `minute`, `second`, `millisecond`, `tick`, `isValid`.
63
+ - `setHour/Minute/Second/Millisecond`, `addHours/Minutes/Seconds/Milliseconds` (24시간 순환).
64
+ - `toFormatString(fmt)` / `toString()` = `"HH:mm:ss.fff"`.
65
+
66
+ ## LazyGcMap<TKey, TValue>
67
+
68
+ LRU 접근 시간 갱신 + 주기 GC 로 자동 만료되는 `Map`.
69
+
70
+ ```ts
71
+ new LazyGcMap({
72
+ expireTime: number, // 만료 ms (필수). 마지막 접근부터 경과 시 삭제
73
+ gcInterval?: number, // GC 주기 ms. 기본 = max(expireTime/10, 1000)
74
+ onExpire?: (key, value) => void|Promise<void>, // 만료 시 콜백. 비동기 가능, throw하면 로그만
75
+ })
76
+
77
+ has(key) // 접근 시간 갱신 X
78
+ get(key) // 접근 시간 갱신 O
79
+ set(key, val) // GC 타이머 시작
80
+ delete(key) // 비면 GC 중지
81
+ clear() // 전체 삭제 + GC 중지 (인스턴스 재사용 가능)
82
+ dispose() // 영구 정리 (이후 모든 작업 no-op, getOrCreate만 throw)
83
+ getOrCreate(key, factory)
84
+ size, keys(), values(), entries()
76
85
  ```
77
-
78
- ## LazyGcMap
79
-
80
- LRU 자동 만료 Map. **사용 후 반드시 `dispose()` 호출** — 안 하면 GC 타이머가 살아 메모리 누수.
81
-
82
- ```typescript
83
- const m = new LazyGcMap<K, V>({
84
- expireTime: 60_000, // 마지막 접근 이후 ms
85
- gcInterval?: 6_000, // 기본: expireTime/10, 최소 1000ms
86
- onExpire?: (k, v) => ... , // 비동기 가능, 에러는 로그
87
- });
88
-
89
- m.size; m.has(k); m.get(k); m.set(k, v); m.delete(k); m.clear(); m.dispose();
90
- m.getOrCreate(k, factory); // dispose 후 호출 시 throw
91
- m.keys() / m.values() / m.entries(); // Iterator
92
- ```
93
-
94
- `get`/`getOrCreate` 만 접근시간 갱신(LRU). `has` 는 갱신 X. GC 실행 중 같은 key 재등록 시 새 항목 보존.
95
-
96
- ## date-format 토큰
97
-
98
- `DateTime/DateOnly/Time#toFormatString(formatStr)` 에서 사용. C# 호환.
99
-
100
- | 토큰 | 의미 | 예 |
101
- |------|------|----|
102
- | `yyyy`/`yy` | 연도 4/2자리 | 2024 / 24 |
103
- | `MM`/`M` | 월 패딩/미패딩 | 01 / 1 |
104
- | `ddd` | 한글 요일 | 일~토 |
105
- | `dd`/`d` | 일 패딩/미패딩 | 01 / 1 |
106
- | `tt` | AM/PM | AM |
107
- | `hh`/`h` | 12시간 패딩/미패딩 | 01 / 1 |
108
- | `HH`/`H` | 24시간 패딩/미패딩 | 14 / 14 |
109
- | `mm`/`m` | 분 | 30 / 30 |
110
- | `ss`/`s` | 초 | 45 / 45 |
111
- | `fff`/`ff`/`f` | 밀리초 3/2/1자리 | 123 / 12 / 1 |
112
- | `zzz`/`zz`/`z` | 타임존 ±HH:mm / ±HH / ±H | +09:00 |
113
-
114
- 긴 토큰이 먼저 매칭 → 부분 매칭 방지.
86
+ - **반드시 `dispose()` 호출 필요** — 안 하면 GC 타이머 leak.
87
+ - GC 중복 실행 방지(`_isGcRunning`). `onExpire` 실행 도중 같은 key로 `set()`되면 새 값은 보존(참조 동일성 비교).
88
+ - 비어있으면 자동 GC 중지 → 재 `set()` 시 자동 재시작.