@simplysm/core-common 13.0.100 → 14.0.4
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 +86 -92
- package/dist/common.types.d.ts +14 -14
- package/dist/common.types.js +2 -1
- package/dist/common.types.js.map +1 -6
- package/dist/env.d.ts +8 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +13 -9
- package/dist/env.js.map +1 -6
- package/dist/errors/argument-error.d.ts +10 -10
- package/dist/errors/argument-error.d.ts.map +1 -1
- package/dist/errors/argument-error.js +31 -14
- package/dist/errors/argument-error.js.map +1 -6
- package/dist/errors/not-implemented-error.d.ts +8 -8
- package/dist/errors/not-implemented-error.js +30 -12
- package/dist/errors/not-implemented-error.js.map +1 -6
- package/dist/errors/sd-error.d.ts +10 -10
- package/dist/errors/sd-error.d.ts.map +1 -1
- package/dist/errors/sd-error.js +45 -24
- package/dist/errors/sd-error.js.map +1 -6
- package/dist/errors/timeout-error.d.ts +10 -10
- package/dist/errors/timeout-error.js +34 -15
- package/dist/errors/timeout-error.js.map +1 -6
- package/dist/extensions/arr-ext.d.ts +2 -2
- package/dist/extensions/arr-ext.helpers.d.ts +10 -10
- package/dist/extensions/arr-ext.helpers.js +112 -89
- package/dist/extensions/arr-ext.helpers.js.map +1 -6
- package/dist/extensions/arr-ext.js +458 -422
- package/dist/extensions/arr-ext.js.map +1 -6
- package/dist/extensions/arr-ext.types.d.ts +57 -57
- package/dist/extensions/arr-ext.types.d.ts.map +1 -1
- package/dist/extensions/arr-ext.types.js +6 -1
- package/dist/extensions/arr-ext.types.js.map +1 -6
- package/dist/extensions/map-ext.d.ts +16 -16
- package/dist/extensions/map-ext.js +27 -22
- package/dist/extensions/map-ext.js.map +1 -6
- package/dist/extensions/set-ext.d.ts +11 -11
- package/dist/extensions/set-ext.js +32 -25
- package/dist/extensions/set-ext.js.map +1 -6
- package/dist/features/debounce-queue.d.ts +17 -17
- package/dist/features/debounce-queue.js +98 -70
- package/dist/features/debounce-queue.js.map +1 -6
- package/dist/features/event-emitter.d.ts +20 -20
- package/dist/features/event-emitter.js +101 -78
- package/dist/features/event-emitter.js.map +1 -6
- package/dist/features/serial-queue.d.ts +11 -11
- package/dist/features/serial-queue.js +78 -57
- package/dist/features/serial-queue.js.map +1 -6
- package/dist/globals.d.ts +4 -4
- package/dist/globals.js +9 -1
- package/dist/globals.js.map +1 -6
- package/dist/index.js +28 -27
- package/dist/index.js.map +1 -6
- package/dist/types/date-only.d.ts +64 -64
- package/dist/types/date-only.d.ts.map +1 -1
- package/dist/types/date-only.js +263 -252
- package/dist/types/date-only.js.map +1 -6
- package/dist/types/date-time.d.ts +36 -36
- package/dist/types/date-time.d.ts.map +1 -1
- package/dist/types/date-time.js +196 -288
- package/dist/types/date-time.js.map +1 -6
- package/dist/types/lazy-gc-map.d.ts +26 -26
- package/dist/types/lazy-gc-map.d.ts.map +1 -1
- package/dist/types/lazy-gc-map.js +202 -159
- package/dist/types/lazy-gc-map.js.map +1 -6
- package/dist/types/time.d.ts +23 -23
- package/dist/types/time.d.ts.map +1 -1
- package/dist/types/time.js +169 -158
- package/dist/types/time.js.map +1 -6
- package/dist/types/uuid.d.ts +11 -11
- package/dist/types/uuid.d.ts.map +1 -1
- package/dist/types/uuid.js +95 -70
- package/dist/types/uuid.js.map +1 -6
- package/dist/utils/bytes.d.ts +17 -17
- package/dist/utils/bytes.js +137 -81
- package/dist/utils/bytes.js.map +1 -6
- package/dist/utils/date-format.d.ts +40 -40
- package/dist/utils/date-format.js +187 -101
- package/dist/utils/date-format.js.map +1 -6
- package/dist/utils/error.d.ts +4 -4
- package/dist/utils/error.js +11 -6
- package/dist/utils/error.js.map +1 -6
- package/dist/utils/json.d.ts +19 -19
- package/dist/utils/json.js +187 -135
- package/dist/utils/json.js.map +1 -6
- package/dist/utils/num.d.ts +20 -20
- package/dist/utils/num.js +76 -34
- package/dist/utils/num.js.map +1 -6
- package/dist/utils/obj.d.ts +111 -111
- package/dist/utils/obj.d.ts.map +1 -1
- package/dist/utils/obj.js +706 -496
- package/dist/utils/obj.js.map +1 -6
- package/dist/utils/path.d.ts +10 -10
- package/dist/utils/path.js +35 -18
- package/dist/utils/path.js.map +1 -6
- package/dist/utils/primitive.d.ts +5 -5
- package/dist/utils/primitive.js +34 -14
- package/dist/utils/primitive.js.map +1 -6
- package/dist/utils/str.d.ts +38 -38
- package/dist/utils/str.js +217 -113
- package/dist/utils/str.js.map +1 -6
- package/dist/utils/template-strings.d.ts +26 -26
- package/dist/utils/template-strings.js +113 -40
- package/dist/utils/template-strings.js.map +1 -6
- package/dist/utils/transferable.d.ts +18 -18
- package/dist/utils/transferable.js +218 -151
- package/dist/utils/transferable.js.map +1 -6
- package/dist/utils/wait.d.ts +9 -9
- package/dist/utils/wait.js +30 -15
- package/dist/utils/wait.js.map +1 -6
- package/dist/utils/xml.d.ts +13 -13
- package/dist/utils/xml.js +84 -46
- package/dist/utils/xml.js.map +1 -6
- package/dist/utils/zip.d.ts +22 -22
- package/dist/utils/zip.js +172 -148
- package/dist/utils/zip.js.map +1 -6
- package/docs/array-extensions.md +430 -0
- package/docs/env.md +52 -0
- package/docs/errors.md +41 -56
- package/docs/features.md +82 -97
- package/docs/type-utilities.md +91 -0
- package/docs/types.md +221 -201
- package/docs/utils.md +319 -435
- package/package.json +7 -5
- package/src/common.types.ts +14 -14
- package/src/env.ts +12 -3
- package/src/errors/argument-error.ts +15 -15
- package/src/errors/not-implemented-error.ts +9 -9
- package/src/errors/sd-error.ts +12 -12
- package/src/errors/timeout-error.ts +12 -12
- package/src/extensions/arr-ext.helpers.ts +16 -16
- package/src/extensions/arr-ext.ts +35 -35
- package/src/extensions/arr-ext.types.ts +57 -57
- package/src/extensions/map-ext.ts +16 -16
- package/src/extensions/set-ext.ts +11 -11
- package/src/features/debounce-queue.ts +23 -23
- package/src/features/event-emitter.ts +25 -25
- package/src/features/serial-queue.ts +13 -13
- package/src/globals.ts +4 -4
- package/src/index.ts +5 -5
- package/src/types/date-only.ts +84 -83
- package/src/types/date-time.ts +43 -42
- package/src/types/lazy-gc-map.ts +44 -44
- package/src/types/time.ts +29 -29
- package/src/types/uuid.ts +15 -15
- package/src/utils/bytes.ts +35 -35
- package/src/utils/date-format.ts +59 -59
- package/src/utils/error.ts +4 -4
- package/src/utils/json.ts +41 -41
- package/src/utils/num.ts +20 -20
- package/src/utils/obj.ts +138 -138
- package/src/utils/path.ts +10 -10
- package/src/utils/primitive.ts +6 -6
- package/src/utils/str.ts +48 -48
- package/src/utils/template-strings.ts +29 -29
- package/src/utils/transferable.ts +38 -38
- package/src/utils/wait.ts +10 -10
- package/src/utils/xml.ts +19 -19
- package/src/utils/zip.ts +25 -25
- package/docs/extensions.md +0 -387
- package/tests/errors/errors.spec.ts +0 -80
- package/tests/extensions/array-extension.spec.ts +0 -654
- package/tests/extensions/map-extension.spec.ts +0 -117
- package/tests/extensions/set-extension.spec.ts +0 -67
- package/tests/types/date-only.spec.ts +0 -533
- package/tests/types/date-time.spec.ts +0 -246
- package/tests/types/lazy-gc-map.spec.ts +0 -606
- package/tests/types/time.spec.ts +0 -428
- package/tests/types/uuid.spec.ts +0 -74
- package/tests/utils/bytes-utils.spec.ts +0 -197
- package/tests/utils/date-format.spec.ts +0 -350
- package/tests/utils/debounce-queue.spec.ts +0 -226
- package/tests/utils/json.spec.ts +0 -400
- package/tests/utils/number.spec.ts +0 -136
- package/tests/utils/object.spec.ts +0 -810
- package/tests/utils/path.spec.ts +0 -70
- package/tests/utils/primitive.spec.ts +0 -43
- package/tests/utils/sd-event-emitter.spec.ts +0 -189
- package/tests/utils/serial-queue.spec.ts +0 -305
- package/tests/utils/string.spec.ts +0 -265
- package/tests/utils/template-strings.spec.ts +0 -48
- package/tests/utils/transferable.spec.ts +0 -639
- package/tests/utils/wait.spec.ts +0 -123
- package/tests/utils/xml.spec.ts +0 -146
- package/tests/utils/zip.spec.ts +0 -221
package/src/utils/date-format.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 월 설정 시 연/월/일 정규화 결과
|
|
3
3
|
*/
|
|
4
4
|
export interface DtNormalizedMonth {
|
|
5
5
|
year: number;
|
|
@@ -8,27 +8,27 @@ export interface DtNormalizedMonth {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
11
|
+
* 월 설정 시 연/월/일 정규화
|
|
12
|
+
* - 월이 1-12 범위를 벗어나면 연도 조정
|
|
13
|
+
* - 현재 일이 대상 월의 일수보다 크면 해당 월의 마지막 일로 조정
|
|
14
14
|
*
|
|
15
|
-
* @param year
|
|
16
|
-
* @param month
|
|
17
|
-
* @param day
|
|
18
|
-
* @returns
|
|
15
|
+
* @param year 기준 연도
|
|
16
|
+
* @param month 설정할 월 (1-12 범위 밖의 값 허용)
|
|
17
|
+
* @param day 기준 일
|
|
18
|
+
* @returns 정규화된 연도, 월, 일
|
|
19
19
|
*
|
|
20
20
|
* @example
|
|
21
21
|
* normalizeMonth(2025, 13, 15) // { year: 2026, month: 1, day: 15 }
|
|
22
22
|
* normalizeMonth(2025, 2, 31) // { year: 2025, month: 2, day: 28 }
|
|
23
23
|
*/
|
|
24
24
|
export function normalizeMonth(year: number, month: number, day: number): DtNormalizedMonth {
|
|
25
|
-
//
|
|
26
|
-
//
|
|
25
|
+
// 월 오버플로우/언더플로우 정규화
|
|
26
|
+
// 월이 1-12 범위를 벗어나면 연도 조정
|
|
27
27
|
const normalizedYear = year + Math.floor((month - 1) / 12);
|
|
28
|
-
// JavaScript %
|
|
28
|
+
// JavaScript % 연산자는 음수에 대해 음수를 반환하므로 (% 12 + 12) % 12 패턴으로 0-11 범위를 보장한 후 1-12로 변환
|
|
29
29
|
const normalizedMonth = ((((month - 1) % 12) + 12) % 12) + 1;
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// 대상 월의 마지막 일 조회
|
|
32
32
|
const lastDay = new Date(normalizedYear, normalizedMonth, 0).getDate();
|
|
33
33
|
const normalizedDay = Math.min(day, lastDay);
|
|
34
34
|
|
|
@@ -36,13 +36,13 @@ export function normalizeMonth(year: number, month: number, day: number): DtNorm
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* 12시간 형식을 24시간 형식으로 변환
|
|
40
40
|
* - 12:00 AM = 0:00, 12:00 PM = 12:00
|
|
41
41
|
* - 1-11 AM = 1-11, 1-11 PM = 13-23
|
|
42
42
|
*
|
|
43
|
-
* @param rawHour 12
|
|
44
|
-
* @param isPM
|
|
45
|
-
* @returns 24
|
|
43
|
+
* @param rawHour 12시간 형식의 시 (1-12)
|
|
44
|
+
* @param isPM 오후 여부
|
|
45
|
+
* @returns 24시간 형식의 시 (0-23)
|
|
46
46
|
*/
|
|
47
47
|
export function convert12To24(rawHour: number, isPM: boolean): number {
|
|
48
48
|
if (rawHour === 12) {
|
|
@@ -51,15 +51,15 @@ export function convert12To24(rawHour: number, isPM: boolean): number {
|
|
|
51
51
|
return isPM ? rawHour + 12 : rawHour;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
//#region
|
|
54
|
+
//#region 정규식 캐싱 (모듈 로드 시 한 번만 생성)
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
57
|
+
* 형식 패턴 정규식
|
|
58
58
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
59
|
+
* 순서가 중요함:
|
|
60
|
+
* dtFormat() 함수에서 긴 패턴(yyyy, MM, dd 등)이 먼저 처리되어야
|
|
61
|
+
* 짧은 패턴(y, M, d 등)이 부분적으로 매칭되는 것을 방지함.
|
|
62
|
+
* 예: "yyyy"가 먼저 처리되지 않으면 "yy"가 두 번 매칭될 수 있음
|
|
63
63
|
*/
|
|
64
64
|
const patterns = {
|
|
65
65
|
yyyy: /yyyy/g,
|
|
@@ -91,38 +91,38 @@ const weekStrings = ["일", "월", "화", "수", "목", "금", "토"];
|
|
|
91
91
|
//#endregion
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
94
|
+
* 형식 문자열에 따라 날짜/시간을 문자열로 변환
|
|
95
95
|
*
|
|
96
|
-
* @param formatString
|
|
97
|
-
* @param args
|
|
96
|
+
* @param formatString 형식 문자열
|
|
97
|
+
* @param args 날짜/시간 구성요소
|
|
98
98
|
*
|
|
99
99
|
* @remarks
|
|
100
|
-
*
|
|
100
|
+
* C#과 동일한 형식 문자열을 지원:
|
|
101
101
|
*
|
|
102
|
-
* |
|
|
103
|
-
*
|
|
104
|
-
* | yyyy | 4
|
|
105
|
-
* | yy | 2
|
|
106
|
-
* | MM |
|
|
107
|
-
* | M |
|
|
108
|
-
* | ddd |
|
|
109
|
-
* | dd |
|
|
110
|
-
* | d |
|
|
111
|
-
* | tt |
|
|
112
|
-
* | hh |
|
|
113
|
-
* | h | 12
|
|
114
|
-
* | HH |
|
|
115
|
-
* | H | 24
|
|
116
|
-
* | mm |
|
|
117
|
-
* | m |
|
|
118
|
-
* | ss |
|
|
119
|
-
* | s |
|
|
120
|
-
* | fff |
|
|
121
|
-
* | ff |
|
|
122
|
-
* | f |
|
|
123
|
-
* | zzz |
|
|
124
|
-
* | zz |
|
|
125
|
-
* | z |
|
|
102
|
+
* | 형식 | 설명 | 예시 |
|
|
103
|
+
* |------|------|------|
|
|
104
|
+
* | yyyy | 4자리 연도 | 2024 |
|
|
105
|
+
* | yy | 2자리 연도 | 24 |
|
|
106
|
+
* | MM | 0 채움 월 | 01~12 |
|
|
107
|
+
* | M | 월 | 1~12 |
|
|
108
|
+
* | ddd | 요일 | 일, 월, 화, 수, 목, 금, 토 |
|
|
109
|
+
* | dd | 0 채움 일 | 01~31 |
|
|
110
|
+
* | d | 일 | 1~31 |
|
|
111
|
+
* | tt | 오전/오후 | AM, PM |
|
|
112
|
+
* | hh | 0 채움 12시간 | 01~12 |
|
|
113
|
+
* | h | 12시간 | 1~12 |
|
|
114
|
+
* | HH | 0 채움 24시간 | 00~23 |
|
|
115
|
+
* | H | 24시간 | 0~23 |
|
|
116
|
+
* | mm | 0 채움 분 | 00~59 |
|
|
117
|
+
* | m | 분 | 0~59 |
|
|
118
|
+
* | ss | 0 채움 초 | 00~59 |
|
|
119
|
+
* | s | 초 | 0~59 |
|
|
120
|
+
* | fff | 밀리초 (3자리) | 000~999 |
|
|
121
|
+
* | ff | 밀리초 (2자리) | 00~99 |
|
|
122
|
+
* | f | 밀리초 (1자리) | 0~9 |
|
|
123
|
+
* | zzz | 타임존 오프셋 (±HH:mm) | +09:00 |
|
|
124
|
+
* | zz | 타임존 오프셋 (±HH) | +09 |
|
|
125
|
+
* | z | 타임존 오프셋 (±H) | +9 |
|
|
126
126
|
*
|
|
127
127
|
* @example
|
|
128
128
|
* ```typescript
|
|
@@ -165,33 +165,33 @@ export function format(
|
|
|
165
165
|
|
|
166
166
|
let result = formatString;
|
|
167
167
|
|
|
168
|
-
//
|
|
168
|
+
// 연도
|
|
169
169
|
if (year !== undefined) {
|
|
170
170
|
const yearStr = year.toString();
|
|
171
171
|
result = result.replace(patterns.yyyy, yearStr);
|
|
172
172
|
result = result.replace(patterns.yy, yearStr.substring(2, 4));
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
//
|
|
175
|
+
// 월
|
|
176
176
|
if (month !== undefined) {
|
|
177
177
|
const monthStr = month.toString();
|
|
178
178
|
result = result.replace(patterns.MM, monthStr.padStart(2, "0"));
|
|
179
179
|
result = result.replace(patterns.M, monthStr);
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
//
|
|
182
|
+
// 요일
|
|
183
183
|
if (week !== undefined) {
|
|
184
184
|
result = result.replace(patterns.ddd, weekStrings[week]);
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
//
|
|
187
|
+
// 일
|
|
188
188
|
if (day !== undefined) {
|
|
189
189
|
const dayStr = day.toString();
|
|
190
190
|
result = result.replace(patterns.dd, dayStr.padStart(2, "0"));
|
|
191
191
|
result = result.replace(patterns.d, dayStr);
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
//
|
|
194
|
+
// 시
|
|
195
195
|
if (hour !== undefined) {
|
|
196
196
|
result = result.replace(patterns.tt, hour < 12 ? "AM" : "PM");
|
|
197
197
|
|
|
@@ -205,21 +205,21 @@ export function format(
|
|
|
205
205
|
result = result.replace(patterns.H, hourStr);
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
//
|
|
208
|
+
// 분
|
|
209
209
|
if (minute !== undefined) {
|
|
210
210
|
const minuteStr = minute.toString();
|
|
211
211
|
result = result.replace(patterns.mm, minuteStr.padStart(2, "0"));
|
|
212
212
|
result = result.replace(patterns.m, minuteStr);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
//
|
|
215
|
+
// 초
|
|
216
216
|
if (second !== undefined) {
|
|
217
217
|
const secondStr = second.toString();
|
|
218
218
|
result = result.replace(patterns.ss, secondStr.padStart(2, "0"));
|
|
219
219
|
result = result.replace(patterns.s, secondStr);
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
//
|
|
222
|
+
// 밀리초
|
|
223
223
|
if (millisecond !== undefined) {
|
|
224
224
|
const msStr = millisecond.toString().padStart(3, "0");
|
|
225
225
|
result = result.replace(patterns.fff, msStr);
|
|
@@ -227,7 +227,7 @@ export function format(
|
|
|
227
227
|
result = result.replace(patterns.f, msStr.substring(0, 1));
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
//
|
|
230
|
+
// 타임존
|
|
231
231
|
if (offsetSign !== undefined && offsetHour !== undefined && offsetMinute !== undefined) {
|
|
232
232
|
result = result.replace(
|
|
233
233
|
patterns.zzz,
|
package/src/utils/error.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* unknown 타입 에러에서 메시지를 추출하는 유틸리티.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Error 인스턴스이면 message 속성을 반환하고, 그렇지 않으면 String 변환 결과를 반환.
|
|
5
5
|
*
|
|
6
|
-
* @param err -
|
|
7
|
-
* @returns
|
|
6
|
+
* @param err - catch 블록의 unknown 에러
|
|
7
|
+
* @returns 에러 메시지 문자열
|
|
8
8
|
*/
|
|
9
9
|
export function message(err: unknown): string {
|
|
10
10
|
return err instanceof Error ? err.message : String(err);
|
package/src/utils/json.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* JSON
|
|
3
|
-
*
|
|
2
|
+
* JSON 변환 유틸리티
|
|
3
|
+
* 커스텀 타입(DateTime, DateOnly, Time, Uuid 등)을 지원하는 JSON 직렬화/역직렬화
|
|
4
4
|
*/
|
|
5
5
|
import { DateTime } from "../types/date-time";
|
|
6
6
|
import { DateOnly } from "../types/date-only";
|
|
@@ -19,19 +19,19 @@ interface TypedObject {
|
|
|
19
19
|
//#region stringify
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* 객체를 JSON 문자열로 직렬화
|
|
23
|
+
* DateTime, DateOnly, Time, Uuid, Set, Map, Error, Uint8Array 등 커스텀 타입 지원.
|
|
24
24
|
*
|
|
25
|
-
* @param obj
|
|
26
|
-
* @param options
|
|
27
|
-
* @param options.space JSON
|
|
28
|
-
* @param options.replacer
|
|
29
|
-
* @param options.redactBytes
|
|
25
|
+
* @param obj 직렬화할 객체
|
|
26
|
+
* @param options 직렬화 옵션
|
|
27
|
+
* @param options.space JSON 들여쓰기 (숫자: 공백 수, 문자열: 들여쓰기 문자열)
|
|
28
|
+
* @param options.replacer 커스텀 replacer 함수. 기본 타입 변환 전에 호출됨
|
|
29
|
+
* @param options.redactBytes true이면 Uint8Array 내용을 "__hidden__"으로 대체 (로깅용). 이 옵션으로 직렬화된 결과는 jsonParse()로 원래 Uint8Array를 복원할 수 없음
|
|
30
30
|
*
|
|
31
31
|
* @remarks
|
|
32
|
-
* -
|
|
33
|
-
* -
|
|
34
|
-
* -
|
|
32
|
+
* - 순환 참조가 있는 객체는 TypeError를 발생시킴
|
|
33
|
+
* - 객체에 toJSON 메서드가 있으면 호출하여 결과를 사용 (Date, DateTime 같은 커스텀 타입 제외)
|
|
34
|
+
* - 전역 프로토타입을 수정하지 않으므로 Worker 환경에서 안전
|
|
35
35
|
*/
|
|
36
36
|
export function stringify(
|
|
37
37
|
obj: unknown,
|
|
@@ -41,21 +41,21 @@ export function stringify(
|
|
|
41
41
|
redactBytes?: boolean;
|
|
42
42
|
},
|
|
43
43
|
): string {
|
|
44
|
-
//
|
|
44
|
+
// 순환 참조 감지용 WeakSet
|
|
45
45
|
const seen = new WeakSet<object>();
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
48
|
+
* 객체를 재귀적으로 순회하며 특수 타입을 __type__ 형식으로 변환
|
|
49
49
|
*
|
|
50
|
-
* JSON.stringify
|
|
51
|
-
*
|
|
52
|
-
*
|
|
50
|
+
* JSON.stringify의 replacer는 toJSON 호출 후의 값을 받으므로,
|
|
51
|
+
* Date 같은 타입은 사전에 변환해야 올바르게 처리됨.
|
|
52
|
+
* 전역 프로토타입을 수정하지 않으므로 Worker 환경에서 안전.
|
|
53
53
|
*
|
|
54
|
-
* @param key
|
|
55
|
-
* @param value
|
|
54
|
+
* @param key 현재 값의 key (루트는 undefined)
|
|
55
|
+
* @param value 변환할 값
|
|
56
56
|
*/
|
|
57
57
|
const convertSpecialTypes = (key: string | undefined, value: unknown): unknown => {
|
|
58
|
-
//
|
|
58
|
+
// 커스텀 replacer 적용
|
|
59
59
|
const currValue = options?.replacer !== undefined ? options.replacer(key, value) : value;
|
|
60
60
|
|
|
61
61
|
if (currValue instanceof Date) {
|
|
@@ -108,9 +108,9 @@ export function stringify(
|
|
|
108
108
|
return { __type__: "Uint8Array", data: toHex(currValue) };
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
// Array
|
|
111
|
+
// Array 처리
|
|
112
112
|
if (Array.isArray(currValue)) {
|
|
113
|
-
//
|
|
113
|
+
// 순환 참조 감지
|
|
114
114
|
if (seen.has(currValue)) {
|
|
115
115
|
throw new TypeError("Converting circular structure to JSON");
|
|
116
116
|
}
|
|
@@ -120,15 +120,15 @@ export function stringify(
|
|
|
120
120
|
return result;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
//
|
|
123
|
+
// 일반 객체 처리
|
|
124
124
|
if (currValue !== null && typeof currValue === "object") {
|
|
125
|
-
//
|
|
125
|
+
// 순환 참조 감지
|
|
126
126
|
if (seen.has(currValue)) {
|
|
127
127
|
throw new TypeError("Converting circular structure to JSON");
|
|
128
128
|
}
|
|
129
129
|
seen.add(currValue);
|
|
130
130
|
|
|
131
|
-
//
|
|
131
|
+
// toJSON 메서드가 있으면 호출 (Date, DateTime 같은 커스텀 타입은 위에서 이미 처리됨)
|
|
132
132
|
if (
|
|
133
133
|
"toJSON" in currValue &&
|
|
134
134
|
typeof (currValue as { toJSON: unknown }).toJSON === "function"
|
|
@@ -141,7 +141,7 @@ export function stringify(
|
|
|
141
141
|
const result: Record<string, unknown> = {};
|
|
142
142
|
for (const [k, v] of Object.entries(currValue)) {
|
|
143
143
|
const converted = convertSpecialTypes(k, v);
|
|
144
|
-
// undefined
|
|
144
|
+
// undefined는 JSON에서 제외됨
|
|
145
145
|
if (converted !== undefined) {
|
|
146
146
|
result[k] = converted;
|
|
147
147
|
}
|
|
@@ -153,8 +153,8 @@ export function stringify(
|
|
|
153
153
|
return currValue;
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
-
//
|
|
157
|
-
//
|
|
156
|
+
// 전체 객체를 먼저 변환한 후 JSON.stringify 호출
|
|
157
|
+
// Date.prototype.toJSON을 수정하지 않으므로 동시성 환경에서 안전
|
|
158
158
|
const converted = convertSpecialTypes(undefined, obj);
|
|
159
159
|
return JSON.stringify(converted, null, options?.space);
|
|
160
160
|
}
|
|
@@ -164,25 +164,25 @@ export function stringify(
|
|
|
164
164
|
//#region parse
|
|
165
165
|
|
|
166
166
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
167
|
+
* JSON 문자열을 객체로 역직렬화
|
|
168
|
+
* DateTime, DateOnly, Time, Uuid, Set, Map, Error, Uint8Array 등 커스텀 타입 복원.
|
|
169
169
|
*
|
|
170
170
|
* @remarks
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
171
|
+
* `__type__`과 `data` key를 가진 객체가 타입 복원에 사용됨.
|
|
172
|
+
* 사용자 데이터에 `{ __type__: "Date" | "DateTime" | "DateOnly" | "Time" | "Uuid" | "Set" | "Map" | "Error" | "Uint8Array", data: ... }`
|
|
173
|
+
* 형식의 객체가 포함되어 있으면 의도치 않게 타입으로 변환될 수 있으므로 주의.
|
|
174
|
+
* 모든 JSON null 값은 undefined로 변환됨.
|
|
175
|
+
* 이는 simplysm 프레임워크의 null-free 규칙을 위한 의도적인 동작.
|
|
176
176
|
*
|
|
177
|
-
* @security
|
|
178
|
-
*
|
|
177
|
+
* @security 개발 모드(`__DEV__`)에서는 에러 메시지에 전체 JSON 문자열이 포함됨.
|
|
178
|
+
* 운영 모드에서는 JSON 길이만 포함됨.
|
|
179
179
|
*/
|
|
180
180
|
export function parse<TResult = unknown>(json: string): TResult {
|
|
181
181
|
try {
|
|
182
182
|
return nullToUndefined(
|
|
183
183
|
JSON.parse(json, (_key, value: unknown) => {
|
|
184
184
|
if (value != null && typeof value === "object") {
|
|
185
|
-
//
|
|
185
|
+
// __type__ 마커 기반 타입 복원
|
|
186
186
|
if ("__type__" in value && "data" in value) {
|
|
187
187
|
const typed = value as TypedObject;
|
|
188
188
|
if (typed.__type__ === "Date" && typeof typed.data === "string") {
|
|
@@ -215,7 +215,7 @@ export function parse<TResult = unknown>(json: string): TResult {
|
|
|
215
215
|
if (typed.__type__ === "Uint8Array" && typeof typed.data === "string") {
|
|
216
216
|
if (typed.data === "__hidden__") {
|
|
217
217
|
throw new SdError(
|
|
218
|
-
"
|
|
218
|
+
"redactBytes 옵션으로 직렬화된 Uint8Array는 parse로 복원할 수 없습니다",
|
|
219
219
|
);
|
|
220
220
|
}
|
|
221
221
|
return fromHex(typed.data);
|
|
@@ -228,9 +228,9 @@ export function parse<TResult = unknown>(json: string): TResult {
|
|
|
228
228
|
) as TResult;
|
|
229
229
|
} catch (err) {
|
|
230
230
|
if (env.DEV) {
|
|
231
|
-
throw new SdError(err, "JSON
|
|
231
|
+
throw new SdError(err, "JSON 파싱 오류: \n" + json);
|
|
232
232
|
}
|
|
233
|
-
throw new SdError(err, `JSON
|
|
233
|
+
throw new SdError(err, `JSON 파싱 오류 (길이: ${json.length})`);
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
|
package/src/utils/num.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 숫자 유틸리티 함수
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
//#region parseInt / parseFloat / parseRoundedInt
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* 문자열을 정수로 파싱
|
|
9
|
+
* 파싱 전 비숫자 문자(0-9, -, . 제외) 제거
|
|
10
10
|
*
|
|
11
|
-
* @note
|
|
12
|
-
*
|
|
13
|
-
* @note
|
|
14
|
-
*
|
|
11
|
+
* @note 소수점이 있는 문자열은 정수 부분만 반환 (예: '12.34' → 12).
|
|
12
|
+
* 반올림이 필요하면 {@link parseRoundedInt} 사용.
|
|
13
|
+
* @note 문자열 중간의 하이픈(-)은 유지되므로 의도치 않은 음수가 될 수 있음.
|
|
14
|
+
* 예: `"A-123B"` → `-123`
|
|
15
15
|
*/
|
|
16
16
|
export function parseInt(text: unknown): number | undefined {
|
|
17
17
|
if (typeof text === "number") return Math.trunc(text);
|
|
@@ -24,7 +24,7 @@ export function parseInt(text: unknown): number | undefined {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* 문자열을 float로 파싱한 후 반올림하여 정수 반환
|
|
28
28
|
*/
|
|
29
29
|
export function parseRoundedInt(text: unknown): number | undefined {
|
|
30
30
|
const float = parseFloat(text);
|
|
@@ -32,8 +32,8 @@ export function parseRoundedInt(text: unknown): number | undefined {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
35
|
+
* 문자열을 float로 파싱
|
|
36
|
+
* 파싱 전 비숫자 문자 제거
|
|
37
37
|
*/
|
|
38
38
|
export function parseFloat(text: unknown): number | undefined {
|
|
39
39
|
if (typeof text === "number") return text;
|
|
@@ -50,13 +50,13 @@ export function parseFloat(text: unknown): number | undefined {
|
|
|
50
50
|
//#region isNullOrEmpty
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
53
|
+
* undefined, null, 0 검사 (타입 가드)
|
|
54
54
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
55
|
+
* 타입 가드로 동작하여, true를 반환하면 `val`이 `0 | undefined`임을 보장.
|
|
56
|
+
* false를 반환하면 `val`이 유효한 0이 아닌 숫자임을 보장.
|
|
57
57
|
*
|
|
58
|
-
* @param val
|
|
59
|
-
* @returns
|
|
58
|
+
* @param val 검사할 값
|
|
59
|
+
* @returns undefined, null, 또는 0이면 true
|
|
60
60
|
* @example
|
|
61
61
|
* const count: number | undefined = getValue();
|
|
62
62
|
* if (isNullOrEmpty(count)) {
|
|
@@ -76,11 +76,11 @@ export function isNullOrEmpty(val: number | undefined): val is 0 | undefined {
|
|
|
76
76
|
//#region format
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
80
|
-
* @param val
|
|
81
|
-
* @param digit
|
|
82
|
-
* @param digit.max
|
|
83
|
-
* @param digit.min
|
|
79
|
+
* 숫자를 천 단위 구분자가 포함된 문자열로 포맷
|
|
80
|
+
* @param val 포맷할 숫자
|
|
81
|
+
* @param digit 소수점 옵션
|
|
82
|
+
* @param digit.max 최대 소수점 자릿수
|
|
83
|
+
* @param digit.min 최소 소수점 자릿수 (부족하면 0으로 채움)
|
|
84
84
|
* @example
|
|
85
85
|
* format(1234.567, { max: 2 }) // "1,234.57"
|
|
86
86
|
* format(1234, { min: 2 }) // "1,234.00"
|