@simplysm/sd-claude 14.0.89 → 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 (79) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -17
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +52 -30
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +200 -38
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +41 -53
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +66 -22
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +127 -40
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +60 -43
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +56 -20
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +74 -74
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +50 -40
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +55 -15
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +59 -42
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +77 -62
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +8 -7
  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 +22 -14
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +19 -19
  18. package/claude/references/sd-simplysm14/apis/core-browser/README.md +17 -17
  19. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +28 -28
  20. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +37 -37
  21. package/claude/references/sd-simplysm14/apis/core-common/README.md +87 -219
  22. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +54 -98
  23. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +57 -99
  24. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +60 -103
  25. package/claude/references/sd-simplysm14/apis/core-common/errors.md +42 -47
  26. package/claude/references/sd-simplysm14/apis/core-common/obj.md +42 -88
  27. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +55 -0
  28. package/claude/references/sd-simplysm14/apis/core-node/README.md +6 -7
  29. package/claude/references/sd-simplysm14/apis/core-node/consola.md +17 -12
  30. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +14 -13
  31. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +9 -8
  32. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +14 -13
  33. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +4 -8
  34. package/claude/references/sd-simplysm14/apis/core-node/worker.md +14 -12
  35. package/claude/references/sd-simplysm14/apis/excel/README.md +22 -22
  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/orm-common/README.md +6 -8
  43. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +118 -67
  44. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +83 -86
  45. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +102 -93
  46. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +138 -81
  47. package/claude/references/sd-simplysm14/apis/orm-common/types.md +49 -44
  48. package/claude/references/sd-simplysm14/apis/orm-node/README.md +42 -42
  49. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +44 -33
  50. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +11 -10
  51. package/claude/references/sd-simplysm14/apis/service-client/README.md +56 -52
  52. package/claude/references/sd-simplysm14/apis/service-client/orm.md +33 -28
  53. package/claude/references/sd-simplysm14/apis/service-client/transport.md +23 -21
  54. package/claude/references/sd-simplysm14/apis/service-common/README.md +83 -48
  55. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +126 -34
  56. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +109 -54
  57. package/claude/references/sd-simplysm14/apis/service-server/README.md +69 -81
  58. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +46 -43
  59. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +63 -37
  60. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +40 -30
  61. package/claude/references/sd-simplysm14/apis/storage/README.md +17 -17
  62. package/claude/references/sd-simplysm14/manuals/client-app-structure.md +142 -140
  63. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -1
  64. package/claude/references/sd-simplysm14/manuals/client-service.md +19 -7
  65. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +2 -2
  66. package/claude/references/sd-simplysm14/manuals/client-system-log.md +11 -3
  67. package/claude/references/sd-simplysm14/manuals/data-log.md +0 -1
  68. package/claude/references/sd-simplysm14/manuals/orm.md +16 -0
  69. package/claude/rules/sd-design-rules.md +10 -0
  70. package/claude/skills/sd-demo/SKILL.md +0 -6
  71. package/claude/skills/sd-docs/SKILL.md +58 -0
  72. package/claude/{workflows/sd-docs.rules.md → skills/sd-docs/references/subagent-prompt.md} +103 -103
  73. package/claude/skills/sd-impl/SKILL.md +7 -4
  74. package/claude/skills/sd-spec/SKILL.md +842 -15
  75. package/claude/skills/sd-spec/references/example-spec.md +26 -36
  76. package/package.json +1 -1
  77. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +0 -53
  78. package/claude/skills/sd-spec/references/spec-authoring.md +0 -519
  79. package/claude/workflows/sd-docs.js +0 -84
@@ -1,128 +1,86 @@
1
1
  # @simplysm/core-common — 비동기 런타임
2
2
 
3
- 비동기 실행 흐름·이벤트·캐시·로깅을 다룰 때 함께 읽히는 묶음. `DebounceQueue`/`SerialQueue`(실행 )는 `EventEmitter` 를 상속해 `error` 이벤트를 발행하며, `LazyGcMap`(자동 만료 캐시)·`createLogger`(태그 로거)와 함께 쓰인다.
3
+ 디바운스/직렬 실행, 타입 안전 이벤트, 조건 대기, 자동 만료 Map 이 필요할 때 함께 읽히는 묶음. 큐 클래스들은 `EventEmitter` 를 상속해 `"error"` 이벤트를 발행함.
4
4
 
5
- ## EventEmitter
5
+ ## EventEmitter<TEvents>
6
6
 
7
- 브라우저/Node 공통의 타입 안전 이벤트 이미터(내부적으로 `EventTarget` 사용).
7
+ `EventTarget` 기반 타입 안전 이벤트 이미터(브라우저·Node 공용). 보통 상속해서 사용. `TEvents` 는 `{ 이벤트명: 데이터타입 }` 맵.
8
8
 
9
- ```typescript
10
- class EventEmitter<TEvents extends { [K in keyof TEvents]: unknown } = Record<string, unknown>> {
11
- on<K extends keyof TEvents & string>(type: K, listener: (data: TEvents[K]) => void): void;
12
- off<K extends keyof TEvents & string>(type: K, listener: (data: TEvents[K]) => void): void;
13
- emit<K extends keyof TEvents & string>(type: K, ...args: TEvents[K] extends void ? [] : [data: TEvents[K]]): void;
14
- listenerCount<K extends keyof TEvents & string>(type: K): number;
15
- dispose(): void;
16
- }
17
- ```
18
-
19
- - `TEvents` — 이벤트명→데이터 타입 맵. `void` 타입 이벤트는 `emit("done")` 처럼 인자 없이 발행.
20
- - `on` — 같은 (type, listener) 조합 중복 등록은 무시. `off` — 해당 listener 제거.
21
- - `emit` — 등록 리스너에 데이터 디스패치. `listenerCount(type)` — 현재 리스너 수(없으면 0). `dispose` — 전체 리스너 해제.
9
+ - `on(type, listener)`: → void — 리스너 등록. 같은 (type, listener) 중복 등록은 무시.
10
+ - `off(type, listener)`: void 리스너 제거.
11
+ - `emit(type, data)`: void 발행. 데이터 타입이 `void` 이벤트는 인자 없이 `emit(type)`.
12
+ - `listenerCount(type)`: number 해당 이벤트 리스너 수.
13
+ - `dispose()`: void 모든 리스너 제거.
22
14
 
23
- ```typescript
15
+ ```ts
16
+ import { EventEmitter } from "@simplysm/core-common";
24
17
  interface MyEvents { data: string; done: void; }
25
- class My extends EventEmitter<MyEvents> {}
26
- const e = new My();
27
- e.on("data", (s) => console.log(s));
28
- e.emit("data", "hi");
18
+ class MyEmitter extends EventEmitter<MyEvents> {}
19
+ const e = new MyEmitter();
20
+ e.on("data", (d) => console.log(d)); // d: string
21
+ e.emit("data", "hello");
22
+ e.emit("done");
29
23
  ```
30
24
 
31
25
  ## DebounceQueue
32
26
 
33
- 짧은 시간 내 여러 호출 중 **마지막만** 실행하는 디바운스 큐. `EventEmitter<{ error: SdError }>` 상속.
27
+ 짧은 시간 내 여러 호출 중 **마지막 요청만** 실행. 입력 자동완성·연속 상태 변경 일괄 처리에. `EventEmitter<{ error: SdError }>` 상속.
34
28
 
35
- ```typescript
36
- class DebounceQueue extends EventEmitter<{ error: SdError }> {
37
- constructor(delay?: number); // 지연 ms (생략 다음 이벤트 루프에 즉시)
38
- run(fn: () => void | Promise<void>): void; // 대기 작업 등록(기존 대기 작업 교체)
39
- override dispose(): void; // 타이머·대기 작업 정리
40
- }
41
- ```
29
+ - `new DebounceQueue(delay?)` — delay: number(ms). 생략 시 즉시(다음 이벤트 루프).
30
+ - `run(fn)`: void — `fn: () => void | Promise<void>` 등록. 이전 대기 fn 은 교체됨. delay 후 실행. 실행 중 들어온 요청은 delay 없이 현재 실행 직후 즉시 처리(요청 누락 방지).
31
+ - `dispose()`: void 대기 작업·타이머 정리(+ 리스너 제거).
32
+ - fn 에서 throw `"error"` 리스너가 있으면 SdError emit, 없으면 내부 logger.error.
42
33
 
43
- - `constructor(delay)` — 디바운스 지연. 생략하면 0(다음 틱 실행).
44
- - `run(fn)` 호출할 때마다 이전 대기 fn 을 교체. 지연 후 마지막 fn 만 실행. **실행 중**에 들어온 추가 요청은 지연 없이 현재 실행 직후 즉시 처리(의도적 설계 — 누락 방지).
45
- - 에러 처리: fn 이 throw 하면 `SdError` 로 감싸, `error` 리스너가 있으면 `emit("error")`, 없으면 내부 로거로 출력. (문제 발생 = error 심각도)
46
-
47
- ```typescript
34
+ ```ts
35
+ import { DebounceQueue } from "@simplysm/core-common";
48
36
  const q = new DebounceQueue(300);
49
- q.on("error", (e) => console.error(e));
50
- input.addEventListener("input", () => q.run(() => search(input.value)));
37
+ q.on("error", (err) => console.error(err));
38
+ q.run(() => save(value)); // 300ms 안에 다시 run 하면 이전 건 취소
51
39
  ```
52
40
 
53
41
  ## SerialQueue
54
42
 
55
- 추가된 작업을 **순차** 실행하는 큐(이전 완료 후 다음 시작). 에러가 나도 후속 작업은 계속 진행. `EventEmitter<{ error: SdError }>` 상속.
43
+ 큐에 넣은 작업을 **순차** 실행( 작업 완료 후 다음). 에러가 나도 후속 작업은 계속. `EventEmitter<{ error: SdError }>` 상속.
56
44
 
57
- ```typescript
58
- class SerialQueue extends EventEmitter<{ error: SdError }> {
59
- constructor(gap?: number); // 작업 사이 간격 ms (기본 0)
60
- run(fn: () => void | Promise<void>): void; // 큐에 추가하고 실행 시작
61
- override dispose(): void; // 대기 큐 비움(실행 중 작업은 완료됨)
62
- }
63
- ```
45
+ - `new SerialQueue(gap?)` — gap: number(ms, 기본 0). 각 작업 사이 대기 간격.
46
+ - `run(fn)`: void — `fn: () => void | Promise<void>` 추가 후 처리 시작.
47
+ - `dispose()`: void 대기 큐 비움(실행 중 작업은 완료됨)(+ 리스너 제거).
48
+ - fn throw 처리: DebounceQueue 동일(`"error"` emit 또는 logger.error).
64
49
 
65
- - `constructor(gap)` — 각 작업 사이 대기 간격(ms). 0 이면 연속 실행.
66
- - `run(fn)` — FIFO 로 순차 실행. 한 작업이 throw 하면 `SdError` 로 감싸 `error` 이벤트 발행(리스너 없으면 로그) 후 다음 작업 진행 — 후속 작업을 막지 않으려는 의도.
67
- - `dispose` — 아직 시작 안 한 대기분만 제거(실행 중인 건은 완료).
50
+ ## wait 네임스페이스
68
51
 
69
- ```typescript
70
- const q = new SerialQueue();
71
- q.run(async () => save(a)); // 순서 보장
72
- q.run(async () => save(b));
73
- ```
52
+ - `wait.until(forwarder, milliseconds?, maxCount?)`: → `Promise<void>` — `forwarder()`(boolean | Promise<boolean>) 가 true 될 때까지 대기. 첫 호출에서 true 면 즉시 반환. milliseconds=확인 간격(기본 100). maxCount=최대 시도 횟수(미지정 무제한). 초과 시 **TimeoutError throw**.
53
+ - `wait.time(millisecond)`: `Promise<void>` — 지정 시간만큼 sleep.
74
54
 
75
- ## LazyGcMap
76
-
77
- 마지막 접근 이후 일정 시간이 지나면 항목을 자동 삭제하는 Map(LRU 접근 시간 갱신). 타이머를 쓰므로 사용 후 `dispose()` 필수.
78
-
79
- ```typescript
80
- class LazyGcMap<TKey, TValue> {
81
- constructor(options: {
82
- gcInterval?: number; // GC 주기 ms (기본: expireTime/10, 최소 1000)
83
- expireTime: number; // 만료 시간 ms (마지막 접근 이후)
84
- onExpire?: (key: TKey, value: TValue) => void | Promise<void>;
85
- });
86
- get size: number;
87
- has(key): boolean; // 접근 시간 갱신 안 함
88
- get(key): TValue | undefined; // 접근 시간 갱신(LRU)
89
- set(key, value): void; // 저장 + GC 타이머 시작
90
- delete(key): boolean; // 비면 타이머 중지
91
- getOrCreate(key, factory: () => TValue): TValue; // dispose 후 호출 시 throw
92
- clear(): void; // 항목만 비움(재사용 가능)
93
- dispose(): void; // 타이머 중지 + 비움(이후 set/get 무력화)
94
- values(): IterableIterator<TValue>;
95
- keys(): IterableIterator<TKey>;
96
- entries(): IterableIterator<[TKey, TValue]>;
97
- }
55
+ ```ts
56
+ import { wait } from "@simplysm/core-common";
57
+ await wait.until(() => isReady, 100, 50); // 100ms 간격 최대 50회, 초과 TimeoutError
58
+ await wait.time(500);
98
59
  ```
99
60
 
100
- - `expireTime` — 필수. 마지막 접근 후 이 시간이 지나면 만료. `gcInterval` — 만료 스캔 주기(미지정 시 `expireTime/10`, 최소 1000ms).
101
- - `onExpire(key, value)` — 만료 직전 호출(비동기 가능). 콜백이 throw 해도 로그만 남기고 GC 계속. 콜백 도중 같은 key 가 재등록되면 새 항목은 삭제하지 않음.
102
- - `get` 접근 시간을 갱신(LRU), `has`갱신하지 않음. `set` 호출돼야 GC 타이머가 시작되고, 항목이 모두 비면 타이머가 자동 중지.
103
- - `getOrCreate` 는 dispose 이후 호출하면 throw(silent 동작 금지). `dispose` 미호출 시 타이머가 계속 돌아 메모리 누수.
104
-
105
- ```typescript
106
- const cache = new LazyGcMap<string, Session>({ expireTime: 60000 });
61
+ ## LazyGcMap<TKey, TValue>
62
+
63
+ 마지막 접근 이후 일정 시간 지나면 항목을 자동 삭제하는 Map(LRU 접근 시간 갱신). GC항목이 있을 때만 타이머로 동작. 사용 **반드시 `dispose()`** 하면 타이머가 남아 메모리 누수.
64
+
65
+ - `new LazyGcMap({ expireTime, gcInterval?, onExpire? })`
66
+ - expireTime: number(ms) — 마지막 접근 후 이 시간 지나면 만료. (필수)
67
+ - gcInterval?: number(ms) GC 점검 주기. 기본 `max(expireTime/10, 1000)`.
68
+ - onExpire?: `(key, value) => void | Promise<void>` — 만료 시 콜백(비동기 가능). 콜백 throw 는 logger.error 후 계속 진행. 만료 항목 정리·리소스 해제에.
69
+ - `get(key)`: → `V | undefined` — 조회(접근 시간 갱신=만료 연장).
70
+ - `has(key)`: → boolean — 존재 확인(접근 시간 갱신 안 함).
71
+ - `set(key, value)`: → void — 저장(GC 타이머 시작).
72
+ - `getOrCreate(key, factory)`: → V — 없으면 factory()로 생성·저장 후 반환(있으면 접근 시간 갱신). dispose 후 호출 시 throw.
73
+ - `delete(key)`: → boolean / `clear()`: → void(인스턴스 재사용 가능) / `dispose()`: → void(타이머 중지+정리, 이후 사용 불가).
74
+ - `values()` / `keys()` / `entries()`: → Iterator — 순회.
75
+ - `size`: → number — 항목 수.
76
+
77
+ ```ts
78
+ import { LazyGcMap } from "@simplysm/core-common";
79
+ const cache = new LazyGcMap<string, Conn>({ expireTime: 60000, onExpire: (k, c) => c.close() });
107
80
  try {
108
- const s = cache.getOrCreate(id, () => loadSession(id));
81
+ const conn = cache.getOrCreate(key, () => createConn());
109
82
  } finally {
83
+ // 앱/모듈 종료 시
110
84
  cache.dispose();
111
85
  }
112
86
  ```
113
-
114
- ## createLogger
115
-
116
- `consola` 기반 태그 로거를 지연 생성하는 팩토리. 모듈 최상위에서 호출해도 안전(첫 메서드 접근 시점까지 `consola.withTag` 생성을 미뤄, 이후 `setupConsola()` 의 level/reporter 변경이 반영됨).
117
-
118
- ```typescript
119
- createLogger(tag: string): ConsolaInstance;
120
- ```
121
-
122
- - `tag` — 로그에 붙는 태그(클래스/모듈명 등). 반환값은 `consola` 인스턴스라 `.info`/`.warn`/`.error`/`.success` 등을 그대로 사용.
123
- - 즉시 `consola.withTag()` 를 부르면 호출 시점 옵션이 고정되는 문제를 피하기 위한 Proxy 래퍼. 테스트의 `vi.spyOn` 과도 호환.
124
-
125
- ```typescript
126
- const logger = createLogger("MyService");
127
- logger.error("처리 실패", err);
128
- ```
@@ -1,129 +1,86 @@
1
- # @simplysm/core-common — 날짜·시간
1
+ # @simplysm/core-common — 날짜·시간 값 타입
2
2
 
3
- 불변 날짜/시간 값 `DateTime`(날짜+시간)·`DateOnly`(날짜)·`Time`(시간)과 포맷 문자열을 처리하는 `dt` 네임스페이스. 클래스 모두 로컬 타임존 기준이며, 모든 set/add 메서드는 원본을 변경하지 않고 인스턴스를 반환한다. 파싱 실패 `ArgumentError` throw.
3
+ 날짜·시간을 **불변(immutable)** 값으로 다룰 함께 읽히는 묶음. JS `Date` 대신 사용. `set*`·`add*` 메서드는 모두 새 인스턴스를 반환하고 원본을 바꾸지 않음. 모두 로컬 타임존 기준으로 동작. ORM 컬럼 타입(`DateTime`/`DateOnly`/`Time`)·JSON/Worker 직렬화에서 1급 지원됨.
4
4
 
5
- 공통 포맷 토큰(`toFormatString` 인자): `yyyy`/`yy`(연), `MM`/`M`(월), `ddd`(요일 한글: 일~토)/`dd`/`d`(일), `tt`(AM/PM), `hh`/`h`(12시간), `HH`/`H`(24시간), `mm`/`m`(분), `ss`/`s`(초), `fff`/`ff`/`f`(밀리초), `zzz`/`zz`/`z`(타임존 오프셋, DateTime 만). 긴 토큰이 먼저 치환됨.
5
+ 클래스 공통:
6
+ - `parse(str)` 정적 메서드로 문자열 파싱(미지원 형식이면 ArgumentError).
7
+ - `tick` getter — 내부 밀리초 값. `equal`·정렬·복제의 동등성 기준.
8
+ - `isValid` getter — 유효 값 여부.
9
+ - `toFormatString(formatStr)` / `toString()` — 포맷 문자열 변환(아래 `dt` 네임스페이스의 포맷 토큰 사용).
6
10
 
7
11
  ## DateTime
8
12
 
9
- 날짜+시간을 ms 정밀도로 담는 불변 클래스. 내부에 `readonly date: Date` 보유.
10
-
11
- ```typescript
12
- class DateTime {
13
- constructor(); // 현재 시각
14
- constructor(year, month, day, hour?, minute?, second?, millisecond?); // month 는 1~12
15
- constructor(tick: number); // epoch ms
16
- constructor(date: Date);
17
- static parse(str: string): DateTime;
18
-
19
- readonly date: Date;
20
- get year/month/day/hour/minute/second/millisecond/tick: number;
21
- get dayOfWeek: number; // 0(일)~6(토)
22
- get timezoneOffsetMinutes: number; // UTC 대비 분 (KST = +540)
23
- get isValid: boolean;
24
-
25
- setYear/setMonth/setDay/setHour/setMinute/setSecond/setMillisecond(v: number): DateTime;
26
- addYears/addMonths/addDays/addHours/addMinutes/addSeconds/addMilliseconds(n: number): DateTime;
27
- toFormatString(formatStr: string): string;
28
- toString(): string; // "yyyy-MM-ddTHH:mm:ss.fffzzz"
29
- }
30
- ```
13
+ 날짜+시간(밀리초 정밀도) 불변 클래스.
14
+
15
+ 생성자 오버로드:
16
+ - `new DateTime()` — 현재 시각.
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, 읽기 전용).
31
24
 
32
- - 생성자 `month` 인자는 1~12(내부에서 `month-1` 로 Date 에 전달). `tick`/`Date` 오버로드는 단일 숫자/Date 로 구분.
33
- - `parse` 지원 형식: `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` 를 시도.
34
- - `setMonth(month)` — 1~12 밖이면 연도로 흡수, 대상 월 일수보다 현재 일이 크면 말일로 보정(1/31 → setMonth(2) → 2/28). `setYear` 도 윤년 말일 보정.
35
- - `addMonths`/`addDays` 는 각각 `setMonth`/`setDay` 기반이라 월말 보정/오버플로 규칙을 따름. `addHours` 이하는 tick 가산이라 타임존 전환 영향 없음.
36
- - `isValid` — 내부 Date 가 NaN 이 아닌지. `parse` 가 아닌 잘못된 tick/Date 로 만든 경우 점검용.
25
+ 불변 변환(새 인스턴스): `setYear`·`setMonth`·`setDay`·`setHour`·`setMinute`·`setSecond`·`setMillisecond`. 산술( 인스턴스): `addYears`·`addMonths`·`addDays`·`addHours`·`addMinutes`·`addSeconds`·`addMilliseconds`.
37
26
 
38
- ```typescript
39
- new DateTime(2025, 1, 31).setMonth(2).toFormatString("yyyy-MM-dd"); // "2025-02-28"
40
- DateTime.parse("2025-01-15 오후 2:30:00").hour; // 14
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"
41
34
  ```
42
35
 
43
36
  ## DateOnly
44
37
 
45
- 시간 정보 없이 날짜만 담는 불변 클래스(`readonly date: Date`, 자정 고정). 주차 계산 API 포함.
46
-
47
- ```typescript
48
- class DateOnly {
49
- constructor(); // 오늘
50
- constructor(year, month, day); // month 1~12
51
- constructor(tick: number);
52
- constructor(date: Date);
53
- static parse(str: string): DateOnly;
54
- static getDateByYearWeekSeq(arg: { year: number; month?: number; weekSeq: number }, weekStartDay?: number, minDaysInFirstWeek?: number): DateOnly;
55
-
56
- readonly date: Date;
57
- get year/month/day/tick/dayOfWeek: number; // dayOfWeek 0(일)~6(토)
58
- get isValid: boolean;
59
- setYear/setMonth/setDay(v: number): DateOnly;
60
- addYears/addMonths/addDays(n: number): DateOnly;
61
-
62
- getBaseYearMonthSeqForWeekSeq(weekStartDay?: number, minDaysInFirstWeek?: number): { year: number; monthSeq: number };
63
- getWeekSeqStartDate(weekStartDay?: number, minDaysInFirstWeek?: number): DateOnly;
64
- getWeekSeqOfYear(weekStartDay?: number, minDaysInFirstWeek?: number): { year: number; weekSeq: number };
65
- getWeekSeqOfMonth(weekStartDay?: number, minDaysInFirstWeek?: number): { year: number; monthSeq: number; weekSeq: number };
66
-
67
- toFormatString(formatStr: string): string;
68
- toString(): string; // "yyyy-MM-dd"
69
- }
70
- ```
38
+ 시간 없는 날짜(yyyy-MM-dd) 불변 클래스.
39
+
40
+ 생성자: `new DateOnly()`(오늘) / `(year, month, day)` / `(tick)` / `(date)`.
71
41
 
72
- - `parse` 형식: `yyyy-MM-dd`·`yyyyMMdd`(둘 다 타임존 무관, 문자열에서 직접 추출), ISO 8601(UTC 해석 후 로컬 변환). 서버/클라 타임존이 다르면 `yyyy-MM-dd` 권장.
73
- - 주차 계산 공통 인자: `weekStartDay` 주 시작 요일(0=일~6=토, 기본 1=월), `minDaysInFirstWeek` 첫 주로 인정할 최소 일수(1~7, 기본 4=ISO 8601). 미국식은 `(0, 1)`.
74
- - `getWeekSeqOfYear` — 연 기준 몇 째 주인지. `getWeekSeqOfMonth` — 월 기준 주차(+기준 연·월). `getWeekSeqStartDate` — 이 날짜가 속한 주의 시작일. `getBaseYearMonthSeqForWeekSeq` — 주차 귀속 기준 연·월(월 경계 조정).
75
- - `getDateByYearWeekSeq(arg, ...)` — 정적. `{ year, weekSeq }`(연 주차) 또는 `{ year, month, weekSeq }`(월 주차)로 해당 주 시작일 역산.
42
+ - `DateOnly.parse(str)`: DateOnly — `yyyy-MM-dd`·`yyyyMMdd`(타임존 무관, 문자열에서 직접 추출)·ISO 8601(UTC 해석 후 로컬 변환). 서버/클라 타임존이 다르면 `yyyy-MM-dd` 형식 권장.
76
43
 
77
- ```typescript
78
- new DateOnly(2025, 1, 6).getWeekSeqOfYear(); // { year: 2025, weekSeq: 2 }
79
- DateOnly.getDateByYearWeekSeq({ year: 2025, weekSeq: 2 }); // 2025-01-06 (월)
44
+ getter: `year`·`month`·`day`·`tick`·`dayOfWeek`·`isValid`·`date`. 불변 변환: `setYear`·`setMonth`·`setDay`(DateTime 과 동일한 월말/캐리 보정). 산술: `addYears`·`addMonths`·`addDays`.
45
+
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 }` 로 그 주의 시작일을 역산.
52
+
53
+ 옵션 풀이:
54
+ - weekStartDay: 0~6 — 주 시작 요일. 0=일요일(미국식), 1=월요일(ISO, 기본). 달력 표시 기준에 맞춤.
55
+ - minDaysInFirstWeek: 1~7 — 첫 주로 인정할 최소 일수. 4=ISO(주의 과반), 1=시작일 포함 즉시 1주차(미국식).
56
+
57
+ ```ts
58
+ import { DateOnly } from "@simplysm/core-common";
59
+ new DateOnly(2025, 1, 6).getWeekSeqOfYear(); // { year: 2025, weekSeq: 2 }
80
60
  ```
81
61
 
82
62
  ## Time
83
63
 
84
- 날짜 없이 시각(HH:mm:ss.fff) 담는 불변 클래스. 24시간을 넘거나 음수인 tick 은 24시간 순환으로 정규화된다.
85
-
86
- ```typescript
87
- class Time {
88
- constructor(); // 현재 시각의 시간 부분
89
- constructor(hour, minute, second?, millisecond?);
90
- constructor(tick: number); // 자정 기준 ms
91
- constructor(date: Date); // Date 의 시간 부분만
92
- static parse(str: string): Time;
93
-
94
- get hour/minute/second/millisecond/tick: number;
95
- get isValid: boolean;
96
- setHour/setMinute/setSecond/setMillisecond(v: number): Time;
97
- addHours/addMinutes/addSeconds/addMilliseconds(n: number): Time; // 24시간 순환
98
- toFormatString(formatStr: string): string;
99
- toString(): string; // "HH:mm:ss.fff"
100
- }
101
- ```
64
+ 날짜 없는 시간(HH:mm:ss.fff) 불변 클래스. 24시간 초과·음수 tick 은 자동으로 0~24h 범위로 순환 정규화됨.
102
65
 
103
- - 생성자 다중 인자(`hour, minute, ...`) 단일 `tick`/`Date` 오버로드. 결과 tick 은 항상 `[0, 24h)` 범위로 wrap.
104
- - `parse` 형식: `HH:mm:ss[.fff]`, `AM/PM HH:mm:ss[.fff]`, ISO 8601(시간 부분만 추출, 타임존 변환은 Date 위임).
105
- - `addHours` 등 산술은 24시간을 넘으면 다시 0시부터(`23:00` + 2h → `01:00`). 날짜 개념이 없으므로 일자 carry 는 버려짐.
66
+ 생성자: `new Time()`(현재 시각) / `(hour, minute, second?, millisecond?)` / `(tick)` / `(date)`(Date 시간부만).
106
67
 
107
- ```typescript
108
- Time.parse("AM 10:30:00").addHours(15).toString(); // "01:30:00.000"
109
- ```
68
+ - `Time.parse(str)`: → Time — `HH:mm:ss[.fff]`·`AM/PM HH:mm:ss`·ISO 8601(시간부만 추출).
110
69
 
111
- ## dt 네임스페이스
70
+ getter: `hour`·`minute`·`second`·`millisecond`·`tick`·`isValid`. 불변 변환: `setHour`·`setMinute`·`setSecond`·`setMillisecond`. 산술: `addHours`·`addMinutes`·`addSeconds`·`addMilliseconds` (모두 24시간 순환 — 23:30 에 +1h → 00:30).
112
71
 
113
- `import { dt } from "@simplysm/core-common"`. 위 클래스들이 내부에서 쓰는 포맷/정규화 함수를 직접 노출.
72
+ ## dt 네임스페이스 (날짜/시간 포맷)
114
73
 
115
- ```typescript
116
- dt.format(formatString: string, args: { year?; month?; day?; hour?; minute?; second?; millisecond?; timezoneOffsetMinutes?: number }): string;
117
- dt.normalizeMonth(year: number, month: number, day: number): { year: number; month: number; day: number };
118
- dt.convert12To24(rawHour: number, isPM: boolean): number;
119
- interface DtNormalizedMonth { year: number; month: number; day: number; }
120
- ```
74
+ `toFormatString` 이 내부적으로 쓰는 포맷 토큰 정의. 직접 호출도 가능.
75
+
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 }`.
121
80
 
122
- - `dt.format(fmt, args)` 공통 토큰 표를 따르는 저수준 포맷터. 누락한 구성요소(예: `hour`) 해당 토큰만 치환하고 나머지는 원문 유지. `DateTime`/`DateOnly`/`Time` `toFormatString` 이 함수를 호출.
123
- - `dt.normalizeMonth(year, month, day)` — 월이 1~12 밖이면 연도로 흡수, 일이 대상 월 일수 초과면 말일로 보정한 `{year, month, day}`. `setMonth` 의 보정 규칙 그대로.
124
- - `dt.convert12To24(rawHour, isPM)` — 12시간(1~12)+AM/PM 을 0~23 으로. `12 AM`→0, `12 PM`→12.
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, 타임존 오프셋).
125
82
 
126
- ```typescript
83
+ ```ts
84
+ import { dt } from "@simplysm/core-common";
127
85
  dt.format("yyyy-MM-dd (ddd)", { year: 2024, month: 3, day: 15 }); // "2024-03-15 (금)"
128
- dt.normalizeMonth(2025, 13, 15); // { year: 2026, month: 1, day: 15 }
129
86
  ```
@@ -1,91 +1,86 @@
1
1
  # @simplysm/core-common — 에러 클래스
2
2
 
3
- 원인 체인(`cause`)을 가진 에러를 throw 하거나 타입별로 분기할 때 함께 읽히는 묶음. 모두 `SdError` 를 베이스로 하며 각 클래스는 `name` 자기 클래스명으로 설정해 `instanceof`·`name` 양쪽으로 식별 가능.
3
+ `throw` 던지거나, 에러 원인을 체인으로 감싸거나, catch 에서 `instanceof` 로 분기할 때 함께 읽히는 묶음. 모두 `SdError` 를 상속하므로 `instanceof SdError` 한꺼번에 잡을 있음.
4
4
 
5
5
  ## SdError
6
6
 
7
- ES2024 `cause` 를 활용해 에러를 트리로 감싸는 베이스 클래스. 메시지는 **역순으로 결합**되어 상위(가장 바깥) 메시지가 앞에 온다.
8
-
9
- ```typescript
7
+ ```ts
10
8
  class SdError extends Error {
11
9
  override cause?: Error;
12
- constructor(cause: Error, ...messages: string[]); // 원인 에러 + 상위 메시지들
13
- constructor(...messages: string[]); // 메시지들만
10
+ constructor(cause: Error, ...messages: string[]); // 원인 에러를 감싸기
11
+ constructor(...messages: string[]); // 메시지만으로 생성
14
12
  }
15
13
  ```
16
14
 
17
- - `cause: Error` 인자가 `Error` 원인으로 저장되고 stack 이 현재 stack 뒤에 `---- cause stack ----` 이어붙음. 인자가 문자열/기타면 일반 메시지로 취급.
18
- - `...messages` — 추가 설명 메시지들. 결합 시 `messages` 가 먼저 reverse 되어 `상위 => 하위 => 원인` 순으로 `" => "` 결합. null/undefined 메시지는 제외.
15
+ ES2024 `cause` 활용한 트리형 에러. 메시지는 **역순으로** `" => "`결합됨(상위 메시지가 앞).
19
16
 
20
- ```typescript
21
- throw new SdError(err, "API 호출 실패", "사용자 로드 실패");
22
- // message: "사용자 로드 실패 => API 호출 실패 => 원본 에러 메시지"
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` 생성자 프레임 제거.
23
20
 
24
- throw new SdError("잘못된 상태", "처리 불가");
25
- // message: "처리 불가 => 잘못된 상태"
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
+ }
26
28
  ```
27
29
 
28
- 주의: 하위 에러를 잡아 컨텍스트를 덧붙여 다시 throw 사용. 원본 에러를 삼키지 말고 인자로 넘겨 체인을 보존.
30
+ 주의: 인자가 Error 아니면(문자열·기타) cause 없이 메시지로만 취급됨. `new SdError("잘못된 상태", "처리 불가")` `"처리 불가 => 잘못된 상태"`.
29
31
 
30
32
  ## ArgumentError
31
33
 
32
- 유효하지 않은 인자를 받았을 때 throw. 인자 객체를 YAML 로 직렬화해 메시지에 포함(디버깅용). `SdError` 상속.
33
-
34
- ```typescript
34
+ ```ts
35
35
  class ArgumentError extends SdError {
36
- constructor(argObj: Record<string, unknown>); // 기본 메시지 + 인자
37
- constructor(message: string, argObj: Record<string, unknown>); // 커스텀 메시지 + 인자
36
+ constructor(argObj: Record<string, unknown>);
37
+ constructor(message: string, argObj: Record<string, unknown>);
38
38
  }
39
39
  ```
40
40
 
41
- - `argObj` 문제가 인자들을 담은 객체. `YAML.stringify` 결과가 메시지 본문 뒤에 띄고 붙음. 어떤 값이 잘못됐는지 그대로 노출하려는 의도.
42
- - `message` — 생략 시 `"잘못된 인자입니다."` 가 기본값.
41
+ 유효하지 않은 인자를 받았을 던지는 에러. 디버깅을 위해 인자 객체를 **YAML 형식**으로 메시지에 붙임. `name` `"ArgumentError"`.
42
+
43
+ - argObj: Record<string, unknown> — 메시지에 YAML 로 직렬화해 포함할 인자값들. 어떤 입력이 문제였는지 드러낼 때.
44
+ - message: string — 커스텀 머리말. 생략 시 `"잘못된 인자입니다."` 사용.
43
45
 
44
- ```typescript
45
- throw new ArgumentError("잘못된 사용자", { userId: 123, name: null });
46
- // "잘못된 사용자\n\nuserId: 123\nname: null"
46
+ ```ts
47
+ import { ArgumentError } from "@simplysm/core-common";
48
+ throw new ArgumentError("유효하지 않은 UUID 형식입니다.", { uuid });
49
+ // 메시지: "유효하지 않은 UUID 형식입니다.\n\nuuid: ..."
47
50
  ```
48
51
 
49
- 이 패키지 내부(`Uuid`, `bytes`, `num` 파싱, `obj` 체인 함수 등)에서 입력 검증 실패 광범위하게 사용된다.
52
+ 이 패키지 내부 검증(Uuid·bytes·obj 체인 등)에서 이미 광범위하게 throw 하므로, 유효성 위반은 직접 처리하지 말고 그대로 전파하는 편이 일관적.
50
53
 
51
54
  ## NotImplementedError
52
55
 
53
- 아직 구현되지 않은 코드 경로가 호출됐을 때 throw. `SdError` 상속.
54
-
55
- ```typescript
56
+ ```ts
56
57
  class NotImplementedError extends SdError {
57
- constructor(message?: string); // "미구현" 또는 "미구현: <message>"
58
+ constructor(message?: string);
58
59
  }
59
60
  ```
60
61
 
61
- - `message` 어떤 기능이 미구현인지 보조 설명. 생략 메시지는 `"미구현"`.
62
+ 아직 구현되지 않은 기능이 호출됐을 때. 메시지는 `"미구현"` 또는 `"미구현: <message>"`. `name` 은 `"NotImplementedError"`. 추상 메서드 스텁, 미구현 분기에 사용.
62
63
 
63
- ```typescript
64
- switch (type) {
65
- case "A": return handleA();
66
- case "B": throw new NotImplementedError(`타입 ${type} 처리`); // "미구현: 타입 B 처리"
67
- }
68
- ```
69
-
70
- 추상 메서드 스텁이나 미완성 분기에 의도적 throw 로 두어 silent skip 을 막는 용도.
64
+ - message?: string — 무엇이 미구현인지 추가 설명. 예: `throw new NotImplementedError(\`타입 ${type} 처리\`)`.
71
65
 
72
66
  ## TimeoutError
73
67
 
74
- 대기 시간이 초과됐을 때 throw. `wait.until()` 이 `maxCount` 초과 시 자동으로 발생시킨다. `SdError` 상속.
75
-
76
- ```typescript
68
+ ```ts
77
69
  class TimeoutError extends SdError {
78
- constructor(count?: number, message?: string); // "대기 시간 초과(<count>회 시도): <message>"
70
+ constructor(count?: number, message?: string);
79
71
  }
80
72
  ```
81
73
 
82
- - `count` 시도 횟수. 지정 `(N회 시도)` 메시지에 삽입됨.
83
- - `message` — 무엇을 대기하다 실패했는지 보조 설명.
74
+ 대기 시간 초과 에러. 메시지는 `"대기 시간 초과"` + (count 있으면 `(N회 시도)`) + (message 있으면 `: <message>`). `name` 은 `"TimeoutError"`.
75
+
76
+ - count?: number — 시도 횟수. `wait.until(...)` 이 최대 시도 초과 시 자동으로 이 에러를 throw(시도 횟수를 넣어).
77
+ - message?: string — 무엇을 기다리다 초과했는지 추가 설명.
84
78
 
85
- ```typescript
79
+ ```ts
80
+ import { TimeoutError, wait } from "@simplysm/core-common";
86
81
  try {
87
82
  await wait.until(() => isReady, 100, 50);
88
- } catch (e) {
89
- if (e instanceof TimeoutError) { /* 타임아웃 분기 */ }
83
+ } catch (err) {
84
+ if (err instanceof TimeoutError) { /* 타임아웃 처리 */ }
90
85
  }
91
86
  ```