@simplysm/core-common 13.0.99 → 14.0.1
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/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 +5 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +12 -8
- 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/package.json +5 -7
- package/src/common.types.ts +14 -14
- package/src/env.ts +9 -1
- 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/README.md +0 -160
- package/docs/errors.md +0 -119
- package/docs/extensions.md +0 -387
- package/docs/features.md +0 -143
- package/docs/types.md +0 -287
- package/docs/utils.md +0 -757
- 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/obj.ts
CHANGED
|
@@ -7,26 +7,26 @@ import { ArgumentError } from "../errors/argument-error";
|
|
|
7
7
|
//#region clone
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
10
|
+
* 깊은 복사
|
|
11
|
+
* - 순환 참조 지원
|
|
12
|
+
* - 커스텀 타입 복사 지원 (DateTime, DateOnly, Time, Uuid, Uint8Array)
|
|
13
13
|
*
|
|
14
|
-
* @note
|
|
15
|
-
* @note WeakMap
|
|
16
|
-
* @note
|
|
17
|
-
* @note
|
|
14
|
+
* @note 함수와 Symbol은 복사되지 않고 참조가 유지됨
|
|
15
|
+
* @note WeakMap과 WeakSet은 지원되지 않음 (일반 객체로 복사되어 빈 객체가 됨)
|
|
16
|
+
* @note 프로토타입 체인이 유지됨 (Object.setPrototypeOf 사용)
|
|
17
|
+
* @note Getter/setter는 현재 값으로 평가되어 복사됨 (접근자 속성 자체는 복사되지 않음)
|
|
18
18
|
*/
|
|
19
19
|
export function clone<TObj>(source: TObj): TObj {
|
|
20
20
|
return cloneImpl(source) as TObj;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function cloneImpl(source: unknown, prevClones?: WeakMap<object, unknown>): unknown {
|
|
24
|
-
//
|
|
24
|
+
// 원시 값은 그대로 반환
|
|
25
25
|
if (typeof source !== "object" || source === null) {
|
|
26
26
|
return source;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
//
|
|
29
|
+
// 불변과 유사한 타입 (내부 객체 참조 없음)
|
|
30
30
|
if (source instanceof Date) {
|
|
31
31
|
return new Date(source.getTime());
|
|
32
32
|
}
|
|
@@ -52,14 +52,14 @@ function cloneImpl(source: unknown, prevClones?: WeakMap<object, unknown>): unkn
|
|
|
52
52
|
return new RegExp(source.source, source.flags);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
//
|
|
55
|
+
// 순환 참조 검사 (Error를 포함한 모든 객체 타입에 적용)
|
|
56
56
|
const currPrevClones = prevClones ?? new WeakMap<object, unknown>();
|
|
57
57
|
if (currPrevClones.has(source)) {
|
|
58
58
|
return currPrevClones.get(source);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
// Error (
|
|
62
|
-
//
|
|
61
|
+
// Error (cause 포함)
|
|
62
|
+
// 생성자 호출 대신 프로토타입 기반 복사 - 커스텀 Error 클래스 호환성 보장
|
|
63
63
|
if (source instanceof Error) {
|
|
64
64
|
const cloned = Object.create(Object.getPrototypeOf(source)) as Error;
|
|
65
65
|
currPrevClones.set(source, cloned);
|
|
@@ -69,7 +69,7 @@ function cloneImpl(source: unknown, prevClones?: WeakMap<object, unknown>): unkn
|
|
|
69
69
|
if (source.cause !== undefined) {
|
|
70
70
|
cloned.cause = cloneImpl(source.cause, currPrevClones);
|
|
71
71
|
}
|
|
72
|
-
//
|
|
72
|
+
// 커스텀 Error 속성 복사
|
|
73
73
|
for (const key of Object.keys(source)) {
|
|
74
74
|
if (!["message", "name", "stack", "cause"].includes(key)) {
|
|
75
75
|
const desc = Object.getOwnPropertyDescriptor(source, key);
|
|
@@ -117,7 +117,7 @@ function cloneImpl(source: unknown, prevClones?: WeakMap<object, unknown>): unkn
|
|
|
117
117
|
return result;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
//
|
|
120
|
+
// 기타 Object 타입
|
|
121
121
|
const result: Record<string, unknown> = {};
|
|
122
122
|
Object.setPrototypeOf(result, Object.getPrototypeOf(source));
|
|
123
123
|
currPrevClones.set(source, result);
|
|
@@ -134,40 +134,40 @@ function cloneImpl(source: unknown, prevClones?: WeakMap<object, unknown>): unkn
|
|
|
134
134
|
|
|
135
135
|
//#region equal
|
|
136
136
|
|
|
137
|
-
/** equal
|
|
137
|
+
/** equal 옵션 타입 */
|
|
138
138
|
export interface EqualOptions {
|
|
139
|
-
/**
|
|
139
|
+
/** 비교할 key 목록. 지정 시 해당 key만 비교 (최상위 레벨에만 적용) */
|
|
140
140
|
topLevelIncludes?: string[];
|
|
141
|
-
/**
|
|
141
|
+
/** 비교에서 제외할 key 목록 (최상위 레벨에만 적용) */
|
|
142
142
|
topLevelExcludes?: string[];
|
|
143
|
-
/**
|
|
143
|
+
/** array 순서를 무시할지 여부. true면 O(n²) 복잡도 */
|
|
144
144
|
ignoreArrayIndex?: boolean;
|
|
145
|
-
/**
|
|
145
|
+
/** 얕은 비교 여부. true면 1단계만 비교 (참조 비교) */
|
|
146
146
|
shallow?: boolean;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
/**
|
|
150
|
-
*
|
|
150
|
+
* 깊은 동등성 비교
|
|
151
151
|
*
|
|
152
|
-
* @param source
|
|
153
|
-
* @param target
|
|
154
|
-
* @param options
|
|
155
|
-
* @param options.topLevelIncludes
|
|
156
|
-
* @example `{ topLevelIncludes: ["id", "name"] }` -
|
|
157
|
-
* @param options.topLevelExcludes
|
|
158
|
-
* @example `{ topLevelExcludes: ["updatedAt"] }` -
|
|
159
|
-
* @param options.ignoreArrayIndex
|
|
160
|
-
* @param options.shallow
|
|
152
|
+
* @param source 비교 대상 1
|
|
153
|
+
* @param target 비교 대상 2
|
|
154
|
+
* @param options 비교 옵션
|
|
155
|
+
* @param options.topLevelIncludes 비교할 key 목록. 지정 시 해당 key만 비교 (최상위 레벨에만 적용)
|
|
156
|
+
* @example `{ topLevelIncludes: ["id", "name"] }` - id와 name key만 비교
|
|
157
|
+
* @param options.topLevelExcludes 비교에서 제외할 key 목록 (최상위 레벨에만 적용)
|
|
158
|
+
* @example `{ topLevelExcludes: ["updatedAt"] }` - updatedAt key를 제외하고 비교
|
|
159
|
+
* @param options.ignoreArrayIndex array 순서를 무시할지 여부. true면 O(n²) 복잡도
|
|
160
|
+
* @param options.shallow 얕은 비교 여부. true면 1단계만 비교 (참조 비교)
|
|
161
161
|
*
|
|
162
|
-
* @note topLevelIncludes/topLevelExcludes
|
|
163
|
-
*
|
|
164
|
-
* @note
|
|
165
|
-
* -
|
|
166
|
-
* -
|
|
167
|
-
* (
|
|
168
|
-
* @note `ignoreArrayIndex: true`
|
|
169
|
-
* -
|
|
170
|
-
* -
|
|
162
|
+
* @note topLevelIncludes/topLevelExcludes 옵션은 객체 속성 key에만 적용됨.
|
|
163
|
+
* Map의 모든 key는 항상 비교에 포함됨.
|
|
164
|
+
* @note 성능 고려사항:
|
|
165
|
+
* - 기본 array 비교: O(n) 시간 복잡도
|
|
166
|
+
* - `ignoreArrayIndex: true` 사용 시: O(n²) 시간 복잡도
|
|
167
|
+
* (대형 array에서 성능 저하 가능)
|
|
168
|
+
* @note `ignoreArrayIndex: true` 동작 특성:
|
|
169
|
+
* - array 순서를 무시하고 요소가 같은 집합의 순열인지 확인
|
|
170
|
+
* - 예: `[1,2,3]`과 `[3,2,1]` → true, `[1,1,1]`과 `[1,2,3]` → false
|
|
171
171
|
*/
|
|
172
172
|
export function equal(source: unknown, target: unknown, options?: EqualOptions): boolean {
|
|
173
173
|
if (source === target) return true;
|
|
@@ -235,7 +235,7 @@ function equalArray(source: unknown[], target: unknown[], options?: EqualOptions
|
|
|
235
235
|
return false;
|
|
236
236
|
});
|
|
237
237
|
} else {
|
|
238
|
-
//
|
|
238
|
+
// 재귀 호출 시 topLevelIncludes/topLevelExcludes 옵션은 최상위 레벨에만 적용되므로 제외
|
|
239
239
|
const recursiveOptions = {
|
|
240
240
|
ignoreArrayIndex: options.ignoreArrayIndex,
|
|
241
241
|
shallow: options.shallow,
|
|
@@ -259,7 +259,7 @@ function equalArray(source: unknown[], target: unknown[], options?: EqualOptions
|
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
} else {
|
|
262
|
-
//
|
|
262
|
+
// 재귀 호출 시 topLevelIncludes/topLevelExcludes 옵션은 최상위 레벨에만 적용되므로 제외
|
|
263
263
|
for (let i = 0; i < source.length; i++) {
|
|
264
264
|
if (
|
|
265
265
|
!equal(source[i], target[i], {
|
|
@@ -277,16 +277,16 @@ function equalArray(source: unknown[], target: unknown[], options?: EqualOptions
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
/**
|
|
280
|
-
* Map
|
|
281
|
-
* @note
|
|
282
|
-
* @note
|
|
280
|
+
* Map 객체 비교
|
|
281
|
+
* @note 비문자열 key(객체, array 등) 처리 시 O(n²) 복잡도
|
|
282
|
+
* @note 대용량 데이터에는 shallow: true 옵션 사용 권장 (참조 비교로 O(n)으로 개선)
|
|
283
283
|
*/
|
|
284
284
|
function equalMap(
|
|
285
285
|
source: Map<unknown, unknown>,
|
|
286
286
|
target: Map<unknown, unknown>,
|
|
287
287
|
options?: EqualOptions,
|
|
288
288
|
): boolean {
|
|
289
|
-
//
|
|
289
|
+
// Map 비교 시 topLevelIncludes/topLevelExcludes 옵션은 무시됨 (객체 속성 key에만 적용)
|
|
290
290
|
const sourceKeys = Array.from(source.keys()).filter((key) => source.get(key) != null);
|
|
291
291
|
const targetKeys = Array.from(target.keys()).filter((key) => target.get(key) != null);
|
|
292
292
|
|
|
@@ -296,7 +296,7 @@ function equalMap(
|
|
|
296
296
|
|
|
297
297
|
const usedTargetKeys = new Set<number>();
|
|
298
298
|
for (const sourceKey of sourceKeys) {
|
|
299
|
-
//
|
|
299
|
+
// 문자열 key: 직접 비교
|
|
300
300
|
if (typeof sourceKey === "string") {
|
|
301
301
|
const sourceValue = source.get(sourceKey);
|
|
302
302
|
const targetValue = target.get(sourceKey);
|
|
@@ -313,7 +313,7 @@ function equalMap(
|
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
} else {
|
|
316
|
-
//
|
|
316
|
+
// 비문자열 key: targetKeys에서 동등한 key 검색
|
|
317
317
|
let found = false;
|
|
318
318
|
for (let i = 0; i < targetKeys.length; i++) {
|
|
319
319
|
const targetKey = targetKeys[i];
|
|
@@ -387,9 +387,9 @@ function equalObject(
|
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
/**
|
|
390
|
-
* Set
|
|
391
|
-
* @note
|
|
392
|
-
*
|
|
390
|
+
* Set 깊은 동등성 비교
|
|
391
|
+
* @note 깊은 비교(`shallow: false`)는 O(n²) 시간 복잡도.
|
|
392
|
+
* 원시 타입 Set이거나 성능이 중요한 경우 `shallow: true` 사용 권장
|
|
393
393
|
*/
|
|
394
394
|
function equalSet(source: Set<unknown>, target: Set<unknown>, options?: EqualOptions): boolean {
|
|
395
395
|
if (source.size !== target.size) {
|
|
@@ -403,8 +403,8 @@ function equalSet(source: Set<unknown>, target: Set<unknown>, options?: EqualOpt
|
|
|
403
403
|
}
|
|
404
404
|
}
|
|
405
405
|
} else {
|
|
406
|
-
//
|
|
407
|
-
//
|
|
406
|
+
// 깊은 비교: 루프 외부에서 target array를 한 번만 생성
|
|
407
|
+
// 중복 매칭 방지를 위해 매칭된 인덱스 추적
|
|
408
408
|
const targetArr = [...target];
|
|
409
409
|
const matchedIndices = new Set<number>();
|
|
410
410
|
for (const sourceItem of source) {
|
|
@@ -425,31 +425,31 @@ function equalSet(source: Set<unknown>, target: Set<unknown>, options?: EqualOpt
|
|
|
425
425
|
|
|
426
426
|
//#region merge
|
|
427
427
|
|
|
428
|
-
/** merge
|
|
428
|
+
/** merge 옵션 타입 */
|
|
429
429
|
export interface MergeOptions {
|
|
430
|
-
/** Array
|
|
430
|
+
/** Array 처리 방식. "replace": target으로 교체 (기본값), "concat": 병합 (중복 제거) */
|
|
431
431
|
arrayProcess?: "replace" | "concat";
|
|
432
|
-
/**
|
|
432
|
+
/** target이 null일 때 해당 key를 삭제할지 여부 */
|
|
433
433
|
useDelTargetNull?: boolean;
|
|
434
434
|
}
|
|
435
435
|
|
|
436
436
|
/**
|
|
437
|
-
*
|
|
437
|
+
* 깊은 병합 (source를 기반으로 target을 병합)
|
|
438
438
|
*
|
|
439
|
-
* @param source
|
|
440
|
-
* @param target
|
|
441
|
-
* @param opt
|
|
442
|
-
* @param opt.arrayProcess Array
|
|
443
|
-
* - `"replace"`:
|
|
444
|
-
* - `"concat"`:
|
|
445
|
-
* @param opt.useDelTargetNull
|
|
446
|
-
* - `true`:
|
|
447
|
-
* - `false`
|
|
439
|
+
* @param source 기본 객체
|
|
440
|
+
* @param target 병합할 객체
|
|
441
|
+
* @param opt 병합 옵션
|
|
442
|
+
* @param opt.arrayProcess Array 처리 방식
|
|
443
|
+
* - `"replace"`: target array로 교체 (기본값)
|
|
444
|
+
* - `"concat"`: source와 target array를 병합 (Set으로 중복 제거)
|
|
445
|
+
* @param opt.useDelTargetNull target 값이 null일 때 해당 key를 삭제할지 여부
|
|
446
|
+
* - `true`: target이 null이면 결과에서 해당 key 삭제
|
|
447
|
+
* - `false` 또는 미지정: source 값 유지
|
|
448
448
|
*
|
|
449
|
-
* @note
|
|
450
|
-
* @note
|
|
451
|
-
*
|
|
452
|
-
* @note
|
|
449
|
+
* @note 원본 객체를 수정하지 않고 새 객체를 반환 (불변성 보장)
|
|
450
|
+
* @note arrayProcess="concat" 사용 시 Set으로 중복 제거되며,
|
|
451
|
+
* 객체 array의 경우 참조(주소) 비교로 중복 여부 판단
|
|
452
|
+
* @note 타입이 다르면 target 값으로 덮어씀
|
|
453
453
|
*/
|
|
454
454
|
export function merge<TSource, TMergeTarget>(
|
|
455
455
|
source: TSource,
|
|
@@ -486,7 +486,7 @@ export function merge<TSource, TMergeTarget>(
|
|
|
486
486
|
return clone(target) as TSource & TMergeTarget;
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
-
//
|
|
489
|
+
// source가 객체가 아니거나 source와 target이 다른 타입의 객체이면 target으로 덮어씀
|
|
490
490
|
if (typeof source !== "object" || source.constructor !== target.constructor) {
|
|
491
491
|
return clone(target) as TSource & TMergeTarget;
|
|
492
492
|
}
|
|
@@ -524,33 +524,33 @@ export function merge<TSource, TMergeTarget>(
|
|
|
524
524
|
return resultRec as TSource & TMergeTarget;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
/** merge3
|
|
527
|
+
/** merge3 옵션 타입 */
|
|
528
528
|
export interface Merge3KeyOptions {
|
|
529
|
-
/**
|
|
529
|
+
/** 비교할 하위 key 목록 (equal의 topLevelIncludes와 동일) */
|
|
530
530
|
keys?: string[];
|
|
531
|
-
/**
|
|
531
|
+
/** 비교에서 제외할 하위 key 목록 */
|
|
532
532
|
excludes?: string[];
|
|
533
|
-
/**
|
|
533
|
+
/** array 순서를 무시할지 여부 */
|
|
534
534
|
ignoreArrayIndex?: boolean;
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
/**
|
|
538
|
-
* 3-way
|
|
538
|
+
* 3-way 병합
|
|
539
539
|
*
|
|
540
|
-
*
|
|
541
|
-
* -
|
|
542
|
-
* -
|
|
543
|
-
* -
|
|
544
|
-
* -
|
|
540
|
+
* source, origin, target 세 객체를 비교하여 병합.
|
|
541
|
+
* - source와 origin이 같고 target이 다르면 → target 값 사용
|
|
542
|
+
* - target과 origin이 같고 source가 다르면 → source 값 사용
|
|
543
|
+
* - source와 target이 같으면 → 해당 값 사용
|
|
544
|
+
* - 세 값이 모두 다르면 → 충돌 발생 (origin 값 유지)
|
|
545
545
|
*
|
|
546
|
-
* @param source
|
|
547
|
-
* @param origin
|
|
548
|
-
* @param target
|
|
549
|
-
* @param optionsObj
|
|
550
|
-
* - `keys`:
|
|
551
|
-
* - `excludes`:
|
|
552
|
-
* - `ignoreArrayIndex`:
|
|
553
|
-
* @returns conflict:
|
|
546
|
+
* @param source 변경된 버전 1
|
|
547
|
+
* @param origin 기본 버전 (공통 조상)
|
|
548
|
+
* @param target 변경된 버전 2
|
|
549
|
+
* @param optionsObj key별 비교 옵션. 각 key에 대해 equal() 비교 옵션을 개별 지정
|
|
550
|
+
* - `keys`: 비교할 하위 key 목록 (equal의 topLevelIncludes와 동일)
|
|
551
|
+
* - `excludes`: 비교에서 제외할 하위 key 목록
|
|
552
|
+
* - `ignoreArrayIndex`: array 순서를 무시할지 여부
|
|
553
|
+
* @returns conflict: 충돌 발생 여부, result: 병합 결과
|
|
554
554
|
*
|
|
555
555
|
* @example
|
|
556
556
|
* const { conflict, result } = merge3(
|
|
@@ -599,10 +599,10 @@ export function merge3<
|
|
|
599
599
|
//#region omit / pick
|
|
600
600
|
|
|
601
601
|
/**
|
|
602
|
-
*
|
|
603
|
-
* @param item
|
|
604
|
-
* @param omitKeys
|
|
605
|
-
* @returns
|
|
602
|
+
* 객체에서 특정 key 제외
|
|
603
|
+
* @param item 원본 객체
|
|
604
|
+
* @param omitKeys 제외할 key 배열
|
|
605
|
+
* @returns 지정된 key가 제외된 새 객체
|
|
606
606
|
* @example
|
|
607
607
|
* const user = { name: "Alice", age: 30, email: "alice@example.com" };
|
|
608
608
|
* omit(user, ["email"]);
|
|
@@ -622,11 +622,11 @@ export function omit<T extends Record<string, unknown>, K extends keyof T>(
|
|
|
622
622
|
}
|
|
623
623
|
|
|
624
624
|
/**
|
|
625
|
-
*
|
|
625
|
+
* 조건에 맞는 key 제외
|
|
626
626
|
* @internal
|
|
627
|
-
* @param item
|
|
628
|
-
* @param omitKeyFn
|
|
629
|
-
* @returns
|
|
627
|
+
* @param item 원본 객체
|
|
628
|
+
* @param omitKeyFn key를 받아 제외 여부를 반환하는 함수 (true면 제외)
|
|
629
|
+
* @returns 조건에 맞는 key가 제외된 새 객체
|
|
630
630
|
* @example
|
|
631
631
|
* const data = { name: "Alice", _internal: "secret", age: 30 };
|
|
632
632
|
* omitByFilter(data, (key) => key.startsWith("_"));
|
|
@@ -646,10 +646,10 @@ export function omitByFilter<T extends Record<string, unknown>>(
|
|
|
646
646
|
}
|
|
647
647
|
|
|
648
648
|
/**
|
|
649
|
-
*
|
|
650
|
-
* @param item
|
|
651
|
-
* @param keys
|
|
652
|
-
* @returns
|
|
649
|
+
* 객체에서 특정 key만 선택
|
|
650
|
+
* @param item 원본 객체
|
|
651
|
+
* @param keys 선택할 key 배열
|
|
652
|
+
* @returns 지정된 key만 포함된 새 객체
|
|
653
653
|
* @example
|
|
654
654
|
* const user = { name: "Alice", age: 30, email: "alice@example.com" };
|
|
655
655
|
* pick(user, ["name", "age"]);
|
|
@@ -670,7 +670,7 @@ export function pick<T extends Record<string, unknown>, K extends keyof T>(
|
|
|
670
670
|
|
|
671
671
|
//#region getChainValue / setChainValue / deleteChainValue
|
|
672
672
|
|
|
673
|
-
//
|
|
673
|
+
// 정규식 캐싱 (모듈 로드 시 한 번만 생성)
|
|
674
674
|
const chainSplitRegex = /[.[\]]/g;
|
|
675
675
|
const chainCleanRegex = /[?!'"]/g;
|
|
676
676
|
const chainNumericRegex = /^[0-9]*$/;
|
|
@@ -693,7 +693,7 @@ function getChainSplits(chain: string): (string | number)[] {
|
|
|
693
693
|
}
|
|
694
694
|
|
|
695
695
|
/**
|
|
696
|
-
*
|
|
696
|
+
* 체인 경로로 값 조회
|
|
697
697
|
* @example getChainValue(obj, "a.b[0].c")
|
|
698
698
|
*/
|
|
699
699
|
export function getChainValue(obj: unknown, chain: string, optional: true): unknown | undefined;
|
|
@@ -716,13 +716,13 @@ export function getChainValue(
|
|
|
716
716
|
}
|
|
717
717
|
|
|
718
718
|
/**
|
|
719
|
-
*
|
|
719
|
+
* 같은 key로 지정된 깊이만큼 하강
|
|
720
720
|
* @internal
|
|
721
|
-
* @param obj
|
|
722
|
-
* @param key
|
|
723
|
-
* @param depth
|
|
724
|
-
* @param optional
|
|
725
|
-
* @throws ArgumentError
|
|
721
|
+
* @param obj 대상 객체
|
|
722
|
+
* @param key 하강에 사용할 key
|
|
723
|
+
* @param depth 하강 깊이 (1 이상)
|
|
724
|
+
* @param optional true이면 중간에 null/undefined를 만나도 에러 없이 undefined 반환
|
|
725
|
+
* @throws ArgumentError depth가 1 미만인 경우
|
|
726
726
|
* @example getChainValueByDepth({ parent: { parent: { name: 'a' } } }, 'parent', 2) => { name: 'a' }
|
|
727
727
|
*/
|
|
728
728
|
export function getChainValueByDepth<TObject, TKey extends keyof TObject>(
|
|
@@ -743,7 +743,7 @@ export function getChainValueByDepth<TObject, TKey extends keyof TObject>(
|
|
|
743
743
|
optional?: true,
|
|
744
744
|
): TObject[TKey] | undefined {
|
|
745
745
|
if (depth < 1) {
|
|
746
|
-
throw new ArgumentError("depth
|
|
746
|
+
throw new ArgumentError("depth는 1 이상이어야 합니다", { depth });
|
|
747
747
|
}
|
|
748
748
|
let result: unknown = obj;
|
|
749
749
|
for (let i = 0; i < depth; i++) {
|
|
@@ -757,13 +757,13 @@ export function getChainValueByDepth<TObject, TKey extends keyof TObject>(
|
|
|
757
757
|
}
|
|
758
758
|
|
|
759
759
|
/**
|
|
760
|
-
*
|
|
760
|
+
* 체인 경로로 값 설정
|
|
761
761
|
* @example setChainValue(obj, "a.b[0].c", value)
|
|
762
762
|
*/
|
|
763
763
|
export function setChainValue(obj: unknown, chain: string, value: unknown): void {
|
|
764
764
|
const splits = getChainSplits(chain);
|
|
765
765
|
if (splits.length === 0) {
|
|
766
|
-
throw new ArgumentError("
|
|
766
|
+
throw new ArgumentError("chain이 비어있습니다", { chain });
|
|
767
767
|
}
|
|
768
768
|
|
|
769
769
|
let curr: Record<string | number, unknown> = obj as Record<string | number, unknown>;
|
|
@@ -777,19 +777,19 @@ export function setChainValue(obj: unknown, chain: string, value: unknown): void
|
|
|
777
777
|
}
|
|
778
778
|
|
|
779
779
|
/**
|
|
780
|
-
*
|
|
780
|
+
* 체인 경로로 값 삭제
|
|
781
781
|
* @example deleteChainValue(obj, "a.b[0].c")
|
|
782
782
|
*/
|
|
783
783
|
export function deleteChainValue(obj: unknown, chain: string): void {
|
|
784
784
|
const splits = getChainSplits(chain);
|
|
785
785
|
if (splits.length === 0) {
|
|
786
|
-
throw new ArgumentError("
|
|
786
|
+
throw new ArgumentError("chain이 비어있습니다", { chain });
|
|
787
787
|
}
|
|
788
788
|
|
|
789
789
|
let curr: Record<string | number, unknown> = obj as Record<string | number, unknown>;
|
|
790
790
|
for (const splitItem of splits.slice(0, -1)) {
|
|
791
791
|
const next = curr[splitItem];
|
|
792
|
-
//
|
|
792
|
+
// 중간 경로가 존재하지 않으면 조용히 반환 (삭제할 것이 없음)
|
|
793
793
|
if (next == null || typeof next !== "object") {
|
|
794
794
|
return;
|
|
795
795
|
}
|
|
@@ -805,10 +805,10 @@ export function deleteChainValue(obj: unknown, chain: string): void {
|
|
|
805
805
|
//#region clearUndefined / clear / nullToUndefined / unflatten
|
|
806
806
|
|
|
807
807
|
/**
|
|
808
|
-
*
|
|
808
|
+
* 객체에서 undefined 값을 가진 key 삭제
|
|
809
809
|
* @internal
|
|
810
810
|
*
|
|
811
|
-
* @mutates
|
|
811
|
+
* @mutates 원본 객체를 직접 수정
|
|
812
812
|
*/
|
|
813
813
|
export function clearUndefined<T extends object>(obj: T): T {
|
|
814
814
|
const record = obj as Record<string, unknown>;
|
|
@@ -821,10 +821,10 @@ export function clearUndefined<T extends object>(obj: T): T {
|
|
|
821
821
|
}
|
|
822
822
|
|
|
823
823
|
/**
|
|
824
|
-
*
|
|
824
|
+
* 객체의 모든 key 삭제
|
|
825
825
|
* @internal
|
|
826
826
|
*
|
|
827
|
-
* @mutates
|
|
827
|
+
* @mutates 원본 객체를 직접 수정
|
|
828
828
|
*/
|
|
829
829
|
export function clear<T extends Record<string, unknown>>(obj: T): Record<string, never> {
|
|
830
830
|
for (const key of Object.keys(obj)) {
|
|
@@ -834,10 +834,10 @@ export function clear<T extends Record<string, unknown>>(obj: T): Record<string,
|
|
|
834
834
|
}
|
|
835
835
|
|
|
836
836
|
/**
|
|
837
|
-
*
|
|
837
|
+
* null을 undefined로 변환 (재귀)
|
|
838
838
|
* @internal
|
|
839
839
|
*
|
|
840
|
-
* @mutates
|
|
840
|
+
* @mutates 원본 array/객체를 직접 수정
|
|
841
841
|
*/
|
|
842
842
|
export function nullToUndefined<TObject>(obj: TObject): TObject | undefined {
|
|
843
843
|
return nullToUndefinedImpl(obj, new WeakSet());
|
|
@@ -882,7 +882,7 @@ function nullToUndefinedImpl<TObject>(obj: TObject, seen: WeakSet<object>): TObj
|
|
|
882
882
|
}
|
|
883
883
|
|
|
884
884
|
/**
|
|
885
|
-
*
|
|
885
|
+
* 평탄화된 객체를 중첩 객체로 변환
|
|
886
886
|
* @internal
|
|
887
887
|
* @example unflatten({ "a.b.c": 1 }) => { a: { b: { c: 1 } } }
|
|
888
888
|
*/
|
|
@@ -912,10 +912,10 @@ export function unflatten(flatObj: Record<string, unknown>): Record<string, unkn
|
|
|
912
912
|
|
|
913
913
|
//#endregion
|
|
914
914
|
|
|
915
|
-
//#region
|
|
915
|
+
//#region 타입 유틸리티
|
|
916
916
|
|
|
917
917
|
/**
|
|
918
|
-
*
|
|
918
|
+
* undefined를 가진 속성을 optional로 변환
|
|
919
919
|
* @example { a: string; b: string | undefined } → { a: string; b?: string | undefined }
|
|
920
920
|
*/
|
|
921
921
|
export type UndefToOptional<TObject> = {
|
|
@@ -923,7 +923,7 @@ export type UndefToOptional<TObject> = {
|
|
|
923
923
|
} & { [K in keyof TObject as undefined extends TObject[K] ? never : K]: TObject[K] };
|
|
924
924
|
|
|
925
925
|
/**
|
|
926
|
-
*
|
|
926
|
+
* optional 속성을 필수 + undefined 유니온으로 변환
|
|
927
927
|
* @example { a: string; b?: string } → { a: string; b: string | undefined }
|
|
928
928
|
*/
|
|
929
929
|
export type OptionalToUndef<TObject> = {
|
|
@@ -933,27 +933,27 @@ export type OptionalToUndef<TObject> = {
|
|
|
933
933
|
//#endregion
|
|
934
934
|
|
|
935
935
|
/**
|
|
936
|
-
*
|
|
937
|
-
* @param obj
|
|
938
|
-
* @returns
|
|
936
|
+
* 타입 안전한 Object.keys
|
|
937
|
+
* @param obj key를 추출할 객체
|
|
938
|
+
* @returns 객체 key 배열
|
|
939
939
|
*/
|
|
940
940
|
export function keys<T extends object>(obj: T): (keyof T)[] {
|
|
941
941
|
return Object.keys(obj) as (keyof T)[];
|
|
942
942
|
}
|
|
943
943
|
|
|
944
944
|
/**
|
|
945
|
-
*
|
|
946
|
-
* @param obj
|
|
947
|
-
* @returns
|
|
945
|
+
* 타입 안전한 Object.entries
|
|
946
|
+
* @param obj 엔트리를 추출할 객체
|
|
947
|
+
* @returns [key, value] 튜플 배열
|
|
948
948
|
*/
|
|
949
949
|
export function entries<T extends object>(obj: T): Entries<T> {
|
|
950
950
|
return Object.entries(obj) as Entries<T>;
|
|
951
951
|
}
|
|
952
952
|
|
|
953
953
|
/**
|
|
954
|
-
*
|
|
955
|
-
* @param entries
|
|
956
|
-
* @returns
|
|
954
|
+
* 타입 안전한 Object.fromEntries
|
|
955
|
+
* @param entries [key, value] 튜플 배열
|
|
956
|
+
* @returns 생성된 객체
|
|
957
957
|
*/
|
|
958
958
|
export function fromEntries<T extends [string, unknown]>(entryPairs: T[]): { [K in T[0]]: T[1] } {
|
|
959
959
|
return Object.fromEntries(entryPairs) as { [K in T[0]]: T[1] };
|
|
@@ -962,18 +962,18 @@ export function fromEntries<T extends [string, unknown]>(entryPairs: T[]): { [K
|
|
|
962
962
|
type Entries<TObject> = { [K in keyof TObject]: [K, TObject[K]] }[keyof TObject][];
|
|
963
963
|
|
|
964
964
|
/**
|
|
965
|
-
*
|
|
966
|
-
* @param obj
|
|
967
|
-
* @param fn
|
|
968
|
-
* @returns
|
|
965
|
+
* 객체의 각 엔트리를 변환하여 새 객체 반환
|
|
966
|
+
* @param obj 변환할 객체
|
|
967
|
+
* @param fn 변환 함수 (key, value) => [newKey, newValue]
|
|
968
|
+
* @returns key와 value가 변환된 새 객체
|
|
969
969
|
* @example
|
|
970
970
|
* const colors = { primary: "255, 0, 0", secondary: "0, 255, 0" };
|
|
971
971
|
*
|
|
972
|
-
* //
|
|
972
|
+
* // 값만 변환
|
|
973
973
|
* map(colors, (key, rgb) => [null, `rgb(${rgb})`]);
|
|
974
974
|
* // { primary: "rgb(255, 0, 0)", secondary: "rgb(0, 255, 0)" }
|
|
975
975
|
*
|
|
976
|
-
* //
|
|
976
|
+
* // key와 값 모두 변환
|
|
977
977
|
* map(colors, (key, rgb) => [`${key}Light`, `rgb(${rgb})`]);
|
|
978
978
|
* // { primaryLight: "rgb(255, 0, 0)", secondaryLight: "rgb(0, 255, 0)" }
|
|
979
979
|
*/
|
package/src/utils/path.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* 경로 유틸리티 함수
|
|
3
|
+
* Node.js path 모듈 대체 (브라우저 환경 지원)
|
|
4
4
|
*
|
|
5
|
-
* @note
|
|
6
|
-
* Windows
|
|
7
|
-
*
|
|
5
|
+
* @note 이 유틸리티는 POSIX 스타일 경로(슬래시 `/`)만 지원.
|
|
6
|
+
* Windows 백슬래시(`\`) 경로는 지원하지 않음.
|
|
7
|
+
* 브라우저 환경 및 Capacitor 플러그인용으로 설계됨.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* @note
|
|
11
|
+
* 경로 결합 (path.join 대체)
|
|
12
|
+
* @note POSIX 스타일 경로(슬래시 `/`)만 지원
|
|
13
13
|
*/
|
|
14
14
|
export function join(...segments: string[]): string {
|
|
15
15
|
return segments
|
|
@@ -19,7 +19,7 @@ export function join(...segments: string[]): string {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* 파일명 추출 (path.basename 대체)
|
|
23
23
|
*/
|
|
24
24
|
export function basename(filePath: string, ext?: string): string {
|
|
25
25
|
const name = filePath.split("/").pop() ?? "";
|
|
@@ -30,8 +30,8 @@ export function basename(filePath: string, ext?: string): string {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
* @note
|
|
33
|
+
* 파일 확장자 추출 (path.extname 대체)
|
|
34
|
+
* @note 숨김 파일(예: `.gitignore`)은 빈 문자열 반환 (Node.js path.extname과 동일)
|
|
35
35
|
*/
|
|
36
36
|
export function extname(filePath: string): string {
|
|
37
37
|
const name = filePath.split("/").pop() ?? "";
|