@simplysm/core-common 14.0.49 → 14.0.51
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/README.md +57 -152
- package/dist/errors/sd-error.d.ts.map +1 -1
- package/dist/errors/sd-error.js +1 -1
- package/dist/errors/sd-error.js.map +1 -1
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/obj.js.map +1 -1
- package/docs/errors/argument-error.md +26 -0
- package/docs/errors/not-implemented-error.md +33 -0
- package/docs/errors/sd-error.md +38 -0
- package/docs/errors/timeout-error.md +36 -0
- package/docs/extensions/array.md +125 -0
- package/docs/extensions/map.md +43 -0
- package/docs/extensions/set.md +35 -0
- package/docs/features/debounce-queue.md +48 -0
- package/docs/features/event-emitter.md +52 -0
- package/docs/features/serial-queue.md +44 -0
- package/docs/type-utils/common-types.md +100 -0
- package/docs/type-utils/env.md +42 -0
- package/docs/types/date-only.md +86 -0
- package/docs/types/date-time.md +106 -0
- package/docs/types/lazy-gc-map.md +59 -0
- package/docs/types/time.md +62 -0
- package/docs/types/uuid.md +41 -0
- package/docs/utils/bytes.md +36 -0
- package/docs/utils/dt.md +60 -0
- package/docs/utils/err.md +26 -0
- package/docs/utils/json.md +58 -0
- package/docs/utils/num.md +56 -0
- package/docs/utils/obj.md +107 -0
- package/docs/utils/path.md +30 -0
- package/docs/utils/primitive.md +28 -0
- package/docs/utils/str.md +63 -0
- package/docs/utils/template-strings.md +49 -0
- package/docs/utils/transfer.md +35 -0
- package/docs/utils/wait.md +35 -0
- package/docs/utils/xml.md +49 -0
- package/docs/utils/zip-archive.md +77 -0
- package/package.json +1 -1
- package/src/errors/sd-error.ts +1 -4
- package/src/utils/json.ts +1 -1
- package/src/utils/obj.ts +2 -2
- package/docs/errors.md +0 -82
- package/docs/extensions.md +0 -167
- package/docs/features.md +0 -136
- package/docs/types.md +0 -245
- package/docs/utils.md +0 -591
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Array Extensions
|
|
2
|
+
|
|
3
|
+
`@simplysm/core-common`을 import하면 `Array.prototype`에 확장 메서드가 자동 등록된다. `ReadonlyArray<T>`와 `Array<T>` 모두에 적용된다.
|
|
4
|
+
|
|
5
|
+
side-effect import로 활성화:
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import "@simplysm/core-common";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 불변(Immutable) 메서드
|
|
12
|
+
|
|
13
|
+
원본 배열을 변경하지 않고 새 배열 또는 값을 반환한다.
|
|
14
|
+
|
|
15
|
+
| Method | Signature | Description |
|
|
16
|
+
|--------|-----------|-------------|
|
|
17
|
+
| `single` | `(predicate?) => T \| undefined` | 조건에 맞는 단일 요소 반환. 2개 이상이면 `ArgumentError` 발생 |
|
|
18
|
+
| `first` | `(predicate?) => T \| undefined` | 첫 번째 요소 반환 |
|
|
19
|
+
| `last` | `(predicate?) => T \| undefined` | 마지막 요소 반환 |
|
|
20
|
+
| `filterExists` | `() => NonNullable<T>[]` | null/undefined 제거 |
|
|
21
|
+
| `ofType` | `(type: PrimitiveTypeStr \| Type<N>) => N[]` | 특정 타입의 요소만 필터 |
|
|
22
|
+
| `filterAsync` | `(predicate) => Promise<T[]>` | 비동기 필터 (순차 실행) |
|
|
23
|
+
| `mapAsync` | `(selector) => Promise<R[]>` | 비동기 매핑 (순차 실행) |
|
|
24
|
+
| `mapMany` | `(selector?) => R[]` | 매핑 후 평탄화 (또는 중첩 배열 평탄화) |
|
|
25
|
+
| `mapManyAsync` | `(selector?) => Promise<R[]>` | 비동기 매핑 후 평탄화 (순차 실행) |
|
|
26
|
+
| `parallelAsync` | `(fn) => Promise<R[]>` | 비동기 병렬 처리 (`Promise.all` 사용). 하나라도 reject되면 전체 reject |
|
|
27
|
+
| `groupBy` | `(keySelector, valueSelector?) => { key, values }[]` | key 기준 그룹화. 객체 key 지원을 위해 O(n²) 복잡도. 원시 key는 O(n) |
|
|
28
|
+
| `toMap` | `(keySelector, valueSelector?) => Map<K, V>` | Map으로 변환. 중복 key이면 `ArgumentError` 발생 |
|
|
29
|
+
| `toMapAsync` | `(keySelector, valueSelector?) => Promise<Map<K, V>>` | 비동기 Map 변환 |
|
|
30
|
+
| `toArrayMap` | `(keySelector, valueSelector?) => Map<K, V[]>` | 배열 값 Map으로 변환 (O(n)) |
|
|
31
|
+
| `toSetMap` | `(keySelector, valueSelector?) => Map<K, Set<V>>` | Set 값 Map으로 변환 |
|
|
32
|
+
| `toMapValues` | `(keySelector, valueSelector) => Map<K, V>` | 그룹별 집계 Map으로 변환 |
|
|
33
|
+
| `toObject` | `(keySelector, valueSelector?) => Record<string, V>` | 객체로 변환. 중복 key이면 `ArgumentError` 발생 |
|
|
34
|
+
| `toTree` | `(keyProp, parentKey) => TreeArray<T>[]` | 평면 배열을 트리 구조로 변환 (O(n)). `parentKey`가 null/undefined인 항목이 루트 |
|
|
35
|
+
| `distinct` | `(options?) => T[]` | 중복 제거. 객체 배열에서 keyFn 없이 사용하면 O(n²) |
|
|
36
|
+
| `orderBy` | `(selector?) => T[]` | 오름차순 정렬 (새 배열 반환) |
|
|
37
|
+
| `orderByDesc` | `(selector?) => T[]` | 내림차순 정렬 (새 배열 반환) |
|
|
38
|
+
| `diffs` | `(target, options?) => ArrayDiffsResult<T, P>[]` | 두 배열 비교 (INSERT/DELETE/UPDATE) |
|
|
39
|
+
| `oneWayDiffs` | `(orgItems, keyPropNameOrGetValFn, options?) => ArrayOneWayDiffResult<T>[]` | 단방향 비교 (create/update/same) |
|
|
40
|
+
| `merge` | `(target, options?) => (T \| P \| T & P)[]` | 두 배열 병합 (`diffs` 기반) |
|
|
41
|
+
| `sum` | `(selector?) => number` | 합계. 빈 배열이면 0 반환 |
|
|
42
|
+
| `min` | `(selector?) => T \| undefined` | 최솟값 |
|
|
43
|
+
| `max` | `(selector?) => T \| undefined` | 최댓값 |
|
|
44
|
+
| `shuffle` | `() => T[]` | 무작위 순서로 섞은 새 배열 반환 |
|
|
45
|
+
|
|
46
|
+
## 가변(Mutable) 메서드
|
|
47
|
+
|
|
48
|
+
원본 배열을 직접 수정하고 `this`를 반환한다.
|
|
49
|
+
|
|
50
|
+
| Method | Signature | Description |
|
|
51
|
+
|--------|-----------|-------------|
|
|
52
|
+
| `remove` | `(item: T) => this` | 항목 제거 |
|
|
53
|
+
| `remove` | `(selector: (item, index) => boolean) => this` | 조건에 맞는 항목 제거 |
|
|
54
|
+
| `insert` | `(index: number, ...items: T[]) => this` | 특정 위치에 항목 삽입 |
|
|
55
|
+
| `toggle` | `(item: T) => this` | 항목 토글 (있으면 제거, 없으면 추가) |
|
|
56
|
+
| `clear` | `() => this` | 배열 비우기 |
|
|
57
|
+
| `distinctThis` | `(options?) => T[]` | 원본 배열에서 중복 제거 |
|
|
58
|
+
| `orderByThis` | `(selector?) => T[]` | 원본 배열 오름차순 정렬 |
|
|
59
|
+
| `orderByDescThis` | `(selector?) => T[]` | 원본 배열 내림차순 정렬 |
|
|
60
|
+
|
|
61
|
+
## Related Types
|
|
62
|
+
|
|
63
|
+
### `ArrayDiffsResult<TOriginal, TOther>`
|
|
64
|
+
|
|
65
|
+
`diffs()` 결과 타입. Discriminated union:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
export type ArrayDiffsResult<TOriginal, TOther> =
|
|
69
|
+
| { source: undefined; target: TOther } // INSERT (target에만 있음)
|
|
70
|
+
| { source: TOriginal; target: undefined } // DELETE (source에만 있음)
|
|
71
|
+
| { source: TOriginal; target: TOther }; // UPDATE (양쪽에 있고 다름)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### `ArrayOneWayDiffResult<TItem>`
|
|
75
|
+
|
|
76
|
+
`oneWayDiffs()` 결과 타입. Discriminated union (`type` 필드로 분기):
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
export type ArrayOneWayDiffResult<TItem> =
|
|
80
|
+
| { type: "create"; item: TItem; orgItem: undefined }
|
|
81
|
+
| { type: "update"; item: TItem; orgItem: TItem }
|
|
82
|
+
| { type: "same"; item: TItem; orgItem: TItem };
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `TreeArray<TNode>`
|
|
86
|
+
|
|
87
|
+
`toTree()` 결과 타입. 원본 타입에 `children` 속성이 추가된다:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
export type TreeArray<TNode> = TNode & { children: TreeArray<TNode>[] };
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `ComparableType`
|
|
94
|
+
|
|
95
|
+
`orderBy`/`orderByDesc` selector의 반환 타입:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
export type ComparableType = string | number | boolean | DateTime | DateOnly | Time | undefined;
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Usage
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import "@simplysm/core-common";
|
|
105
|
+
|
|
106
|
+
const users = [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }];
|
|
107
|
+
|
|
108
|
+
// 불변 메서드
|
|
109
|
+
const alice = users.single((u) => u.id === 1);
|
|
110
|
+
const sorted = users.orderBy((u) => u.name);
|
|
111
|
+
const grouped = users.groupBy((u) => u.name[0]);
|
|
112
|
+
const diffs = newUsers.diffs(oldUsers, { keys: ["id"] });
|
|
113
|
+
|
|
114
|
+
// toTree
|
|
115
|
+
const items = [
|
|
116
|
+
{ id: 1, parentId: undefined, name: "root" },
|
|
117
|
+
{ id: 2, parentId: 1, name: "child" },
|
|
118
|
+
];
|
|
119
|
+
const tree = items.toTree("id", "parentId");
|
|
120
|
+
|
|
121
|
+
// 가변 메서드
|
|
122
|
+
const list = [1, 2, 3, 4, 5];
|
|
123
|
+
list.remove((x) => x % 2 === 0); // [1, 3, 5]
|
|
124
|
+
list.insert(1, 10); // [1, 10, 3, 5]
|
|
125
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Map Extensions
|
|
2
|
+
|
|
3
|
+
`@simplysm/core-common`을 import하면 `Map.prototype`에 확장 메서드가 자동 등록된다.
|
|
4
|
+
|
|
5
|
+
side-effect import로 활성화:
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import "@simplysm/core-common";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Methods
|
|
12
|
+
|
|
13
|
+
| Method | Signature | Description |
|
|
14
|
+
|--------|-----------|-------------|
|
|
15
|
+
| `getOrCreate` | `(key: K, newValue: V) => V` | key가 없으면 값을 설정하고 반환 |
|
|
16
|
+
| `getOrCreate` | `(key: K, newValueFn: () => V) => V` | key가 없으면 팩토리 함수를 호출하여 값을 설정하고 반환 |
|
|
17
|
+
| `update` | `(key: K, updateFn: (v: V \| undefined) => V) => void` | 현재 값을 받아 새 값으로 업데이트. key가 없으면 `undefined`가 전달됨 |
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import "@simplysm/core-common";
|
|
23
|
+
|
|
24
|
+
// getOrCreate — 값으로
|
|
25
|
+
const map = new Map<string, number[]>();
|
|
26
|
+
const arr = map.getOrCreate("key", []); // 없으면 [] 설정 후 반환
|
|
27
|
+
|
|
28
|
+
// getOrCreate — 팩토리로 (비용이 큰 연산에 사용)
|
|
29
|
+
const val = map.getOrCreate("key", () => expensiveComputation());
|
|
30
|
+
|
|
31
|
+
// 함수를 값으로 저장 시 팩토리로 감싸야 함
|
|
32
|
+
const fnMap = new Map<string, () => void>();
|
|
33
|
+
const myFn = () => console.log("hello");
|
|
34
|
+
fnMap.getOrCreate("key", () => myFn); // 팩토리로 감싸기
|
|
35
|
+
|
|
36
|
+
// update — 카운터
|
|
37
|
+
const countMap = new Map<string, number>();
|
|
38
|
+
countMap.update("key", (v) => (v ?? 0) + 1);
|
|
39
|
+
|
|
40
|
+
// update — 배열에 추가
|
|
41
|
+
const arrayMap = new Map<string, string[]>();
|
|
42
|
+
arrayMap.update("key", (v) => [...(v ?? []), "item"]);
|
|
43
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Set Extensions
|
|
2
|
+
|
|
3
|
+
`@simplysm/core-common`을 import하면 `Set.prototype`에 확장 메서드가 자동 등록된다.
|
|
4
|
+
|
|
5
|
+
side-effect import로 활성화:
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import "@simplysm/core-common";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Methods
|
|
12
|
+
|
|
13
|
+
| Method | Signature | Description |
|
|
14
|
+
|--------|-----------|-------------|
|
|
15
|
+
| `adds` | `(...values: T[]) => this` | 여러 값을 한 번에 추가 |
|
|
16
|
+
| `toggle` | `(value: T, addOrDel?: "add" \| "del") => this` | 값 토글 (있으면 제거, 없으면 추가). `addOrDel`로 강제 추가/제거 가능 |
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import "@simplysm/core-common";
|
|
22
|
+
|
|
23
|
+
const set = new Set<number>([1, 2, 3]);
|
|
24
|
+
|
|
25
|
+
// adds — 여러 항목 추가
|
|
26
|
+
set.adds(4, 5, 6); // {1, 2, 3, 4, 5, 6}
|
|
27
|
+
|
|
28
|
+
// toggle — 자동
|
|
29
|
+
set.toggle(2); // 2가 있으므로 제거 → {1, 3, 4, 5, 6}
|
|
30
|
+
set.toggle(10); // 10이 없으므로 추가 → {1, 3, 4, 5, 6, 10}
|
|
31
|
+
|
|
32
|
+
// toggle — 강제
|
|
33
|
+
const isAdmin = true;
|
|
34
|
+
set.toggle(5, isAdmin ? "add" : "del"); // 강제 추가
|
|
35
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# DebounceQueue
|
|
2
|
+
|
|
3
|
+
비동기 디바운스 큐. 짧은 시간 내에 여러 번 호출되면 마지막 요청만 실행하고 이전 요청은 무시한다. 입력 필드 자동완성, 연속적인 상태 변경 일괄 처리 등에 유용하다. [`EventEmitter`](./event-emitter.md)를 상속한다.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
export class DebounceQueue extends EventEmitter<{ error: SdError }> {
|
|
7
|
+
/**
|
|
8
|
+
* @param _delay 디바운스 지연 시간 (밀리초). 생략 시 즉시 실행 (다음 이벤트 루프)
|
|
9
|
+
*/
|
|
10
|
+
constructor(_delay?: number);
|
|
11
|
+
|
|
12
|
+
run(fn: () => void | Promise<void>): void;
|
|
13
|
+
override dispose(): void;
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
에러 발생 시 `"error"` 이벤트로 전파되며, 리스너가 없으면 `consola`로 로그 출력된다.
|
|
18
|
+
|
|
19
|
+
실행 중에 추가된 요청은 디바운스 지연 없이 현재 실행이 완료된 직후 즉시 처리된다. 이는 실행 완료 전에 도착한 요청을 놓치지 않기 위한 의도적인 설계다.
|
|
20
|
+
|
|
21
|
+
## Members
|
|
22
|
+
|
|
23
|
+
| Member | Kind | Type | Description |
|
|
24
|
+
|--------|------|------|-------------|
|
|
25
|
+
| `run` | method | `(fn: () => void \| Promise<void>) => void` | 큐에 함수 추가. 이전에 추가된 함수가 있으면 교체됨 |
|
|
26
|
+
| `dispose` | method | `() => void` | 대기 중인 작업과 타이머 정리 후 부모 `dispose()` 호출 |
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { DebounceQueue } from "@simplysm/core-common";
|
|
32
|
+
|
|
33
|
+
const queue = new DebounceQueue(300); // 300ms 지연
|
|
34
|
+
|
|
35
|
+
// 에러 처리
|
|
36
|
+
queue.on("error", (err) => console.error(err));
|
|
37
|
+
|
|
38
|
+
queue.run(() => console.log("1")); // 무시됨
|
|
39
|
+
queue.run(() => console.log("2")); // 무시됨
|
|
40
|
+
queue.run(() => console.log("3")); // 300ms 후 실행
|
|
41
|
+
|
|
42
|
+
// 자원 정리
|
|
43
|
+
try {
|
|
44
|
+
queue.run(() => saveData());
|
|
45
|
+
} finally {
|
|
46
|
+
queue.dispose();
|
|
47
|
+
}
|
|
48
|
+
```
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# EventEmitter
|
|
2
|
+
|
|
3
|
+
타입 안전 EventEmitter. 내부적으로 `EventTarget`을 사용하며 브라우저와 Node.js 모두에서 사용 가능하다. `events`/`eventemitter3` 대신 이 클래스를 사용한다.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
export class EventEmitter<
|
|
7
|
+
TEvents extends { [K in keyof TEvents]: unknown } = Record<string, unknown>,
|
|
8
|
+
> {
|
|
9
|
+
on<TEventName extends keyof TEvents & string>(type: TEventName, listener: (data: TEvents[TEventName]) => void): void;
|
|
10
|
+
off<TEventName extends keyof TEvents & string>(type: TEventName, listener: (data: TEvents[TEventName]) => void): void;
|
|
11
|
+
emit<TEventName extends keyof TEvents & string>(type: TEventName, ...args: TEvents[TEventName] extends void ? [] : [data: TEvents[TEventName]]): void;
|
|
12
|
+
listenerCount<TEventName extends keyof TEvents & string>(type: TEventName): number;
|
|
13
|
+
dispose(): void;
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Members
|
|
18
|
+
|
|
19
|
+
| Member | Kind | Type | Description |
|
|
20
|
+
|--------|------|------|-------------|
|
|
21
|
+
| `on` | method | `(type, listener) => void` | 이벤트 리스너 등록. 같은 리스너를 같은 이벤트에 중복 등록하면 무시됨 |
|
|
22
|
+
| `off` | method | `(type, listener) => void` | 이벤트 리스너 제거 |
|
|
23
|
+
| `emit` | method | `(type, ...args) => void` | 이벤트 발행. `void` 타입 이벤트는 인자 없이 호출 |
|
|
24
|
+
| `listenerCount` | method | `(type) => number` | 특정 이벤트의 등록된 리스너 수 반환 |
|
|
25
|
+
| `dispose` | method | `() => void` | 모든 이벤트 리스너 제거 |
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { EventEmitter } from "@simplysm/core-common";
|
|
31
|
+
|
|
32
|
+
// 이벤트 타입 정의
|
|
33
|
+
interface MyEvents {
|
|
34
|
+
data: string;
|
|
35
|
+
error: Error;
|
|
36
|
+
done: void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// EventEmitter를 상속하여 사용
|
|
40
|
+
class MyService extends EventEmitter<MyEvents> {
|
|
41
|
+
async load() {
|
|
42
|
+
this.emit("data", "Loading...");
|
|
43
|
+
this.emit("done"); // void 타입은 인자 없이 호출
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const svc = new MyService();
|
|
48
|
+
svc.on("data", (data) => { /* data: string */ });
|
|
49
|
+
svc.on("done", () => { /* ... */ });
|
|
50
|
+
await svc.load();
|
|
51
|
+
svc.dispose(); // 모든 리스너 정리
|
|
52
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# SerialQueue
|
|
2
|
+
|
|
3
|
+
비동기 직렬 큐. 큐에 추가된 함수들은 순차적으로 실행된다. 하나의 작업이 완료된 후에야 다음 작업이 시작된다. 에러가 발생해도 후속 작업은 계속 실행된다. [`EventEmitter`](./event-emitter.md)를 상속한다.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
export class SerialQueue extends EventEmitter<{ error: SdError }> {
|
|
7
|
+
/**
|
|
8
|
+
* @param _gap 각 작업 사이의 간격 (ms). 기본값: 0
|
|
9
|
+
*/
|
|
10
|
+
constructor(_gap?: number);
|
|
11
|
+
|
|
12
|
+
run(fn: () => void | Promise<void>): void;
|
|
13
|
+
override dispose(): void;
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
에러 발생 시 `"error"` 이벤트로 전파되며, 리스너가 없으면 `consola`로 로그 출력된다.
|
|
18
|
+
|
|
19
|
+
## Members
|
|
20
|
+
|
|
21
|
+
| Member | Kind | Type | Description |
|
|
22
|
+
|--------|------|------|-------------|
|
|
23
|
+
| `run` | method | `(fn: () => void \| Promise<void>) => void` | 큐에 함수 추가하고 실행 예약 |
|
|
24
|
+
| `dispose` | method | `() => void` | 대기 중인 큐 비우기 (현재 실행 중인 작업은 완료됨) |
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { SerialQueue } from "@simplysm/core-common";
|
|
30
|
+
|
|
31
|
+
const queue = new SerialQueue();
|
|
32
|
+
|
|
33
|
+
// 에러 처리
|
|
34
|
+
queue.on("error", (err) => console.error(err));
|
|
35
|
+
|
|
36
|
+
queue.run(async () => { await fetch("/api/1"); });
|
|
37
|
+
queue.run(async () => { await fetch("/api/2"); }); // 1 완료 후 실행
|
|
38
|
+
queue.run(async () => { await fetch("/api/3"); }); // 2 완료 후 실행
|
|
39
|
+
|
|
40
|
+
// 간격 있는 큐
|
|
41
|
+
const gapQueue = new SerialQueue(100); // 각 작업 사이 100ms 간격
|
|
42
|
+
gapQueue.run(() => step1());
|
|
43
|
+
gapQueue.run(() => step2()); // step1 완료 후 100ms 뒤에 실행
|
|
44
|
+
```
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Common Types
|
|
2
|
+
|
|
3
|
+
공유 타입 정의. `Buffer` 대신 사용하는 바이너리 타입, ORM과 공유하는 원시 타입, 범용 유틸리티 타입을 제공한다.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { Bytes, PrimitiveTypeMap, PrimitiveTypeStr, PrimitiveType, DeepPartial, Type } from "@simplysm/core-common";
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Types
|
|
10
|
+
|
|
11
|
+
### `Bytes`
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
export type Bytes = Uint8Array;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`Buffer` 대신 사용하는 바이너리 타입 별칭.
|
|
18
|
+
|
|
19
|
+
### `PrimitiveTypeMap`
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
export type PrimitiveTypeMap = {
|
|
23
|
+
string: string;
|
|
24
|
+
number: number;
|
|
25
|
+
boolean: boolean;
|
|
26
|
+
DateTime: DateTime;
|
|
27
|
+
DateOnly: DateOnly;
|
|
28
|
+
Time: Time;
|
|
29
|
+
Uuid: Uuid;
|
|
30
|
+
Bytes: Bytes;
|
|
31
|
+
};
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
원시 타입 문자열 key → 실제 타입 매핑. `@simplysm/orm-common`과 공유한다.
|
|
35
|
+
|
|
36
|
+
### `PrimitiveTypeStr`
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
export type PrimitiveTypeStr = keyof PrimitiveTypeMap;
|
|
40
|
+
// "string" | "number" | "boolean" | "DateTime" | "DateOnly" | "Time" | "Uuid" | "Bytes"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
원시 타입 문자열 key union.
|
|
44
|
+
|
|
45
|
+
### `PrimitiveType`
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
export type PrimitiveType = PrimitiveTypeMap[PrimitiveTypeStr] | undefined;
|
|
49
|
+
// string | number | boolean | DateTime | DateOnly | Time | Uuid | Bytes | undefined
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
원시 타입 union.
|
|
53
|
+
|
|
54
|
+
### `DeepPartial<TObject>`
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
export type DeepPartial<TObject> = Partial<{
|
|
58
|
+
[K in keyof TObject]: TObject[K] extends PrimitiveType ? TObject[K] : DeepPartial<TObject[K]>;
|
|
59
|
+
}>;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
객체의 모든 속성을 재귀적으로 optional로 변환한다. 원시 타입(`PrimitiveType`)은 그대로 유지하고, object/array 타입에만 재귀적으로 `Partial`을 적용한다.
|
|
63
|
+
|
|
64
|
+
### `Type<TInstance>`
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
export interface Type<TInstance> extends Function {
|
|
68
|
+
new (...args: unknown[]): TInstance;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
생성자 타입. 의존성 주입, 팩토리 패턴, instanceof 체크 등에 활용한다.
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { Bytes, PrimitiveTypeStr, DeepPartial, Type } from "@simplysm/core-common";
|
|
78
|
+
|
|
79
|
+
// Bytes
|
|
80
|
+
const data: Bytes = new Uint8Array([1, 2, 3]);
|
|
81
|
+
|
|
82
|
+
// PrimitiveTypeStr
|
|
83
|
+
const typeStr: PrimitiveTypeStr = "DateTime";
|
|
84
|
+
|
|
85
|
+
// DeepPartial
|
|
86
|
+
interface Config {
|
|
87
|
+
server: { host: string; port: number };
|
|
88
|
+
db: { name: string; user: string };
|
|
89
|
+
}
|
|
90
|
+
const partial: DeepPartial<Config> = {
|
|
91
|
+
server: { host: "localhost" }, // port 생략 가능
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Type<T>
|
|
95
|
+
function create<T>(ctor: Type<T>): T {
|
|
96
|
+
return new ctor();
|
|
97
|
+
}
|
|
98
|
+
class MyService {}
|
|
99
|
+
const svc = create(MyService);
|
|
100
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# env / parseBoolEnv
|
|
2
|
+
|
|
3
|
+
환경변수 접근 유틸리티 함수. `process.env`/`import.meta.env` 직접 접근 대신 이 함수를 사용해야 한다.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
export function env(key: string): string | undefined;
|
|
7
|
+
export function env(key: string, value: string): void;
|
|
8
|
+
|
|
9
|
+
export function parseBoolEnv(value: unknown): boolean;
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
직접 named import로 사용한다:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { env, parseBoolEnv } from "@simplysm/core-common";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Functions
|
|
19
|
+
|
|
20
|
+
| Function | Signature | Description |
|
|
21
|
+
|----------|-----------|-------------|
|
|
22
|
+
| `env` | `(key: string) => string \| undefined` | 환경변수 값 읽기. `process.env` 우선, fallback `import.meta.env` |
|
|
23
|
+
| `env` | `(key: string, value: string) => void` | 환경변수 값 쓰기 (`process.env`에 저장) |
|
|
24
|
+
| `parseBoolEnv` | `(value: unknown) => boolean` | 환경변수 값을 boolean으로 파싱. `"true"`, `"1"`, `"yes"`, `"on"` (대소문자 무시) → `true`, 그 외 → `false` |
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { env, parseBoolEnv } from "@simplysm/core-common";
|
|
30
|
+
|
|
31
|
+
// 읽기
|
|
32
|
+
const apiUrl = env("API_URL"); // string | undefined
|
|
33
|
+
|
|
34
|
+
// 쓰기
|
|
35
|
+
env("DEBUG", "true");
|
|
36
|
+
|
|
37
|
+
// boolean 파싱
|
|
38
|
+
parseBoolEnv(env("DEBUG")); // true
|
|
39
|
+
parseBoolEnv("yes"); // true
|
|
40
|
+
parseBoolEnv("false"); // false
|
|
41
|
+
parseBoolEnv("0"); // false
|
|
42
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# DateOnly
|
|
2
|
+
|
|
3
|
+
불변 날짜 클래스 (시간 제외: `yyyy-MM-dd`). 시간 정보 없이 날짜만 저장하며 로컬 타임존 기준으로 동작한다. 주차 계산을 지원한다. 수정 메서드는 모두 새 인스턴스를 반환한다.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
export class DateOnly {
|
|
7
|
+
readonly date: Date;
|
|
8
|
+
|
|
9
|
+
constructor();
|
|
10
|
+
constructor(year: number, month: number, day: number);
|
|
11
|
+
constructor(tick: number);
|
|
12
|
+
constructor(date: Date);
|
|
13
|
+
|
|
14
|
+
static parse(str: string): DateOnly;
|
|
15
|
+
static getDateByYearWeekSeq(
|
|
16
|
+
arg: { year: number; month?: number; weekSeq: number },
|
|
17
|
+
weekStartDay?: number,
|
|
18
|
+
minDaysInFirstWeek?: number,
|
|
19
|
+
): DateOnly;
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Members
|
|
24
|
+
|
|
25
|
+
| Member | Kind | Type | Description |
|
|
26
|
+
|--------|------|------|-------------|
|
|
27
|
+
| `date` | property | `Date` | 내부 Date 객체 (시간 부분은 항상 00:00:00) |
|
|
28
|
+
| `year` | getter | `number` | 연도 |
|
|
29
|
+
| `month` | getter | `number` | 월 (1-12) |
|
|
30
|
+
| `day` | getter | `number` | 일 (1-31) |
|
|
31
|
+
| `tick` | getter | `number` | Unix 타임스탬프 (밀리초) |
|
|
32
|
+
| `dayOfWeek` | getter | `number` | 요일 (일요일=0 ~ 토요일=6) |
|
|
33
|
+
| `isValid` | getter | `boolean` | 날짜가 올바르게 설정되었는지 여부 |
|
|
34
|
+
| `parse` | static | `(str: string) => DateOnly` | 문자열을 파싱하여 DateOnly 생성 |
|
|
35
|
+
| `getDateByYearWeekSeq` | static | `(arg, weekStartDay?, minDaysInFirstWeek?) => DateOnly` | 연도·(월)·주차로 해당 주의 시작 날짜 반환 |
|
|
36
|
+
| `setYear` | method | `(year: number) => DateOnly` | 지정된 연도로 새 인스턴스 반환 |
|
|
37
|
+
| `setMonth` | method | `(month: number) => DateOnly` | 지정된 월로 새 인스턴스 반환. 현재 일이 대상 월의 일수보다 크면 마지막 일로 조정됨 |
|
|
38
|
+
| `setDay` | method | `(day: number) => DateOnly` | 지정된 일로 새 인스턴스 반환 |
|
|
39
|
+
| `addYears` | method | `(years: number) => DateOnly` | 지정된 연수를 더한 새 인스턴스 반환 |
|
|
40
|
+
| `addMonths` | method | `(months: number) => DateOnly` | 지정된 월수를 더한 새 인스턴스 반환 |
|
|
41
|
+
| `addDays` | method | `(days: number) => DateOnly` | 지정된 일수를 더한 새 인스턴스 반환 |
|
|
42
|
+
| `getBaseYearMonthSeqForWeekSeq` | method | `(weekStartDay?, minDaysInFirstWeek?) => { year: number; monthSeq: number }` | 이 날짜가 포함된 주의 기준 연도와 월 반환 |
|
|
43
|
+
| `getWeekSeqStartDate` | method | `(weekStartDay?, minDaysInFirstWeek?) => DateOnly` | 이 날짜가 포함된 주의 시작 날짜 반환 |
|
|
44
|
+
| `getWeekSeqOfYear` | method | `(weekStartDay?, minDaysInFirstWeek?) => { year: number; weekSeq: number }` | 연도와 주차 번호 반환 |
|
|
45
|
+
| `getWeekSeqOfMonth` | method | `(weekStartDay?, minDaysInFirstWeek?) => { year: number; monthSeq: number; weekSeq: number }` | 연도, 월, 해당 월 내의 주차 번호 반환 |
|
|
46
|
+
| `toFormatString` | method | `(formatStr: string) => string` | 지정된 형식 문자열로 변환 (형식 토큰은 [`DateTime`](./date-time.md) 참조) |
|
|
47
|
+
| `toString` | method | `() => string` | `"yyyy-MM-dd"` 형식 문자열 반환 |
|
|
48
|
+
|
|
49
|
+
## `parse` — 지원 형식
|
|
50
|
+
|
|
51
|
+
| 형식 | 예시 | 타임존 |
|
|
52
|
+
|------|------|--------|
|
|
53
|
+
| `yyyy-MM-dd` | `"2025-01-15"` | 타임존 무관 (직접 추출) |
|
|
54
|
+
| `yyyyMMdd` | `"20250115"` | 타임존 무관 (직접 추출) |
|
|
55
|
+
| ISO 8601 | `"2025-01-15T00:00:00Z"` | UTC로 해석 후 로컬 타임존 변환 |
|
|
56
|
+
|
|
57
|
+
서버/클라이언트 타임존이 다른 경우 `yyyy-MM-dd` 형식 권장.
|
|
58
|
+
|
|
59
|
+
## 주차 계산 파라미터
|
|
60
|
+
|
|
61
|
+
| 파라미터 | 기본값 | 설명 |
|
|
62
|
+
|----------|--------|------|
|
|
63
|
+
| `weekStartDay` | `1` (월요일) | 주 시작 요일 (0=일요일, 1=월요일, ..., 6=토요일) |
|
|
64
|
+
| `minDaysInFirstWeek` | `4` | 첫 번째 주로 간주되기 위한 최소 일수 (ISO 8601 표준) |
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { DateOnly } from "@simplysm/core-common";
|
|
70
|
+
|
|
71
|
+
const today = new DateOnly();
|
|
72
|
+
const specific = new DateOnly(2025, 1, 15);
|
|
73
|
+
const parsed = DateOnly.parse("2025-01-15");
|
|
74
|
+
|
|
75
|
+
// 불변 수정
|
|
76
|
+
const nextWeek = today.addDays(7);
|
|
77
|
+
const firstDayOfMonth = today.setDay(1);
|
|
78
|
+
|
|
79
|
+
// 주차 계산 (ISO 8601 기본값: 월요일 시작, 첫 주 최소 4일)
|
|
80
|
+
const { year, weekSeq } = new DateOnly(2025, 1, 6).getWeekSeqOfYear();
|
|
81
|
+
// { year: 2025, weekSeq: 2 }
|
|
82
|
+
|
|
83
|
+
// 주차로 날짜 계산
|
|
84
|
+
const weekStart = DateOnly.getDateByYearWeekSeq({ year: 2025, weekSeq: 2 });
|
|
85
|
+
// 2025-01-06 (월요일)
|
|
86
|
+
```
|