@simplysm/core-common 14.0.51 → 14.0.52

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/core-common",
3
- "version": "14.0.51",
3
+ "version": "14.0.52",
4
4
  "description": "심플리즘 패키지 - 코어 (common)",
5
5
  "author": "심플리즘",
6
6
  "license": "Apache-2.0",
@@ -14,8 +14,7 @@
14
14
  "types": "./dist/index.d.ts",
15
15
  "files": [
16
16
  "dist",
17
- "src",
18
- "docs"
17
+ "src"
19
18
  ],
20
19
  "sideEffects": [
21
20
  "./src/extensions/arr-ext.ts",
package/README.md DELETED
@@ -1,160 +0,0 @@
1
- # @simplysm/core-common
2
-
3
- 브라우저와 Node.js 모두에서 사용 가능한 순수 공통 유틸리티 패키지. 다른 `@simplysm/*` 패키지에 대한 내부 의존성이 없는 리프 패키지다.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install @simplysm/core-common
9
- ```
10
-
11
- ## API Overview
12
-
13
- ### Errors
14
-
15
- | Entry | Kind | Description |
16
- |-------|------|-------------|
17
- | [`SdError`](./docs/errors/sd-error.md) | class | 트리 구조 에러 체인 지원. 메시지를 역순으로 결합 (상위 => 하위 => 원인) |
18
- | [`ArgumentError`](./docs/errors/argument-error.md) | class | 인자 유효성 오류. 인자 객체를 YAML 형식으로 메시지에 포함 |
19
- | [`NotImplementedError`](./docs/errors/not-implemented-error.md) | class | 미구현 기능 호출 시 발생 |
20
- | [`TimeoutError`](./docs/errors/timeout-error.md) | class | 대기 시간 초과 시 발생 (시도 횟수 포함) |
21
-
22
- ### Types (Value Objects)
23
-
24
- | Entry | Kind | Description |
25
- |-------|------|-------------|
26
- | [`DateTime`](./docs/types/date-time.md) | class | 불변 날짜시간 (밀리초 정밀도, 로컬 타임존) |
27
- | [`DateOnly`](./docs/types/date-only.md) | class | 불변 날짜 (시간 제외, 주차 계산 지원) |
28
- | [`Time`](./docs/types/time.md) | class | 불변 시간 (24시간 순환, 날짜 제외) |
29
- | [`Uuid`](./docs/types/uuid.md) | class | UUID v4 (`crypto.getRandomValues` 기반) |
30
- | [`LazyGcMap`](./docs/types/lazy-gc-map.md) | class | 자동 만료 기능이 있는 LRU Map |
31
-
32
- ### Features
33
-
34
- | Entry | Kind | Description |
35
- |-------|------|-------------|
36
- | [`EventEmitter`](./docs/features/event-emitter.md) | class | 타입 안전 EventEmitter (EventTarget 기반, 브라우저/Node.js 모두 지원) |
37
- | [`DebounceQueue`](./docs/features/debounce-queue.md) | class | 비동기 디바운스 큐 (짧은 시간 내 다수 호출 시 마지막만 실행) |
38
- | [`SerialQueue`](./docs/features/serial-queue.md) | class | 비동기 직렬 큐 (순차 실행 보장) |
39
-
40
- ### Extensions (Prototype)
41
-
42
- `@simplysm/core-common`을 import하면 `Array`, `Map`, `Set` 프로토타입 확장이 자동 등록된다.
43
-
44
- | Entry | Description |
45
- |-------|-------------|
46
- | [`Array Extensions`](./docs/extensions/array.md) | 불변 메서드 (`single`, `groupBy`, `diffs`, `toTree` 등) + 가변 메서드 (`remove`, `insert`, `toggle` 등) |
47
- | [`Map Extensions`](./docs/extensions/map.md) | `getOrCreate`, `update` |
48
- | [`Set Extensions`](./docs/extensions/set.md) | `adds`, `toggle` |
49
-
50
- ### Utils (Namespace Imports)
51
-
52
- ```typescript
53
- import { obj, str, num, bytes, path, json, xml, wait, transfer, err, dt, primitive } from "@simplysm/core-common";
54
- ```
55
-
56
- | Entry | Kind | Description |
57
- |-------|------|-------------|
58
- | [`obj`](./docs/utils/obj.md) | namespace | `clone`, `equal`, `merge`, `merge3`, `omit`, `pick`, `getChainValue`, `setChainValue`, `keys`, `entries`, `map` 등 |
59
- | [`str`](./docs/utils/str.md) | namespace | `getKoreanSuffix`, `toPascalCase`, `toCamelCase`, `toKebabCase`, `toSnakeCase`, `isNullOrEmpty`, `insert`, `replaceFullWidth` |
60
- | [`num`](./docs/utils/num.md) | namespace | `parseInt`, `parseFloat`, `parseRoundedInt`, `isNullOrEmpty`, `format` |
61
- | [`bytes`](./docs/utils/bytes.md) | namespace | `concat`, `toHex`, `fromHex`, `toBase64`, `fromBase64` |
62
- | [`path`](./docs/utils/path.md) | namespace | `join`, `basename`, `extname` (POSIX 경로만 지원) |
63
- | [`json`](./docs/utils/json.md) | namespace | `stringify`, `parse` (커스텀 타입 지원) |
64
- | [`xml`](./docs/utils/xml.md) | namespace | `parse`, `stringify` |
65
- | [`wait`](./docs/utils/wait.md) | namespace | `until`, `time` |
66
- | [`transfer`](./docs/utils/transfer.md) | namespace | `encode`, `decode` (Worker 전송용) |
67
- | [`err`](./docs/utils/err.md) | namespace | `message` (unknown 에러 메시지 추출) |
68
- | [`dt`](./docs/utils/dt.md) | namespace | `format`, `normalizeMonth`, `convert12To24` |
69
- | [`primitive`](./docs/utils/primitive.md) | namespace | `typeStr` |
70
-
71
- ### Utils (Direct Exports)
72
-
73
- | Entry | Kind | Description |
74
- |-------|------|-------------|
75
- | [`js`, `ts`, `html`, `tsql`, `mysql`, `pgsql`](./docs/utils/template-strings.md) | function | IDE 코드 하이라이팅용 태그드 템플릿 리터럴 |
76
- | [`ZipArchive`](./docs/utils/zip-archive.md) | class | ZIP 파일 읽기/쓰기/압축/해제 (캐싱, 진행률 콜백 지원) |
77
-
78
- ### Type Utilities
79
-
80
- | Entry | Kind | Description |
81
- |-------|------|-------------|
82
- | [`env`, `parseBoolEnv`](./docs/type-utils/env.md) | function | 환경변수 get/set. `process.env`/`import.meta.env` 직접 접근 대신 사용 |
83
- | [`Bytes`, `PrimitiveTypeMap`, `PrimitiveTypeStr`, `PrimitiveType`, `DeepPartial`, `Type`](./docs/type-utils/common-types.md) | type/interface | 공용 타입 정의 |
84
-
85
- ## Usage Examples
86
-
87
- ### 프로토타입 확장 사용
88
-
89
- ```typescript
90
- import "@simplysm/core-common";
91
-
92
- const users = [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }];
93
- const user = users.single((u) => u.id === 1);
94
- const grouped = users.groupBy((u) => u.name[0]);
95
- const sorted = users.orderBy((u) => u.name);
96
- const diffs = newUsers.diffs(oldUsers, { keys: ["id"] });
97
-
98
- const cache = new Map<string, number[]>();
99
- const arr = cache.getOrCreate("key", []);
100
-
101
- const set = new Set<string>();
102
- set.adds("a", "b", "c");
103
- ```
104
-
105
- ### 에러 체인 처리
106
-
107
- ```typescript
108
- import { SdError } from "@simplysm/core-common";
109
-
110
- try {
111
- await fetch(url);
112
- } catch (err) {
113
- throw new SdError(err, "API 호출 실패", "사용자 로드 실패");
114
- // message: "사용자 로드 실패 => API 호출 실패 => 원본 에러 메시지"
115
- }
116
- ```
117
-
118
- ### 네임스페이스 유틸리티 사용
119
-
120
- ```typescript
121
- import { obj, str, json, DateTime, Uuid } from "@simplysm/core-common";
122
-
123
- const copied = obj.clone({ nested: { data: [1, 2, 3] } });
124
- const isEqual = obj.equal(a, b, { topLevelExcludes: ["updatedAt"] });
125
- const noId = obj.omit(user, ["id"]);
126
-
127
- const suffix = str.getKoreanSuffix("파일", "을"); // "을"
128
- const camel = str.toCamelCase("HelloWorld"); // "helloWorld"
129
-
130
- const serialized = json.stringify({ date: new DateTime(), id: Uuid.generate() });
131
- const restored = json.parse(serialized); // DateTime, Uuid 타입 복원됨
132
- ```
133
-
134
- ### 비동기 큐 사용
135
-
136
- ```typescript
137
- import { DebounceQueue, SerialQueue } from "@simplysm/core-common";
138
-
139
- const dq = new DebounceQueue(300);
140
- dq.on("error", (err) => console.error(err));
141
- dq.run(() => saveData());
142
-
143
- const sq = new SerialQueue();
144
- sq.run(async () => await step1());
145
- sq.run(async () => await step2()); // step1 완료 후 실행
146
- ```
147
-
148
- ### DateTime 사용
149
-
150
- ```typescript
151
- import { DateTime, DateOnly, Time, Uuid } from "@simplysm/core-common";
152
-
153
- const now = new DateTime();
154
- const parsed = DateTime.parse("2025-01-15 10:30:00");
155
- const formatted = now.toFormatString("yyyy-MM-dd HH:mm:ss");
156
- const nextMonth = now.addMonths(1);
157
-
158
- const today = new DateOnly();
159
- const id = Uuid.generate();
160
- ```
@@ -1,26 +0,0 @@
1
- # ArgumentError
2
-
3
- 인자 유효성 오류. 유효하지 않은 인자를 전달받았을 때 발생하는 에러. 디버깅을 용이하게 하기 위해 인자 객체를 YAML 형식으로 메시지에 포함한다. [`SdError`](./sd-error.md)를 상속한다.
4
-
5
- ```typescript
6
- export class ArgumentError extends SdError {
7
- /** 기본 메시지("잘못된 인자입니다.")와 함께 인자 객체를 YAML 형식으로 출력 */
8
- constructor(argObj: Record<string, unknown>);
9
- /** 커스텀 메시지와 함께 인자 객체를 YAML 형식으로 출력 */
10
- constructor(message: string, argObj: Record<string, unknown>);
11
- }
12
- ```
13
-
14
- ## Usage
15
-
16
- ```typescript
17
- import { ArgumentError } from "@simplysm/core-common";
18
-
19
- // 인자 객체만 전달
20
- throw new ArgumentError({ userId: 123, name: null });
21
- // message: "잘못된 인자입니다.\n\nuserId: 123\nname: null"
22
-
23
- // 커스텀 메시지와 인자 객체를 전달
24
- throw new ArgumentError("잘못된 사용자", { userId: 123 });
25
- // message: "잘못된 사용자\n\nuserId: 123"
26
- ```
@@ -1,33 +0,0 @@
1
- # NotImplementedError
2
-
3
- 미구현 오류. 아직 구현되지 않은 기능이 호출되었을 때 발생하는 에러. 추상 메서드 스텁, 향후 구현 예정인 분기 등에 사용된다. [`SdError`](./sd-error.md)를 상속한다.
4
-
5
- ```typescript
6
- export class NotImplementedError extends SdError {
7
- /**
8
- * @param message 추가 설명 메시지
9
- */
10
- constructor(message?: string);
11
- }
12
- ```
13
-
14
- 메시지 형식: `"미구현"` 또는 `"미구현: {message}"`
15
-
16
- ## Usage
17
-
18
- ```typescript
19
- import { NotImplementedError } from "@simplysm/core-common";
20
-
21
- // 추상 메서드 구현 전
22
- class BaseService {
23
- process(): void {
24
- throw new NotImplementedError("서브클래스에서 구현 필요");
25
- }
26
- }
27
-
28
- // 향후 구현 예정인 분기
29
- switch (type) {
30
- case "A": return handleA();
31
- case "B": throw new NotImplementedError(`타입 ${type} 처리`);
32
- }
33
- ```
@@ -1,38 +0,0 @@
1
- # SdError
2
-
3
- 트리 구조 조합을 지원하는 에러 클래스. 원인 에러를 감싸서 계층적 메시지를 구성한다. ES2024 `cause` 속성을 활용하며 V8 엔진(Node.js, Chrome)에서는 `captureStackTrace`를 통해 스택 추적을 최적화한다.
4
-
5
- ```typescript
6
- export class SdError extends Error {
7
- override cause?: Error;
8
-
9
- /** 원인 에러를 감싸서 생성. 메시지는 역순으로 결합됨 (상위 메시지 => 하위 메시지 => 원인 메시지) */
10
- constructor(cause: Error, ...messages: string[]);
11
- /** 메시지만으로 생성. 메시지는 역순으로 결합됨 (상위 메시지 => 하위 메시지) */
12
- constructor(...messages: string[]);
13
- }
14
- ```
15
-
16
- ## Members
17
-
18
- | Member | Kind | Type | Description |
19
- |--------|------|------|-------------|
20
- | `cause` | property | `Error \| undefined` | 원인 에러 (override) |
21
-
22
- ## Usage
23
-
24
- ```typescript
25
- import { SdError } from "@simplysm/core-common";
26
-
27
- // 원인 에러를 감싸기
28
- try {
29
- await fetch(url);
30
- } catch (err) {
31
- throw new SdError(err, "API 호출 실패", "사용자 로드 실패");
32
- // message: "사용자 로드 실패 => API 호출 실패 => 원본 에러 메시지"
33
- }
34
-
35
- // 메시지만으로 생성
36
- throw new SdError("잘못된 상태", "처리 불가");
37
- // message: "처리 불가 => 잘못된 상태"
38
- ```
@@ -1,36 +0,0 @@
1
- # TimeoutError
2
-
3
- 타임아웃 오류. 대기 시간이 초과되었을 때 발생하는 에러. [`wait.until()`](../utils/wait.md) 같은 비동기 대기 함수에서 최대 시도 횟수를 초과하면 자동으로 발생한다. [`SdError`](./sd-error.md)를 상속한다.
4
-
5
- ```typescript
6
- export class TimeoutError extends SdError {
7
- /**
8
- * @param count 시도 횟수
9
- * @param message 추가 메시지
10
- */
11
- constructor(count?: number, message?: string);
12
- }
13
- ```
14
-
15
- 메시지 형식: `"대기 시간 초과"`, `"대기 시간 초과(N회 시도)"`, `"대기 시간 초과: {message}"`, `"대기 시간 초과(N회 시도): {message}"`
16
-
17
- ## Usage
18
-
19
- ```typescript
20
- import { TimeoutError } from "@simplysm/core-common";
21
- import { wait } from "@simplysm/core-common";
22
-
23
- // wait.until에서 자동 발생
24
- try {
25
- await wait.until(() => isReady, 100, 50); // 100ms 간격, 최대 50회 시도
26
- } catch (err) {
27
- if (err instanceof TimeoutError) {
28
- console.log("타임아웃 초과");
29
- }
30
- }
31
-
32
- // 직접 발생
33
- if (elapsed > maxTime) {
34
- throw new TimeoutError(undefined, "API 응답 대기 초과");
35
- }
36
- ```
@@ -1,125 +0,0 @@
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
- ```
@@ -1,43 +0,0 @@
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
- ```
@@ -1,35 +0,0 @@
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
- ```
@@ -1,48 +0,0 @@
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
- ```
@@ -1,52 +0,0 @@
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
- ```
@@ -1,44 +0,0 @@
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
- ```