@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.
Files changed (48) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/utils.cjs.js +488 -28
  3. package/dist/utils.cjs.min.js +3 -3
  4. package/dist/utils.esm.js +488 -28
  5. package/dist/utils.esm.min.js +3 -3
  6. package/dist/utils.umd - /345/211/257/346/234/254.js" +749 -0
  7. package/dist/utils.umd.js +490 -32
  8. package/dist/utils.umd.min.js +3 -3
  9. package/dist.zip +0 -0
  10. package/modules.js +24 -4
  11. package/modules.ts +24 -4
  12. package/package.json +1 -1
  13. package/src/arrayMutableMethods - /345/211/257/346/234/254.js" +5 -0
  14. package/src/arrayMutableMethods.js +5 -0
  15. package/src/{mutableMethods.ts → arrayMutableMethods.ts} +3 -3
  16. package/src/deepClone.js +151 -26
  17. package/src/deepClone.ts +194 -35
  18. package/src/deepCloneToJSON - /345/211/257/346/234/254.js" +47 -0
  19. package/src/deepEqual.js +48 -0
  20. package/src/deepEqual.ts +46 -0
  21. package/src/deepMerge.js +34 -0
  22. package/src/deepMerge.ts +40 -0
  23. package/src/deepMergeArrays.js +45 -0
  24. package/src/deepMergeArrays.ts +62 -0
  25. package/src/deepMergeHelper.js +40 -0
  26. package/src/deepMergeHelper.ts +45 -0
  27. package/src/deepMergeMaps - /345/211/257/346/234/254.js" +78 -0
  28. package/src/deepMergeMaps.js +57 -0
  29. package/src/deepMergeMaps.ts +67 -0
  30. package/src/deepMergeObjects.js +82 -0
  31. package/src/deepMergeObjects.ts +85 -0
  32. package/src/deepMergeSets.js +48 -0
  33. package/src/deepMergeSets.ts +55 -0
  34. package/src/getUniqueId.js +11 -7
  35. package/src/getUniqueId.ts +16 -9
  36. package/src/mapMutableMethods.js +5 -0
  37. package/src/mapMutableMethods.ts +15 -0
  38. package/src/mutableMethods.js +2 -2
  39. package/src/setMutableMethods - /345/211/257/346/234/254.js" +5 -0
  40. package/src/setMutableMethods.js +5 -0
  41. package/src/setMutableMethods.ts +14 -0
  42. package/src/wrapArrayMethods.js +5 -5
  43. package/src/wrapArrayMethods.ts +7 -7
  44. package/src/wrapMap - /345/211/257/346/234/254.js" +119 -0
  45. package/src/wrapMapMethods.js +118 -0
  46. package/src/wrapMapMethods.ts +226 -0
  47. package/src/wrapSetMethods.js +112 -0
  48. package/src/wrapSetMethods.ts +215 -0
package/dist/utils.umd.js CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
- * @since Last modified: 2025-12-19 15:23:38
3
+ * @since Last modified: 2025-12-24 17:50:8
4
4
  * @name Utils for web front-end.
5
- * @version 0.0.9
5
+ * @version 0.0.11
6
6
  * @author AXUI development team <3217728223@qq.com>
7
7
  * @description This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.
8
8
  * @see {@link https://www.axui.cn|Official website}
@@ -15,10 +15,10 @@
15
15
  */
16
16
 
17
17
  (function (global, factory) {
18
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
19
- typeof define === 'function' && define.amd ? define(factory) :
20
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.utils = factory());
21
- })(this, (function () { 'use strict';
18
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('assert')) :
19
+ typeof define === 'function' && define.amd ? define(['assert'], factory) :
20
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.utils = factory(global.assert));
21
+ })(this, (function (assert) { 'use strict';
22
22
 
23
23
  const getDataType = (obj) => {
24
24
  let tmp = Object.prototype.toString.call(obj).slice(8, -1), result;
@@ -45,37 +45,100 @@
45
45
  //document.createElementNS('http://www.w3.org/1998/Math/MathML', 'math'); -> MathMLElement
46
46
  };
47
47
 
48
- const deepClone = (data) => {
49
- const dataType = getDataType(data);
50
- if (dataType === 'Object') {
51
- const newObj = {};
52
- const symbols = Object.getOwnPropertySymbols(data);
48
+ //支持原始值的复制,包括Number、String、Boolean、Null
49
+ //支持Date和Regex对象值复制(值转换)
50
+ //支持{},[],Set和Map的遍历
51
+ const deepClone = (data, options = {}) => {
52
+ const dataType = getDataType(data),
53
+ // Default options
54
+ opts = Object.assign({
55
+ cloneSet: true,
56
+ cloneMap: true,
57
+ cloneObject: true,
58
+ cloneArray: true,
59
+ //可重新构建
60
+ cloneDate: true,
61
+ cloneRegex: true,
62
+ //节点默认不复制
63
+ //cloneElement: false,
64
+ //cloneFragment: false,
65
+ }, options);
66
+ // Check interceptor - if it returns a value (not null/undefined), use it directly
67
+ if (opts.interceptor && typeof opts.interceptor === 'function') {
68
+ let result = opts.interceptor(data, dataType);
69
+ if ((result ?? false)) {
70
+ // Call onAfterClone if set
71
+ opts.onAfterClone?.({
72
+ output: result,
73
+ input: data,
74
+ type: dataType,
75
+ cloned: result !== data,
76
+ });
77
+ return result;
78
+ }
79
+ // If interceptor returns null/undefined, continue with normal cloning process
80
+ }
81
+ // Callback before cloning
82
+ opts.onBeforeClone?.(data, dataType);
83
+ let newData, cloned = true;
84
+ if (dataType === 'Object' && opts.cloneObject) {
85
+ const newObj = {}, symbols = Object.getOwnPropertySymbols(data);
53
86
  // Clone regular properties
54
87
  for (const key in data) {
55
- newObj[key] = deepClone(data[key]);
88
+ newObj[key] = deepClone(data[key], opts);
56
89
  }
57
90
  // Clone Symbol properties
58
91
  if (symbols.length > 0) {
59
92
  for (const symbol of symbols) {
60
- newObj[symbol] = deepClone(data[symbol]);
93
+ newObj[symbol] = deepClone(data[symbol], opts);
61
94
  }
62
95
  }
63
- return newObj;
96
+ newData = newObj;
64
97
  }
65
- else if (dataType === 'Array') {
66
- return data.map(item => deepClone(item));
98
+ else if (dataType === 'Array' && opts.cloneArray) {
99
+ newData = data.map(item => deepClone(item, opts));
67
100
  }
68
- else if (dataType === 'Date') {
69
- return new Date(data.getTime());
101
+ else if (dataType === 'Map' && opts.cloneMap) {
102
+ const newMap = new Map();
103
+ for (const [key, value] of data) {
104
+ // Both Map keys and values need deep cloning
105
+ newMap.set(deepClone(key, opts), deepClone(value, opts));
106
+ }
107
+ newData = newMap;
108
+ }
109
+ else if (dataType === 'Set' && opts.cloneSet) {
110
+ const newSet = new Set();
111
+ for (const value of data) {
112
+ // Set values need deep cloning
113
+ newSet.add(deepClone(value, opts));
114
+ }
115
+ newData = newSet;
70
116
  }
71
- else if (dataType === 'RegExp') {
117
+ else if (dataType === 'Date' && opts.cloneDate) {
118
+ newData = new Date(data.getTime());
119
+ }
120
+ else if (dataType === 'RegExp' && opts.cloneRegex) {
72
121
  const regex = data;
73
- return new RegExp(regex.source, regex.flags);
122
+ newData = new RegExp(regex.source, regex.flags);
123
+ // } else if ((dataType.includes('HTML') && opts.cloneElement) ||
124
+ // (dataType === 'DocumentFragment' && opts.cloneFragment)
125
+ //) {
126
+ //Text,Comment,HTML*Element,DocumentFragment,Attr
127
+ // newData = (data as any).cloneNode(true) as T;
74
128
  }
75
129
  else {
76
- // Number, String, Boolean, Symbol,Text,Comment,Set,Map, HTML*Element, Function,Error,Promise,ArrayBuffer,Blob,File, return directly
77
- return data;
130
+ // Number, String, Boolean, Symbol, Function,Error,Promise,ArrayBuffer,Blob,File, return directly
131
+ newData = data;
132
+ cloned = false;
78
133
  }
134
+ // Callback after cloning
135
+ opts.onAfterClone?.({
136
+ output: newData,
137
+ input: data,
138
+ type: dataType,
139
+ cloned,
140
+ });
141
+ return newData;
79
142
  };
80
143
 
81
144
  const deepCloneToJSON = (data) => {
@@ -106,7 +169,7 @@
106
169
  }
107
170
  };
108
171
 
109
- const mutableMethods = [
172
+ const arrayMutableMethods = [
110
173
  'push', 'pop', 'shift', 'unshift', 'splice',
111
174
  'sort', 'reverse', 'copyWithin', 'fill'
112
175
  ];
@@ -118,7 +181,7 @@
118
181
  }
119
182
  //使用默认
120
183
  if (!allowList || allowList?.length) {
121
- allowList = mutableMethods;
184
+ allowList = arrayMutableMethods;
122
185
  }
123
186
  const methods = {};
124
187
  for (let method of allowList) {
@@ -134,10 +197,10 @@
134
197
  context.addedItems = [...args];
135
198
  break;
136
199
  case 'pop':
137
- context.poppedValue = target[len - 1];
200
+ context.poppedItem = target[len - 1];
138
201
  break;
139
202
  case 'shift':
140
- context.shiftedValue = target[0];
203
+ context.shiftedItem = target[0];
141
204
  break;
142
205
  case 'splice':
143
206
  const [s, d] = args,
@@ -207,20 +270,405 @@
207
270
  return dataType;
208
271
  };
209
272
 
210
- const getUniqueId = (prefix, extra = true) => {
273
+ const getUniqueId = (options = {}) => {
274
+ const prefix = options.prefix, suffix = options.suffix, base10 = options.base10, base36 = options.base36;
211
275
  // Current timestamp in milliseconds (since Unix epoch)
212
276
  // This provides the primary uniqueness guarantee
213
277
  const timestamp = Date.now(),
214
278
  // Generate a base-36 random string (0-9, a-z)
215
279
  // Math.random() returns a number in [0, 1), converting to base-36 gives a compact representation
216
280
  // substring(2, 11) extracts 9 characters starting from index 2
217
- random = Math.random().toString(36).substring(2, 11),
281
+ //0.259854635->0.9crs03e8v2
282
+ base36Random = base36 ? '-' + Math.random().toString(36).substring(2, 11) : '',
218
283
  // Additional 4-digit random number for extra randomness
219
284
  // This helps avoid collisions in high-frequency generation scenarios
220
- extraRandom = extra ? '_' + Math.floor(Math.random() * 10000).toString().padStart(4, '0') : '', prefixString = prefix ? prefix + '_' : '';
285
+ base10Random = base10 ? '-' + Math.floor(Math.random() * 10000).toString().padStart(4, '0') : '', prefixString = prefix ? prefix + '-' : '', suffixString = suffix ? '-' + suffix : '';
221
286
  // Construct the final ID string
222
287
  // Format: [prefix_]timestamp_randomBase36_extraRandom
223
- return `${prefixString}${timestamp}_${random}${extraRandom}`;
288
+ return `${prefixString}${timestamp}${base36Random}${base10Random}${suffixString}`;
289
+ };
290
+
291
+ const setMutableMethods = ['add', 'delete', 'clear'];
292
+
293
+ const mapMutableMethods = ['set', 'delete', 'clear'];
294
+
295
+ const wrapSetMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList = setMutableMethods, props = {}, }) => {
296
+ // Validation: Ensure target is a Set
297
+ if (!(target instanceof Set)) {
298
+ throw new TypeError("The 'target' parameter must be a Set.");
299
+ }
300
+ // Create method wrappers
301
+ const methods = {};
302
+ // Helper to create wrapped method
303
+ const createWrappedMethod = (method) => {
304
+ return function (...args) {
305
+ const context = {};
306
+ // Capture pre-mutation context based on method
307
+ switch (method) {
308
+ case 'add': {
309
+ const [value] = args;
310
+ context.addedItem = value;
311
+ //context.existed=true,说明值重复
312
+ context.existed = target.has(value);
313
+ break;
314
+ }
315
+ case 'delete': {
316
+ const [value] = args;
317
+ context.existed = target.has(value);
318
+ context.deletedItem = context.existed ? value : undefined;
319
+ break;
320
+ }
321
+ case 'clear': {
322
+ context.clearedItems = Array.from(target);
323
+ //用来做验证
324
+ context.previousSize = target.size;
325
+ break;
326
+ }
327
+ }
328
+ // Execute before mutation callback
329
+ onBeforeMutate(context);
330
+ // Execute the native Set method
331
+ const result = target[method].apply(target, args);
332
+ // Construct patch object
333
+ const patch = {
334
+ method,
335
+ result,
336
+ args,
337
+ context,
338
+ target,
339
+ ...props
340
+ };
341
+ // Execute after mutation callback
342
+ onAfterMutate(patch);
343
+ return result;
344
+ };
345
+ };
346
+ // Wrap allowed methods
347
+ for (const method of allowList) {
348
+ if (setMutableMethods.includes(method)) {
349
+ methods[method] = createWrappedMethod(method);
350
+ }
351
+ }
352
+ // Add target reference
353
+ Object.defineProperty(methods, 'target', {
354
+ get: () => target,
355
+ enumerable: false,
356
+ configurable: false
357
+ });
358
+ return methods;
359
+ };
360
+
361
+ const wrapMapMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList = mapMutableMethods, props = {}, }) => {
362
+ // Validation: Ensure target is a Map
363
+ if (!(target instanceof Map)) {
364
+ throw new TypeError("The 'target' parameter must be a Map.");
365
+ }
366
+ // Create method wrappers
367
+ const methods = {};
368
+ // Helper to create wrapped method
369
+ const createWrappedMethod = (method) => {
370
+ return function (...args) {
371
+ const context = {};
372
+ // Capture pre-mutation context based on method
373
+ switch (method) {
374
+ case 'set': {
375
+ const [key, newValue] = args;
376
+ context.key = key;
377
+ context.newValue = newValue;
378
+ context.existed = target.has(key);
379
+ context.oldValue = context.existed ? target.get(key) : undefined;
380
+ break;
381
+ }
382
+ case 'delete': {
383
+ const [key] = args;
384
+ context.key = key;
385
+ context.existed = target.has(key);
386
+ context.value = context.existed ? target.get(key) : undefined;
387
+ break;
388
+ }
389
+ case 'clear': {
390
+ context.clearedItems = Array.from(target.entries());
391
+ //用来做验证
392
+ context.previousSize = target.size;
393
+ break;
394
+ }
395
+ }
396
+ // Execute before mutation callback
397
+ onBeforeMutate(context);
398
+ // Execute the native Map method
399
+ const result = target[method].apply(target, args);
400
+ // Construct patch object
401
+ const patch = {
402
+ method,
403
+ result,
404
+ args,
405
+ context,
406
+ target,
407
+ ...props
408
+ };
409
+ // Execute after mutation callback
410
+ onAfterMutate(patch);
411
+ return result;
412
+ };
413
+ };
414
+ // Wrap allowed methods
415
+ for (const method of allowList) {
416
+ if (mapMutableMethods.includes(method)) {
417
+ methods[method] = createWrappedMethod(method);
418
+ }
419
+ }
420
+ // Add target reference
421
+ Object.defineProperty(methods, 'target', {
422
+ get: () => target,
423
+ enumerable: false,
424
+ configurable: false
425
+ });
426
+ return methods;
427
+ };
428
+
429
+ const deepMergeObjects = (target, source, opts = {}) => {
430
+ let targetType = getDataType(target), sourceType = getDataType(source);
431
+ //target不是对象或者source为空则直接返回
432
+ if (targetType !== 'Object' || sourceType !== 'Object') {
433
+ return target;
434
+ }
435
+ const options = Object.assign({ itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }, opts),
436
+ //如果是复制方法,则先复制target
437
+ result = options.targetClone ? deepClone(target) : target;
438
+ for (let k in source) {
439
+ if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
440
+ let resp = deepMergeHelper(result[k], source[k], opts);
441
+ //resp={result,flag,type}
442
+ //flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
443
+ if (!resp.flag) {
444
+ //类型不同则直接覆盖
445
+ if (options.useEnable && result.hasOwnProperty(k) && result[k]?.hasOwnProperty('enable') && typeof source[k] === 'boolean') {
446
+ //部分替换,仅针对result={enable:true/false,a:''},source=false/true这种情况和相反的情况,因为这种情况再笨框架比较多见
447
+ if (result[k]?.hasOwnProperty('enable') && typeof source[k] === 'boolean') {
448
+ //result={enable:true,a:'',b:''},source[k]=false=>result={enable:false,a:'',b:''}
449
+ result[k].enable = source[k];
450
+ }
451
+ else if (source[k]?.hasOwnProperty('enable') && typeof result[k] === 'boolean') {
452
+ //source={enable:true,a:'',b:''},(result as any)[k]=false=>result={enable:false,a:'',b:''}
453
+ result = Object.assign({ enable: result[k] }, source[k]);
454
+ }
455
+ else {
456
+ //完全替换
457
+ result[k] = source[k];
458
+ }
459
+ }
460
+ else {
461
+ //完全替换
462
+ result[k] = source[k];
463
+ }
464
+ }
465
+ else {
466
+ // If both target and source are objects, merge them recursively
467
+ if (resp.type === 'Object') {
468
+ result[k] = resp.result;
469
+ }
470
+ }
471
+ }
472
+ else if (source.hasOwnProperty(k) && !result.hasOwnProperty(k) && options.propAppend) {
473
+ //如果source有属性,result没有该属性,但是options允许追加属性则直接赋值
474
+ result[k] = source[k];
475
+ }
476
+ }
477
+ //Symbol键直接追加,因为Symbol是唯一,结果同Object.assign
478
+ if (options.useSymbol) {
479
+ let symbols = Object.getOwnPropertySymbols(source);
480
+ if (symbols.length > 0) {
481
+ for (let k of symbols) {
482
+ result[k] = source[k];
483
+ }
484
+ }
485
+ }
486
+ return result;
487
+ };
488
+
489
+ const deepMergeArrays = (target, source, options = { itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }) => {
490
+ // Ensure both target and source are arrays
491
+ if (!Array.isArray(target) || !Array.isArray(source))
492
+ return target;
493
+ // Merge options, with default values
494
+ const opts = Object.assign({ itemMode: 'merge', propAppend: true, targetClone: false }, options),
495
+ // If cloning is enabled, create a deep copy of the target array
496
+ result = opts.targetClone ? [...target] : target;
497
+ // Handle different merge strategies based on itemMode
498
+ if (opts.itemMode === 'replace') {
499
+ // Replace mode: clear the target array and push all items from the source array
500
+ result.length = 0;
501
+ result.push(...source);
502
+ return result;
503
+ }
504
+ else if (opts.itemMode === 'concat') {
505
+ // Concatenate mode: append all items from the source array to the target array
506
+ result.push(...source);
507
+ return result;
508
+ }
509
+ else {
510
+ // Default "merge" mode: recursively merge items in the arrays
511
+ for (let i = 0; i < source.length; i++) {
512
+ let resp = deepMergeHelper(result[i], source[i], opts);
513
+ //resp={result,flag,type}
514
+ //flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
515
+ if (!resp.flag) {
516
+ result[i] = source[i];
517
+ }
518
+ }
519
+ return result;
520
+ }
521
+ };
522
+
523
+ const deepMergeMaps = (target, source, options = { itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }) => {
524
+ // Ensure both target and source are Maps
525
+ if (!(target instanceof Map) || !(source instanceof Map))
526
+ return target;
527
+ // Merge options, with default values
528
+ const opts = Object.assign({ itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }, options),
529
+ // If cloning is enabled, create a deep copy of the target Map
530
+ result = opts.targetClone ? new Map(target) : target;
531
+ // Handle different merge strategies based on itemMode
532
+ if (opts.itemMode === 'replace') {
533
+ // Replace mode: clear the target Map and add all entries from the source Map
534
+ result.clear();
535
+ source.forEach((value, key) => result.set(key, value));
536
+ return result;
537
+ }
538
+ else if (opts.itemMode === 'concat') {
539
+ // Concatenate mode: add all entries from the source Map to the target Map
540
+ source.forEach((value, key) => result.set(key, value));
541
+ return result;
542
+ }
543
+ else {
544
+ // Default "merge" mode: recursively merge entries in the Maps
545
+ source.forEach((value, key) => {
546
+ // Check if the key already exists in the target Map
547
+ if (result.has(key)) {
548
+ const targetValue = result.get(key), sourceValue = value, resp = deepMergeHelper(targetValue, sourceValue, opts);
549
+ //resp={result,flag,type}
550
+ //flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
551
+ if (!resp.flag) {
552
+ // For simple values, overwrite the target value with the source value
553
+ result.set(key, sourceValue);
554
+ }
555
+ else {
556
+ // If both target and source are objects, merge them recursively
557
+ resp.type === 'Object' && result.set(key, resp.result);
558
+ }
559
+ }
560
+ else {
561
+ // If the key doesn't exist in the target, add the entry from the source Map
562
+ result.set(key, value);
563
+ }
564
+ });
565
+ return result;
566
+ }
567
+ };
568
+
569
+ const deepEqual = (a, b) => {
570
+ // If both are equal by reference
571
+ if (a === b)
572
+ return true;
573
+ // If both are arrays, check equality recursively
574
+ if (Array.isArray(a) && Array.isArray(b)) {
575
+ if (a.length !== b.length)
576
+ return false;
577
+ for (let i = 0; i < a.length; i++) {
578
+ if (!deepEqual(a[i], b[i]))
579
+ return false;
580
+ }
581
+ return true;
582
+ }
583
+ // If both are objects, check equality recursively
584
+ if (typeof a === 'object' && typeof b === 'object') {
585
+ const keysA = Object.keys(a), keysB = Object.keys(b);
586
+ if (keysA.length !== keysB.length)
587
+ return false;
588
+ for (let key of keysA) {
589
+ if (!keysB.includes(key) || !deepEqual(a[key], b[key]))
590
+ return false;
591
+ }
592
+ return true;
593
+ }
594
+ // For other types, direct comparison
595
+ return a === b;
596
+ };
597
+
598
+ const deepMergeSets = (target, source, options = { itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }) => {
599
+ // Ensure both target and source are Sets
600
+ if (!(target instanceof Set) || !(source instanceof Set))
601
+ return target;
602
+ // Merge options, with default values
603
+ const opts = Object.assign({ itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }, options),
604
+ // If cloning is enabled, create a deep copy of the target Set
605
+ result = opts.targetClone ? new Set(target) : target;
606
+ // Handle different merge strategies based on itemMode
607
+ if (opts.itemMode === 'replace') {
608
+ // Replace mode: clear the target Set and add all items from the source Set
609
+ result.clear();
610
+ for (let item of source)
611
+ result.add(item);
612
+ return result;
613
+ }
614
+ else if (opts.itemMode === 'concat') {
615
+ // Concatenate mode: add all items from the source Set to the target Set
616
+ for (let item of source)
617
+ result.add(item);
618
+ return result;
619
+ }
620
+ else {
621
+ // Default "merge" mode: recursively merge items in the Sets
622
+ for (let item of source) {
623
+ // Check the type of the target and source items
624
+ let _target = [...result].find(val => deepEqual(val, item)), resp = deepMergeHelper(_target, item, opts);
625
+ //resp={result,flag}
626
+ //flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
627
+ !resp.flag && result.add(item);
628
+ }
629
+ return result;
630
+ }
631
+ };
632
+
633
+ // deepMergeHelper.ts
634
+
635
+
636
+ const deepMergeHelper = (target, source, options) => {
637
+ let targetType = getDataType(target), sourceType = getDataType(source), flag = true, type, result;
638
+ if (targetType === 'Object' && sourceType === 'Object') {
639
+ result = deepMergeObjects(target, source, options);
640
+ type = 'Object';
641
+ }
642
+ else if (targetType === 'Array' && sourceType === 'Array') {
643
+ result = deepMergeArrays(target, source, options);
644
+ type = 'Array';
645
+ }
646
+ else if (targetType === 'Set' && sourceType === 'Set') {
647
+ result = deepMergeSets(target, source, options);
648
+ type = 'Set';
649
+ }
650
+ else if (targetType === 'Map' && sourceType === 'Map') {
651
+ result = deepMergeMaps(target, source, options);
652
+ type = 'Map';
653
+ }
654
+ else {
655
+ flag = false;
656
+ result = target; // Default case, replace primitive values
657
+ }
658
+ return {
659
+ result, flag, type
660
+ };
661
+ };
662
+
663
+ const deepMerge = (target, source, opts = {}) => {
664
+ // Get the data types of the target and source
665
+ let options = Object.assign({
666
+ itemMode: 'merge', // Default merge mode
667
+ propAppend: true, // Default to appending properties from source to target
668
+ targetClone: false, // Do not clone target by default
669
+ useEnable: true // Enable special handling for objects with an `enable` property
670
+ }, opts);
671
+ return deepMergeHelper(target, source, options);
224
672
  };
225
673
 
226
674
  const utils = {
@@ -232,8 +680,18 @@
232
680
  deepClone,
233
681
  deepCloneToJSON,
234
682
  wrapArrayMethods,
235
- mutableMethods,
236
- getUniqueId
683
+ arrayMutableMethods,
684
+ setMutableMethods,
685
+ mapMutableMethods,
686
+ wrapSetMethods,
687
+ wrapMapMethods,
688
+ getUniqueId,
689
+ deepEqual: assert.deepEqual,
690
+ deepMerge,
691
+ deepMergeArrays,
692
+ deepMergeMaps,
693
+ deepMergeObjects,
694
+ deepMergeSets,
237
695
  };
238
696
 
239
697
  return utils;
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @since Last modified: 2025-12-19 15:23:38
2
+ * @since Last modified: 2025-12-24 17:50:8
3
3
  * @name Utils for web front-end.
4
- * @version 0.0.9
4
+ * @version 0.0.11
5
5
  * @author AXUI development team <3217728223@qq.com>
6
6
  * @description This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.
7
7
  * @see {@link https://www.axui.cn|Official website}
@@ -12,4 +12,4 @@
12
12
  * @copyright This software supports the MIT License, allowing free learning and commercial use, but please retain the terms 'ax,' 'axui,' 'AX,' and 'AXUI' within the software.
13
13
  * @license MIT license
14
14
  */
15
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).utils=t()}(this,function(){"use strict";const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=e=>{const t=getDataType(e);if("Object"===t){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r]);if(r.length>0)for(const o of r)t[o]=deepClone(e[o]);return t}if("Array"===t)return e.map(e=>deepClone(e));if("Date"===t)return new Date(e.getTime());if("RegExp"===t){const t=e;return new RegExp(t.source,t.flags)}return e},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const r in e)t[r]=deepCloneToJSON(e[r]);for(const e in t)void 0===t[e]&&Reflect.deleteProperty(t,e);return t}if("Array"===t){return e.map((e,t)=>deepCloneToJSON(e)).filter(e=>void 0!==e)}return["Number","String","Boolean","Null"].includes(t)?e:void 0},e=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"];return{getDataType:getDataType,requireTypes:(e,t,r)=>{let o=Array.isArray(t)?t:[t],n=getDataType(e),s=n.toLowerCase(),a=o.map(e=>e.toLowerCase()),i=s.includes("html")?"element":s;if(r)try{if(!a.includes(i))throw new TypeError(`Expected data type(s): [${a.join(", ")}], but got: ${i}`)}catch(e){r(e,n)}else if(!a.includes(i))throw new TypeError(`Expected data type(s): [${a.join(", ")}], but got: ${i}`);return n},deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:({target:t,onBeforeMutate:r=()=>{},onAfterMutate:o=()=>{},allowList:n,props:s={}})=>{if(!Array.isArray(t))throw new TypeError("The 'target' parameter must be an array.");n&&!n?.length||(n=e);const a={};for(let e of n)a[e]=function(...n){const a={},i=t.length;switch(e){case"push":case"unshift":a.addedItems=[...n];break;case"pop":a.poppedValue=t[i-1];break;case"shift":a.shiftedValue=t[0];break;case"splice":const[e,r]=n,o=e<0?Math.max(i+e,0):Math.min(e,i),s=void 0===r?i-o:r;a.deletedItems=t.slice(o,o+s);break;case"sort":case"reverse":a.oldSnapshot=[...t];break;case"fill":case"copyWithin":const c=n[1]||0,l=void 0===n[2]?i:n[2];a.oldItems=t.slice(c,l),a.start=c,a.end=l}r?.(a);const c=Array.prototype[e].apply(t,n),l={value:c,key:e,args:n,context:a,target:t,...s};return o?.(l),c};return a},mutableMethods:e,getUniqueId:(e,t=!0)=>`${e?e+"_":""}${Date.now()}_${Math.random().toString(36).substring(2,11)}${t?"_"+Math.floor(1e4*Math.random()).toString().padStart(4,"0"):""}`}});
15
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("assert")):"function"==typeof define&&define.amd?define(["assert"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).utils=t(e.assert)}(this,function(e){"use strict";const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=(e,t={})=>{const r=getDataType(e),o=Object.assign({cloneSet:!0,cloneMap:!0,cloneObject:!0,cloneArray:!0,cloneDate:!0,cloneRegex:!0},t);if(o.interceptor&&"function"==typeof o.interceptor){let t=o.interceptor(e,r);if(t)return o.onAfterClone?.({output:t,input:e,type:r,cloned:t!==e}),t}o.onBeforeClone?.(e,r);let n,a=!0;if("Object"===r&&o.cloneObject){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r],o);if(r.length>0)for(const n of r)t[n]=deepClone(e[n],o);n=t}else if("Array"===r&&o.cloneArray)n=e.map(e=>deepClone(e,o));else if("Map"===r&&o.cloneMap){const t=new Map;for(const[r,n]of e)t.set(deepClone(r,o),deepClone(n,o));n=t}else if("Set"===r&&o.cloneSet){const t=new Set;for(const r of e)t.add(deepClone(r,o));n=t}else if("Date"===r&&o.cloneDate)n=new Date(e.getTime());else if("RegExp"===r&&o.cloneRegex){const t=e;n=new RegExp(t.source,t.flags)}else n=e,a=!1;return o.onAfterClone?.({output:n,input:e,type:r,cloned:a}),n},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const r in e)t[r]=deepCloneToJSON(e[r]);for(const e in t)void 0===t[e]&&Reflect.deleteProperty(t,e);return t}if("Array"===t){return e.map((e,t)=>deepCloneToJSON(e)).filter(e=>void 0!==e)}return["Number","String","Boolean","Null"].includes(t)?e:void 0},t=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],r=["add","delete","clear"],o=["set","delete","clear"],deepMergeObjects=(e,t,r={})=>{let o=getDataType(e),n=getDataType(t);if("Object"!==o||"Object"!==n)return e;const a=Object.assign({itemMode:"merge",propAppend:!0,targetClone:!1,useEnable:!0},r),s=a.targetClone?deepClone(e):e;for(let e in t)if(t.hasOwnProperty(e)&&s.hasOwnProperty(e)){let o=deepMergeHelper(s[e],t[e],r);o.flag?"Object"===o.type&&(s[e]=o.result):a.useEnable&&s.hasOwnProperty(e)&&s[e]?.hasOwnProperty("enable")&&"boolean"==typeof t[e]?s[e]?.hasOwnProperty("enable")&&"boolean"==typeof t[e]?s[e].enable=t[e]:t[e]?.hasOwnProperty("enable")&&"boolean"==typeof s[e]?s=Object.assign({enable:s[e]},t[e]):s[e]=t[e]:s[e]=t[e]}else t.hasOwnProperty(e)&&!s.hasOwnProperty(e)&&a.propAppend&&(s[e]=t[e]);if(a.useSymbol){let e=Object.getOwnPropertySymbols(t);if(e.length>0)for(let r of e)s[r]=t[r]}return s},deepMergeArrays=(e,t,r={itemMode:"merge",propAppend:!0,targetClone:!1,useEnable:!0})=>{if(!Array.isArray(e)||!Array.isArray(t))return e;const o=Object.assign({itemMode:"merge",propAppend:!0,targetClone:!1},r),n=o.targetClone?[...e]:e;if("replace"===o.itemMode)return n.length=0,n.push(...t),n;if("concat"===o.itemMode)return n.push(...t),n;for(let e=0;e<t.length;e++){deepMergeHelper(n[e],t[e],o).flag||(n[e]=t[e])}return n},deepMergeMaps=(e,t,r={itemMode:"merge",propAppend:!0,targetClone:!1,useEnable:!0})=>{if(!(e instanceof Map&&t instanceof Map))return e;const o=Object.assign({itemMode:"merge",propAppend:!0,targetClone:!1,useEnable:!0},r),n=o.targetClone?new Map(e):e;return"replace"===o.itemMode?(n.clear(),t.forEach((e,t)=>n.set(t,e)),n):"concat"===o.itemMode?(t.forEach((e,t)=>n.set(t,e)),n):(t.forEach((e,t)=>{if(n.has(t)){const r=n.get(t),a=e,s=deepMergeHelper(r,a,o);s.flag?"Object"===s.type&&n.set(t,s.result):n.set(t,a)}else n.set(t,e)}),n)},deepEqual=(e,t)=>{if(e===t)return!0;if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(!deepEqual(e[r],t[r]))return!1;return!0}if("object"==typeof e&&"object"==typeof t){const r=Object.keys(e),o=Object.keys(t);if(r.length!==o.length)return!1;for(let n of r)if(!o.includes(n)||!deepEqual(e[n],t[n]))return!1;return!0}return e===t},deepMergeSets=(e,t,r={itemMode:"merge",propAppend:!0,targetClone:!1,useEnable:!0})=>{if(!(e instanceof Set&&t instanceof Set))return e;const o=Object.assign({itemMode:"merge",propAppend:!0,targetClone:!1,useEnable:!0},r),n=o.targetClone?new Set(e):e;if("replace"===o.itemMode){n.clear();for(let e of t)n.add(e);return n}if("concat"===o.itemMode){for(let e of t)n.add(e);return n}for(let e of t){let t=[...n].find(t=>deepEqual(t,e));!deepMergeHelper(t,e,o).flag&&n.add(e)}return n},deepMergeHelper=(e,t,r)=>{let o,n,a=getDataType(e),s=getDataType(t),l=!0;return"Object"===a&&"Object"===s?(n=deepMergeObjects(e,t,r),o="Object"):"Array"===a&&"Array"===s?(n=deepMergeArrays(e,t,r),o="Array"):"Set"===a&&"Set"===s?(n=deepMergeSets(e,t,r),o="Set"):"Map"===a&&"Map"===s?(n=deepMergeMaps(e,t,r),o="Map"):(l=!1,n=e),{result:n,flag:l,type:o}};return{getDataType:getDataType,requireTypes:(e,t,r)=>{let o=Array.isArray(t)?t:[t],n=getDataType(e),a=n.toLowerCase(),s=o.map(e=>e.toLowerCase()),l=a.includes("html")?"element":a;if(r)try{if(!s.includes(l))throw new TypeError(`Expected data type(s): [${s.join(", ")}], but got: ${l}`)}catch(e){r(e,n)}else if(!s.includes(l))throw new TypeError(`Expected data type(s): [${s.join(", ")}], but got: ${l}`);return n},deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:({target:e,onBeforeMutate:r=()=>{},onAfterMutate:o=()=>{},allowList:n,props:a={}})=>{if(!Array.isArray(e))throw new TypeError("The 'target' parameter must be an array.");n&&!n?.length||(n=t);const s={};for(let t of n)s[t]=function(...n){const s={},l=e.length;switch(t){case"push":case"unshift":s.addedItems=[...n];break;case"pop":s.poppedItem=e[l-1];break;case"shift":s.shiftedItem=e[0];break;case"splice":const[t,r]=n,o=t<0?Math.max(l+t,0):Math.min(t,l),a=void 0===r?l-o:r;s.deletedItems=e.slice(o,o+a);break;case"sort":case"reverse":s.oldSnapshot=[...e];break;case"fill":case"copyWithin":const c=n[1]||0,i=void 0===n[2]?l:n[2];s.oldItems=e.slice(c,i),s.start=c,s.end=i}r?.(s);const c=Array.prototype[t].apply(e,n),i={value:c,key:t,args:n,context:s,target:e,...a};return o?.(i),c};return s},arrayMutableMethods:t,setMutableMethods:r,mapMutableMethods:o,wrapSetMethods:({target:e,onBeforeMutate:t=()=>{},onAfterMutate:o=()=>{},allowList:n=r,props:a={}})=>{if(!(e instanceof Set))throw new TypeError("The 'target' parameter must be a Set.");const s={},createWrappedMethod=r=>function(...n){const s={};switch(r){case"add":{const[t]=n;s.addedItem=t,s.existed=e.has(t);break}case"delete":{const[t]=n;s.existed=e.has(t),s.deletedItem=s.existed?t:void 0;break}case"clear":s.clearedItems=Array.from(e),s.previousSize=e.size}t(s);const l=e[r].apply(e,n),c={method:r,result:l,args:n,context:s,target:e,...a};return o(c),l};for(const e of n)r.includes(e)&&(s[e]=createWrappedMethod(e));return Object.defineProperty(s,"target",{get:()=>e,enumerable:!1,configurable:!1}),s},wrapMapMethods:({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:n=o,props:a={}})=>{if(!(e instanceof Map))throw new TypeError("The 'target' parameter must be a Map.");const s={},createWrappedMethod=o=>function(...n){const s={};switch(o){case"set":{const[t,r]=n;s.key=t,s.newValue=r,s.existed=e.has(t),s.oldValue=s.existed?e.get(t):void 0;break}case"delete":{const[t]=n;s.key=t,s.existed=e.has(t),s.value=s.existed?e.get(t):void 0;break}case"clear":s.clearedItems=Array.from(e.entries()),s.previousSize=e.size}t(s);const l=e[o].apply(e,n),c={method:o,result:l,args:n,context:s,target:e,...a};return r(c),l};for(const e of n)o.includes(e)&&(s[e]=createWrappedMethod(e));return Object.defineProperty(s,"target",{get:()=>e,enumerable:!1,configurable:!1}),s},getUniqueId:(e={})=>{const t=e.prefix,r=e.suffix,o=e.base10,n=e.base36;return`${t?t+"-":""}${Date.now()}${n?"-"+Math.random().toString(36).substring(2,11):""}${o?"-"+Math.floor(1e4*Math.random()).toString().padStart(4,"0"):""}${r?"-"+r:""}`},deepEqual:e.deepEqual,deepMerge:(e,t,r={})=>{let o=Object.assign({itemMode:"merge",propAppend:!0,targetClone:!1,useEnable:!0},r);return deepMergeHelper(e,t,o)},deepMergeArrays:deepMergeArrays,deepMergeMaps:deepMergeMaps,deepMergeObjects:deepMergeObjects,deepMergeSets:deepMergeSets}});
package/dist.zip CHANGED
Binary file