@codady/utils 0.0.9 → 0.0.11
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 +38 -0
- package/dist/utils.cjs.js +488 -28
- package/dist/utils.cjs.min.js +3 -3
- package/dist/utils.esm.js +488 -28
- package/dist/utils.esm.min.js +3 -3
- package/dist/utils.umd - /345/211/257/346/234/254.js" +749 -0
- package/dist/utils.umd.js +490 -32
- package/dist/utils.umd.min.js +3 -3
- package/dist.zip +0 -0
- package/modules.js +24 -4
- package/modules.ts +24 -4
- 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/deepEqual.js +48 -0
- package/src/deepEqual.ts +46 -0
- package/src/deepMerge.js +34 -0
- package/src/deepMerge.ts +40 -0
- package/src/deepMergeArrays.js +45 -0
- package/src/deepMergeArrays.ts +62 -0
- package/src/deepMergeHelper.js +40 -0
- package/src/deepMergeHelper.ts +45 -0
- package/src/deepMergeMaps - /345/211/257/346/234/254.js" +78 -0
- package/src/deepMergeMaps.js +57 -0
- package/src/deepMergeMaps.ts +67 -0
- package/src/deepMergeObjects.js +82 -0
- package/src/deepMergeObjects.ts +85 -0
- package/src/deepMergeSets.js +48 -0
- package/src/deepMergeSets.ts +55 -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
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2025/12/24 17:46:42
|
|
3
|
+
* @function deepMergeObjects
|
|
4
|
+
* @description Merge two objects. The key name of Source is allowed to be the Symbol type, and the Symbol key name of the source will be added directly to the target.
|
|
5
|
+
* @param {Object} target - The original target of the merge.
|
|
6
|
+
* @param {Object} source - The object that will merge into the target.
|
|
7
|
+
* @param {DeepMergeOptions} [opts] - Parameters used for merging objects.
|
|
8
|
+
* @param {boolean} [opts.arrAppend=false] - If value is an array, whether to append or override.
|
|
9
|
+
* @param {boolean} [opts.propAppend=true] - Whether to append properties from source to target if not already present.
|
|
10
|
+
* @param {boolean} [opts.targetClone=false] - Whether to clone the target object, if true, the original target will not be changed.
|
|
11
|
+
* @param {boolean} [opts.useEnable=true] - Whether to merge enable property.
|
|
12
|
+
* @param {boolean} [opts.useSymbol=true] - Whether to merge symbol properties.
|
|
13
|
+
* @returns {Object} - The merged object.
|
|
14
|
+
* @example
|
|
15
|
+
* let x ={a:'man',b:0,c:[]}, b={a:'woman',b:2};
|
|
16
|
+
* deepMergeObjects(x, b);
|
|
17
|
+
* // Returns {a:'woman', b:2, c:[]}
|
|
18
|
+
*/
|
|
19
|
+
'use strict';
|
|
20
|
+
import getDataType from './getDataType';
|
|
21
|
+
import deepClone from './deepClone';
|
|
22
|
+
import deepMergeArrays, { DeepMergeOptions } from './deepMergeArrays';
|
|
23
|
+
import deepMergeSets from './deepMergeSets';
|
|
24
|
+
import deepMergeMaps from './deepMergeMaps';
|
|
25
|
+
import deepMergeHelper from './deepMergeHelper';
|
|
26
|
+
const deepMergeObjects = (target: Object, source: any, opts: DeepMergeOptions = {}): Object => {
|
|
27
|
+
let targetType = getDataType(target),
|
|
28
|
+
sourceType = getDataType(source);
|
|
29
|
+
//target不是对象或者source为空则直接返回
|
|
30
|
+
if (targetType !== 'Object' || sourceType !== 'Object') {
|
|
31
|
+
return target;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const options = Object.assign({ itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }, opts),
|
|
35
|
+
//如果是复制方法,则先复制target
|
|
36
|
+
result = options.targetClone ? deepClone(target) : target;
|
|
37
|
+
|
|
38
|
+
for (let k in source) {
|
|
39
|
+
if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
|
|
40
|
+
|
|
41
|
+
let resp = deepMergeHelper((result as any)[k], source[k], opts);
|
|
42
|
+
//resp={result,flag,type}
|
|
43
|
+
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
44
|
+
if (!resp.flag) {
|
|
45
|
+
//类型不同则直接覆盖
|
|
46
|
+
if (options.useEnable && result.hasOwnProperty(k) && (result as any)[k]?.hasOwnProperty('enable') && typeof source[k] === 'boolean') {
|
|
47
|
+
//部分替换,仅针对result={enable:true/false,a:''},source=false/true这种情况和相反的情况,因为这种情况再笨框架比较多见
|
|
48
|
+
if ((result as any)[k]?.hasOwnProperty('enable') && typeof source[k] === 'boolean') {
|
|
49
|
+
//result={enable:true,a:'',b:''},source[k]=false=>result={enable:false,a:'',b:''}
|
|
50
|
+
(result as any)[k].enable = source[k];
|
|
51
|
+
} else if (source[k]?.hasOwnProperty('enable') && typeof (result as any)[k] === 'boolean') {
|
|
52
|
+
//source={enable:true,a:'',b:''},(result as any)[k]=false=>result={enable:false,a:'',b:''}
|
|
53
|
+
(result as any) = Object.assign({ enable: (result as any)[k] }, source[k]);
|
|
54
|
+
} else {
|
|
55
|
+
//完全替换
|
|
56
|
+
(result as any)[k] = source[k];
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
//完全替换
|
|
60
|
+
(result as any)[k] = source[k];
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
// If both target and source are objects, merge them recursively
|
|
64
|
+
if (resp.type === 'Object') {
|
|
65
|
+
(result as any)[k] = resp.result;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
} else if (source.hasOwnProperty(k) && !result.hasOwnProperty(k) && options.propAppend) {
|
|
70
|
+
//如果source有属性,result没有该属性,但是options允许追加属性则直接赋值
|
|
71
|
+
(result as any)[k] = source[k];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//Symbol键直接追加,因为Symbol是唯一,结果同Object.assign
|
|
75
|
+
if (options.useSymbol) {
|
|
76
|
+
let symbols = Object.getOwnPropertySymbols(source);
|
|
77
|
+
if (symbols.length > 0) {
|
|
78
|
+
for (let k of symbols) {
|
|
79
|
+
(result as any)[k] = source[k];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
export default deepMergeObjects;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2025/12/24 17:48:38
|
|
3
|
+
* @function deepMergeSets
|
|
4
|
+
* Deeply merges two Sets, with flexible options to replace, concatenate, or merge items.
|
|
5
|
+
* @param target The target Set to merge into
|
|
6
|
+
* @param source The source Set to merge from
|
|
7
|
+
* @param options Configuration options for merging
|
|
8
|
+
* @returns The deeply merged Set
|
|
9
|
+
*/
|
|
10
|
+
'use strict';
|
|
11
|
+
import deepEqual from './deepEqual';
|
|
12
|
+
import deepMergeHelper from './deepMergeHelper';
|
|
13
|
+
const deepMergeSets = (target, source, options = { itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }) => {
|
|
14
|
+
// Ensure both target and source are Sets
|
|
15
|
+
if (!(target instanceof Set) || !(source instanceof Set))
|
|
16
|
+
return target;
|
|
17
|
+
// Merge options, with default values
|
|
18
|
+
const opts = Object.assign({ itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }, options),
|
|
19
|
+
// If cloning is enabled, create a deep copy of the target Set
|
|
20
|
+
result = opts.targetClone ? new Set(target) : target;
|
|
21
|
+
// Handle different merge strategies based on itemMode
|
|
22
|
+
if (opts.itemMode === 'replace') {
|
|
23
|
+
// Replace mode: clear the target Set and add all items from the source Set
|
|
24
|
+
result.clear();
|
|
25
|
+
for (let item of source)
|
|
26
|
+
result.add(item);
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
else if (opts.itemMode === 'concat') {
|
|
30
|
+
// Concatenate mode: add all items from the source Set to the target Set
|
|
31
|
+
for (let item of source)
|
|
32
|
+
result.add(item);
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Default "merge" mode: recursively merge items in the Sets
|
|
37
|
+
for (let item of source) {
|
|
38
|
+
// Check the type of the target and source items
|
|
39
|
+
let _target = [...result].find(val => deepEqual(val, item)), resp = deepMergeHelper(_target, item, opts);
|
|
40
|
+
//resp={result,flag}
|
|
41
|
+
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
42
|
+
!resp.flag && result.add(item);
|
|
43
|
+
}
|
|
44
|
+
;
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
export default deepMergeSets;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2025/12/24 17:48:38
|
|
3
|
+
* @function deepMergeSets
|
|
4
|
+
* Deeply merges two Sets, with flexible options to replace, concatenate, or merge items.
|
|
5
|
+
* @param target The target Set to merge into
|
|
6
|
+
* @param source The source Set to merge from
|
|
7
|
+
* @param options Configuration options for merging
|
|
8
|
+
* @returns The deeply merged Set
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
import getDataType from './getDataType';
|
|
14
|
+
import deepMergeObjects from './deepMergeObjects';
|
|
15
|
+
import deepMergeArrays, { DeepMergeOptions } from './deepMergeArrays';
|
|
16
|
+
import deepEqual from './deepEqual';
|
|
17
|
+
import deepMergeMaps from './deepMergeMaps';
|
|
18
|
+
import deepMergeHelper from './deepMergeHelper';
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const deepMergeSets = (target: Set<any>, source: Set<any>, options: DeepMergeOptions = { itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }): Set<any> => {
|
|
22
|
+
// Ensure both target and source are Sets
|
|
23
|
+
if (!(target instanceof Set) || !(source instanceof Set)) return target;
|
|
24
|
+
|
|
25
|
+
// Merge options, with default values
|
|
26
|
+
const opts = Object.assign({ itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }, options),
|
|
27
|
+
|
|
28
|
+
// If cloning is enabled, create a deep copy of the target Set
|
|
29
|
+
result = opts.targetClone ? new Set(target) : target;
|
|
30
|
+
|
|
31
|
+
// Handle different merge strategies based on itemMode
|
|
32
|
+
if (opts.itemMode === 'replace') {
|
|
33
|
+
// Replace mode: clear the target Set and add all items from the source Set
|
|
34
|
+
result.clear();
|
|
35
|
+
for (let item of source) result.add(item);
|
|
36
|
+
return result;
|
|
37
|
+
} else if (opts.itemMode === 'concat') {
|
|
38
|
+
// Concatenate mode: add all items from the source Set to the target Set
|
|
39
|
+
for (let item of source) result.add(item);
|
|
40
|
+
return result;
|
|
41
|
+
} else {
|
|
42
|
+
// Default "merge" mode: recursively merge items in the Sets
|
|
43
|
+
for (let item of source) {
|
|
44
|
+
// Check the type of the target and source items
|
|
45
|
+
let _target = [...result].find(val => deepEqual(val, item)),
|
|
46
|
+
resp = deepMergeHelper(_target, item, opts);
|
|
47
|
+
//resp={result,flag}
|
|
48
|
+
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
49
|
+
!resp.flag && result.add(item);
|
|
50
|
+
};
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default deepMergeSets;
|
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;
|
package/src/wrapArrayMethods.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
2
|
+
* @since Last modified: 2025/12/22 17:14:00
|
|
3
3
|
* Mutates an array by applying one of the allowed mutation methods such as push, pop, shift, unshift, splice, etc.
|
|
4
4
|
* Supports tracking of added, removed, and updated items for undo/redo or tracking purposes.
|
|
5
5
|
* The mutation methods that can be tracked include push, pop, shift, unshift, splice, sort, reverse, fill, and copyWithin.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* methods.push(4); // arr is now [1, 2, 3, 4]
|
|
19
19
|
*/
|
|
20
20
|
'use strict';
|
|
21
|
-
import
|
|
21
|
+
import arrayMutableMethods from "./arrayMutableMethods";
|
|
22
22
|
const wrapArrayMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList, props = {}, }) => {
|
|
23
23
|
// Validation: Ensure target is an array and method is allowed
|
|
24
24
|
if (!Array.isArray(target)) {
|
|
@@ -26,7 +26,7 @@ const wrapArrayMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate =
|
|
|
26
26
|
}
|
|
27
27
|
//使用默认
|
|
28
28
|
if (!allowList || allowList?.length) {
|
|
29
|
-
allowList =
|
|
29
|
+
allowList = arrayMutableMethods;
|
|
30
30
|
}
|
|
31
31
|
const methods = {};
|
|
32
32
|
for (let method of allowList) {
|
|
@@ -42,10 +42,10 @@ const wrapArrayMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate =
|
|
|
42
42
|
context.addedItems = [...args];
|
|
43
43
|
break;
|
|
44
44
|
case 'pop':
|
|
45
|
-
context.
|
|
45
|
+
context.poppedItem = target[len - 1];
|
|
46
46
|
break;
|
|
47
47
|
case 'shift':
|
|
48
|
-
context.
|
|
48
|
+
context.shiftedItem = target[0];
|
|
49
49
|
break;
|
|
50
50
|
case 'splice':
|
|
51
51
|
const [s, d] = args,
|
package/src/wrapArrayMethods.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
2
|
+
* @since Last modified: 2025/12/22 17:14:00
|
|
3
3
|
* Mutates an array by applying one of the allowed mutation methods such as push, pop, shift, unshift, splice, etc.
|
|
4
4
|
* Supports tracking of added, removed, and updated items for undo/redo or tracking purposes.
|
|
5
5
|
* The mutation methods that can be tracked include push, pop, shift, unshift, splice, sort, reverse, fill, and copyWithin.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
'use strict';
|
|
21
21
|
|
|
22
|
-
import
|
|
22
|
+
import arrayMutableMethods, { ArrayMutationNames } from "./arrayMutableMethods";
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -27,8 +27,8 @@ import mutableMethods, { ArrayMutationNames } from "./mutableMethods";
|
|
|
27
27
|
*/
|
|
28
28
|
export interface MutationContextt {
|
|
29
29
|
addedItems?: any[]; // Items added to the array
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
poppedItem?: any; // Value popped from the array
|
|
31
|
+
shiftedItem?: any; // Value shifted from the array
|
|
32
32
|
deletedItems?: any[]; // Items removed from the array
|
|
33
33
|
oldSnapshot?: any[]; // Snapshot of the array before methods like 'sort' or 'reverse'
|
|
34
34
|
oldItems?: any[]; // Items overwritten (for methods like 'fill' or 'copyWithin')
|
|
@@ -87,7 +87,7 @@ const wrapArrayMethods = <T = any>({
|
|
|
87
87
|
}
|
|
88
88
|
//使用默认
|
|
89
89
|
if (!allowList || allowList?.length) {
|
|
90
|
-
allowList =
|
|
90
|
+
allowList = arrayMutableMethods;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
const methods: Partial<ArrayMutationMethods<T>> = {};
|
|
@@ -109,10 +109,10 @@ const wrapArrayMethods = <T = any>({
|
|
|
109
109
|
context.addedItems = [...args];
|
|
110
110
|
break;
|
|
111
111
|
case 'pop':
|
|
112
|
-
context.
|
|
112
|
+
context.poppedItem = target[len - 1];
|
|
113
113
|
break;
|
|
114
114
|
case 'shift':
|
|
115
|
-
context.
|
|
115
|
+
context.shiftedItem = target[0];
|
|
116
116
|
break;
|
|
117
117
|
case 'splice':
|
|
118
118
|
const [s, d] = args,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2025/12/22 16:37:23
|
|
3
|
+
* Wraps Map objects with mutation tracking capabilities.
|
|
4
|
+
* Provides hooks for before/after mutation events and captures detailed context
|
|
5
|
+
* for undo/redo or change tracking purposes.
|
|
6
|
+
*
|
|
7
|
+
* @template K - Map key type
|
|
8
|
+
* @template V - Map value type
|
|
9
|
+
* @param {MapMutationOptions<K, V>} options - Configuration object containing target Map and callbacks
|
|
10
|
+
* @returns {MapMutationMethods<K, V>} - Object containing wrapped Map mutation methods
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Basic usage with tracking
|
|
14
|
+
* const map = new Map([['key1', 'value1']]);
|
|
15
|
+
* const methods = wrapMap({
|
|
16
|
+
* target: map,
|
|
17
|
+
* onAfterMutate: (patch) => {
|
|
18
|
+
* console.log(`Map ${patch.method} called`, patch.context);
|
|
19
|
+
* }
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* methods.set('key2', 'value2'); // Tracked
|
|
23
|
+
* methods.delete('key1'); // Tracked
|
|
24
|
+
* methods.clear(); // Tracked
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // With undo/redo support
|
|
28
|
+
* const history: MapMutationPatch<any, any>[] = [];
|
|
29
|
+
* const map = new Map<string, number>();
|
|
30
|
+
*
|
|
31
|
+
* const methods = wrapMap({
|
|
32
|
+
* target: map,
|
|
33
|
+
* onBeforeMutate: (context) => {
|
|
34
|
+
* console.log('Before mutation:', context);
|
|
35
|
+
* },
|
|
36
|
+
* onAfterMutate: (patch) => {
|
|
37
|
+
* history.push(patch);
|
|
38
|
+
* console.log('Mutation recorded:', patch.method, 'History size:', history.length);
|
|
39
|
+
* }
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
42
|
+
'use strict';
|
|
43
|
+
/**
|
|
44
|
+
* Available Map mutation methods
|
|
45
|
+
*/
|
|
46
|
+
export const mapMutableMethods = ['set', 'delete', 'clear'];
|
|
47
|
+
/**
|
|
48
|
+
* Creates wrapped mutation methods for Map with tracking capabilities
|
|
49
|
+
*
|
|
50
|
+
* @param options Configuration options
|
|
51
|
+
* @returns Object containing wrapped Map mutation methods
|
|
52
|
+
* @throws TypeError if target is not a Map
|
|
53
|
+
*/
|
|
54
|
+
const wrapMap = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList = mapMutableMethods, props = {}, }) => {
|
|
55
|
+
// Validation: Ensure target is a Map
|
|
56
|
+
if (!(target instanceof Map)) {
|
|
57
|
+
throw new TypeError("The 'target' parameter must be a Map.");
|
|
58
|
+
}
|
|
59
|
+
// Create method wrappers
|
|
60
|
+
const methods = {};
|
|
61
|
+
// Helper to create wrapped method
|
|
62
|
+
const createWrappedMethod = (method) => {
|
|
63
|
+
return function (...args) {
|
|
64
|
+
const context = {};
|
|
65
|
+
// Capture pre-mutation context based on method
|
|
66
|
+
switch (method) {
|
|
67
|
+
case 'set': {
|
|
68
|
+
const [key, newValue] = args;
|
|
69
|
+
context.key = key;
|
|
70
|
+
context.newValue = newValue;
|
|
71
|
+
context.hadKey = target.has(key);
|
|
72
|
+
context.oldValue = context.hadKey ? target.get(key) : undefined;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case 'delete': {
|
|
76
|
+
const [key] = args;
|
|
77
|
+
context.key = key;
|
|
78
|
+
context.existed = target.has(key);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case 'clear': {
|
|
82
|
+
context.clearedEntries = Array.from(target.entries());
|
|
83
|
+
context.previousSize = target.size;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Execute before mutation callback
|
|
88
|
+
onBeforeMutate(context);
|
|
89
|
+
// Execute the native Map method
|
|
90
|
+
const result = target[method].apply(target, args);
|
|
91
|
+
// Construct patch object
|
|
92
|
+
const patch = {
|
|
93
|
+
method,
|
|
94
|
+
result,
|
|
95
|
+
args,
|
|
96
|
+
context,
|
|
97
|
+
target,
|
|
98
|
+
...props
|
|
99
|
+
};
|
|
100
|
+
// Execute after mutation callback
|
|
101
|
+
onAfterMutate(patch);
|
|
102
|
+
return result;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
// Wrap allowed methods
|
|
106
|
+
for (const method of allowList) {
|
|
107
|
+
if (mapMutableMethods.includes(method)) {
|
|
108
|
+
methods[method] = createWrappedMethod(method);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Add target reference
|
|
112
|
+
Object.defineProperty(methods, 'target', {
|
|
113
|
+
get: () => target,
|
|
114
|
+
enumerable: false,
|
|
115
|
+
configurable: false
|
|
116
|
+
});
|
|
117
|
+
return methods;
|
|
118
|
+
};
|
|
119
|
+
export default wrapMap;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2025/12/22 17:19:32
|
|
3
|
+
* Wraps Map objects with mutation tracking capabilities.
|
|
4
|
+
* Provides hooks for before/after mutation events and captures detailed context
|
|
5
|
+
* for undo/redo or change tracking purposes.
|
|
6
|
+
*
|
|
7
|
+
* @template K - Map key type
|
|
8
|
+
* @template V - Map value type
|
|
9
|
+
* @param {MapMutationOptions<K, V>} options - Configuration object containing target Map and callbacks
|
|
10
|
+
* @returns {MapMutationMethods<K, V>} - Object containing wrapped Map mutation methods
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Basic usage with tracking
|
|
14
|
+
* const map = new Map([['key1', 'value1']]);
|
|
15
|
+
* const methods = wrapMap({
|
|
16
|
+
* target: map,
|
|
17
|
+
* onAfterMutate: (patch) => {
|
|
18
|
+
* console.log(`Map ${patch.method} called`, patch.context);
|
|
19
|
+
* }
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* methods.set('key2', 'value2'); // Tracked
|
|
23
|
+
* methods.delete('key1'); // Tracked
|
|
24
|
+
* methods.clear(); // Tracked
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // With undo/redo support
|
|
28
|
+
* const history: MapMutationPatch<any, any>[] = [];
|
|
29
|
+
* const map = new Map<string, number>();
|
|
30
|
+
*
|
|
31
|
+
* const methods = wrapMap({
|
|
32
|
+
* target: map,
|
|
33
|
+
* onBeforeMutate: (context) => {
|
|
34
|
+
* console.log('Before mutation:', context);
|
|
35
|
+
* },
|
|
36
|
+
* onAfterMutate: (patch) => {
|
|
37
|
+
* history.push(patch);
|
|
38
|
+
* console.log('Mutation recorded:', patch.method, 'History size:', history.length);
|
|
39
|
+
* }
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
42
|
+
'use strict';
|
|
43
|
+
import mapMutableMethods from "./mapMutableMethods";
|
|
44
|
+
/**
|
|
45
|
+
* Creates wrapped mutation methods for Map with tracking capabilities
|
|
46
|
+
*
|
|
47
|
+
* @param options Configuration options
|
|
48
|
+
* @returns Object containing wrapped Map mutation methods
|
|
49
|
+
* @throws TypeError if target is not a Map
|
|
50
|
+
*/
|
|
51
|
+
const wrapMapMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList = mapMutableMethods, props = {}, }) => {
|
|
52
|
+
// Validation: Ensure target is a Map
|
|
53
|
+
if (!(target instanceof Map)) {
|
|
54
|
+
throw new TypeError("The 'target' parameter must be a Map.");
|
|
55
|
+
}
|
|
56
|
+
// Create method wrappers
|
|
57
|
+
const methods = {};
|
|
58
|
+
// Helper to create wrapped method
|
|
59
|
+
const createWrappedMethod = (method) => {
|
|
60
|
+
return function (...args) {
|
|
61
|
+
const context = {};
|
|
62
|
+
// Capture pre-mutation context based on method
|
|
63
|
+
switch (method) {
|
|
64
|
+
case 'set': {
|
|
65
|
+
const [key, newValue] = args;
|
|
66
|
+
context.key = key;
|
|
67
|
+
context.newValue = newValue;
|
|
68
|
+
context.existed = target.has(key);
|
|
69
|
+
context.oldValue = context.existed ? target.get(key) : undefined;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case 'delete': {
|
|
73
|
+
const [key] = args;
|
|
74
|
+
context.key = key;
|
|
75
|
+
context.existed = target.has(key);
|
|
76
|
+
context.value = context.existed ? target.get(key) : undefined;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case 'clear': {
|
|
80
|
+
context.clearedItems = Array.from(target.entries());
|
|
81
|
+
//用来做验证
|
|
82
|
+
context.previousSize = target.size;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Execute before mutation callback
|
|
87
|
+
onBeforeMutate(context);
|
|
88
|
+
// Execute the native Map method
|
|
89
|
+
const result = target[method].apply(target, args);
|
|
90
|
+
// Construct patch object
|
|
91
|
+
const patch = {
|
|
92
|
+
method,
|
|
93
|
+
result,
|
|
94
|
+
args,
|
|
95
|
+
context,
|
|
96
|
+
target,
|
|
97
|
+
...props
|
|
98
|
+
};
|
|
99
|
+
// Execute after mutation callback
|
|
100
|
+
onAfterMutate(patch);
|
|
101
|
+
return result;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
// Wrap allowed methods
|
|
105
|
+
for (const method of allowList) {
|
|
106
|
+
if (mapMutableMethods.includes(method)) {
|
|
107
|
+
methods[method] = createWrappedMethod(method);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Add target reference
|
|
111
|
+
Object.defineProperty(methods, 'target', {
|
|
112
|
+
get: () => target,
|
|
113
|
+
enumerable: false,
|
|
114
|
+
configurable: false
|
|
115
|
+
});
|
|
116
|
+
return methods;
|
|
117
|
+
};
|
|
118
|
+
export default wrapMapMethods;
|