@simplysm/core-common 13.0.96 → 13.0.97
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/dist/env.d.ts +5 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +6 -3
- package/dist/env.js.map +1 -1
- package/package.json +2 -2
- package/src/env.ts +13 -3
- package/README.md +0 -113
- package/docs/features.md +0 -343
- package/docs/types.md +0 -341
- package/docs/utilities.md +0 -359
package/dist/env.d.ts
CHANGED
package/dist/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,GAAG,EAAE;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAKxB,CAAC"}
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAEA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,UAAU;QAClB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC/B;CACF;AAMD,eAAO,MAAM,GAAG,EAAE;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAKxB,CAAC"}
|
package/dist/env.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
const _metaEnv = import.meta.env ?? {};
|
|
2
|
+
const _processEnv = typeof process !== "undefined" ? process.env : {};
|
|
3
|
+
const _raw = { ..._metaEnv, ..._processEnv };
|
|
1
4
|
const env = {
|
|
2
|
-
...
|
|
3
|
-
DEV: JSON.parse(
|
|
4
|
-
VER:
|
|
5
|
+
..._raw,
|
|
6
|
+
DEV: JSON.parse(String(_raw["DEV"] != null && String(_raw["DEV"]) !== "" ? _raw["DEV"] : false)),
|
|
7
|
+
VER: _raw["VER"]
|
|
5
8
|
};
|
|
6
9
|
export {
|
|
7
10
|
env
|
package/dist/env.js.map
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/env.ts"],
|
|
4
|
-
"mappings": "
|
|
4
|
+
"mappings": "AAQA,MAAM,WAAoC,YAAY,OAAO,CAAC;AAC9D,MAAM,cAAuC,OAAO,YAAY,cAAc,QAAQ,MAAM,CAAC;AAC7F,MAAM,OAAgC,EAAE,GAAG,UAAU,GAAG,YAAY;AAE7D,MAAM,MAIT;AAAA,EACF,GAAG;AAAA,EACH,KAAK,KAAK,MAAM,OAAO,KAAK,KAAK,KAAK,QAAQ,OAAO,KAAK,KAAK,CAAC,MAAM,KAAK,KAAK,KAAK,IAAI,KAAK,CAAC;AAAA,EAC/F,KAAK,KAAK,KAAK;AACjB;",
|
|
5
5
|
"names": []
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/core-common",
|
|
3
|
-
"version": "13.0.
|
|
3
|
+
"version": "13.0.97",
|
|
4
4
|
"description": "Simplysm package - Core module (common)",
|
|
5
5
|
"author": "simplysm",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@zip.js/zip.js": "^2.8.23",
|
|
33
33
|
"consola": "^3.4.2",
|
|
34
|
-
"fast-xml-parser": "^5.5.
|
|
34
|
+
"fast-xml-parser": "^5.5.8",
|
|
35
35
|
"yaml": "^2.8.2"
|
|
36
36
|
}
|
|
37
37
|
}
|
package/src/env.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
declare const process: { env: { DEV?: string; VER?: string; [key: string]: string | undefined } };
|
|
2
2
|
|
|
3
|
+
declare global {
|
|
4
|
+
interface ImportMeta {
|
|
5
|
+
env?: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const _metaEnv: Record<string, unknown> = import.meta.env ?? {};
|
|
10
|
+
const _processEnv: Record<string, unknown> = typeof process !== "undefined" ? process.env : {};
|
|
11
|
+
const _raw: Record<string, unknown> = { ..._metaEnv, ..._processEnv };
|
|
12
|
+
|
|
3
13
|
export const env: {
|
|
4
14
|
DEV: boolean;
|
|
5
15
|
VER?: string;
|
|
6
16
|
[key: string]: unknown;
|
|
7
17
|
} = {
|
|
8
|
-
...
|
|
9
|
-
DEV: JSON.parse(
|
|
10
|
-
VER:
|
|
18
|
+
..._raw,
|
|
19
|
+
DEV: JSON.parse(String(_raw["DEV"] != null && String(_raw["DEV"]) !== "" ? _raw["DEV"] : false)),
|
|
20
|
+
VER: _raw["VER"] as string | undefined,
|
|
11
21
|
};
|
package/README.md
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
# @simplysm/core-common
|
|
2
|
-
|
|
3
|
-
Simplysm 프레임워크의 기반 유틸리티 패키지. 플랫폼 중립적인 타입, 유틸리티 함수, 에러 클래스, 확장 메서드를 제공한다.
|
|
4
|
-
|
|
5
|
-
## 설치
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @simplysm/core-common
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## 의존성
|
|
12
|
-
|
|
13
|
-
- `@zip.js/zip.js` -- ZIP 파일 처리
|
|
14
|
-
- `consola` -- 로깅
|
|
15
|
-
- `fast-xml-parser` -- XML 파싱/직렬화
|
|
16
|
-
- `yaml` -- YAML 직렬화 (ArgumentError 메시지용)
|
|
17
|
-
|
|
18
|
-
## 문서
|
|
19
|
-
|
|
20
|
-
| 카테고리 | 설명 |
|
|
21
|
-
|---------|------|
|
|
22
|
-
| [타입](docs/types.md) | DateTime, DateOnly, Time, Uuid, LazyGcMap 등 불변 타입 |
|
|
23
|
-
| [유틸리티](docs/utilities.md) | obj, str, num, bytes, json, xml, zip 등 네임스페이스 유틸리티 |
|
|
24
|
-
| [기능](docs/features.md) | EventEmitter, DebounceQueue, SerialQueue, 에러 클래스, 확장 메서드, env |
|
|
25
|
-
|
|
26
|
-
## 빠른 시작
|
|
27
|
-
|
|
28
|
-
### 날짜/시간 타입
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
import { DateTime, DateOnly, Time } from "@simplysm/core-common";
|
|
32
|
-
|
|
33
|
-
const now = new DateTime();
|
|
34
|
-
const tomorrow = now.addDays(1);
|
|
35
|
-
const formatted = now.toFormatString("yyyy-MM-dd HH:mm:ss");
|
|
36
|
-
|
|
37
|
-
const today = new DateOnly();
|
|
38
|
-
const weekInfo = today.getWeekSeqOfYear(); // { year, weekSeq }
|
|
39
|
-
|
|
40
|
-
const time = Time.parse("14:30:00");
|
|
41
|
-
const later = time.addHours(2); // 24시간 래핑
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### 유틸리티 함수
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
import { obj, str, json, bytes } from "@simplysm/core-common";
|
|
48
|
-
|
|
49
|
-
// 객체 딥 클론/비교/병합
|
|
50
|
-
const cloned = obj.clone(source);
|
|
51
|
-
const isEqual = obj.equal(a, b);
|
|
52
|
-
const merged = obj.merge(source, target);
|
|
53
|
-
|
|
54
|
-
// 문자열 변환
|
|
55
|
-
str.toPascalCase("hello-world"); // "HelloWorld"
|
|
56
|
-
str.toKebabCase("HelloWorld"); // "hello-world"
|
|
57
|
-
|
|
58
|
-
// JSON (커스텀 타입 지원)
|
|
59
|
-
const serialized = json.stringify({ date: new DateTime() });
|
|
60
|
-
const parsed = json.parse<{ date: DateTime }>(serialized);
|
|
61
|
-
|
|
62
|
-
// 바이너리
|
|
63
|
-
const hex = bytes.toHex(data);
|
|
64
|
-
const b64 = bytes.toBase64(data);
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### 이벤트 에미터
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
import { EventEmitter } from "@simplysm/core-common";
|
|
71
|
-
|
|
72
|
-
const emitter = new EventEmitter<{ change: string; error: Error }>();
|
|
73
|
-
emitter.on("change", (data) => { /* ... */ });
|
|
74
|
-
emitter.emit("change", "updated");
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### 배열 확장 메서드
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
import "@simplysm/core-common"; // side-effect import
|
|
81
|
-
|
|
82
|
-
const items = [1, 2, 3, 4, 5];
|
|
83
|
-
items.first(); // 1
|
|
84
|
-
items.last(); // 5
|
|
85
|
-
items.distinct(); // 중복 제거
|
|
86
|
-
items.orderBy((x) => x); // 정렬
|
|
87
|
-
items.groupBy((x) => x % 2); // 그룹화
|
|
88
|
-
items.sum(); // 15
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### ZIP 처리
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
import { ZipArchive } from "@simplysm/core-common";
|
|
95
|
-
|
|
96
|
-
await using zip = new ZipArchive(data);
|
|
97
|
-
const content = await zip.get("file.txt");
|
|
98
|
-
zip.write("new-file.txt", bytes);
|
|
99
|
-
const compressed = await zip.compress();
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### 에러 클래스
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
import { SdError, ArgumentError, TimeoutError } from "@simplysm/core-common";
|
|
106
|
-
|
|
107
|
-
// 에러 체인 (=> 구분자로 조인)
|
|
108
|
-
throw new SdError(originalError, "추가 컨텍스트");
|
|
109
|
-
// 메시지: "추가 컨텍스트 => 원본 에러 메시지"
|
|
110
|
-
|
|
111
|
-
// 인자 에러 (YAML 형식)
|
|
112
|
-
throw new ArgumentError({ userId: -1, name: "" });
|
|
113
|
-
```
|
package/docs/features.md
DELETED
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
# 기능
|
|
2
|
-
|
|
3
|
-
## EventEmitter
|
|
4
|
-
|
|
5
|
-
타입 안전한 이벤트 에미터. `EventTarget` 기반으로 브라우저와 Node.js 모두에서 사용 가능하다.
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
import { EventEmitter } from "@simplysm/core-common";
|
|
9
|
-
|
|
10
|
-
const emitter = new EventEmitter<{
|
|
11
|
-
change: string;
|
|
12
|
-
error: Error;
|
|
13
|
-
ready: void;
|
|
14
|
-
}>();
|
|
15
|
-
|
|
16
|
-
// 리스너 등록 (동일 리스너 중복 등록 무시)
|
|
17
|
-
emitter.on("change", (data) => { /* data: string */ });
|
|
18
|
-
|
|
19
|
-
// 리스너 제거
|
|
20
|
-
emitter.off("change", listener);
|
|
21
|
-
|
|
22
|
-
// 이벤트 발생 (void 타입은 인자 없이 호출)
|
|
23
|
-
emitter.emit("change", "updated");
|
|
24
|
-
emitter.emit("ready");
|
|
25
|
-
|
|
26
|
-
// 리스너 수 확인
|
|
27
|
-
emitter.listenerCount("change");
|
|
28
|
-
|
|
29
|
-
// 정리 (Disposable 지원)
|
|
30
|
-
emitter.dispose();
|
|
31
|
-
// 또는
|
|
32
|
-
using emitter2 = new EventEmitter<{ tick: number }>();
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### 상속 패턴
|
|
36
|
-
|
|
37
|
-
```typescript
|
|
38
|
-
interface MyEvents {
|
|
39
|
-
data: string;
|
|
40
|
-
error: Error;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
class MyService extends EventEmitter<MyEvents> {
|
|
44
|
-
process(): void {
|
|
45
|
-
this.emit("data", "result");
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## DebounceQueue
|
|
53
|
-
|
|
54
|
-
디바운스된 비동기 작업 실행. delay 내 마지막 요청만 실행된다.
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
import { DebounceQueue } from "@simplysm/core-common";
|
|
58
|
-
|
|
59
|
-
const queue = new DebounceQueue(300); // 300ms 디바운스 (생략 시 즉시 실행)
|
|
60
|
-
|
|
61
|
-
queue.on("error", (err) => { /* SdError */ });
|
|
62
|
-
|
|
63
|
-
// 300ms 내 마지막 호출만 실행
|
|
64
|
-
queue.run(async () => {
|
|
65
|
-
await saveData();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
queue.dispose();
|
|
69
|
-
// 또는
|
|
70
|
-
using queue2 = new DebounceQueue(100);
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
동작 방식:
|
|
74
|
-
- delay 내 새 요청이 들어오면 이전 요청을 취소하고 새 요청만 실행
|
|
75
|
-
- 실행 중 새 요청이 들어오면 실행 완료 후 **즉시** 새 요청 처리 (디바운스 delay 없이)
|
|
76
|
-
- 에러 발생 시 "error" 이벤트 리스너가 있으면 이벤트 발생, 없으면 consola로 로깅
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## SerialQueue
|
|
81
|
-
|
|
82
|
-
순차 비동기 작업 실행. 작업 간 선택적 간격(gap) 설정 가능. 에러가 발생해도 후속 작업은 계속 실행된다.
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
import { SerialQueue } from "@simplysm/core-common";
|
|
86
|
-
|
|
87
|
-
const queue = new SerialQueue(100); // 작업 간 100ms 간격 (기본값: 0)
|
|
88
|
-
|
|
89
|
-
queue.on("error", (err) => { /* SdError */ });
|
|
90
|
-
|
|
91
|
-
queue.run(async () => await task1());
|
|
92
|
-
queue.run(async () => await task2()); // task1 완료 + 100ms 후 실행
|
|
93
|
-
|
|
94
|
-
queue.dispose(); // 대기 중인 큐 비우기 (현재 실행 중인 작업은 완료)
|
|
95
|
-
// 또는
|
|
96
|
-
using queue2 = new SerialQueue();
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## 에러 클래스
|
|
102
|
-
|
|
103
|
-
### SdError
|
|
104
|
-
|
|
105
|
-
에러 체인을 지원하는 기본 에러 클래스. ES2024 `cause` 속성을 활용한다. 메시지는 역순으로 `=>` 구분자로 조인된다.
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
import { SdError } from "@simplysm/core-common";
|
|
109
|
-
|
|
110
|
-
// 메시지만
|
|
111
|
-
throw new SdError("작업 실패");
|
|
112
|
-
|
|
113
|
-
// 여러 메시지 (역순 조인)
|
|
114
|
-
throw new SdError("하위 메시지", "상위 메시지");
|
|
115
|
-
// 메시지: "상위 메시지 => 하위 메시지"
|
|
116
|
-
|
|
117
|
-
// 원인 에러 래핑
|
|
118
|
-
throw new SdError(originalError, "추가 컨텍스트");
|
|
119
|
-
// 메시지: "추가 컨텍스트 => 원본 에러 메시지"
|
|
120
|
-
// cause 속성에 originalError 저장
|
|
121
|
-
// stack에 원인 에러 스택도 포함 (---- cause stack ---- 구분자)
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### ArgumentError
|
|
125
|
-
|
|
126
|
-
잘못된 인자를 YAML 형식으로 표시하는 에러. `SdError`를 상속한다.
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
import { ArgumentError } from "@simplysm/core-common";
|
|
130
|
-
|
|
131
|
-
throw new ArgumentError({ userId: -1, name: "" });
|
|
132
|
-
// 메시지: "Invalid arguments.\n\nuserId: -1\nname: ''"
|
|
133
|
-
|
|
134
|
-
throw new ArgumentError("유효하지 않은 입력", { userId: -1 });
|
|
135
|
-
// 메시지: "유효하지 않은 입력\n\nuserId: -1"
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### NotImplementedError
|
|
139
|
-
|
|
140
|
-
미구현 기능을 표시하는 에러. `SdError`를 상속한다.
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
import { NotImplementedError } from "@simplysm/core-common";
|
|
144
|
-
|
|
145
|
-
throw new NotImplementedError();
|
|
146
|
-
// 메시지: "Not implemented"
|
|
147
|
-
|
|
148
|
-
throw new NotImplementedError("이 기능은 아직 구현되지 않았습니다");
|
|
149
|
-
// 메시지: "Not implemented: 이 기능은 아직 구현되지 않았습니다"
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### TimeoutError
|
|
153
|
-
|
|
154
|
-
타임아웃 에러. `SdError`를 상속한다.
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
import { TimeoutError } from "@simplysm/core-common";
|
|
158
|
-
|
|
159
|
-
throw new TimeoutError();
|
|
160
|
-
// 메시지: "Waiting time exceeded"
|
|
161
|
-
|
|
162
|
-
throw new TimeoutError(3);
|
|
163
|
-
// 메시지: "Waiting time exceeded(3 attempts)"
|
|
164
|
-
|
|
165
|
-
throw new TimeoutError(3, "API 응답 대기 초과");
|
|
166
|
-
// 메시지: "Waiting time exceeded(3 attempts): API 응답 대기 초과"
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
## 배열 확장 메서드
|
|
172
|
-
|
|
173
|
-
`import "@simplysm/core-common"` (side-effect import) 시 `Array.prototype`에 추가되는 메서드.
|
|
174
|
-
|
|
175
|
-
### 요소 접근
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
[1, 2, 3].first(); // 1
|
|
179
|
-
[1, 2, 3].first((x) => x > 1); // 2
|
|
180
|
-
[1, 2, 3].last(); // 3
|
|
181
|
-
[1, 2, 3].last((x) => x < 3); // 2
|
|
182
|
-
[1, 2, 3].single((x) => x === 2); // 2 (복수 매칭 시 ArgumentError)
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### 필터링
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
[1, null, 2, undefined].filterExists(); // [1, 2]
|
|
189
|
-
items.ofType("string"); // string 타입만 필터
|
|
190
|
-
items.ofType(MyClass); // MyClass 인스턴스만 필터
|
|
191
|
-
await items.filterAsync(async (x) => check(x)); // 순차 비동기 필터
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### 변환
|
|
195
|
-
|
|
196
|
-
```typescript
|
|
197
|
-
items.mapMany(); // 2차원 -> 1차원 평탄화 (null 제거)
|
|
198
|
-
items.mapMany((x) => x.children); // 매핑 후 평탄화
|
|
199
|
-
await items.mapAsync(async (x) => transform(x)); // 순차 비동기 매핑
|
|
200
|
-
await items.mapManyAsync(async (x) => getChildren(x)); // 순차 비동기 매핑 후 평탄화
|
|
201
|
-
await items.parallelAsync(async (x) => process(x)); // Promise.all 병렬 처리
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### 그룹화/매핑
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
items.groupBy((x) => x.category); // { key, values }[]
|
|
208
|
-
items.groupBy((x) => x.cat, (x) => x.name); // 값 변환 포함
|
|
209
|
-
|
|
210
|
-
items.toMap((x) => x.id); // Map<id, item> (중복 키 시 ArgumentError)
|
|
211
|
-
items.toMap((x) => x.id, (x) => x.name); // Map<id, name>
|
|
212
|
-
await items.toMapAsync(async (x) => x.id); // 비동기 키 매핑
|
|
213
|
-
|
|
214
|
-
items.toArrayMap((x) => x.category); // Map<category, item[]>
|
|
215
|
-
items.toArrayMap((x) => x.cat, (x) => x.name); // Map<category, name[]>
|
|
216
|
-
|
|
217
|
-
items.toSetMap((x) => x.category); // Map<category, Set<item>>
|
|
218
|
-
items.toMapValues((x) => x.cat, (arr) => arr.length); // Map<cat, count>
|
|
219
|
-
|
|
220
|
-
items.toObject((x) => x.id); // Record<string, item>
|
|
221
|
-
items.toObject((x) => x.id, (x) => x.name); // Record<string, name>
|
|
222
|
-
|
|
223
|
-
items.toTree("id", "parentId"); // TreeArray<T>[] (parentId가 null인 항목이 루트)
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### 정렬/고유
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
items.distinct(); // 고유 요소 (객체: 딥 비교 O(n^2))
|
|
230
|
-
items.distinct({ matchAddress: true }); // 참조 비교 (Set 기반 O(n))
|
|
231
|
-
items.distinct({ keyFn: (x) => x.id }); // 커스텀 키 (O(n))
|
|
232
|
-
|
|
233
|
-
items.orderBy((x) => x.name); // 오름차순 (null/undefined 우선)
|
|
234
|
-
items.orderByDesc((x) => x.score); // 내림차순
|
|
235
|
-
|
|
236
|
-
items.shuffle(); // 랜덤 셔플 (Fisher-Yates)
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
### 집계
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
items.sum((x) => x.price); // 합계 (빈 배열이면 0)
|
|
243
|
-
items.min((x) => x.date); // 최솟값 (number | string)
|
|
244
|
-
items.max((x) => x.date); // 최댓값 (number | string)
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### 비교/병합
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
// 배열 diff (INSERT/DELETE/UPDATE)
|
|
251
|
-
items.diffs(target);
|
|
252
|
-
// 반환: { source, target }[]
|
|
253
|
-
// source만 있으면 DELETE, target만 있으면 INSERT, 둘 다 있으면 UPDATE
|
|
254
|
-
|
|
255
|
-
items.diffs(target, { keys: ["id"] }); // 키 기준 매칭
|
|
256
|
-
items.diffs(target, { excludes: ["updatedAt"] }); // 비교 제외 키
|
|
257
|
-
|
|
258
|
-
// 단방향 diff
|
|
259
|
-
items.oneWayDiffs(orgItems, "id");
|
|
260
|
-
items.oneWayDiffs(orgItems, (x) => x.id, { includeSame: true });
|
|
261
|
-
// 반환: { type: "create"|"update"|"same", item, orgItem }[]
|
|
262
|
-
|
|
263
|
-
// 배열 병합
|
|
264
|
-
items.merge(target);
|
|
265
|
-
items.merge(target, { keys: ["id"], excludes: ["updatedAt"] });
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### 원본 변경 메서드 (@mutates)
|
|
269
|
-
|
|
270
|
-
```typescript
|
|
271
|
-
items.distinctThis(); // 원본 배열에서 중복 제거
|
|
272
|
-
items.orderByThis((x) => x.name); // 원본 배열 오름차순 정렬
|
|
273
|
-
items.orderByDescThis((x) => x.score); // 원본 배열 내림차순 정렬
|
|
274
|
-
items.insert(2, newItem1, newItem2); // 인덱스 2에 삽입
|
|
275
|
-
items.remove(item); // 참조 일치 항목 제거
|
|
276
|
-
items.remove((x) => x.expired); // 조건 일치 항목 제거
|
|
277
|
-
items.toggle(item); // 있으면 제거, 없으면 추가
|
|
278
|
-
items.clear(); // 전체 제거
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### 내보내는 타입
|
|
282
|
-
|
|
283
|
-
```typescript
|
|
284
|
-
import type {
|
|
285
|
-
ArrayDiffsResult, // { source, target } 유니온
|
|
286
|
-
ArrayOneWayDiffResult, // { type, item, orgItem } 유니온
|
|
287
|
-
TreeArray, // T & { children: TreeArray<T>[] }
|
|
288
|
-
ComparableType, // string | number | boolean | DateTime | DateOnly | Time | undefined
|
|
289
|
-
} from "@simplysm/core-common";
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
---
|
|
293
|
-
|
|
294
|
-
## Map 확장 메서드
|
|
295
|
-
|
|
296
|
-
```typescript
|
|
297
|
-
const map = new Map<string, number[]>();
|
|
298
|
-
|
|
299
|
-
// 없으면 생성 (값 또는 팩토리 함수)
|
|
300
|
-
map.getOrCreate("key", []); // 직접 값
|
|
301
|
-
map.getOrCreate("key", () => []); // 팩토리 함수
|
|
302
|
-
// 주의: V 타입이 함수이면 팩토리로 인식되므로 () => myFn 형태로 래핑 필요
|
|
303
|
-
|
|
304
|
-
// 값 갱신 (키가 없으면 prev는 undefined)
|
|
305
|
-
map.update("key", (prev) => [...(prev ?? []), 1]);
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
---
|
|
309
|
-
|
|
310
|
-
## Set 확장 메서드
|
|
311
|
-
|
|
312
|
-
```typescript
|
|
313
|
-
const set = new Set<string>();
|
|
314
|
-
|
|
315
|
-
set.adds("a", "b", "c"); // 다수 추가 (체이닝)
|
|
316
|
-
|
|
317
|
-
set.toggle("a"); // 있으면 제거, 없으면 추가
|
|
318
|
-
set.toggle("b", "add"); // 강제 추가
|
|
319
|
-
set.toggle("b", "del"); // 강제 제거
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
---
|
|
323
|
-
|
|
324
|
-
## 환경 변수
|
|
325
|
-
|
|
326
|
-
```typescript
|
|
327
|
-
import { env } from "@simplysm/core-common";
|
|
328
|
-
|
|
329
|
-
env.DEV; // boolean -- 개발 모드 여부 (process.env.DEV를 JSON.parse)
|
|
330
|
-
env.VER; // string | undefined -- 버전 문자열 (process.env.VER)
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
### `__DEV__` 글로벌 상수
|
|
334
|
-
|
|
335
|
-
빌드 시점에 치환되는 글로벌 상수. 라이브러리 빌드에서는 치환되지 않고, 클라이언트/서버 빌드에서 `define: { '__DEV__': 'true/false' }`로 치환된다.
|
|
336
|
-
|
|
337
|
-
```typescript
|
|
338
|
-
declare const __DEV__: boolean;
|
|
339
|
-
|
|
340
|
-
if (__DEV__) {
|
|
341
|
-
// 개발 모드 전용 로직
|
|
342
|
-
}
|
|
343
|
-
```
|
package/docs/types.md
DELETED
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
# 타입
|
|
2
|
-
|
|
3
|
-
## DateTime
|
|
4
|
-
|
|
5
|
-
불변(immutable) 날짜/시간 타입. 모든 변경 메서드는 새 인스턴스를 반환한다.
|
|
6
|
-
|
|
7
|
-
### 생성
|
|
8
|
-
|
|
9
|
-
```typescript
|
|
10
|
-
import { DateTime } from "@simplysm/core-common";
|
|
11
|
-
|
|
12
|
-
new DateTime(); // 현재 시각
|
|
13
|
-
new DateTime(2024, 1, 15); // 2024-01-15 00:00:00
|
|
14
|
-
new DateTime(2024, 1, 15, 14, 30, 0); // 2024-01-15 14:30:00
|
|
15
|
-
new DateTime(2024, 1, 15, 14, 30, 0, 500); // 밀리초 포함
|
|
16
|
-
new DateTime(tick); // tick(밀리초)으로 생성
|
|
17
|
-
new DateTime(new Date()); // Date 객체에서 생성
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
### 파싱
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
DateTime.parse("2024-01-15T14:30:00.000Z"); // ISO 8601
|
|
24
|
-
DateTime.parse("2024-01-15 14:30:00"); // yyyy-MM-dd HH:mm:ss
|
|
25
|
-
DateTime.parse("2024-01-15 14:30:00.123"); // yyyy-MM-dd HH:mm:ss.fff
|
|
26
|
-
DateTime.parse("20240115143000"); // yyyyMMddHHmmss
|
|
27
|
-
DateTime.parse("2024-01-15 AM 10:30:00"); // yyyy-MM-dd AM/PM HH:mm:ss
|
|
28
|
-
DateTime.parse("2024-01-15 오전 10:30:00"); // yyyy-MM-dd 오전/오후 HH:mm:ss
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### 속성
|
|
32
|
-
|
|
33
|
-
| 속성 | 타입 | 설명 |
|
|
34
|
-
|------|------|------|
|
|
35
|
-
| `year` | `number` | 연도 |
|
|
36
|
-
| `month` | `number` | 월 (1~12) |
|
|
37
|
-
| `day` | `number` | 일 |
|
|
38
|
-
| `hour` | `number` | 시 (0~23) |
|
|
39
|
-
| `minute` | `number` | 분 |
|
|
40
|
-
| `second` | `number` | 초 |
|
|
41
|
-
| `millisecond` | `number` | 밀리초 |
|
|
42
|
-
| `tick` | `number` | epoch 밀리초 |
|
|
43
|
-
| `dayOfWeek` | `number` | 요일 (0=일요일) |
|
|
44
|
-
| `timezoneOffsetMinutes` | `number` | 타임존 오프셋(분) |
|
|
45
|
-
| `isValid` | `boolean` | 유효성 |
|
|
46
|
-
| `date` | `Date` | 내부 Date 객체 (readonly) |
|
|
47
|
-
|
|
48
|
-
### 변경 (새 인스턴스 반환)
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
const dt = new DateTime(2024, 1, 15, 14, 30, 0);
|
|
52
|
-
|
|
53
|
-
dt.setYear(2025); // 2025-01-15 14:30:00
|
|
54
|
-
dt.setMonth(6); // 2024-06-15 14:30:00
|
|
55
|
-
dt.setDay(20); // 2024-01-20 14:30:00
|
|
56
|
-
dt.setHour(10); // 2024-01-15 10:30:00
|
|
57
|
-
dt.setMinute(45); // 2024-01-15 14:45:00
|
|
58
|
-
dt.setSecond(30); // 2024-01-15 14:30:30
|
|
59
|
-
dt.setMillisecond(500); // 2024-01-15 14:30:00.500
|
|
60
|
-
|
|
61
|
-
dt.addYears(1); // 2025-01-15 14:30:00
|
|
62
|
-
dt.addMonths(3); // 2024-04-15 14:30:00
|
|
63
|
-
dt.addDays(10); // 2024-01-25 14:30:00
|
|
64
|
-
dt.addHours(-2); // 2024-01-15 12:30:00
|
|
65
|
-
dt.addMinutes(15); // 2024-01-15 14:45:00
|
|
66
|
-
dt.addSeconds(30); // 2024-01-15 14:30:30
|
|
67
|
-
dt.addMilliseconds(500); // 2024-01-15 14:30:00.500
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### 포맷
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
dt.toFormatString("yyyy-MM-dd HH:mm:ss"); // "2024-01-15 14:30:00"
|
|
74
|
-
dt.toFormatString("yy/M/d tt h:mm"); // "24/1/15 PM 2:30"
|
|
75
|
-
dt.toString(); // ISO 8601 형식 "2024-01-15T14:30:00.000+09:00"
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
포맷 패턴:
|
|
79
|
-
|
|
80
|
-
| 패턴 | 설명 | 예시 |
|
|
81
|
-
|------|------|------|
|
|
82
|
-
| `yyyy` | 4자리 연도 | 2024 |
|
|
83
|
-
| `yy` | 2자리 연도 | 24 |
|
|
84
|
-
| `MM` | 0패딩 월 | 01~12 |
|
|
85
|
-
| `M` | 월 | 1~12 |
|
|
86
|
-
| `ddd` | 요일 | 일, 월, 화, 수, 목, 금, 토 |
|
|
87
|
-
| `dd` | 0패딩 일 | 01~31 |
|
|
88
|
-
| `d` | 일 | 1~31 |
|
|
89
|
-
| `tt` | 오전/오후 | AM, PM |
|
|
90
|
-
| `hh` | 0패딩 12시간 | 01~12 |
|
|
91
|
-
| `h` | 12시간 | 1~12 |
|
|
92
|
-
| `HH` | 0패딩 24시간 | 00~23 |
|
|
93
|
-
| `H` | 24시간 | 0~23 |
|
|
94
|
-
| `mm` | 0패딩 분 | 00~59 |
|
|
95
|
-
| `m` | 분 | 0~59 |
|
|
96
|
-
| `ss` | 0패딩 초 | 00~59 |
|
|
97
|
-
| `s` | 초 | 0~59 |
|
|
98
|
-
| `fff` | 밀리초(3자리) | 000~999 |
|
|
99
|
-
| `ff` | 밀리초(2자리) | 00~99 |
|
|
100
|
-
| `f` | 밀리초(1자리) | 0~9 |
|
|
101
|
-
| `zzz` | 타임존(+-HH:mm) | +09:00 |
|
|
102
|
-
| `zz` | 타임존(+-HH) | +09 |
|
|
103
|
-
| `z` | 타임존(+-H) | +9 |
|
|
104
|
-
|
|
105
|
-
---
|
|
106
|
-
|
|
107
|
-
## DateOnly
|
|
108
|
-
|
|
109
|
-
불변 날짜 전용 타입 (시간 정보 없음).
|
|
110
|
-
|
|
111
|
-
### 생성/파싱
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
import { DateOnly } from "@simplysm/core-common";
|
|
115
|
-
|
|
116
|
-
new DateOnly(); // 오늘
|
|
117
|
-
new DateOnly(2024, 1, 15); // 2024-01-15
|
|
118
|
-
new DateOnly(tick); // tick(밀리초)으로 생성
|
|
119
|
-
new DateOnly(new Date()); // Date 객체에서 생성
|
|
120
|
-
DateOnly.parse("2024-01-15"); // yyyy-MM-dd
|
|
121
|
-
DateOnly.parse("20240115"); // yyyyMMdd
|
|
122
|
-
DateOnly.parse("2024-01-15T00:00:00Z"); // ISO 8601 (UTC -> 로컬 변환)
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### 속성
|
|
126
|
-
|
|
127
|
-
| 속성 | 타입 | 설명 |
|
|
128
|
-
|------|------|------|
|
|
129
|
-
| `year` | `number` | 연도 |
|
|
130
|
-
| `month` | `number` | 월 (1~12) |
|
|
131
|
-
| `day` | `number` | 일 |
|
|
132
|
-
| `tick` | `number` | epoch 밀리초 |
|
|
133
|
-
| `dayOfWeek` | `number` | 요일 (0=일요일) |
|
|
134
|
-
| `isValid` | `boolean` | 유효성 |
|
|
135
|
-
| `date` | `Date` | 내부 Date 객체 (readonly) |
|
|
136
|
-
|
|
137
|
-
### 주차 계산
|
|
138
|
-
|
|
139
|
-
모든 주차 메서드는 `weekStartDay`(주 시작 요일, 기본값 1=월요일)와 `minDaysInFirstWeek`(첫 주 최소 일수, 기본값 4=ISO 8601) 옵션을 지원한다.
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
const date = new DateOnly(2024, 1, 15);
|
|
143
|
-
|
|
144
|
-
// 연간 주차
|
|
145
|
-
date.getWeekSeqOfYear();
|
|
146
|
-
// { year: 2024, weekSeq: 3 }
|
|
147
|
-
|
|
148
|
-
// 월간 주차
|
|
149
|
-
date.getWeekSeqOfMonth();
|
|
150
|
-
// { year: 2024, monthSeq: 1, weekSeq: 3 }
|
|
151
|
-
|
|
152
|
-
// 해당 주의 시작일
|
|
153
|
-
date.getWeekSeqStartDate();
|
|
154
|
-
|
|
155
|
-
// 주차 기준 기본 연/월
|
|
156
|
-
date.getBaseYearMonthSeqForWeekSeq();
|
|
157
|
-
// { year: 2024, monthSeq: 1 }
|
|
158
|
-
|
|
159
|
-
// 연/주차로 날짜 계산
|
|
160
|
-
DateOnly.getDateByYearWeekSeq({ year: 2024, weekSeq: 3 });
|
|
161
|
-
// 2024년 3주차 시작일 (월요일)
|
|
162
|
-
|
|
163
|
-
// 월/주차로 날짜 계산
|
|
164
|
-
DateOnly.getDateByYearWeekSeq({ year: 2025, month: 1, weekSeq: 3 });
|
|
165
|
-
// 2025년 1월 3주차 시작일
|
|
166
|
-
|
|
167
|
-
// US 스타일 (일요일 시작, 첫 주 최소 1일)
|
|
168
|
-
date.getWeekSeqOfYear(0, 1);
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### 변경/포맷
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
// 변경 메서드 (새 인스턴스 반환)
|
|
175
|
-
date.setYear(2025); date.setMonth(6); date.setDay(20);
|
|
176
|
-
date.addYears(1); date.addMonths(3); date.addDays(10);
|
|
177
|
-
|
|
178
|
-
// 포맷
|
|
179
|
-
date.toFormatString("yyyy-MM-dd"); // "2024-01-15"
|
|
180
|
-
date.toString(); // "2024-01-15"
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
---
|
|
184
|
-
|
|
185
|
-
## Time
|
|
186
|
-
|
|
187
|
-
불변 시간 전용 타입 (24시간 래핑).
|
|
188
|
-
|
|
189
|
-
### 생성/파싱
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
import { Time } from "@simplysm/core-common";
|
|
193
|
-
|
|
194
|
-
new Time(); // 현재 시각
|
|
195
|
-
new Time(14, 30); // 14:30:00
|
|
196
|
-
new Time(14, 30, 15, 500); // 14:30:15.500
|
|
197
|
-
new Time(tick); // tick(밀리초)으로 생성
|
|
198
|
-
new Time(new Date()); // Date 객체에서 시간 부분만 추출
|
|
199
|
-
Time.parse("14:30:00"); // HH:mm:ss
|
|
200
|
-
Time.parse("14:30:00.123"); // HH:mm:ss.fff
|
|
201
|
-
Time.parse("AM 10:30:00"); // AM/PM HH:mm:ss
|
|
202
|
-
Time.parse("2025-01-15T10:30:00Z"); // ISO 8601 (시간 부분만 추출)
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### 속성
|
|
206
|
-
|
|
207
|
-
| 속성 | 타입 | 설명 |
|
|
208
|
-
|------|------|------|
|
|
209
|
-
| `hour` | `number` | 시 (0~23) |
|
|
210
|
-
| `minute` | `number` | 분 |
|
|
211
|
-
| `second` | `number` | 초 |
|
|
212
|
-
| `millisecond` | `number` | 밀리초 |
|
|
213
|
-
| `tick` | `number` | 하루 기준 밀리초 |
|
|
214
|
-
| `isValid` | `boolean` | 유효성 |
|
|
215
|
-
|
|
216
|
-
### 24시간 래핑
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
const time = new Time(23, 0);
|
|
220
|
-
time.addHours(3); // 02:00:00 (다음날로 넘어감)
|
|
221
|
-
|
|
222
|
-
const time2 = new Time(1, 0);
|
|
223
|
-
time2.addHours(-3); // 22:00:00 (이전날로 넘어감)
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### 변경/포맷
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
// 변경 메서드 (새 인스턴스 반환, 24시간 래핑)
|
|
230
|
-
time.setHour(10); time.setMinute(45);
|
|
231
|
-
time.setSecond(30); time.setMillisecond(500);
|
|
232
|
-
time.addHours(2); time.addMinutes(30);
|
|
233
|
-
time.addSeconds(30); time.addMilliseconds(500);
|
|
234
|
-
|
|
235
|
-
// 포맷
|
|
236
|
-
time.toFormatString("HH:mm:ss"); // "14:30:00"
|
|
237
|
-
time.toString(); // "14:30:00.000"
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## Uuid
|
|
243
|
-
|
|
244
|
-
UUID v4 생성/변환. `crypto.getRandomValues` 기반.
|
|
245
|
-
|
|
246
|
-
```typescript
|
|
247
|
-
import { Uuid } from "@simplysm/core-common";
|
|
248
|
-
|
|
249
|
-
const id = Uuid.generate(); // 새 UUID 생성
|
|
250
|
-
const fromStr = new Uuid("550e8400-e29b-41d4-a716-446655440000");
|
|
251
|
-
const fromBytes = Uuid.fromBytes(bytes); // 16바이트 배열에서 생성 (길이가 16이 아니면 ArgumentError)
|
|
252
|
-
id.toString(); // "xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx"
|
|
253
|
-
id.toBytes(); // Uint8Array (16 bytes)
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
---
|
|
257
|
-
|
|
258
|
-
## LazyGcMap
|
|
259
|
-
|
|
260
|
-
자동 만료 기능이 있는 LRU 캐시. GC 타이머를 사용하므로 반드시 `dispose()` 또는 `using` 문으로 정리해야 한다.
|
|
261
|
-
|
|
262
|
-
```typescript
|
|
263
|
-
import { LazyGcMap } from "@simplysm/core-common";
|
|
264
|
-
|
|
265
|
-
const cache = new LazyGcMap<string, object>({
|
|
266
|
-
expireTime: 60_000, // 60초 만료 (필수)
|
|
267
|
-
gcInterval: 10_000, // 10초마다 GC (기본값: expireTime/10, 최소 1000ms)
|
|
268
|
-
onExpire: (key, value) => { /* 정리 로직, async 가능 */ },
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
cache.set("key", value); // 값 저장 (GC 타이머 시작)
|
|
272
|
-
cache.get("key"); // 값 조회 (접근 시 만료 시간 갱신)
|
|
273
|
-
cache.getOrCreate("key", () => createValue()); // 없으면 생성
|
|
274
|
-
cache.has("key"); // 존재 여부 (접근 시간 갱신 안 함)
|
|
275
|
-
cache.delete("key"); // 삭제
|
|
276
|
-
cache.clear(); // 전체 삭제 (인스턴스는 재사용 가능)
|
|
277
|
-
cache.size; // 저장된 항목 수
|
|
278
|
-
|
|
279
|
-
// 이터레이터
|
|
280
|
-
for (const value of cache.values()) { /* ... */ }
|
|
281
|
-
for (const key of cache.keys()) { /* ... */ }
|
|
282
|
-
for (const [key, value] of cache.entries()) { /* ... */ }
|
|
283
|
-
|
|
284
|
-
// 정리 (Disposable 지원)
|
|
285
|
-
cache.dispose();
|
|
286
|
-
// 또는
|
|
287
|
-
using cache2 = new LazyGcMap({ expireTime: 30_000 });
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
| 메서드 | 시그니처 | 설명 |
|
|
291
|
-
|--------|---------|------|
|
|
292
|
-
| `get` | `get(key: TKey): TValue \| undefined` | 값 조회 (만료 갱신) |
|
|
293
|
-
| `set` | `set(key: TKey, value: TValue): void` | 값 설정 |
|
|
294
|
-
| `getOrCreate` | `getOrCreate(key: TKey, factory: () => TValue): TValue` | 없으면 생성 |
|
|
295
|
-
| `has` | `has(key: TKey): boolean` | 존재 여부 (만료 갱신 안 함) |
|
|
296
|
-
| `delete` | `delete(key: TKey): boolean` | 삭제 |
|
|
297
|
-
| `clear` | `clear(): void` | 전체 삭제 |
|
|
298
|
-
| `size` | `number` (getter) | 저장된 항목 수 |
|
|
299
|
-
| `values` | `values(): IterableIterator<TValue>` | 값 이터레이터 |
|
|
300
|
-
| `keys` | `keys(): IterableIterator<TKey>` | 키 이터레이터 |
|
|
301
|
-
| `entries` | `entries(): IterableIterator<[TKey, TValue]>` | 엔트리 이터레이터 |
|
|
302
|
-
| `dispose` | `dispose(): void` | GC 타이머 정리 및 데이터 삭제 |
|
|
303
|
-
|
|
304
|
-
---
|
|
305
|
-
|
|
306
|
-
## Bytes
|
|
307
|
-
|
|
308
|
-
`Uint8Array`의 타입 별칭. `Buffer` 대신 사용한다.
|
|
309
|
-
|
|
310
|
-
```typescript
|
|
311
|
-
import type { Bytes } from "@simplysm/core-common";
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
---
|
|
315
|
-
|
|
316
|
-
## 공통 타입
|
|
317
|
-
|
|
318
|
-
```typescript
|
|
319
|
-
// 프리미티브 타입 맵
|
|
320
|
-
type PrimitiveTypeMap = {
|
|
321
|
-
string: string;
|
|
322
|
-
number: number;
|
|
323
|
-
boolean: boolean;
|
|
324
|
-
DateTime: DateTime;
|
|
325
|
-
DateOnly: DateOnly;
|
|
326
|
-
Time: Time;
|
|
327
|
-
Uuid: Uuid;
|
|
328
|
-
Bytes: Bytes;
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
type PrimitiveTypeStr = keyof PrimitiveTypeMap;
|
|
332
|
-
type PrimitiveType = PrimitiveTypeMap[PrimitiveTypeStr] | undefined;
|
|
333
|
-
|
|
334
|
-
// 재귀 Partial
|
|
335
|
-
type DeepPartial<T> = Partial<{
|
|
336
|
-
[K in keyof T]: T[K] extends PrimitiveType ? T[K] : DeepPartial<T[K]>;
|
|
337
|
-
}>;
|
|
338
|
-
|
|
339
|
-
// 생성자 타입 (DI용)
|
|
340
|
-
interface Type<T> extends Function { new (...args: unknown[]): T; }
|
|
341
|
-
```
|
package/docs/utilities.md
DELETED
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
# 유틸리티
|
|
2
|
-
|
|
3
|
-
네임스페이스로 그룹화된 유틸리티 함수들.
|
|
4
|
-
|
|
5
|
-
## obj -- 객체 유틸리티
|
|
6
|
-
|
|
7
|
-
### 클론/비교/병합
|
|
8
|
-
|
|
9
|
-
```typescript
|
|
10
|
-
import { obj } from "@simplysm/core-common";
|
|
11
|
-
|
|
12
|
-
// 딥 클론 (순환 참조, 커스텀 타입 지원)
|
|
13
|
-
// 지원: DateTime, DateOnly, Time, Uuid, Uint8Array, Date, RegExp, Error, Map, Set, Array
|
|
14
|
-
// 함수/Symbol은 참조 유지, 프로토타입 체인 유지
|
|
15
|
-
const cloned = obj.clone(source);
|
|
16
|
-
|
|
17
|
-
// 딥 비교
|
|
18
|
-
obj.equal(a, b);
|
|
19
|
-
obj.equal(a, b, { shallow: true }); // 1단계만 비교
|
|
20
|
-
obj.equal(a, b, { ignoreArrayIndex: true }); // 배열 순서 무시 (O(n^2))
|
|
21
|
-
obj.equal(a, b, { topLevelIncludes: ["name", "age"] }); // 특정 키만 비교 (최상위만)
|
|
22
|
-
obj.equal(a, b, { topLevelExcludes: ["updatedAt"] }); // 특정 키 제외 (최상위만)
|
|
23
|
-
|
|
24
|
-
// 딥 병합
|
|
25
|
-
obj.merge(source, target);
|
|
26
|
-
obj.merge(source, target, { arrayProcess: "concat" }); // 배열 연결 (Set 기반 중복 제거)
|
|
27
|
-
obj.merge(source, target, { arrayProcess: "replace" }); // 배열 교체 (기본값)
|
|
28
|
-
obj.merge(source, target, { useDelTargetNull: true }); // target이 null이면 키 삭제
|
|
29
|
-
|
|
30
|
-
// 3-way 병합
|
|
31
|
-
const { conflict, result } = obj.merge3(source, origin, target);
|
|
32
|
-
// source와 origin 같고 target 다름 -> target 값 사용
|
|
33
|
-
// target과 origin 같고 source 다름 -> source 값 사용
|
|
34
|
-
// 세 값 모두 다름 -> conflict = true, origin 값 유지
|
|
35
|
-
|
|
36
|
-
// 키별 비교 옵션
|
|
37
|
-
obj.merge3(source, origin, target, {
|
|
38
|
-
name: { keys: ["first", "last"] },
|
|
39
|
-
tags: { ignoreArrayIndex: true },
|
|
40
|
-
});
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### 객체 조작
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
// pick/omit
|
|
47
|
-
obj.pick(item, ["name", "age"]); // { name, age }
|
|
48
|
-
obj.omit(item, ["password"]); // password 제외
|
|
49
|
-
obj.omitByFilter(item, (key) => key.startsWith("_")); // 조건부 제외
|
|
50
|
-
|
|
51
|
-
// 체인 접근 (점 표기법)
|
|
52
|
-
obj.getChainValue(data, "user.address[0].city");
|
|
53
|
-
obj.getChainValue(data, "user.name", true); // optional: 중간 경로 없으면 undefined
|
|
54
|
-
obj.setChainValue(data, "user.name", "new"); // 중간 경로 없으면 자동 생성
|
|
55
|
-
obj.deleteChainValue(data, "user.temp"); // 중간 경로 없으면 무시
|
|
56
|
-
|
|
57
|
-
// 깊이 기반 체인 접근
|
|
58
|
-
obj.getChainValueByDepth(data, "parent", 2); // data.parent.parent
|
|
59
|
-
obj.getChainValueByDepth(data, "parent", 2, true); // optional 모드
|
|
60
|
-
|
|
61
|
-
// 기타
|
|
62
|
-
obj.clearUndefined(data); // undefined 키 제거 (mutate)
|
|
63
|
-
obj.clear(data); // 모든 키 제거 (mutate)
|
|
64
|
-
obj.nullToUndefined(data); // null -> undefined (재귀, mutate, 순환 참조 안전)
|
|
65
|
-
obj.unflatten({ "a.b.c": 1 }); // { a: { b: { c: 1 } } }
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### 타입 유틸리티
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
obj.keys(myObj); // 타입 안전한 Object.keys
|
|
72
|
-
obj.entries(myObj); // 타입 안전한 Object.entries
|
|
73
|
-
obj.fromEntries(pairs); // 타입 안전한 Object.fromEntries
|
|
74
|
-
|
|
75
|
-
// 객체 엔트리 변환
|
|
76
|
-
obj.map(colors, (key, rgb) => [null, `rgb(${rgb})`]);
|
|
77
|
-
// key를 null로 반환하면 원래 key 유지, 새 key를 반환하면 key 변환
|
|
78
|
-
|
|
79
|
-
type A = obj.UndefToOptional<{ a: string | undefined }>; // { a?: string | undefined }
|
|
80
|
-
type B = obj.OptionalToUndef<{ a?: string }>; // { a: string | undefined }
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## str -- 문자열 유틸리티
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
import { str } from "@simplysm/core-common";
|
|
89
|
-
|
|
90
|
-
str.toPascalCase("hello-world"); // "HelloWorld"
|
|
91
|
-
str.toCamelCase("hello-world"); // "helloWorld"
|
|
92
|
-
str.toKebabCase("HelloWorld"); // "hello-world"
|
|
93
|
-
str.toSnakeCase("HelloWorld"); // "hello_world"
|
|
94
|
-
|
|
95
|
-
str.isNullOrEmpty(value); // value is "" | undefined (타입 가드)
|
|
96
|
-
str.insert("abcde", 2, "XY"); // "abXYcde"
|
|
97
|
-
str.replaceFullWidth("ABC"); // "ABC" (전각 -> 반각)
|
|
98
|
-
|
|
99
|
-
// 한국어 조사
|
|
100
|
-
str.getKoreanSuffix("사과", "을"); // "를"
|
|
101
|
-
str.getKoreanSuffix("바나나", "을"); // "을"
|
|
102
|
-
str.getKoreanSuffix("칼", "로"); // "로" (ㄹ 종성 특수 처리)
|
|
103
|
-
// 지원 타입: "을"(을/를), "은"(은/는), "이"(이/가), "와"(과/와), "랑"(이랑/랑), "로"(으로/로), "라"(이라/라)
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## num -- 숫자 유틸리티
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
import { num } from "@simplysm/core-common";
|
|
112
|
-
|
|
113
|
-
// 파싱 (비숫자 문자 자동 제거)
|
|
114
|
-
num.parseInt("1,234원"); // 1234
|
|
115
|
-
num.parseFloat("$1,234.56"); // 1234.56
|
|
116
|
-
num.parseRoundedInt("1.7"); // 2
|
|
117
|
-
|
|
118
|
-
num.isNullOrEmpty(value); // value is 0 | undefined (타입 가드)
|
|
119
|
-
|
|
120
|
-
// 포맷 (천 단위 구분자)
|
|
121
|
-
num.format(1234567); // "1,234,567"
|
|
122
|
-
num.format(1234.5678, { max: 2 }); // "1,234.57"
|
|
123
|
-
num.format(1234.5, { min: 2 }); // "1,234.50"
|
|
124
|
-
num.format(1234.5, { min: 2, max: 4 }); // "1,234.50"
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
## bytes -- 바이너리 유틸리티
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
import { bytes } from "@simplysm/core-common";
|
|
133
|
-
|
|
134
|
-
bytes.concat([arr1, arr2, arr3]); // Uint8Array 연결
|
|
135
|
-
bytes.toHex(data); // 소문자 hex 문자열
|
|
136
|
-
bytes.fromHex("48656c6c6f"); // Uint8Array
|
|
137
|
-
bytes.toBase64(data); // base64 인코딩
|
|
138
|
-
bytes.fromBase64(b64str); // base64 디코딩
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## json -- JSON 직렬화
|
|
144
|
-
|
|
145
|
-
커스텀 타입(DateTime, DateOnly, Time, Uuid, Set, Map, Error, Uint8Array, Date, RegExp 등)을 지원하는 JSON 직렬화/역직렬화.
|
|
146
|
-
|
|
147
|
-
내부적으로 `__type__`/`data` 형식의 태그 객체를 사용하여 타입 정보를 보존한다.
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
import { json, DateTime } from "@simplysm/core-common";
|
|
151
|
-
|
|
152
|
-
// 직렬화 (커스텀 타입 자동 처리)
|
|
153
|
-
const str = json.stringify({
|
|
154
|
-
date: new DateTime(),
|
|
155
|
-
id: Uuid.generate(),
|
|
156
|
-
items: new Set([1, 2, 3]),
|
|
157
|
-
mapping: new Map([["a", 1]]),
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// 역직렬화 (타입 자동 복원, null -> undefined 변환)
|
|
161
|
-
const data = json.parse<{ date: DateTime }>(str);
|
|
162
|
-
|
|
163
|
-
// 옵션
|
|
164
|
-
json.stringify(obj, { space: 2 }); // 들여쓰기
|
|
165
|
-
json.stringify(obj, { redactBytes: true }); // Uint8Array를 "__hidden__"로 대체 (로깅용)
|
|
166
|
-
json.stringify(obj, { replacer: (key, value) => value }); // 커스텀 replacer
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
**주의사항:**
|
|
170
|
-
- `parse()`는 모든 JSON null을 undefined로 변환한다 (simplysm 프레임워크의 null-free 규칙)
|
|
171
|
-
- `redactBytes: true`로 직렬화한 결과는 `parse()`로 복원 불가 (SdError 발생)
|
|
172
|
-
- 순환 참조가 있으면 TypeError 발생
|
|
173
|
-
|
|
174
|
-
---
|
|
175
|
-
|
|
176
|
-
## xml -- XML 처리
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
import { xml } from "@simplysm/core-common";
|
|
180
|
-
|
|
181
|
-
// 파싱 (속성은 $ 객체, 텍스트는 _ 키, 자식 요소는 배열)
|
|
182
|
-
const obj = xml.parse('<root attr="1"><child>text</child></root>');
|
|
183
|
-
// { root: { $: { attr: "1" }, child: [{ _: "text" }] } }
|
|
184
|
-
|
|
185
|
-
// 네임스페이스 접두사 제거
|
|
186
|
-
xml.parse(xmlStr, { stripTagPrefix: true });
|
|
187
|
-
|
|
188
|
-
// 직렬화
|
|
189
|
-
const xmlStr = xml.stringify(obj);
|
|
190
|
-
// fast-xml-parser의 XmlBuilderOptions를 두 번째 인자로 전달 가능
|
|
191
|
-
xml.stringify(obj, { format: true });
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
## path -- 경로 유틸리티
|
|
197
|
-
|
|
198
|
-
POSIX 스타일 전용 경로 유틸리티. 브라우저 환경과 Capacitor 플러그인을 위해 설계되었다. Windows 백슬래시 경로는 지원하지 않는다.
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
import { path } from "@simplysm/core-common";
|
|
202
|
-
|
|
203
|
-
path.join("a", "b", "c.txt"); // "a/b/c.txt"
|
|
204
|
-
path.basename("/a/b/file.ts"); // "file.ts"
|
|
205
|
-
path.basename("/a/b/file.ts", ".ts"); // "file"
|
|
206
|
-
path.extname("/a/b/file.ts"); // ".ts"
|
|
207
|
-
path.extname("/a/b/.gitignore"); // "" (숨김 파일은 빈 문자열)
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
---
|
|
211
|
-
|
|
212
|
-
## wait -- 비동기 대기
|
|
213
|
-
|
|
214
|
-
```typescript
|
|
215
|
-
import { wait } from "@simplysm/core-common";
|
|
216
|
-
|
|
217
|
-
// 조건 충족까지 대기
|
|
218
|
-
await wait.until(() => isReady, 100, 50);
|
|
219
|
-
// 100ms 간격으로 체크, 최대 50회 (초과 시 TimeoutError)
|
|
220
|
-
// 첫 호출에서 true면 즉시 반환
|
|
221
|
-
|
|
222
|
-
// 시간 대기
|
|
223
|
-
await wait.time(1000); // 1초 대기
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
---
|
|
227
|
-
|
|
228
|
-
## err -- 에러 처리
|
|
229
|
-
|
|
230
|
-
```typescript
|
|
231
|
-
import { err } from "@simplysm/core-common";
|
|
232
|
-
|
|
233
|
-
try { /* ... */ } catch (e) {
|
|
234
|
-
const message = err.message(e); // unknown 타입에서 안전하게 메시지 추출
|
|
235
|
-
// Error 인스턴스면 .message, 아니면 String(e)
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
## dt -- 날짜 포맷
|
|
242
|
-
|
|
243
|
-
```typescript
|
|
244
|
-
import { dt } from "@simplysm/core-common";
|
|
245
|
-
|
|
246
|
-
// 포맷 (DateTime/DateOnly/Time의 toFormatString이 내부적으로 사용)
|
|
247
|
-
dt.format("yyyy-MM-dd", { year: 2024, month: 1, day: 15 });
|
|
248
|
-
dt.format("HH:mm:ss", { hour: 14, minute: 30, second: 0 });
|
|
249
|
-
|
|
250
|
-
// 월 정규화
|
|
251
|
-
dt.normalizeMonth(2024, 13, 1); // { year: 2025, month: 1, day: 1 }
|
|
252
|
-
dt.normalizeMonth(2025, 2, 31); // { year: 2025, month: 2, day: 28 }
|
|
253
|
-
|
|
254
|
-
// 12시간 -> 24시간 변환
|
|
255
|
-
dt.convert12To24(12, false); // 0 (12 AM = 0시)
|
|
256
|
-
dt.convert12To24(12, true); // 12 (12 PM = 12시)
|
|
257
|
-
dt.convert12To24(2, true); // 14 (PM 2시 = 14시)
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
|
-
## primitive -- 프리미티브 타입 판별
|
|
263
|
-
|
|
264
|
-
```typescript
|
|
265
|
-
import { primitive } from "@simplysm/core-common";
|
|
266
|
-
|
|
267
|
-
primitive.typeStr("hello"); // "string"
|
|
268
|
-
primitive.typeStr(123); // "number"
|
|
269
|
-
primitive.typeStr(true); // "boolean"
|
|
270
|
-
primitive.typeStr(new DateTime()); // "DateTime"
|
|
271
|
-
primitive.typeStr(new DateOnly()); // "DateOnly"
|
|
272
|
-
primitive.typeStr(new Time()); // "Time"
|
|
273
|
-
primitive.typeStr(new Uuid("...")); // "Uuid"
|
|
274
|
-
primitive.typeStr(new Uint8Array()); // "Bytes"
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
---
|
|
278
|
-
|
|
279
|
-
## transfer -- Worker 데이터 전송
|
|
280
|
-
|
|
281
|
-
Worker `postMessage`용 직렬화/역직렬화. 순환 참조 감지(경로 포함 TypeError), 커스텀 타입 지원.
|
|
282
|
-
|
|
283
|
-
지원 타입: Date, DateTime, DateOnly, Time, Uuid, RegExp, Error(cause/code/detail 포함), Uint8Array, Array, Map, Set, plain object.
|
|
284
|
-
|
|
285
|
-
```typescript
|
|
286
|
-
import { transfer } from "@simplysm/core-common";
|
|
287
|
-
|
|
288
|
-
// Worker로 전송
|
|
289
|
-
const { result, transferList } = transfer.encode(data);
|
|
290
|
-
worker.postMessage(result, transferList);
|
|
291
|
-
// Uint8Array의 ArrayBuffer가 자동으로 transferList에 추가됨
|
|
292
|
-
// SharedArrayBuffer는 transferList에 포함되지 않음
|
|
293
|
-
|
|
294
|
-
// Worker에서 수신
|
|
295
|
-
const original = transfer.decode(event.data);
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
---
|
|
299
|
-
|
|
300
|
-
## 템플릿 문자열 태그
|
|
301
|
-
|
|
302
|
-
IDE 구문 강조를 위한 태그 함수. 들여쓰기 자동 정규화 (공통 들여쓰기 제거, 앞뒤 빈 줄 제거).
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
import { js, ts, html, tsql, mysql, pgsql } from "@simplysm/core-common";
|
|
306
|
-
|
|
307
|
-
const query = tsql`
|
|
308
|
-
SELECT *
|
|
309
|
-
FROM Users
|
|
310
|
-
WHERE id = ${userId}
|
|
311
|
-
`;
|
|
312
|
-
|
|
313
|
-
const markup = html`
|
|
314
|
-
<div class="container">
|
|
315
|
-
<h1>${title}</h1>
|
|
316
|
-
</div>
|
|
317
|
-
`;
|
|
318
|
-
|
|
319
|
-
// js, ts, mysql, pgsql도 동일하게 동작
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
---
|
|
323
|
-
|
|
324
|
-
## ZipArchive -- ZIP 처리
|
|
325
|
-
|
|
326
|
-
`@zip.js/zip.js` 기반 ZIP 파일 처리. 내부 캐싱으로 동일 파일 중복 해제를 방지한다.
|
|
327
|
-
|
|
328
|
-
```typescript
|
|
329
|
-
import { ZipArchive } from "@simplysm/core-common";
|
|
330
|
-
|
|
331
|
-
// ZIP 읽기 (await using 지원)
|
|
332
|
-
await using zip = new ZipArchive(blobOrBytes);
|
|
333
|
-
const fileData = await zip.get("path/to/file.txt");
|
|
334
|
-
const exists = await zip.exists("file.txt");
|
|
335
|
-
|
|
336
|
-
// 전체 파일 추출 (진행률 콜백)
|
|
337
|
-
const allFiles = await zip.extractAll((progress) => {
|
|
338
|
-
// progress: { fileName, totalSize, extractedSize }
|
|
339
|
-
});
|
|
340
|
-
// 반환: Map<string, Bytes | undefined>
|
|
341
|
-
|
|
342
|
-
// ZIP 생성
|
|
343
|
-
await using newZip = new ZipArchive();
|
|
344
|
-
newZip.write("file1.txt", data1);
|
|
345
|
-
newZip.write("dir/file2.txt", data2);
|
|
346
|
-
const compressed = await newZip.compress();
|
|
347
|
-
|
|
348
|
-
// 수동 정리 (await using을 사용하지 않는 경우)
|
|
349
|
-
await zip.close();
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
| 메서드 | 시그니처 | 설명 |
|
|
353
|
-
|--------|---------|------|
|
|
354
|
-
| `get` | `get(fileName: string): Promise<Bytes \| undefined>` | 파일 추출 (캐시 사용) |
|
|
355
|
-
| `exists` | `exists(fileName: string): Promise<boolean>` | 파일 존재 여부 |
|
|
356
|
-
| `extractAll` | `extractAll(cb?): Promise<Map<string, Bytes \| undefined>>` | 전체 추출 |
|
|
357
|
-
| `write` | `write(fileName: string, bytes: Bytes): void` | 파일 쓰기 (캐시에 저장) |
|
|
358
|
-
| `compress` | `compress(): Promise<Bytes>` | 캐시 내용을 ZIP으로 압축 |
|
|
359
|
-
| `close` | `close(): Promise<void>` | 리더 닫기 및 캐시 정리 |
|