@simplysm/core-common 13.0.69 → 13.0.71
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 +66 -267
- package/dist/common.types.d.ts +14 -14
- 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 +2 -2
- package/dist/errors/argument-error.js.map +1 -1
- package/dist/errors/not-implemented-error.d.ts +8 -8
- package/dist/errors/not-implemented-error.js +2 -2
- package/dist/errors/not-implemented-error.js.map +1 -1
- package/dist/errors/sd-error.d.ts +10 -10
- package/dist/errors/sd-error.d.ts.map +1 -1
- package/dist/errors/timeout-error.d.ts +10 -10
- package/dist/errors/timeout-error.js +3 -3
- package/dist/errors/timeout-error.js.map +1 -1
- package/dist/extensions/arr-ext.d.ts +2 -2
- package/dist/extensions/arr-ext.helpers.d.ts +8 -8
- package/dist/extensions/arr-ext.helpers.js +1 -1
- package/dist/extensions/arr-ext.helpers.js.map +1 -1
- package/dist/extensions/arr-ext.js +13 -13
- package/dist/extensions/arr-ext.js.map +1 -1
- package/dist/extensions/arr-ext.types.d.ts +57 -57
- package/dist/extensions/arr-ext.types.d.ts.map +1 -1
- package/dist/extensions/map-ext.d.ts +16 -16
- package/dist/extensions/set-ext.d.ts +11 -11
- package/dist/features/debounce-queue.d.ts +17 -15
- package/dist/features/debounce-queue.d.ts.map +1 -1
- package/dist/features/debounce-queue.js +6 -6
- package/dist/features/debounce-queue.js.map +1 -1
- package/dist/features/event-emitter.d.ts +20 -20
- package/dist/features/event-emitter.js +17 -17
- package/dist/features/serial-queue.d.ts +11 -11
- package/dist/features/serial-queue.js +5 -5
- package/dist/features/serial-queue.js.map +1 -1
- package/dist/globals.d.ts +4 -4
- 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 +63 -63
- package/dist/types/date-time.d.ts +37 -37
- package/dist/types/date-time.d.ts.map +1 -1
- package/dist/types/date-time.js +54 -37
- package/dist/types/date-time.js.map +1 -1
- 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 +26 -26
- package/dist/types/lazy-gc-map.js.map +1 -1
- package/dist/types/time.d.ts +25 -25
- package/dist/types/time.d.ts.map +1 -1
- package/dist/types/time.js +25 -25
- package/dist/types/time.js.map +1 -1
- package/dist/types/uuid.d.ts +11 -11
- package/dist/types/uuid.d.ts.map +1 -1
- package/dist/types/uuid.js +12 -12
- package/dist/types/uuid.js.map +1 -1
- package/dist/utils/bytes.d.ts +17 -17
- package/dist/utils/bytes.js +4 -4
- package/dist/utils/bytes.js.map +1 -1
- package/dist/utils/date-format.d.ts +45 -45
- package/dist/utils/date-format.js +1 -1
- package/dist/utils/date-format.js.map +1 -1
- package/dist/utils/error.d.ts +4 -4
- package/dist/utils/json.d.ts +17 -17
- package/dist/utils/json.js +3 -3
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/num.d.ts +23 -23
- package/dist/utils/obj.d.ts +111 -111
- package/dist/utils/obj.d.ts.map +1 -1
- package/dist/utils/obj.js +3 -3
- package/dist/utils/obj.js.map +1 -1
- package/dist/utils/path.d.ts +10 -10
- package/dist/utils/primitive.d.ts +5 -5
- package/dist/utils/primitive.js +1 -1
- package/dist/utils/primitive.js.map +1 -1
- package/dist/utils/str.d.ts +46 -46
- package/dist/utils/str.d.ts.map +1 -1
- package/dist/utils/str.js +5 -5
- package/dist/utils/str.js.map +1 -1
- package/dist/utils/template-strings.d.ts +26 -26
- package/dist/utils/transferable.d.ts +18 -18
- package/dist/utils/transferable.js +1 -1
- package/dist/utils/transferable.js.map +1 -1
- package/dist/utils/wait.d.ts +9 -9
- package/dist/utils/xml.d.ts +13 -13
- package/dist/utils/xml.d.ts.map +1 -1
- package/dist/utils/xml.js +1 -0
- package/dist/utils/xml.js.map +1 -1
- package/dist/zip/sd-zip.d.ts +22 -22
- package/dist/zip/sd-zip.js +16 -16
- package/package.json +4 -4
- package/src/common.types.ts +17 -17
- 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 +10 -10
- package/src/extensions/arr-ext.ts +57 -57
- package/src/extensions/arr-ext.types.ts +59 -59
- package/src/extensions/map-ext.ts +16 -16
- package/src/extensions/set-ext.ts +11 -11
- package/src/features/debounce-queue.ts +21 -19
- 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 +1 -1
- package/src/types/date-only.ts +83 -83
- package/src/types/date-time.ts +64 -44
- package/src/types/lazy-gc-map.ts +45 -45
- package/src/types/time.ts +34 -34
- package/src/types/uuid.ts +17 -17
- package/src/utils/bytes.ts +35 -35
- package/src/utils/date-format.ts +65 -65
- package/src/utils/error.ts +4 -4
- package/src/utils/json.ts +39 -39
- package/src/utils/num.ts +23 -23
- 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 +260 -261
- package/src/utils/template-strings.ts +29 -29
- package/src/utils/transferable.ts +284 -284
- package/src/utils/wait.ts +10 -10
- package/src/utils/xml.ts +20 -19
- package/src/zip/sd-zip.ts +25 -25
- package/tests/errors/errors.spec.ts +80 -0
- package/tests/extensions/array-extension.spec.ts +796 -0
- package/tests/extensions/map-extension.spec.ts +147 -0
- package/tests/extensions/set-extension.spec.ts +74 -0
- package/tests/types/date-only.spec.ts +638 -0
- package/tests/types/date-time.spec.ts +391 -0
- package/tests/types/lazy-gc-map.spec.ts +692 -0
- package/tests/types/time.spec.ts +559 -0
- package/tests/types/uuid.spec.ts +74 -0
- package/tests/utils/bytes-utils.spec.ts +230 -0
- package/tests/utils/date-format.spec.ts +373 -0
- package/tests/utils/debounce-queue.spec.ts +272 -0
- package/tests/utils/json.spec.ts +486 -0
- package/tests/utils/number.spec.ts +157 -0
- package/tests/utils/object.spec.ts +829 -0
- package/tests/utils/path.spec.ts +78 -0
- package/tests/utils/primitive.spec.ts +43 -0
- package/tests/utils/sd-event-emitter.spec.ts +216 -0
- package/tests/utils/serial-queue.spec.ts +365 -0
- package/tests/utils/string.spec.ts +281 -0
- package/tests/utils/template-strings.spec.ts +57 -0
- package/tests/utils/transferable.spec.ts +703 -0
- package/tests/utils/wait.spec.ts +145 -0
- package/tests/utils/xml.spec.ts +146 -0
- package/tests/zip/sd-zip.spec.ts +238 -0
- package/docs/extensions.md +0 -503
- package/docs/features.md +0 -109
- package/docs/types.md +0 -486
- package/docs/utils.md +0 -780
package/src/utils/obj.ts
CHANGED
|
@@ -7,26 +7,26 @@ import { ArgumentError } from "../errors/argument-error";
|
|
|
7
7
|
//#region objClone
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
10
|
+
* Deep clone
|
|
11
|
+
* - Supports circular references
|
|
12
|
+
* - Supports copying custom types (DateTime, DateOnly, Time, Uuid, Uint8Array)
|
|
13
13
|
*
|
|
14
|
-
* @note
|
|
15
|
-
* @note WeakMap
|
|
16
|
-
* @note
|
|
17
|
-
* @note
|
|
14
|
+
* @note Functions and Symbols are not cloned and references are maintained
|
|
15
|
+
* @note WeakMap and WeakSet are not supported (copied as generic objects, resulting in empty objects)
|
|
16
|
+
* @note Prototype chain is maintained (using Object.setPrototypeOf)
|
|
17
|
+
* @note Getters/setters are evaluated as current values and copied (accessor properties themselves are not copied)
|
|
18
18
|
*/
|
|
19
19
|
export function objClone<TSource>(source: TSource): TSource {
|
|
20
20
|
return objCloneImpl(source) as TSource;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function objCloneImpl(source: unknown, prevClones?: WeakMap<object, unknown>): unknown {
|
|
24
|
-
//
|
|
24
|
+
// Primitives are returned as-is
|
|
25
25
|
if (typeof source !== "object" || source === null) {
|
|
26
26
|
return source;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
// Immutable-like
|
|
29
|
+
// Immutable-like types (no internal object references)
|
|
30
30
|
if (source instanceof Date) {
|
|
31
31
|
return new Date(source.getTime());
|
|
32
32
|
}
|
|
@@ -52,14 +52,14 @@ function objCloneImpl(source: unknown, prevClones?: WeakMap<object, unknown>): u
|
|
|
52
52
|
return new RegExp(source.source, source.flags);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
//
|
|
55
|
+
// Circular reference check (applies to all object types including 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 (cause
|
|
62
|
-
//
|
|
61
|
+
// Error (including cause)
|
|
62
|
+
// Prototype-based copying instead of constructor call - ensures custom Error class compatibility
|
|
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 objCloneImpl(source: unknown, prevClones?: WeakMap<object, unknown>): u
|
|
|
69
69
|
if (source.cause !== undefined) {
|
|
70
70
|
cloned.cause = objCloneImpl(source.cause, currPrevClones);
|
|
71
71
|
}
|
|
72
|
-
//
|
|
72
|
+
// Copy custom Error properties
|
|
73
73
|
for (const key of Object.keys(source)) {
|
|
74
74
|
if (!["message", "name", "stack", "cause"].includes(key)) {
|
|
75
75
|
(cloned as unknown as Record<string, unknown>)[key] = objCloneImpl(
|
|
@@ -114,7 +114,7 @@ function objCloneImpl(source: unknown, prevClones?: WeakMap<object, unknown>): u
|
|
|
114
114
|
return result;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
//
|
|
117
|
+
// Other Object types
|
|
118
118
|
const result: Record<string, unknown> = {};
|
|
119
119
|
Object.setPrototypeOf(result, Object.getPrototypeOf(source));
|
|
120
120
|
currPrevClones.set(source, result);
|
|
@@ -131,40 +131,40 @@ function objCloneImpl(source: unknown, prevClones?: WeakMap<object, unknown>): u
|
|
|
131
131
|
|
|
132
132
|
//#region objEqual
|
|
133
133
|
|
|
134
|
-
/** objEqual
|
|
134
|
+
/** objEqual options type */
|
|
135
135
|
export interface EqualOptions {
|
|
136
|
-
/**
|
|
136
|
+
/** List of keys to compare. When specified, only those keys are compared (applies only to top level) */
|
|
137
137
|
topLevelIncludes?: string[];
|
|
138
|
-
/**
|
|
138
|
+
/** List of keys to exclude from comparison (applies only to top level) */
|
|
139
139
|
topLevelExcludes?: string[];
|
|
140
|
-
/**
|
|
140
|
+
/** Whether to ignore array order. O(n²) complexity when true */
|
|
141
141
|
ignoreArrayIndex?: boolean;
|
|
142
|
-
/**
|
|
142
|
+
/** Whether to do shallow comparison. Only compare 1 level (reference comparison) when true */
|
|
143
143
|
onlyOneDepth?: boolean;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
|
-
*
|
|
147
|
+
* Deep equality comparison
|
|
148
148
|
*
|
|
149
|
-
* @param source
|
|
150
|
-
* @param target
|
|
151
|
-
* @param options
|
|
152
|
-
* @param options.topLevelIncludes
|
|
153
|
-
* @example `{ topLevelIncludes: ["id", "name"] }` - id
|
|
154
|
-
* @param options.topLevelExcludes
|
|
155
|
-
* @example `{ topLevelExcludes: ["updatedAt"] }` -
|
|
156
|
-
* @param options.ignoreArrayIndex
|
|
157
|
-
* @param options.onlyOneDepth
|
|
149
|
+
* @param source Comparison target 1
|
|
150
|
+
* @param target Comparison target 2
|
|
151
|
+
* @param options Comparison options
|
|
152
|
+
* @param options.topLevelIncludes List of keys to compare. When specified, only those keys are compared (applies only to top level)
|
|
153
|
+
* @example `{ topLevelIncludes: ["id", "name"] }` - Compare only id and name keys
|
|
154
|
+
* @param options.topLevelExcludes List of keys to exclude from comparison (applies only to top level)
|
|
155
|
+
* @example `{ topLevelExcludes: ["updatedAt"] }` - Compare excluding updatedAt key
|
|
156
|
+
* @param options.ignoreArrayIndex Whether to ignore array order. O(n²) complexity when true
|
|
157
|
+
* @param options.onlyOneDepth Whether to do shallow comparison. Only compare 1 level (reference comparison) when true
|
|
158
158
|
*
|
|
159
|
-
* @note topLevelIncludes/topLevelExcludes
|
|
160
|
-
* Map
|
|
161
|
-
* @note
|
|
162
|
-
* -
|
|
163
|
-
* - `ignoreArrayIndex: true
|
|
164
|
-
* (
|
|
165
|
-
* @note `ignoreArrayIndex: true`
|
|
166
|
-
* -
|
|
167
|
-
* -
|
|
159
|
+
* @note topLevelIncludes/topLevelExcludes options apply only to object property keys.
|
|
160
|
+
* All keys in Map are always included in comparison.
|
|
161
|
+
* @note Performance considerations:
|
|
162
|
+
* - Basic array comparison: O(n) time complexity
|
|
163
|
+
* - When using `ignoreArrayIndex: true`: O(n²) time complexity
|
|
164
|
+
* (possible performance degradation on large arrays)
|
|
165
|
+
* @note `ignoreArrayIndex: true` behavior characteristics:
|
|
166
|
+
* - Ignore array order and check if elements are permutations of the same set
|
|
167
|
+
* - Example: `[1,2,3]` and `[3,2,1]` → true, `[1,1,1]` and `[1,2,3]` → false
|
|
168
168
|
*/
|
|
169
169
|
export function objEqual(source: unknown, target: unknown, options?: EqualOptions): boolean {
|
|
170
170
|
if (source === target) return true;
|
|
@@ -232,7 +232,7 @@ function objEqualArray(source: unknown[], target: unknown[], options?: EqualOpti
|
|
|
232
232
|
return false;
|
|
233
233
|
});
|
|
234
234
|
} else {
|
|
235
|
-
//
|
|
235
|
+
// On recursive calls, topLevelIncludes/topLevelExcludes options apply only to top level, so exclude them
|
|
236
236
|
const recursiveOptions = {
|
|
237
237
|
ignoreArrayIndex: options.ignoreArrayIndex,
|
|
238
238
|
onlyOneDepth: options.onlyOneDepth,
|
|
@@ -256,7 +256,7 @@ function objEqualArray(source: unknown[], target: unknown[], options?: EqualOpti
|
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
} else {
|
|
259
|
-
//
|
|
259
|
+
// On recursive calls, topLevelIncludes/topLevelExcludes options apply only to top level, so exclude them
|
|
260
260
|
for (let i = 0; i < source.length; i++) {
|
|
261
261
|
if (
|
|
262
262
|
!objEqual(source[i], target[i], {
|
|
@@ -274,16 +274,16 @@ function objEqualArray(source: unknown[], target: unknown[], options?: EqualOpti
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
/**
|
|
277
|
-
* Map
|
|
278
|
-
* @note
|
|
279
|
-
* @note
|
|
277
|
+
* Map object comparison
|
|
278
|
+
* @note O(n²) complexity when handling non-string keys (objects, arrays, etc.)
|
|
279
|
+
* @note Recommended to use onlyOneDepth: true option for large datasets (improves to O(n) with reference comparison)
|
|
280
280
|
*/
|
|
281
281
|
function objEqualMap(
|
|
282
282
|
source: Map<unknown, unknown>,
|
|
283
283
|
target: Map<unknown, unknown>,
|
|
284
284
|
options?: EqualOptions,
|
|
285
285
|
): boolean {
|
|
286
|
-
//
|
|
286
|
+
// When comparing Maps, topLevelIncludes/topLevelExcludes options are ignored (apply only to object property keys)
|
|
287
287
|
const sourceKeys = Array.from(source.keys()).filter((key) => source.get(key) != null);
|
|
288
288
|
const targetKeys = Array.from(target.keys()).filter((key) => target.get(key) != null);
|
|
289
289
|
|
|
@@ -293,7 +293,7 @@ function objEqualMap(
|
|
|
293
293
|
|
|
294
294
|
const usedTargetKeys = new Set<number>();
|
|
295
295
|
for (const sourceKey of sourceKeys) {
|
|
296
|
-
//
|
|
296
|
+
// String keys: compare directly
|
|
297
297
|
if (typeof sourceKey === "string") {
|
|
298
298
|
const sourceValue = source.get(sourceKey);
|
|
299
299
|
const targetValue = target.get(sourceKey);
|
|
@@ -310,7 +310,7 @@ function objEqualMap(
|
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
} else {
|
|
313
|
-
//
|
|
313
|
+
// Non-string keys: find equivalent key in targetKeys
|
|
314
314
|
let found = false;
|
|
315
315
|
for (let i = 0; i < targetKeys.length; i++) {
|
|
316
316
|
const targetKey = targetKeys[i];
|
|
@@ -384,9 +384,9 @@ function objEqualObject(
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
/**
|
|
387
|
-
* Set
|
|
388
|
-
* @note
|
|
389
|
-
*
|
|
387
|
+
* Set deep equality comparison
|
|
388
|
+
* @note Deep equal comparison (`onlyOneDepth: false`) has O(n²) time complexity.
|
|
389
|
+
* Recommended to use `onlyOneDepth: true` for primitive Sets or when performance is critical
|
|
390
390
|
*/
|
|
391
391
|
function objEqualSet(source: Set<unknown>, target: Set<unknown>, options?: EqualOptions): boolean {
|
|
392
392
|
if (source.size !== target.size) {
|
|
@@ -400,8 +400,8 @@ function objEqualSet(source: Set<unknown>, target: Set<unknown>, options?: Equal
|
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
402
|
} else {
|
|
403
|
-
//
|
|
404
|
-
//
|
|
403
|
+
// Deep equal: create target array only once outside loop
|
|
404
|
+
// Track matched indices to prevent duplicate matching
|
|
405
405
|
const targetArr = [...target];
|
|
406
406
|
const matchedIndices = new Set<number>();
|
|
407
407
|
for (const sourceItem of source) {
|
|
@@ -422,31 +422,31 @@ function objEqualSet(source: Set<unknown>, target: Set<unknown>, options?: Equal
|
|
|
422
422
|
|
|
423
423
|
//#region objMerge
|
|
424
424
|
|
|
425
|
-
/** objMerge
|
|
425
|
+
/** objMerge options type */
|
|
426
426
|
export interface ObjMergeOptions {
|
|
427
|
-
/**
|
|
427
|
+
/** Array processing method. "replace": replace with target (default), "concat": merge (deduplicate) */
|
|
428
428
|
arrayProcess?: "replace" | "concat";
|
|
429
|
-
/**
|
|
429
|
+
/** Whether to delete the key when target is null */
|
|
430
430
|
useDelTargetNull?: boolean;
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
/**
|
|
434
|
-
*
|
|
434
|
+
* Deep merge (merge target into source as base)
|
|
435
435
|
*
|
|
436
|
-
* @param source
|
|
437
|
-
* @param target
|
|
438
|
-
* @param opt
|
|
439
|
-
* @param opt.arrayProcess
|
|
440
|
-
* - `"replace"`: target
|
|
441
|
-
* - `"concat"`: source
|
|
442
|
-
* @param opt.useDelTargetNull
|
|
443
|
-
* - `true`:
|
|
444
|
-
* - `false`
|
|
436
|
+
* @param source Base object
|
|
437
|
+
* @param target Object to merge
|
|
438
|
+
* @param opt Merge options
|
|
439
|
+
* @param opt.arrayProcess Array processing method
|
|
440
|
+
* - `"replace"`: Replace with target array (default)
|
|
441
|
+
* - `"concat"`: Merge source and target arrays (deduplicate with Set)
|
|
442
|
+
* @param opt.useDelTargetNull Whether to delete the key when target value is null
|
|
443
|
+
* - `true`: Delete the key from result if target is null
|
|
444
|
+
* - `false` or not specified: Keep source value
|
|
445
445
|
*
|
|
446
|
-
* @note
|
|
447
|
-
* @note arrayProcess="concat"
|
|
448
|
-
*
|
|
449
|
-
* @note
|
|
446
|
+
* @note Returns a new object without modifying original objects (guarantees immutability)
|
|
447
|
+
* @note When using arrayProcess="concat", deduplication is done with Set,
|
|
448
|
+
* and for object arrays, deduplication is determined by reference (address) comparison
|
|
449
|
+
* @note If types are different, overwrite with target value
|
|
450
450
|
*/
|
|
451
451
|
export function objMerge<TSource, TMergeTarget>(
|
|
452
452
|
source: TSource,
|
|
@@ -483,7 +483,7 @@ export function objMerge<TSource, TMergeTarget>(
|
|
|
483
483
|
return objClone(target) as TSource & TMergeTarget;
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
-
// source
|
|
486
|
+
// If source is not an object or source and target are different types of objects, overwrite with target
|
|
487
487
|
if (typeof source !== "object" || source.constructor !== target.constructor) {
|
|
488
488
|
return objClone(target) as TSource & TMergeTarget;
|
|
489
489
|
}
|
|
@@ -521,33 +521,33 @@ export function objMerge<TSource, TMergeTarget>(
|
|
|
521
521
|
return resultRec as TSource & TMergeTarget;
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
-
/** merge3
|
|
524
|
+
/** merge3 options type */
|
|
525
525
|
export interface ObjMerge3KeyOptions {
|
|
526
|
-
/**
|
|
526
|
+
/** List of sub-keys to compare (same as equal's topLevelIncludes) */
|
|
527
527
|
keys?: string[];
|
|
528
|
-
/**
|
|
528
|
+
/** List of sub-keys to exclude from comparison */
|
|
529
529
|
excludes?: string[];
|
|
530
|
-
/**
|
|
530
|
+
/** Whether to ignore array order */
|
|
531
531
|
ignoreArrayIndex?: boolean;
|
|
532
532
|
}
|
|
533
533
|
|
|
534
534
|
/**
|
|
535
|
-
* 3-way
|
|
535
|
+
* 3-way merge
|
|
536
536
|
*
|
|
537
|
-
* source, origin, target
|
|
538
|
-
* - source
|
|
539
|
-
* - target
|
|
540
|
-
* - source
|
|
541
|
-
* -
|
|
537
|
+
* Merge by comparing three objects: source, origin, and target.
|
|
538
|
+
* - If source and origin are equal and target differs → use target value
|
|
539
|
+
* - If target and origin are equal and source differs → use source value
|
|
540
|
+
* - If source and target are equal → use that value
|
|
541
|
+
* - If all three values differ → conflict occurs (maintain origin value)
|
|
542
542
|
*
|
|
543
|
-
* @param source
|
|
544
|
-
* @param origin
|
|
545
|
-
* @param target
|
|
546
|
-
* @param optionsObj
|
|
547
|
-
* - `keys`:
|
|
548
|
-
* - `excludes`:
|
|
549
|
-
* - `ignoreArrayIndex`:
|
|
550
|
-
* @returns conflict:
|
|
543
|
+
* @param source Changed version 1
|
|
544
|
+
* @param origin Base version (common ancestor)
|
|
545
|
+
* @param target Changed version 2
|
|
546
|
+
* @param optionsObj Comparison options per key. Specify equal() comparison options individually for each key
|
|
547
|
+
* - `keys`: List of sub-keys to compare (same as equal's topLevelIncludes)
|
|
548
|
+
* - `excludes`: List of sub-keys to exclude from comparison
|
|
549
|
+
* - `ignoreArrayIndex`: Whether to ignore array order
|
|
550
|
+
* @returns conflict: Whether conflict occurred, result: Merge result
|
|
551
551
|
*
|
|
552
552
|
* @example
|
|
553
553
|
* const { conflict, result } = merge3(
|
|
@@ -596,10 +596,10 @@ export function objMerge3<
|
|
|
596
596
|
//#region objOmit / objPick
|
|
597
597
|
|
|
598
598
|
/**
|
|
599
|
-
*
|
|
600
|
-
* @param item
|
|
601
|
-
* @param omitKeys
|
|
602
|
-
* @returns
|
|
599
|
+
* Exclude specific keys from object
|
|
600
|
+
* @param item Source object
|
|
601
|
+
* @param omitKeys Array of keys to exclude
|
|
602
|
+
* @returns New object with specified keys excluded
|
|
603
603
|
* @example
|
|
604
604
|
* const user = { name: "Alice", age: 30, email: "alice@example.com" };
|
|
605
605
|
* objOmit(user, ["email"]);
|
|
@@ -619,11 +619,11 @@ export function objOmit<T extends Record<string, unknown>, K extends keyof T>(
|
|
|
619
619
|
}
|
|
620
620
|
|
|
621
621
|
/**
|
|
622
|
-
*
|
|
622
|
+
* Exclude keys matching condition
|
|
623
623
|
* @internal
|
|
624
|
-
* @param item
|
|
625
|
-
* @param omitKeyFn
|
|
626
|
-
* @returns
|
|
624
|
+
* @param item Source object
|
|
625
|
+
* @param omitKeyFn Function that receives key and returns whether to exclude (true to exclude)
|
|
626
|
+
* @returns New object with keys matching condition excluded
|
|
627
627
|
* @example
|
|
628
628
|
* const data = { name: "Alice", _internal: "secret", age: 30 };
|
|
629
629
|
* objOmitByFilter(data, (key) => key.startsWith("_"));
|
|
@@ -643,10 +643,10 @@ export function objOmitByFilter<T extends Record<string, unknown>>(
|
|
|
643
643
|
}
|
|
644
644
|
|
|
645
645
|
/**
|
|
646
|
-
*
|
|
647
|
-
* @param item
|
|
648
|
-
* @param keys
|
|
649
|
-
* @returns
|
|
646
|
+
* Select specific keys from object
|
|
647
|
+
* @param item Source object
|
|
648
|
+
* @param keys Array of keys to select
|
|
649
|
+
* @returns New object containing only specified keys
|
|
650
650
|
* @example
|
|
651
651
|
* const user = { name: "Alice", age: 30, email: "alice@example.com" };
|
|
652
652
|
* objPick(user, ["name", "age"]);
|
|
@@ -667,7 +667,7 @@ export function objPick<T extends Record<string, unknown>, K extends keyof T>(
|
|
|
667
667
|
|
|
668
668
|
//#region objGetChainValue / objSetChainValue / objDeleteChainValue
|
|
669
669
|
|
|
670
|
-
//
|
|
670
|
+
// Regex caching (created once at module load)
|
|
671
671
|
const chainSplitRegex = /[.[\]]/g;
|
|
672
672
|
const chainCleanRegex = /[?!'"]/g;
|
|
673
673
|
const chainNumericRegex = /^[0-9]*$/;
|
|
@@ -690,7 +690,7 @@ function getChainSplits(chain: string): (string | number)[] {
|
|
|
690
690
|
}
|
|
691
691
|
|
|
692
692
|
/**
|
|
693
|
-
*
|
|
693
|
+
* Get value by chain path
|
|
694
694
|
* @example objGetChainValue(obj, "a.b[0].c")
|
|
695
695
|
*/
|
|
696
696
|
export function objGetChainValue(obj: unknown, chain: string, optional: true): unknown | undefined;
|
|
@@ -713,13 +713,13 @@ export function objGetChainValue(
|
|
|
713
713
|
}
|
|
714
714
|
|
|
715
715
|
/**
|
|
716
|
-
*
|
|
716
|
+
* Descend by the same key for depth levels
|
|
717
717
|
* @internal
|
|
718
|
-
* @param obj
|
|
719
|
-
* @param key
|
|
720
|
-
* @param depth
|
|
721
|
-
* @param optional true
|
|
722
|
-
* @throws ArgumentError depth
|
|
718
|
+
* @param obj Target object
|
|
719
|
+
* @param key Key to descend by
|
|
720
|
+
* @param depth Depth to descend (1 or more)
|
|
721
|
+
* @param optional If true, return undefined without error if null/undefined found in the middle
|
|
722
|
+
* @throws ArgumentError If depth is less than 1
|
|
723
723
|
* @example objGetChainValueByDepth({ parent: { parent: { name: 'a' } } }, 'parent', 2) => { name: 'a' }
|
|
724
724
|
*/
|
|
725
725
|
export function objGetChainValueByDepth<TObject, TKey extends keyof TObject>(
|
|
@@ -740,7 +740,7 @@ export function objGetChainValueByDepth<TObject, TKey extends keyof TObject>(
|
|
|
740
740
|
optional?: true,
|
|
741
741
|
): TObject[TKey] | undefined {
|
|
742
742
|
if (depth < 1) {
|
|
743
|
-
throw new ArgumentError("depth
|
|
743
|
+
throw new ArgumentError("depth must be 1 or greater", { depth });
|
|
744
744
|
}
|
|
745
745
|
let result: unknown = obj;
|
|
746
746
|
for (let i = 0; i < depth; i++) {
|
|
@@ -754,13 +754,13 @@ export function objGetChainValueByDepth<TObject, TKey extends keyof TObject>(
|
|
|
754
754
|
}
|
|
755
755
|
|
|
756
756
|
/**
|
|
757
|
-
*
|
|
757
|
+
* Set value by chain path
|
|
758
758
|
* @example objSetChainValue(obj, "a.b[0].c", value)
|
|
759
759
|
*/
|
|
760
760
|
export function objSetChainValue(obj: unknown, chain: string, value: unknown): void {
|
|
761
761
|
const splits = getChainSplits(chain);
|
|
762
762
|
if (splits.length === 0) {
|
|
763
|
-
throw new ArgumentError("
|
|
763
|
+
throw new ArgumentError("Chain is empty", { chain });
|
|
764
764
|
}
|
|
765
765
|
|
|
766
766
|
let curr: Record<string | number, unknown> = obj as Record<string | number, unknown>;
|
|
@@ -774,19 +774,19 @@ export function objSetChainValue(obj: unknown, chain: string, value: unknown): v
|
|
|
774
774
|
}
|
|
775
775
|
|
|
776
776
|
/**
|
|
777
|
-
*
|
|
777
|
+
* Delete value by chain path
|
|
778
778
|
* @example objDeleteChainValue(obj, "a.b[0].c")
|
|
779
779
|
*/
|
|
780
780
|
export function objDeleteChainValue(obj: unknown, chain: string): void {
|
|
781
781
|
const splits = getChainSplits(chain);
|
|
782
782
|
if (splits.length === 0) {
|
|
783
|
-
throw new ArgumentError("
|
|
783
|
+
throw new ArgumentError("Chain is empty", { chain });
|
|
784
784
|
}
|
|
785
785
|
|
|
786
786
|
let curr: Record<string | number, unknown> = obj as Record<string | number, unknown>;
|
|
787
787
|
for (const splitItem of splits.slice(0, -1)) {
|
|
788
788
|
const next = curr[splitItem];
|
|
789
|
-
//
|
|
789
|
+
// Silently return if middle path doesn't exist (nothing to delete)
|
|
790
790
|
if (next == null || typeof next !== "object") {
|
|
791
791
|
return;
|
|
792
792
|
}
|
|
@@ -802,10 +802,10 @@ export function objDeleteChainValue(obj: unknown, chain: string): void {
|
|
|
802
802
|
//#region objClearUndefined / objClear / objNullToUndefined / objUnflatten
|
|
803
803
|
|
|
804
804
|
/**
|
|
805
|
-
*
|
|
805
|
+
* Delete keys with undefined values from object
|
|
806
806
|
* @internal
|
|
807
807
|
*
|
|
808
|
-
* @mutates
|
|
808
|
+
* @mutates Modifies the original object directly
|
|
809
809
|
*/
|
|
810
810
|
export function objClearUndefined<T extends object>(obj: T): T {
|
|
811
811
|
const record = obj as Record<string, unknown>;
|
|
@@ -818,10 +818,10 @@ export function objClearUndefined<T extends object>(obj: T): T {
|
|
|
818
818
|
}
|
|
819
819
|
|
|
820
820
|
/**
|
|
821
|
-
*
|
|
821
|
+
* Delete all keys from object
|
|
822
822
|
* @internal
|
|
823
823
|
*
|
|
824
|
-
* @mutates
|
|
824
|
+
* @mutates Modifies the original object directly
|
|
825
825
|
*/
|
|
826
826
|
export function objClear<T extends Record<string, unknown>>(obj: T): Record<string, never> {
|
|
827
827
|
for (const key of Object.keys(obj)) {
|
|
@@ -831,10 +831,10 @@ export function objClear<T extends Record<string, unknown>>(obj: T): Record<stri
|
|
|
831
831
|
}
|
|
832
832
|
|
|
833
833
|
/**
|
|
834
|
-
* null
|
|
834
|
+
* Convert null to undefined (recursive)
|
|
835
835
|
* @internal
|
|
836
836
|
*
|
|
837
|
-
* @mutates
|
|
837
|
+
* @mutates Modifies the original array/object directly
|
|
838
838
|
*/
|
|
839
839
|
export function objNullToUndefined<TObject>(obj: TObject): TObject | undefined {
|
|
840
840
|
return objNullToUndefinedImpl(obj, new WeakSet());
|
|
@@ -879,7 +879,7 @@ function objNullToUndefinedImpl<TObject>(obj: TObject, seen: WeakSet<object>): T
|
|
|
879
879
|
}
|
|
880
880
|
|
|
881
881
|
/**
|
|
882
|
-
*
|
|
882
|
+
* Convert flattened object to nested object
|
|
883
883
|
* @internal
|
|
884
884
|
* @example objUnflatten({ "a.b.c": 1 }) => { a: { b: { c: 1 } } }
|
|
885
885
|
*/
|
|
@@ -909,10 +909,10 @@ export function objUnflatten(flatObj: Record<string, unknown>): Record<string, u
|
|
|
909
909
|
|
|
910
910
|
//#endregion
|
|
911
911
|
|
|
912
|
-
//#region
|
|
912
|
+
//#region Type utilities
|
|
913
913
|
|
|
914
914
|
/**
|
|
915
|
-
* undefined
|
|
915
|
+
* Convert properties with undefined to optional
|
|
916
916
|
* @example { a: string; b: string | undefined } → { a: string; b?: string | undefined }
|
|
917
917
|
*/
|
|
918
918
|
export type ObjUndefToOptional<TObject> = {
|
|
@@ -920,7 +920,7 @@ export type ObjUndefToOptional<TObject> = {
|
|
|
920
920
|
} & { [K in keyof TObject as undefined extends TObject[K] ? never : K]: TObject[K] };
|
|
921
921
|
|
|
922
922
|
/**
|
|
923
|
-
* optional
|
|
923
|
+
* Convert optional properties to required + undefined union
|
|
924
924
|
* @example { a: string; b?: string } → { a: string; b: string | undefined }
|
|
925
925
|
*/
|
|
926
926
|
export type ObjOptionalToUndef<TObject> = {
|
|
@@ -930,27 +930,27 @@ export type ObjOptionalToUndef<TObject> = {
|
|
|
930
930
|
//#endregion
|
|
931
931
|
|
|
932
932
|
/**
|
|
933
|
-
* Object.keys
|
|
934
|
-
* @param obj
|
|
935
|
-
* @returns
|
|
933
|
+
* Type-safe version of Object.keys
|
|
934
|
+
* @param obj Object to extract keys from
|
|
935
|
+
* @returns Array of object keys
|
|
936
936
|
*/
|
|
937
937
|
export function objKeys<T extends object>(obj: T): (keyof T)[] {
|
|
938
938
|
return Object.keys(obj) as (keyof T)[];
|
|
939
939
|
}
|
|
940
940
|
|
|
941
941
|
/**
|
|
942
|
-
* Object.entries
|
|
943
|
-
* @param obj
|
|
944
|
-
* @returns [
|
|
942
|
+
* Type-safe version of Object.entries
|
|
943
|
+
* @param obj Object to extract entries from
|
|
944
|
+
* @returns Array of [key, value] tuples
|
|
945
945
|
*/
|
|
946
946
|
export function objEntries<T extends object>(obj: T): ObjEntries<T> {
|
|
947
947
|
return Object.entries(obj) as ObjEntries<T>;
|
|
948
948
|
}
|
|
949
949
|
|
|
950
950
|
/**
|
|
951
|
-
* Object.fromEntries
|
|
952
|
-
* @param entries [
|
|
953
|
-
* @returns
|
|
951
|
+
* Type-safe version of Object.fromEntries
|
|
952
|
+
* @param entries Array of [key, value] tuples
|
|
953
|
+
* @returns Created object
|
|
954
954
|
*/
|
|
955
955
|
export function objFromEntries<T extends [string, unknown]>(entries: T[]): { [K in T[0]]: T[1] } {
|
|
956
956
|
return Object.fromEntries(entries) as { [K in T[0]]: T[1] };
|
|
@@ -959,18 +959,18 @@ export function objFromEntries<T extends [string, unknown]>(entries: T[]): { [K
|
|
|
959
959
|
type ObjEntries<TObject> = { [K in keyof TObject]: [K, TObject[K]] }[keyof TObject][];
|
|
960
960
|
|
|
961
961
|
/**
|
|
962
|
-
*
|
|
963
|
-
* @param obj
|
|
964
|
-
* @param fn
|
|
965
|
-
* @returns
|
|
962
|
+
* Transform each entry of object and return new object
|
|
963
|
+
* @param obj Object to transform
|
|
964
|
+
* @param fn Transform function (key, value) => [newKey, newValue]
|
|
965
|
+
* @returns New object with transformed keys and values
|
|
966
966
|
* @example
|
|
967
967
|
* const colors = { primary: "255, 0, 0", secondary: "0, 255, 0" };
|
|
968
968
|
*
|
|
969
|
-
* //
|
|
969
|
+
* // Transform only values
|
|
970
970
|
* objMap(colors, (key, rgb) => [null, `rgb(${rgb})`]);
|
|
971
971
|
* // { primary: "rgb(255, 0, 0)", secondary: "rgb(0, 255, 0)" }
|
|
972
972
|
*
|
|
973
|
-
* //
|
|
973
|
+
* // Transform both keys and values
|
|
974
974
|
* objMap(colors, (key, rgb) => [`${key}Light`, `rgb(${rgb})`]);
|
|
975
975
|
* // { primaryLight: "rgb(255, 0, 0)", secondaryLight: "rgb(0, 255, 0)" }
|
|
976
976
|
*/
|
package/src/utils/path.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Node.js path
|
|
2
|
+
* Path utility functions
|
|
3
|
+
* Replacement for Node.js path module (supports browser environments)
|
|
4
4
|
*
|
|
5
|
-
* @note
|
|
6
|
-
* Windows
|
|
7
|
-
*
|
|
5
|
+
* @note This utility supports POSIX style paths (slash `/`) only.
|
|
6
|
+
* Windows backslash (`\`) paths are not supported.
|
|
7
|
+
* Designed for browser environments and Capacitor plugins.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* @note POSIX
|
|
11
|
+
* Combine paths (path.join replacement)
|
|
12
|
+
* @note Supports POSIX style paths only (slash `/`)
|
|
13
13
|
*/
|
|
14
14
|
export function pathJoin(...segments: string[]): string {
|
|
15
15
|
return segments
|
|
@@ -19,7 +19,7 @@ export function pathJoin(...segments: string[]): string {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* Extract filename (path.basename replacement)
|
|
23
23
|
*/
|
|
24
24
|
export function pathBasename(filePath: string, ext?: string): string {
|
|
25
25
|
const name = filePath.split("/").pop() ?? "";
|
|
@@ -30,8 +30,8 @@ export function pathBasename(filePath: string, ext?: string): string {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
* @note
|
|
33
|
+
* Extract file extension (path.extname replacement)
|
|
34
|
+
* @note Hidden files (e.g., `.gitignore`) return empty string (same as Node.js path.extname)
|
|
35
35
|
*/
|
|
36
36
|
export function pathExtname(filePath: string): string {
|
|
37
37
|
const name = filePath.split("/").pop() ?? "";
|