@simplysm/sd-claude 14.0.89 → 14.0.91
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.
- package/claude/references/sd-simplysm14/README.md +16 -17
- package/claude/references/sd-simplysm14/apis/angular/README.md +52 -30
- package/claude/references/sd-simplysm14/apis/angular/controls.md +200 -38
- package/claude/references/sd-simplysm14/apis/angular/crud.md +41 -53
- package/claude/references/sd-simplysm14/apis/angular/directives.md +66 -22
- package/claude/references/sd-simplysm14/apis/angular/features.md +127 -40
- package/claude/references/sd-simplysm14/apis/angular/infra.md +60 -43
- package/claude/references/sd-simplysm14/apis/angular/layout.md +56 -20
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +74 -74
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +50 -40
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +55 -15
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +59 -42
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +77 -62
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +8 -7
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +71 -43
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +22 -14
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +19 -19
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +17 -17
- package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +28 -28
- package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +37 -37
- package/claude/references/sd-simplysm14/apis/core-common/README.md +87 -219
- package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +54 -98
- package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +57 -99
- package/claude/references/sd-simplysm14/apis/core-common/datetime.md +60 -103
- package/claude/references/sd-simplysm14/apis/core-common/errors.md +42 -47
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +42 -88
- package/claude/references/sd-simplysm14/apis/core-common/serialization.md +55 -0
- package/claude/references/sd-simplysm14/apis/core-node/README.md +6 -7
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +17 -12
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +14 -13
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +9 -8
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +14 -13
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +4 -8
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +14 -12
- package/claude/references/sd-simplysm14/apis/excel/README.md +22 -22
- package/claude/references/sd-simplysm14/apis/excel/cell.md +37 -29
- package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +29 -15
- package/claude/references/sd-simplysm14/apis/excel/style.md +33 -27
- package/claude/references/sd-simplysm14/apis/excel/utils.md +29 -19
- package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +78 -55
- package/claude/references/sd-simplysm14/apis/excel/wrapper.md +42 -45
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -8
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +118 -67
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +83 -86
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +102 -93
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +138 -81
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +49 -44
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +42 -42
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +44 -33
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +11 -10
- package/claude/references/sd-simplysm14/apis/service-client/README.md +56 -52
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +33 -28
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +23 -21
- package/claude/references/sd-simplysm14/apis/service-common/README.md +83 -48
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +126 -34
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +109 -54
- package/claude/references/sd-simplysm14/apis/service-server/README.md +69 -81
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +46 -43
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +63 -37
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +40 -30
- package/claude/references/sd-simplysm14/apis/storage/README.md +17 -17
- package/claude/references/sd-simplysm14/manuals/client-app-structure.md +135 -140
- package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -1
- package/claude/references/sd-simplysm14/manuals/client-service.md +19 -7
- package/claude/references/sd-simplysm14/manuals/client-shared-data.md +2 -2
- package/claude/references/sd-simplysm14/manuals/client-system-log.md +16 -4
- package/claude/references/sd-simplysm14/manuals/data-log.md +0 -1
- package/claude/references/sd-simplysm14/manuals/orm.md +16 -0
- package/claude/rules/sd-design-rules.md +10 -0
- package/claude/skills/sd-demo/SKILL.md +0 -6
- package/claude/skills/sd-docs/SKILL.md +60 -0
- package/claude/{workflows/sd-docs.rules.md → skills/sd-docs/references/subagent-prompt.md} +118 -103
- package/claude/skills/sd-impl/SKILL.md +7 -4
- package/claude/skills/sd-spec/SKILL.md +842 -15
- package/claude/skills/sd-spec/references/example-spec.md +26 -36
- package/package.json +1 -1
- package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +0 -53
- package/claude/skills/sd-spec/references/spec-authoring.md +0 -519
- package/claude/workflows/sd-docs.js +0 -84
|
@@ -1,128 +1,86 @@
|
|
|
1
1
|
# @simplysm/core-common — 비동기 런타임
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
디바운스/직렬 실행, 타입 안전 이벤트, 조건 대기, 자동 만료 Map 이 필요할 때 함께 읽히는 묶음. 큐 클래스들은 `EventEmitter` 를 상속해 `"error"` 이벤트를 발행함.
|
|
4
4
|
|
|
5
|
-
## EventEmitter
|
|
5
|
+
## EventEmitter<TEvents>
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`EventTarget` 기반 타입 안전 이벤트 이미터(브라우저·Node 공용). 보통 상속해서 사용. `TEvents` 는 `{ 이벤트명: 데이터타입 }` 맵.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
```
|
|
15
|
+
```ts
|
|
16
|
+
import { EventEmitter } from "@simplysm/core-common";
|
|
24
17
|
interface MyEvents { data: string; done: void; }
|
|
25
|
-
class
|
|
26
|
-
const e = new
|
|
27
|
-
e.on("data", (
|
|
28
|
-
e.emit("data", "
|
|
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
|
-
짧은 시간 내 여러 호출 중
|
|
27
|
+
짧은 시간 내 여러 호출 중 **마지막 요청만** 실행. 입력 자동완성·연속 상태 변경 일괄 처리에. `EventEmitter<{ error: SdError }>` 상속.
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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", (
|
|
50
|
-
|
|
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
|
-
|
|
43
|
+
큐에 넣은 작업을 **순차** 실행(앞 작업 완료 후 다음). 에러가 나도 후속 작업은 계속. `EventEmitter<{ error: SdError }>` 상속.
|
|
56
44
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
66
|
-
- `run(fn)` — FIFO 로 순차 실행. 한 작업이 throw 하면 `SdError` 로 감싸 `error` 이벤트 발행(리스너 없으면 로그) 후 다음 작업 진행 — 후속 작업을 막지 않으려는 의도.
|
|
67
|
-
- `dispose` — 아직 시작 안 한 대기분만 제거(실행 중인 건은 완료).
|
|
50
|
+
## wait 네임스페이스
|
|
68
51
|
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
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
|
-
|
|
3
|
+
날짜·시간을 **불변(immutable)** 값으로 다룰 때 함께 읽히는 묶음. JS `Date` 대신 사용. `set*`·`add*` 메서드는 모두 새 인스턴스를 반환하고 원본을 바꾸지 않음. 모두 로컬 타임존 기준으로 동작. ORM 컬럼 타입(`DateTime`/`DateOnly`/`Time`)·JSON/Worker 직렬화에서 1급 지원됨.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
시간
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
날짜
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
+
## dt 네임스페이스 (날짜/시간 포맷)
|
|
114
73
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
dt.
|
|
118
|
-
dt.
|
|
119
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
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
|
-
|
|
3
|
+
`throw` 를 던지거나, 에러 원인을 체인으로 감싸거나, catch 에서 `instanceof` 로 분기할 때 함께 읽히는 묶음. 모두 `SdError` 를 상속하므로 `instanceof SdError` 로 한꺼번에 잡을 수 있음.
|
|
4
4
|
|
|
5
5
|
## SdError
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
18
|
-
- `...messages` — 추가 설명 메시지들. 결합 시 `messages` 가 먼저 reverse 되어 `상위 => 하위 => 원인` 순으로 `" => "` 결합. null/undefined 메시지는 제외.
|
|
15
|
+
ES2024 `cause` 를 활용한 트리형 에러. 메시지는 **역순으로** `" => "` 로 결합됨(상위 메시지가 앞).
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
주의:
|
|
30
|
+
주의: 첫 인자가 Error 가 아니면(문자열·기타) cause 없이 메시지로만 취급됨. `new SdError("잘못된 상태", "처리 불가")` → `"처리 불가 => 잘못된 상태"`.
|
|
29
31
|
|
|
30
32
|
## ArgumentError
|
|
31
33
|
|
|
32
|
-
|
|
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
|
-
|
|
42
|
-
|
|
41
|
+
유효하지 않은 인자를 받았을 때 던지는 에러. 디버깅을 위해 인자 객체를 **YAML 형식**으로 메시지에 붙임. `name` 은 `"ArgumentError"`.
|
|
42
|
+
|
|
43
|
+
- argObj: Record<string, unknown> — 메시지에 YAML 로 직렬화해 포함할 인자값들. 어떤 입력이 문제였는지 드러낼 때.
|
|
44
|
+
- message: string — 커스텀 머리말. 생략 시 `"잘못된 인자입니다."` 사용.
|
|
43
45
|
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
```ts
|
|
47
|
+
import { ArgumentError } from "@simplysm/core-common";
|
|
48
|
+
throw new ArgumentError("유효하지 않은 UUID 형식입니다.", { uuid });
|
|
49
|
+
// 메시지: "유효하지 않은 UUID 형식입니다.\n\nuuid: ..."
|
|
47
50
|
```
|
|
48
51
|
|
|
49
|
-
이 패키지 내부(
|
|
52
|
+
이 패키지 내부 검증(Uuid·bytes·obj 체인 등)에서 이미 광범위하게 throw 하므로, 유효성 위반은 직접 처리하지 말고 그대로 전파하는 편이 일관적.
|
|
50
53
|
|
|
51
54
|
## NotImplementedError
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
+
```ts
|
|
56
57
|
class NotImplementedError extends SdError {
|
|
57
|
-
constructor(message?: string);
|
|
58
|
+
constructor(message?: string);
|
|
58
59
|
}
|
|
59
60
|
```
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
아직 구현되지 않은 기능이 호출됐을 때. 메시지는 `"미구현"` 또는 `"미구현: <message>"`. `name` 은 `"NotImplementedError"`. 추상 메서드 스텁, 미구현 분기에 사용.
|
|
62
63
|
|
|
63
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
68
|
+
```ts
|
|
77
69
|
class TimeoutError extends SdError {
|
|
78
|
-
constructor(count?: number, message?: string);
|
|
70
|
+
constructor(count?: number, message?: string);
|
|
79
71
|
}
|
|
80
72
|
```
|
|
81
73
|
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
대기 시간 초과 에러. 메시지는 `"대기 시간 초과"` + (count 있으면 `(N회 시도)`) + (message 있으면 `: <message>`). `name` 은 `"TimeoutError"`.
|
|
75
|
+
|
|
76
|
+
- count?: number — 시도 횟수. `wait.until(...)` 이 최대 시도 초과 시 자동으로 이 에러를 throw(시도 횟수를 넣어).
|
|
77
|
+
- message?: string — 무엇을 기다리다 초과했는지 추가 설명.
|
|
84
78
|
|
|
85
|
-
```
|
|
79
|
+
```ts
|
|
80
|
+
import { TimeoutError, wait } from "@simplysm/core-common";
|
|
86
81
|
try {
|
|
87
82
|
await wait.until(() => isReady, 100, 50);
|
|
88
|
-
} catch (
|
|
89
|
-
if (
|
|
83
|
+
} catch (err) {
|
|
84
|
+
if (err instanceof TimeoutError) { /* 타임아웃 처리 */ }
|
|
90
85
|
}
|
|
91
86
|
```
|