@codady/utils 0.0.8 → 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/dist/utils.esm.js CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
- * @since Last modified: 2025-12-19 10:14:9
3
+ * @since Last modified: 2025-12-22 20:26:42
4
4
  * @name Utils for web front-end.
5
- * @version 0.0.7
5
+ * @version 0.0.10
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}
@@ -39,37 +39,100 @@ const getDataType = (obj) => {
39
39
  //document.createElementNS('http://www.w3.org/1998/Math/MathML', 'math'); -> MathMLElement
40
40
  };
41
41
 
42
- const deepClone = (data) => {
43
- const dataType = getDataType(data);
44
- if (dataType === 'Object') {
45
- const newObj = {};
46
- const symbols = Object.getOwnPropertySymbols(data);
42
+ //支持原始值的复制,包括Number、String、Boolean、Null
43
+ //支持Date和Regex对象值复制(值转换)
44
+ //支持{},[],Set和Map的遍历
45
+ const deepClone = (data, options = {}) => {
46
+ const dataType = getDataType(data),
47
+ // Default options
48
+ opts = Object.assign({
49
+ cloneSet: true,
50
+ cloneMap: true,
51
+ cloneObject: true,
52
+ cloneArray: true,
53
+ //可重新构建
54
+ cloneDate: true,
55
+ cloneRegex: true,
56
+ //节点默认不复制
57
+ //cloneElement: false,
58
+ //cloneFragment: false,
59
+ }, options);
60
+ // Check interceptor - if it returns a value (not null/undefined), use it directly
61
+ if (opts.interceptor && typeof opts.interceptor === 'function') {
62
+ let result = opts.interceptor(data, dataType);
63
+ if ((result ?? false)) {
64
+ // Call onAfterClone if set
65
+ opts.onAfterClone?.({
66
+ output: result,
67
+ input: data,
68
+ type: dataType,
69
+ cloned: result !== data,
70
+ });
71
+ return result;
72
+ }
73
+ // If interceptor returns null/undefined, continue with normal cloning process
74
+ }
75
+ // Callback before cloning
76
+ opts.onBeforeClone?.(data, dataType);
77
+ let newData, cloned = true;
78
+ if (dataType === 'Object' && opts.cloneObject) {
79
+ const newObj = {}, symbols = Object.getOwnPropertySymbols(data);
47
80
  // Clone regular properties
48
81
  for (const key in data) {
49
- newObj[key] = deepClone(data[key]);
82
+ newObj[key] = deepClone(data[key], opts);
50
83
  }
51
84
  // Clone Symbol properties
52
85
  if (symbols.length > 0) {
53
86
  for (const symbol of symbols) {
54
- newObj[symbol] = deepClone(data[symbol]);
87
+ newObj[symbol] = deepClone(data[symbol], opts);
55
88
  }
56
89
  }
57
- return newObj;
90
+ newData = newObj;
58
91
  }
59
- else if (dataType === 'Array') {
60
- return data.map(item => deepClone(item));
92
+ else if (dataType === 'Array' && opts.cloneArray) {
93
+ newData = data.map(item => deepClone(item, opts));
61
94
  }
62
- else if (dataType === 'Date') {
63
- return new Date(data.getTime());
95
+ else if (dataType === 'Map' && opts.cloneMap) {
96
+ const newMap = new Map();
97
+ for (const [key, value] of data) {
98
+ // Both Map keys and values need deep cloning
99
+ newMap.set(deepClone(key, opts), deepClone(value, opts));
100
+ }
101
+ newData = newMap;
102
+ }
103
+ else if (dataType === 'Set' && opts.cloneSet) {
104
+ const newSet = new Set();
105
+ for (const value of data) {
106
+ // Set values need deep cloning
107
+ newSet.add(deepClone(value, opts));
108
+ }
109
+ newData = newSet;
64
110
  }
65
- else if (dataType === 'RegExp') {
111
+ else if (dataType === 'Date' && opts.cloneDate) {
112
+ newData = new Date(data.getTime());
113
+ }
114
+ else if (dataType === 'RegExp' && opts.cloneRegex) {
66
115
  const regex = data;
67
- return new RegExp(regex.source, regex.flags);
116
+ newData = new RegExp(regex.source, regex.flags);
117
+ // } else if ((dataType.includes('HTML') && opts.cloneElement) ||
118
+ // (dataType === 'DocumentFragment' && opts.cloneFragment)
119
+ //) {
120
+ //Text,Comment,HTML*Element,DocumentFragment,Attr
121
+ // newData = (data as any).cloneNode(true) as T;
68
122
  }
69
123
  else {
70
- // Number, String, Boolean, Symbol,Text,Comment,Set,Map, HTML*Element, Function,Error,Promise,ArrayBuffer,Blob,File, return directly
71
- return data;
124
+ // Number, String, Boolean, Symbol, Function,Error,Promise,ArrayBuffer,Blob,File, return directly
125
+ newData = data;
126
+ cloned = false;
72
127
  }
128
+ // Callback after cloning
129
+ opts.onAfterClone?.({
130
+ output: newData,
131
+ input: data,
132
+ type: dataType,
133
+ cloned,
134
+ });
135
+ return newData;
73
136
  };
74
137
 
75
138
  const deepCloneToJSON = (data) => {
@@ -100,7 +163,7 @@ const deepCloneToJSON = (data) => {
100
163
  }
101
164
  };
102
165
 
103
- const mutableMethods = [
166
+ const arrayMutableMethods = [
104
167
  'push', 'pop', 'shift', 'unshift', 'splice',
105
168
  'sort', 'reverse', 'copyWithin', 'fill'
106
169
  ];
@@ -112,7 +175,7 @@ const wrapArrayMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate =
112
175
  }
113
176
  //使用默认
114
177
  if (!allowList || allowList?.length) {
115
- allowList = mutableMethods;
178
+ allowList = arrayMutableMethods;
116
179
  }
117
180
  const methods = {};
118
181
  for (let method of allowList) {
@@ -128,10 +191,10 @@ const wrapArrayMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate =
128
191
  context.addedItems = [...args];
129
192
  break;
130
193
  case 'pop':
131
- context.poppedValue = target[len - 1];
194
+ context.poppedItem = target[len - 1];
132
195
  break;
133
196
  case 'shift':
134
- context.shiftedValue = target[0];
197
+ context.shiftedItem = target[0];
135
198
  break;
136
199
  case 'splice':
137
200
  const [s, d] = args,
@@ -201,6 +264,162 @@ const requireTypes = (data, require, cb) => {
201
264
  return dataType;
202
265
  };
203
266
 
267
+ const getUniqueId = (options = {}) => {
268
+ const prefix = options.prefix, suffix = options.suffix, base10 = options.base10, base36 = options.base36;
269
+ // Current timestamp in milliseconds (since Unix epoch)
270
+ // This provides the primary uniqueness guarantee
271
+ const timestamp = Date.now(),
272
+ // Generate a base-36 random string (0-9, a-z)
273
+ // Math.random() returns a number in [0, 1), converting to base-36 gives a compact representation
274
+ // substring(2, 11) extracts 9 characters starting from index 2
275
+ //0.259854635->0.9crs03e8v2
276
+ base36Random = base36 ? '-' + Math.random().toString(36).substring(2, 11) : '',
277
+ // Additional 4-digit random number for extra randomness
278
+ // This helps avoid collisions in high-frequency generation scenarios
279
+ base10Random = base10 ? '-' + Math.floor(Math.random() * 10000).toString().padStart(4, '0') : '', prefixString = prefix ? prefix + '-' : '', suffixString = suffix ? '-' + suffix : '';
280
+ // Construct the final ID string
281
+ // Format: [prefix_]timestamp_randomBase36_extraRandom
282
+ return `${prefixString}${timestamp}${base36Random}${base10Random}${suffixString}`;
283
+ };
284
+
285
+ const setMutableMethods = ['add', 'delete', 'clear'];
286
+
287
+ const mapMutableMethods = ['set', 'delete', 'clear'];
288
+
289
+ const wrapSetMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList = setMutableMethods, props = {}, }) => {
290
+ // Validation: Ensure target is a Set
291
+ if (!(target instanceof Set)) {
292
+ throw new TypeError("The 'target' parameter must be a Set.");
293
+ }
294
+ // Create method wrappers
295
+ const methods = {};
296
+ // Helper to create wrapped method
297
+ const createWrappedMethod = (method) => {
298
+ return function (...args) {
299
+ const context = {};
300
+ // Capture pre-mutation context based on method
301
+ switch (method) {
302
+ case 'add': {
303
+ const [value] = args;
304
+ context.addedItem = value;
305
+ //context.existed=true,说明值重复
306
+ context.existed = target.has(value);
307
+ break;
308
+ }
309
+ case 'delete': {
310
+ const [value] = args;
311
+ context.existed = target.has(value);
312
+ context.deletedItem = context.existed ? value : undefined;
313
+ break;
314
+ }
315
+ case 'clear': {
316
+ context.clearedItems = Array.from(target);
317
+ //用来做验证
318
+ context.previousSize = target.size;
319
+ break;
320
+ }
321
+ }
322
+ // Execute before mutation callback
323
+ onBeforeMutate(context);
324
+ // Execute the native Set method
325
+ const result = target[method].apply(target, args);
326
+ // Construct patch object
327
+ const patch = {
328
+ method,
329
+ result,
330
+ args,
331
+ context,
332
+ target,
333
+ ...props
334
+ };
335
+ // Execute after mutation callback
336
+ onAfterMutate(patch);
337
+ return result;
338
+ };
339
+ };
340
+ // Wrap allowed methods
341
+ for (const method of allowList) {
342
+ if (setMutableMethods.includes(method)) {
343
+ methods[method] = createWrappedMethod(method);
344
+ }
345
+ }
346
+ // Add target reference
347
+ Object.defineProperty(methods, 'target', {
348
+ get: () => target,
349
+ enumerable: false,
350
+ configurable: false
351
+ });
352
+ return methods;
353
+ };
354
+
355
+ const wrapMapMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList = mapMutableMethods, props = {}, }) => {
356
+ // Validation: Ensure target is a Map
357
+ if (!(target instanceof Map)) {
358
+ throw new TypeError("The 'target' parameter must be a Map.");
359
+ }
360
+ // Create method wrappers
361
+ const methods = {};
362
+ // Helper to create wrapped method
363
+ const createWrappedMethod = (method) => {
364
+ return function (...args) {
365
+ const context = {};
366
+ // Capture pre-mutation context based on method
367
+ switch (method) {
368
+ case 'set': {
369
+ const [key, newValue] = args;
370
+ context.key = key;
371
+ context.newValue = newValue;
372
+ context.existed = target.has(key);
373
+ context.oldValue = context.existed ? target.get(key) : undefined;
374
+ break;
375
+ }
376
+ case 'delete': {
377
+ const [key] = args;
378
+ context.key = key;
379
+ context.existed = target.has(key);
380
+ context.value = context.existed ? target.get(key) : undefined;
381
+ break;
382
+ }
383
+ case 'clear': {
384
+ context.clearedItems = Array.from(target.entries());
385
+ //用来做验证
386
+ context.previousSize = target.size;
387
+ break;
388
+ }
389
+ }
390
+ // Execute before mutation callback
391
+ onBeforeMutate(context);
392
+ // Execute the native Map method
393
+ const result = target[method].apply(target, args);
394
+ // Construct patch object
395
+ const patch = {
396
+ method,
397
+ result,
398
+ args,
399
+ context,
400
+ target,
401
+ ...props
402
+ };
403
+ // Execute after mutation callback
404
+ onAfterMutate(patch);
405
+ return result;
406
+ };
407
+ };
408
+ // Wrap allowed methods
409
+ for (const method of allowList) {
410
+ if (mapMutableMethods.includes(method)) {
411
+ methods[method] = createWrappedMethod(method);
412
+ }
413
+ }
414
+ // Add target reference
415
+ Object.defineProperty(methods, 'target', {
416
+ get: () => target,
417
+ enumerable: false,
418
+ configurable: false
419
+ });
420
+ return methods;
421
+ };
422
+
204
423
  const utils = {
205
424
  //executeStr,
206
425
  getDataType,
@@ -210,7 +429,12 @@ const utils = {
210
429
  deepClone,
211
430
  deepCloneToJSON,
212
431
  wrapArrayMethods,
213
- mutableMethods
432
+ arrayMutableMethods,
433
+ setMutableMethods,
434
+ mapMutableMethods,
435
+ wrapSetMethods,
436
+ wrapMapMethods,
437
+ getUniqueId
214
438
  };
215
439
 
216
440
  export { utils as default };
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @since Last modified: 2025-12-19 10:14:9
2
+ * @since Last modified: 2025-12-22 20:26:42
3
3
  * @name Utils for web front-end.
4
- * @version 0.0.7
4
+ * @version 0.0.10
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
- 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},mutableMethods=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],wrapArrayMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:o,props:a={}})=>{if(!Array.isArray(e))throw new TypeError("The 'target' parameter must be an array.");o&&!o?.length||(o=mutableMethods);const s={};for(let n of o)s[n]=function(...o){const s={},p=e.length;switch(n){case"push":case"unshift":s.addedItems=[...o];break;case"pop":s.poppedValue=e[p-1];break;case"shift":s.shiftedValue=e[0];break;case"splice":const[t,r]=o,a=t<0?Math.max(p+t,0):Math.min(t,p),n=void 0===r?p-a:r;s.deletedItems=e.slice(a,a+n);break;case"sort":case"reverse":s.oldSnapshot=[...e];break;case"fill":case"copyWithin":const l=o[1]||0,i=void 0===o[2]?p:o[2];s.oldItems=e.slice(l,i),s.start=l,s.end=i}t?.(s);const l=Array.prototype[n].apply(e,o),i={value:l,key:n,args:o,context:s,target:e,...a};return r?.(i),l};return s},requireTypes=(e,t,r)=>{let o=Array.isArray(t)?t:[t],a=getDataType(e),s=a.toLowerCase(),n=o.map(e=>e.toLowerCase()),p=s.includes("html")?"element":s;if(r)try{if(!n.includes(p))throw new TypeError(`Expected data type(s): [${n.join(", ")}], but got: ${p}`)}catch(e){r(e,a)}else if(!n.includes(p))throw new TypeError(`Expected data type(s): [${n.join(", ")}], but got: ${p}`);return a},utils={getDataType:getDataType,requireTypes:requireTypes,deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:wrapArrayMethods,mutableMethods:mutableMethods};export{utils as default};
15
+ const getDataType=e=>{let t,o=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===o&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===o&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":o,t},deepClone=(e,t={})=>{const o=getDataType(e),r=Object.assign({cloneSet:!0,cloneMap:!0,cloneObject:!0,cloneArray:!0,cloneDate:!0,cloneRegex:!0},t);if(r.interceptor&&"function"==typeof r.interceptor){let t=r.interceptor(e,o);if(t)return r.onAfterClone?.({output:t,input:e,type:o,cloned:t!==e}),t}r.onBeforeClone?.(e,o);let a,s=!0;if("Object"===o&&r.cloneObject){const t={},o=Object.getOwnPropertySymbols(e);for(const o in e)t[o]=deepClone(e[o],r);if(o.length>0)for(const a of o)t[a]=deepClone(e[a],r);a=t}else if("Array"===o&&r.cloneArray)a=e.map(e=>deepClone(e,r));else if("Map"===o&&r.cloneMap){const t=new Map;for(const[o,a]of e)t.set(deepClone(o,r),deepClone(a,r));a=t}else if("Set"===o&&r.cloneSet){const t=new Set;for(const o of e)t.add(deepClone(o,r));a=t}else if("Date"===o&&r.cloneDate)a=new Date(e.getTime());else if("RegExp"===o&&r.cloneRegex){const t=e;a=new RegExp(t.source,t.flags)}else a=e,s=!1;return r.onAfterClone?.({output:a,input:e,type:o,cloned:s}),a},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const o in e)t[o]=deepCloneToJSON(e[o]);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},arrayMutableMethods=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],wrapArrayMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:o=()=>{},allowList:r,props:a={}})=>{if(!Array.isArray(e))throw new TypeError("The 'target' parameter must be an array.");r&&!r?.length||(r=arrayMutableMethods);const s={};for(let n of r)s[n]=function(...r){const s={},l=e.length;switch(n){case"push":case"unshift":s.addedItems=[...r];break;case"pop":s.poppedItem=e[l-1];break;case"shift":s.shiftedItem=e[0];break;case"splice":const[t,o]=r,a=t<0?Math.max(l+t,0):Math.min(t,l),n=void 0===o?l-a:o;s.deletedItems=e.slice(a,a+n);break;case"sort":case"reverse":s.oldSnapshot=[...e];break;case"fill":case"copyWithin":const c=r[1]||0,p=void 0===r[2]?l:r[2];s.oldItems=e.slice(c,p),s.start=c,s.end=p}t?.(s);const c=Array.prototype[n].apply(e,r),p={value:c,key:n,args:r,context:s,target:e,...a};return o?.(p),c};return s},requireTypes=(e,t,o)=>{let r=Array.isArray(t)?t:[t],a=getDataType(e),s=a.toLowerCase(),n=r.map(e=>e.toLowerCase()),l=s.includes("html")?"element":s;if(o)try{if(!n.includes(l))throw new TypeError(`Expected data type(s): [${n.join(", ")}], but got: ${l}`)}catch(e){o(e,a)}else if(!n.includes(l))throw new TypeError(`Expected data type(s): [${n.join(", ")}], but got: ${l}`);return a},getUniqueId=(e={})=>{const t=e.prefix,o=e.suffix,r=e.base10,a=e.base36;return`${t?t+"-":""}${Date.now()}${a?"-"+Math.random().toString(36).substring(2,11):""}${r?"-"+Math.floor(1e4*Math.random()).toString().padStart(4,"0"):""}${o?"-"+o:""}`},setMutableMethods=["add","delete","clear"],mapMutableMethods=["set","delete","clear"],wrapSetMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:o=()=>{},allowList:r=setMutableMethods,props:a={}})=>{if(!(e instanceof Set))throw new TypeError("The 'target' parameter must be a Set.");const s={},createWrappedMethod=r=>function(...s){const n={};switch(r){case"add":{const[t]=s;n.addedItem=t,n.existed=e.has(t);break}case"delete":{const[t]=s;n.existed=e.has(t),n.deletedItem=n.existed?t:void 0;break}case"clear":n.clearedItems=Array.from(e),n.previousSize=e.size}t(n);const l=e[r].apply(e,s),c={method:r,result:l,args:s,context:n,target:e,...a};return o(c),l};for(const e of r)setMutableMethods.includes(e)&&(s[e]=createWrappedMethod(e));return Object.defineProperty(s,"target",{get:()=>e,enumerable:!1,configurable:!1}),s},wrapMapMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:o=()=>{},allowList:r=mapMutableMethods,props:a={}})=>{if(!(e instanceof Map))throw new TypeError("The 'target' parameter must be a Map.");const s={},createWrappedMethod=r=>function(...s){const n={};switch(r){case"set":{const[t,o]=s;n.key=t,n.newValue=o,n.existed=e.has(t),n.oldValue=n.existed?e.get(t):void 0;break}case"delete":{const[t]=s;n.key=t,n.existed=e.has(t),n.value=n.existed?e.get(t):void 0;break}case"clear":n.clearedItems=Array.from(e.entries()),n.previousSize=e.size}t(n);const l=e[r].apply(e,s),c={method:r,result:l,args:s,context:n,target:e,...a};return o(c),l};for(const e of r)mapMutableMethods.includes(e)&&(s[e]=createWrappedMethod(e));return Object.defineProperty(s,"target",{get:()=>e,enumerable:!1,configurable:!1}),s},utils={getDataType:getDataType,requireTypes:requireTypes,deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:wrapArrayMethods,arrayMutableMethods:arrayMutableMethods,setMutableMethods:setMutableMethods,mapMutableMethods:mapMutableMethods,wrapSetMethods:wrapSetMethods,wrapMapMethods:wrapMapMethods,getUniqueId:getUniqueId};export{utils as default};