@codady/utils 0.0.9 → 0.0.10
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/CHANGELOG.md +21 -0
- package/dist/utils.cjs.js +234 -27
- package/dist/utils.cjs.min.js +3 -3
- package/dist/utils.esm.js +234 -27
- package/dist/utils.esm.min.js +3 -3
- package/dist/utils.umd.js +234 -27
- package/dist/utils.umd.min.js +3 -3
- package/dist.zip +0 -0
- package/modules.js +11 -3
- package/modules.ts +11 -3
- package/package.json +1 -1
- package/src/arrayMutableMethods - /345/211/257/346/234/254.js" +5 -0
- package/src/arrayMutableMethods.js +5 -0
- package/src/{mutableMethods.ts → arrayMutableMethods.ts} +3 -3
- package/src/deepClone.js +151 -26
- package/src/deepClone.ts +194 -35
- package/src/deepCloneToJSON - /345/211/257/346/234/254.js" +47 -0
- package/src/getUniqueId.js +11 -7
- package/src/getUniqueId.ts +16 -9
- package/src/mapMutableMethods.js +5 -0
- package/src/mapMutableMethods.ts +15 -0
- package/src/mutableMethods.js +2 -2
- package/src/setMutableMethods - /345/211/257/346/234/254.js" +5 -0
- package/src/setMutableMethods.js +5 -0
- package/src/setMutableMethods.ts +14 -0
- package/src/wrapArrayMethods.js +5 -5
- package/src/wrapArrayMethods.ts +7 -7
- package/src/wrapMap - /345/211/257/346/234/254.js" +119 -0
- package/src/wrapMapMethods.js +118 -0
- package/src/wrapMapMethods.ts +226 -0
- package/src/wrapSetMethods.js +112 -0
- package/src/wrapSetMethods.ts +215 -0
package/src/deepClone.js
CHANGED
|
@@ -1,53 +1,178 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
3
|
-
* Deep clone an array or
|
|
4
|
-
* Supports Symbol keys. These types are returned directly:
|
|
5
|
-
* Number, String, Boolean, Symbol, HTML*Element, Function, Date, RegExp.
|
|
2
|
+
* @since Last modified: 2025/12/22 20:20:47
|
|
3
|
+
* Deep clone an array, object, or other cloneable data types.
|
|
6
4
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Handles nested objects and arrays recursively
|
|
7
|
+
* - Preserves Symbol properties
|
|
8
|
+
* - Customizable cloning behavior via options
|
|
9
|
+
* - Interceptor for custom cloning logic
|
|
10
|
+
* - Lifecycle callbacks for monitoring
|
|
11
|
+
*
|
|
12
|
+
* Default cloning behavior:
|
|
13
|
+
* - Objects and arrays: Deep cloned (enabled by default)
|
|
14
|
+
* - Maps and Sets: Deep cloned (enabled by default)
|
|
15
|
+
* - Dates: Returned directly (disabled by default, set cloneDate: true to clone)
|
|
16
|
+
* - RegExp: Returned directly (disabled by default, set cloneRegex: true to clone)
|
|
17
|
+
* - DOM elements: Returned directly (disabled by default, set cloneElement: true to clone)
|
|
18
|
+
* - DocumentFragment: Returned directly (disabled by default, set cloneFragment: true to clone)
|
|
19
|
+
*
|
|
20
|
+
* Types always returned directly (no cloning):
|
|
21
|
+
* - Primitive types: Number, String, Boolean, BigInt, undefined, null
|
|
22
|
+
* - Functions and Classes
|
|
23
|
+
* - Symbol instances
|
|
24
|
+
* - Errors and Promises
|
|
25
|
+
* - ArrayBuffer, Blob, File
|
|
26
|
+
* - WeakMap and WeakSet
|
|
27
|
+
*
|
|
28
|
+
* @template T - Type of the data to clone
|
|
29
|
+
* @param {T} data - The data to deep clone
|
|
30
|
+
* @param {DeepCloneOptions} [options={}] - Configuration options for cloning behavior
|
|
31
|
+
* @returns {T} - A deep clone of the input data (new memory reference)
|
|
10
32
|
*
|
|
11
33
|
* @example
|
|
12
|
-
*
|
|
34
|
+
* // Basic usage - clones objects and arrays
|
|
35
|
+
* const obj = { a: '', b: 0, c: [], d: new Map([['key', 'value']]) };
|
|
13
36
|
* const cloned = deepClone(obj);
|
|
37
|
+
* console.log(cloned !== obj); // true
|
|
38
|
+
* console.log(cloned.c !== obj.c); // true
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // With Symbol properties
|
|
42
|
+
* const sym = Symbol('id');
|
|
43
|
+
* const objWithSymbol = { [sym]: 'value', normal: 'property' };
|
|
44
|
+
* const clonedWithSymbol = deepClone(objWithSymbol);
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // Custom options - clone Dates and RegExp
|
|
48
|
+
* const data = { date: new Date(), regex: /test/gi };
|
|
49
|
+
* const cloned = deepClone(data, {
|
|
50
|
+
* cloneDate: true,
|
|
51
|
+
* cloneRegex: true
|
|
52
|
+
* });
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // Using interceptor for custom types
|
|
56
|
+
* class CustomClass {
|
|
57
|
+
* constructor(public value: number) {}
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* const customData = new CustomClass(42);
|
|
61
|
+
* const cloned = deepClone(customData, {
|
|
62
|
+
* interceptor: (data, type) => {
|
|
63
|
+
* if (data instanceof CustomClass) {
|
|
64
|
+
* return new CustomClass(data.value);
|
|
65
|
+
* }
|
|
66
|
+
* return null;
|
|
67
|
+
* }
|
|
68
|
+
* });
|
|
14
69
|
*
|
|
15
70
|
* @example
|
|
16
|
-
*
|
|
17
|
-
* const
|
|
71
|
+
* // With lifecycle callbacks
|
|
72
|
+
* const trackedClone = deepClone(someData, {
|
|
73
|
+
* onBeforeClone: (data, type) => {
|
|
74
|
+
* console.log(`Starting to clone ${type}`);
|
|
75
|
+
* },
|
|
76
|
+
* onAfterClone: (result) => {
|
|
77
|
+
* console.log(`Cloned ${result.type}, success: ${result.cloned}`);
|
|
78
|
+
* }
|
|
79
|
+
* });
|
|
18
80
|
*/
|
|
19
81
|
'use strict';
|
|
20
82
|
import getDataType from './getDataType';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
83
|
+
//支持原始值的复制,包括Number、String、Boolean、Null
|
|
84
|
+
//支持Date和Regex对象值复制(值转换)
|
|
85
|
+
//支持{},[],Set和Map的遍历
|
|
86
|
+
const deepClone = (data, options = {}) => {
|
|
87
|
+
const dataType = getDataType(data),
|
|
88
|
+
// Default options
|
|
89
|
+
opts = Object.assign({
|
|
90
|
+
cloneSet: true,
|
|
91
|
+
cloneMap: true,
|
|
92
|
+
cloneObject: true,
|
|
93
|
+
cloneArray: true,
|
|
94
|
+
//可重新构建
|
|
95
|
+
cloneDate: true,
|
|
96
|
+
cloneRegex: true,
|
|
97
|
+
//节点默认不复制
|
|
98
|
+
//cloneElement: false,
|
|
99
|
+
//cloneFragment: false,
|
|
100
|
+
}, options);
|
|
101
|
+
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
102
|
+
if (opts.interceptor && typeof opts.interceptor === 'function') {
|
|
103
|
+
let result = opts.interceptor(data, dataType);
|
|
104
|
+
if ((result ?? false)) {
|
|
105
|
+
// Call onAfterClone if set
|
|
106
|
+
opts.onAfterClone?.({
|
|
107
|
+
output: result,
|
|
108
|
+
input: data,
|
|
109
|
+
type: dataType,
|
|
110
|
+
cloned: result !== data,
|
|
111
|
+
});
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
// If interceptor returns null/undefined, continue with normal cloning process
|
|
115
|
+
}
|
|
116
|
+
// Callback before cloning
|
|
117
|
+
opts.onBeforeClone?.(data, dataType);
|
|
118
|
+
let newData, cloned = true;
|
|
119
|
+
if (dataType === 'Object' && opts.cloneObject) {
|
|
120
|
+
const newObj = {}, symbols = Object.getOwnPropertySymbols(data);
|
|
26
121
|
// Clone regular properties
|
|
27
122
|
for (const key in data) {
|
|
28
|
-
newObj[key] = deepClone(data[key]);
|
|
123
|
+
newObj[key] = deepClone(data[key], opts);
|
|
29
124
|
}
|
|
30
125
|
// Clone Symbol properties
|
|
31
126
|
if (symbols.length > 0) {
|
|
32
127
|
for (const symbol of symbols) {
|
|
33
|
-
newObj[symbol] = deepClone(data[symbol]);
|
|
128
|
+
newObj[symbol] = deepClone(data[symbol], opts);
|
|
34
129
|
}
|
|
35
130
|
}
|
|
36
|
-
|
|
131
|
+
newData = newObj;
|
|
37
132
|
}
|
|
38
|
-
else if (dataType === 'Array') {
|
|
39
|
-
|
|
133
|
+
else if (dataType === 'Array' && opts.cloneArray) {
|
|
134
|
+
newData = data.map(item => deepClone(item, opts));
|
|
135
|
+
}
|
|
136
|
+
else if (dataType === 'Map' && opts.cloneMap) {
|
|
137
|
+
const newMap = new Map();
|
|
138
|
+
for (const [key, value] of data) {
|
|
139
|
+
// Both Map keys and values need deep cloning
|
|
140
|
+
newMap.set(deepClone(key, opts), deepClone(value, opts));
|
|
141
|
+
}
|
|
142
|
+
newData = newMap;
|
|
143
|
+
}
|
|
144
|
+
else if (dataType === 'Set' && opts.cloneSet) {
|
|
145
|
+
const newSet = new Set();
|
|
146
|
+
for (const value of data) {
|
|
147
|
+
// Set values need deep cloning
|
|
148
|
+
newSet.add(deepClone(value, opts));
|
|
149
|
+
}
|
|
150
|
+
newData = newSet;
|
|
40
151
|
}
|
|
41
|
-
else if (dataType === 'Date') {
|
|
42
|
-
|
|
152
|
+
else if (dataType === 'Date' && opts.cloneDate) {
|
|
153
|
+
newData = new Date(data.getTime());
|
|
43
154
|
}
|
|
44
|
-
else if (dataType === 'RegExp') {
|
|
155
|
+
else if (dataType === 'RegExp' && opts.cloneRegex) {
|
|
45
156
|
const regex = data;
|
|
46
|
-
|
|
157
|
+
newData = new RegExp(regex.source, regex.flags);
|
|
158
|
+
// } else if ((dataType.includes('HTML') && opts.cloneElement) ||
|
|
159
|
+
// (dataType === 'DocumentFragment' && opts.cloneFragment)
|
|
160
|
+
//) {
|
|
161
|
+
//Text,Comment,HTML*Element,DocumentFragment,Attr
|
|
162
|
+
// newData = (data as any).cloneNode(true) as T;
|
|
47
163
|
}
|
|
48
164
|
else {
|
|
49
|
-
// Number, String, Boolean, Symbol,
|
|
50
|
-
|
|
165
|
+
// Number, String, Boolean, Symbol, Function,Error,Promise,ArrayBuffer,Blob,File, return directly
|
|
166
|
+
newData = data;
|
|
167
|
+
cloned = false;
|
|
51
168
|
}
|
|
169
|
+
// Callback after cloning
|
|
170
|
+
opts.onAfterClone?.({
|
|
171
|
+
output: newData,
|
|
172
|
+
input: data,
|
|
173
|
+
type: dataType,
|
|
174
|
+
cloned,
|
|
175
|
+
});
|
|
176
|
+
return newData;
|
|
52
177
|
};
|
|
53
178
|
export default deepClone;
|
package/src/deepClone.ts
CHANGED
|
@@ -1,59 +1,218 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
3
|
-
* Deep clone an array or
|
|
4
|
-
* Supports Symbol keys. These types are returned directly:
|
|
5
|
-
* Number, String, Boolean, Symbol, HTML*Element, Function, Date, RegExp.
|
|
2
|
+
* @since Last modified: 2025/12/22 20:20:47
|
|
3
|
+
* Deep clone an array, object, or other cloneable data types.
|
|
6
4
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Handles nested objects and arrays recursively
|
|
7
|
+
* - Preserves Symbol properties
|
|
8
|
+
* - Customizable cloning behavior via options
|
|
9
|
+
* - Interceptor for custom cloning logic
|
|
10
|
+
* - Lifecycle callbacks for monitoring
|
|
11
|
+
*
|
|
12
|
+
* Default cloning behavior:
|
|
13
|
+
* - Objects and arrays: Deep cloned (enabled by default)
|
|
14
|
+
* - Maps and Sets: Deep cloned (enabled by default)
|
|
15
|
+
* - Dates: Returned directly (disabled by default, set cloneDate: true to clone)
|
|
16
|
+
* - RegExp: Returned directly (disabled by default, set cloneRegex: true to clone)
|
|
17
|
+
* - DOM elements: Returned directly (disabled by default, set cloneElement: true to clone)
|
|
18
|
+
* - DocumentFragment: Returned directly (disabled by default, set cloneFragment: true to clone)
|
|
19
|
+
*
|
|
20
|
+
* Types always returned directly (no cloning):
|
|
21
|
+
* - Primitive types: Number, String, Boolean, BigInt, undefined, null
|
|
22
|
+
* - Functions and Classes
|
|
23
|
+
* - Symbol instances
|
|
24
|
+
* - Errors and Promises
|
|
25
|
+
* - ArrayBuffer, Blob, File
|
|
26
|
+
* - WeakMap and WeakSet
|
|
27
|
+
*
|
|
28
|
+
* @template T - Type of the data to clone
|
|
29
|
+
* @param {T} data - The data to deep clone
|
|
30
|
+
* @param {DeepCloneOptions} [options={}] - Configuration options for cloning behavior
|
|
31
|
+
* @returns {T} - A deep clone of the input data (new memory reference)
|
|
10
32
|
*
|
|
11
33
|
* @example
|
|
12
|
-
*
|
|
34
|
+
* // Basic usage - clones objects and arrays
|
|
35
|
+
* const obj = { a: '', b: 0, c: [], d: new Map([['key', 'value']]) };
|
|
13
36
|
* const cloned = deepClone(obj);
|
|
37
|
+
* console.log(cloned !== obj); // true
|
|
38
|
+
* console.log(cloned.c !== obj.c); // true
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // With Symbol properties
|
|
42
|
+
* const sym = Symbol('id');
|
|
43
|
+
* const objWithSymbol = { [sym]: 'value', normal: 'property' };
|
|
44
|
+
* const clonedWithSymbol = deepClone(objWithSymbol);
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // Custom options - clone Dates and RegExp
|
|
48
|
+
* const data = { date: new Date(), regex: /test/gi };
|
|
49
|
+
* const cloned = deepClone(data, {
|
|
50
|
+
* cloneDate: true,
|
|
51
|
+
* cloneRegex: true
|
|
52
|
+
* });
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // Using interceptor for custom types
|
|
56
|
+
* class CustomClass {
|
|
57
|
+
* constructor(public value: number) {}
|
|
58
|
+
* }
|
|
14
59
|
*
|
|
15
|
-
*
|
|
16
|
-
* const
|
|
17
|
-
*
|
|
60
|
+
* const customData = new CustomClass(42);
|
|
61
|
+
* const cloned = deepClone(customData, {
|
|
62
|
+
* interceptor: (data, type) => {
|
|
63
|
+
* if (data instanceof CustomClass) {
|
|
64
|
+
* return new CustomClass(data.value);
|
|
65
|
+
* }
|
|
66
|
+
* return null;
|
|
67
|
+
* }
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* // With lifecycle callbacks
|
|
72
|
+
* const trackedClone = deepClone(someData, {
|
|
73
|
+
* onBeforeClone: (data, type) => {
|
|
74
|
+
* console.log(`Starting to clone ${type}`);
|
|
75
|
+
* },
|
|
76
|
+
* onAfterClone: (result) => {
|
|
77
|
+
* console.log(`Cloned ${result.type}, success: ${result.cloned}`);
|
|
78
|
+
* }
|
|
79
|
+
* });
|
|
18
80
|
*/
|
|
19
81
|
'use strict';
|
|
20
82
|
import getDataType from './getDataType';
|
|
21
83
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
84
|
+
export interface DeepCloneOptions {
|
|
85
|
+
cloneDate?: boolean;
|
|
86
|
+
cloneRegex?: boolean;
|
|
87
|
+
cloneSet?: boolean;
|
|
88
|
+
cloneMap?: boolean;
|
|
89
|
+
cloneObject?: boolean;
|
|
90
|
+
cloneArray?: boolean;
|
|
91
|
+
cloneElement?: boolean;
|
|
92
|
+
cloneFragment?: boolean;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Interceptor function. If returns a non-null value, use it as the cloning result.
|
|
96
|
+
*/
|
|
97
|
+
interceptor?: <T>(data: T, dataType: string) => T | null | undefined;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Callback before cloning.
|
|
101
|
+
*/
|
|
102
|
+
onBeforeClone?: (data: any, dataType: string) => void;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Callback after cloning.
|
|
106
|
+
* @param result Object containing cloning result.
|
|
107
|
+
*/
|
|
108
|
+
onAfterClone?: (result: AfterCloneResult) => void;
|
|
109
|
+
}
|
|
110
|
+
export interface AfterCloneResult<T = any> {
|
|
111
|
+
// Cloned data
|
|
112
|
+
output: T;
|
|
113
|
+
// Original data
|
|
114
|
+
input: any;
|
|
115
|
+
// Data type
|
|
116
|
+
type: string;
|
|
117
|
+
// Whether cloning was performed
|
|
118
|
+
cloned: boolean;
|
|
119
|
+
}
|
|
120
|
+
//支持原始值的复制,包括Number、String、Boolean、Null
|
|
121
|
+
//支持Date和Regex对象值复制(值转换)
|
|
122
|
+
//支持{},[],Set和Map的遍历
|
|
123
|
+
const deepClone = <T>(data: T, options: DeepCloneOptions = {}): T => {
|
|
124
|
+
const dataType = getDataType(data),
|
|
125
|
+
// Default options
|
|
126
|
+
opts = Object.assign({
|
|
127
|
+
cloneSet: true,
|
|
128
|
+
cloneMap: true,
|
|
129
|
+
cloneObject: true,
|
|
130
|
+
cloneArray: true,
|
|
131
|
+
//可重新构建
|
|
132
|
+
cloneDate: true,
|
|
133
|
+
cloneRegex: true,
|
|
134
|
+
//节点默认不复制
|
|
135
|
+
//cloneElement: false,
|
|
136
|
+
//cloneFragment: false,
|
|
137
|
+
}, options);
|
|
138
|
+
|
|
139
|
+
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
140
|
+
if (opts.interceptor && typeof opts.interceptor === 'function') {
|
|
141
|
+
let result = opts.interceptor(data, dataType);
|
|
142
|
+
if ((result ?? false)) {
|
|
143
|
+
// Call onAfterClone if set
|
|
144
|
+
opts.onAfterClone?.({
|
|
145
|
+
output: result,
|
|
146
|
+
input: data,
|
|
147
|
+
type: dataType,
|
|
148
|
+
cloned: result !== data,
|
|
149
|
+
});
|
|
150
|
+
return result as T;
|
|
151
|
+
}
|
|
152
|
+
// If interceptor returns null/undefined, continue with normal cloning process
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Callback before cloning
|
|
156
|
+
opts.onBeforeClone?.(data, dataType);
|
|
157
|
+
|
|
158
|
+
let newData,
|
|
159
|
+
cloned = true;
|
|
160
|
+
|
|
161
|
+
if (dataType === 'Object' && opts.cloneObject) {
|
|
162
|
+
const newObj: Record<string | symbol, any> = {},
|
|
163
|
+
symbols = Object.getOwnPropertySymbols(data);
|
|
164
|
+
|
|
29
165
|
// Clone regular properties
|
|
30
166
|
for (const key in data) {
|
|
31
|
-
newObj[key] = deepClone((data as any)[key]);
|
|
167
|
+
newObj[key] = deepClone((data as any)[key],opts);
|
|
32
168
|
}
|
|
33
|
-
|
|
169
|
+
|
|
34
170
|
// Clone Symbol properties
|
|
35
171
|
if (symbols.length > 0) {
|
|
36
172
|
for (const symbol of symbols) {
|
|
37
|
-
newObj[symbol] = deepClone((data as any)[symbol]);
|
|
173
|
+
newObj[symbol] = deepClone((data as any)[symbol],opts);
|
|
38
174
|
}
|
|
39
175
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
} else if (dataType === '
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
176
|
+
newData = newObj as T;
|
|
177
|
+
} else if (dataType === 'Array' && opts.cloneArray) {
|
|
178
|
+
newData = (data as any[]).map(item => deepClone(item,opts)) as T;
|
|
179
|
+
} else if (dataType === 'Map' && opts.cloneMap) {
|
|
180
|
+
const newMap = new Map();
|
|
181
|
+
for (const [key, value] of data as Map<any, any>) {
|
|
182
|
+
// Both Map keys and values need deep cloning
|
|
183
|
+
newMap.set(deepClone(key,opts), deepClone(value,opts));
|
|
184
|
+
}
|
|
185
|
+
newData = newMap as T;
|
|
186
|
+
} else if (dataType === 'Set' && opts.cloneSet) {
|
|
187
|
+
const newSet = new Set();
|
|
188
|
+
for (const value of data as Set<any>) {
|
|
189
|
+
// Set values need deep cloning
|
|
190
|
+
newSet.add(deepClone(value,opts));
|
|
191
|
+
}
|
|
192
|
+
newData = newSet as T;
|
|
193
|
+
} else if (dataType === 'Date' && opts.cloneDate) {
|
|
194
|
+
newData = new Date((data as Date).getTime()) as T;
|
|
195
|
+
} else if (dataType === 'RegExp' && opts.cloneRegex) {
|
|
50
196
|
const regex = data as RegExp;
|
|
51
|
-
|
|
52
|
-
|
|
197
|
+
newData = new RegExp(regex.source, regex.flags) as T;
|
|
198
|
+
// } else if ((dataType.includes('HTML') && opts.cloneElement) ||
|
|
199
|
+
// (dataType === 'DocumentFragment' && opts.cloneFragment)
|
|
200
|
+
//) {
|
|
201
|
+
//Text,Comment,HTML*Element,DocumentFragment,Attr
|
|
202
|
+
// newData = (data as any).cloneNode(true) as T;
|
|
53
203
|
} else {
|
|
54
|
-
// Number, String, Boolean, Symbol,
|
|
55
|
-
|
|
204
|
+
// Number, String, Boolean, Symbol, Function,Error,Promise,ArrayBuffer,Blob,File, return directly
|
|
205
|
+
newData = data;
|
|
206
|
+
cloned = false;
|
|
56
207
|
}
|
|
208
|
+
// Callback after cloning
|
|
209
|
+
opts.onAfterClone?.({
|
|
210
|
+
output: newData,
|
|
211
|
+
input: data,
|
|
212
|
+
type: dataType,
|
|
213
|
+
cloned,
|
|
214
|
+
});
|
|
215
|
+
return newData;
|
|
57
216
|
};
|
|
58
217
|
|
|
59
218
|
export default deepClone;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2025/12/16 14:43:51
|
|
3
|
+
* Deep clone an array or object to a JSON-compatible structure.
|
|
4
|
+
* Converts non-serializable types like functions, symbols, Date, RegExp to plain values.
|
|
5
|
+
*
|
|
6
|
+
* @template T
|
|
7
|
+
* @param {T} data - Array or object to clone
|
|
8
|
+
* @returns {T} - New object with different memory address, in a JSON-compatible structure
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const obj = { a: '', b: 0, c: [] };
|
|
12
|
+
* const cloned = deepCloneToJSON(obj);
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const arr = [{}, {}, {}];
|
|
16
|
+
* const cloned = deepCloneToJSON(arr);
|
|
17
|
+
*/
|
|
18
|
+
'use strict';
|
|
19
|
+
import getDataType from './getDataType';
|
|
20
|
+
const deepCloneToJSON = (data) => {
|
|
21
|
+
const dataType = getDataType(data);
|
|
22
|
+
// Handle objects
|
|
23
|
+
if (dataType === 'Object') {
|
|
24
|
+
const newObj = {};
|
|
25
|
+
// Clone regular properties
|
|
26
|
+
for (const key in data) {
|
|
27
|
+
newObj[key] = deepCloneToJSON(data[key]);
|
|
28
|
+
}
|
|
29
|
+
for (const key in newObj) {
|
|
30
|
+
newObj[key] === undefined && Reflect.deleteProperty(newObj, key);
|
|
31
|
+
}
|
|
32
|
+
return newObj;
|
|
33
|
+
// Handle arrays
|
|
34
|
+
}
|
|
35
|
+
else if (dataType === 'Array') {
|
|
36
|
+
let tmp = data.map((item, index) => deepCloneToJSON(item)).filter(item => item !== undefined);
|
|
37
|
+
return tmp;
|
|
38
|
+
// Handle Date objects
|
|
39
|
+
}
|
|
40
|
+
else if (!['Number', 'String', 'Boolean', 'Null'].includes(dataType)) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
return data;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export default deepCloneToJSON;
|
package/src/getUniqueId.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
2
|
+
* @since Last modified: 2025/12/22 14:29:27
|
|
3
3
|
* Generates a unique identifier string
|
|
4
4
|
*
|
|
5
5
|
* The ID format is: `[prefix_]timestamp_randomBase36_extraRandom`
|
|
@@ -13,23 +13,27 @@
|
|
|
13
13
|
* 3. Additional 4-digit random number for extra entropy
|
|
14
14
|
* 4. Optional prefix for categorization
|
|
15
15
|
*
|
|
16
|
-
* @param prefix - Optional prefix string for the ID (e.g., 'snax', 'user', 'doc')
|
|
17
|
-
* @param
|
|
16
|
+
* @param options.prefix - Optional prefix string for the ID (e.g., 'snax', 'user', 'doc')
|
|
17
|
+
* @param options.suffix - Optional suffix string for the ID (e.g., 'snax', 'user', 'doc')
|
|
18
|
+
* @param options.base10 - Optional 4-digit random number
|
|
19
|
+
* @param options.base36 - Optional 9-digit random charactor
|
|
18
20
|
* @returns A unique identifier string
|
|
19
21
|
*/
|
|
20
|
-
export const getUniqueId = (
|
|
22
|
+
export const getUniqueId = (options = {}) => {
|
|
23
|
+
const prefix = options.prefix, suffix = options.suffix, base10 = options.base10, base36 = options.base36;
|
|
21
24
|
// Current timestamp in milliseconds (since Unix epoch)
|
|
22
25
|
// This provides the primary uniqueness guarantee
|
|
23
26
|
const timestamp = Date.now(),
|
|
24
27
|
// Generate a base-36 random string (0-9, a-z)
|
|
25
28
|
// Math.random() returns a number in [0, 1), converting to base-36 gives a compact representation
|
|
26
29
|
// substring(2, 11) extracts 9 characters starting from index 2
|
|
27
|
-
|
|
30
|
+
//0.259854635->0.9crs03e8v2
|
|
31
|
+
base36Random = base36 ? '-' + Math.random().toString(36).substring(2, 11) : '',
|
|
28
32
|
// Additional 4-digit random number for extra randomness
|
|
29
33
|
// This helps avoid collisions in high-frequency generation scenarios
|
|
30
|
-
|
|
34
|
+
base10Random = base10 ? '-' + Math.floor(Math.random() * 10000).toString().padStart(4, '0') : '', prefixString = prefix ? prefix + '-' : '', suffixString = suffix ? '-' + suffix : '';
|
|
31
35
|
// Construct the final ID string
|
|
32
36
|
// Format: [prefix_]timestamp_randomBase36_extraRandom
|
|
33
|
-
return `${prefixString}${timestamp}
|
|
37
|
+
return `${prefixString}${timestamp}${base36Random}${base10Random}${suffixString}`;
|
|
34
38
|
};
|
|
35
39
|
export default getUniqueId;
|
package/src/getUniqueId.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
2
|
+
* @since Last modified: 2025/12/22 14:29:27
|
|
3
3
|
* Generates a unique identifier string
|
|
4
4
|
*
|
|
5
5
|
* The ID format is: `[prefix_]timestamp_randomBase36_extraRandom`
|
|
@@ -13,11 +13,17 @@
|
|
|
13
13
|
* 3. Additional 4-digit random number for extra entropy
|
|
14
14
|
* 4. Optional prefix for categorization
|
|
15
15
|
*
|
|
16
|
-
* @param prefix - Optional prefix string for the ID (e.g., 'snax', 'user', 'doc')
|
|
17
|
-
* @param
|
|
16
|
+
* @param options.prefix - Optional prefix string for the ID (e.g., 'snax', 'user', 'doc')
|
|
17
|
+
* @param options.suffix - Optional suffix string for the ID (e.g., 'snax', 'user', 'doc')
|
|
18
|
+
* @param options.base10 - Optional 4-digit random number
|
|
19
|
+
* @param options.base36 - Optional 9-digit random charactor
|
|
18
20
|
* @returns A unique identifier string
|
|
19
21
|
*/
|
|
20
|
-
export const getUniqueId = (prefix?: string,
|
|
22
|
+
export const getUniqueId = (options: { prefix?: string, suffix?: string, base10?: boolean,base36?: boolean } = {}): string => {
|
|
23
|
+
const prefix = options.prefix,
|
|
24
|
+
suffix = options.suffix,
|
|
25
|
+
base10 = options.base10,
|
|
26
|
+
base36 = options.base36;
|
|
21
27
|
// Current timestamp in milliseconds (since Unix epoch)
|
|
22
28
|
// This provides the primary uniqueness guarantee
|
|
23
29
|
const timestamp = Date.now(),
|
|
@@ -25,16 +31,17 @@ export const getUniqueId = (prefix?: string, extra = true): string => {
|
|
|
25
31
|
// Generate a base-36 random string (0-9, a-z)
|
|
26
32
|
// Math.random() returns a number in [0, 1), converting to base-36 gives a compact representation
|
|
27
33
|
// substring(2, 11) extracts 9 characters starting from index 2
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
//0.259854635->0.9crs03e8v2
|
|
35
|
+
base36Random = base36?'-' +Math.random().toString(36).substring(2, 11):'',
|
|
30
36
|
// Additional 4-digit random number for extra randomness
|
|
31
37
|
// This helps avoid collisions in high-frequency generation scenarios
|
|
32
|
-
|
|
33
|
-
prefixString = prefix ? prefix + '
|
|
38
|
+
base10Random = base10 ? '-' + Math.floor(Math.random() * 10000).toString().padStart(4, '0') : '',
|
|
39
|
+
prefixString = prefix ? prefix + '-' : '',
|
|
40
|
+
suffixString = suffix ? '-' + suffix : '';
|
|
34
41
|
|
|
35
42
|
// Construct the final ID string
|
|
36
43
|
// Format: [prefix_]timestamp_randomBase36_extraRandom
|
|
37
|
-
return `${prefixString}${timestamp}
|
|
44
|
+
return `${prefixString}${timestamp}${base36Random}${base10Random}${suffixString}`;
|
|
38
45
|
};
|
|
39
46
|
|
|
40
47
|
export default getUniqueId;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2025/12/22 17:16:39
|
|
3
|
+
*
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Available mutation methods for Map objects
|
|
7
|
+
*/
|
|
8
|
+
export type MapMutationNames = 'set' | 'delete' | 'clear';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Available Map mutation methods
|
|
12
|
+
*/
|
|
13
|
+
export const mapMutableMethods: MapMutationNames[] = ['set', 'delete', 'clear'];
|
|
14
|
+
|
|
15
|
+
export default mapMutableMethods;
|
package/src/mutableMethods.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2025/12/22 17:15:21
|
|
3
|
+
*
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Available mutation methods for Set objects
|
|
7
|
+
*/
|
|
8
|
+
export type SetMutationNames = 'add' | 'delete' | 'clear';
|
|
9
|
+
/**
|
|
10
|
+
* Available Set mutation methods
|
|
11
|
+
*/
|
|
12
|
+
export const setMutableMethods: SetMutationNames[] = ['add', 'delete', 'clear'];
|
|
13
|
+
|
|
14
|
+
export default setMutableMethods;
|