@codady/utils 0.0.38 → 0.0.40

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 (58) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/utils.cjs.js +576 -24
  3. package/dist/utils.cjs.min.js +3 -3
  4. package/dist/utils.esm.js +576 -24
  5. package/dist/utils.esm.min.js +3 -3
  6. package/dist/utils.umd.js +576 -24
  7. package/dist/utils.umd.min.js +3 -3
  8. package/dist.zip +0 -0
  9. package/examples/ajax-download.html +94 -0
  10. package/examples/ajax-get.html +59 -0
  11. package/examples/ajax-hook.html +55 -0
  12. package/examples/ajax-method.html +36 -0
  13. package/examples/ajax-post.html +37 -0
  14. package/examples/ajax-signal.html +91 -0
  15. package/examples/ajax-timeout.html +85 -0
  16. package/examples/buildUrl.html +99 -0
  17. package/examples/getUrlHash.html +71 -0
  18. package/examples/stringToEncodings-collision-test-registry.html +117 -0
  19. package/examples/stringToEncodings-collision-test.html +71 -0
  20. package/examples/stringToEncodings.html +138 -0
  21. package/examples/unicodeToEncodings.html +195 -0
  22. package/modules.js +17 -1
  23. package/modules.ts +17 -1
  24. package/package.json +1 -1
  25. package/src/ajax.js +380 -0
  26. package/src/ajax.ts +470 -0
  27. package/src/buildUrl.js +64 -0
  28. package/src/buildUrl.ts +86 -0
  29. package/src/capitalize.js +19 -0
  30. package/src/capitalize.ts +20 -0
  31. package/src/cleanQueryString.js +19 -0
  32. package/src/cleanQueryString.ts +20 -0
  33. package/src/getBodyHTML.js +53 -0
  34. package/src/getBodyHTML.ts +61 -0
  35. package/src/getEl.js +1 -1
  36. package/src/getEl.ts +6 -5
  37. package/src/getEls.js +1 -1
  38. package/src/getEls.ts +5 -5
  39. package/src/getUrlHash.js +37 -0
  40. package/src/getUrlHash.ts +39 -0
  41. package/src/isEmpty.js +24 -23
  42. package/src/isEmpty.ts +26 -23
  43. package/src/sliceStrEnd.js +63 -0
  44. package/src/sliceStrEnd.ts +60 -0
  45. package/src/stringToEncodings.js +56 -0
  46. package/src/stringToEncodings.ts +110 -0
  47. package/src/unicodeToEncodings.js +51 -0
  48. package/src/unicodeToEncodings.ts +55 -0
  49. package/src/arrayMutableMethods - /345/211/257/346/234/254.js" +0 -5
  50. package/src/comma - /345/211/257/346/234/254.js" +0 -2
  51. package/src/deepCloneToJSON - /345/211/257/346/234/254.js" +0 -47
  52. package/src/deepMergeMaps - /345/211/257/346/234/254.js" +0 -78
  53. package/src/escapeHTML - /345/211/257/346/234/254.js" +0 -29
  54. package/src/getDataType - /345/211/257/346/234/254.js" +0 -38
  55. package/src/isEmpty - /345/211/257/346/234/254.js" +0 -45
  56. package/src/mapMutableMethods - /345/211/257/346/234/254.js" +0 -5
  57. package/src/setMutableMethods - /345/211/257/346/234/254.js" +0 -5
  58. package/src/wrapMap - /345/211/257/346/234/254.js" +0 -119
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @since Last modified: 2026/02/05 15:12:33
3
+ * Generate a stable Unicode code point from a string.
4
+ * - Without registry: FNV-1a 64-bit hash mapping (fast, stateless)
5
+ * - With registry: Guaranteed zero-collision allocation
6
+ */
7
+ export type StringEncodingResult = {
8
+ unicode: string;
9
+ htmlDec: string;
10
+ htmlHex: string;
11
+ hex: string;
12
+ codePoint: number;
13
+ hash: string;
14
+ name: string;
15
+ collision?: boolean;
16
+ };
17
+
18
+ export type StringEncodingOptions = {
19
+ start?: number;
20
+ end?: number;
21
+ registryMap?: Map<string, number>; // optional
22
+ };
23
+
24
+
25
+ const stringToEncodings = (
26
+ name: string,
27
+ options: StringEncodingOptions = {}
28
+ ): StringEncodingResult => {
29
+
30
+ // Default: Supplementary Private Use Area (Plane 15 and Plane 16)
31
+ //1,114,110 places,5000 strings => 0 collision
32
+ const start = options.start ?? 0xF0000,
33
+ end = options.end ?? 0x10FFFD,
34
+ range = BigInt(end - start + 1),
35
+ registry = options.registryMap,
36
+ /** Format result */
37
+ formatResult = (
38
+ name: string,
39
+ codePoint: number,
40
+ hash: string,
41
+ collision: boolean
42
+ ): StringEncodingResult => {
43
+ const hex = codePoint.toString(16).toUpperCase();
44
+
45
+ return {
46
+ name,
47
+ unicode: `U+${hex}`,
48
+ htmlDec: `&#${codePoint};`,
49
+ htmlHex: `&#x${hex};`,
50
+ hex,
51
+ codePoint,
52
+ hash,
53
+ collision,
54
+ };
55
+ };
56
+
57
+
58
+ // -----------------------------
59
+ // 1. Compute FNV-1a 64-bit hash
60
+ // -----------------------------
61
+ let hash = BigInt("0xcbf29ce484222325");
62
+ const prime = BigInt("0x100000001b3");
63
+
64
+ for (const ch of name) {
65
+ hash ^= BigInt(ch.codePointAt(0)!);
66
+ hash *= prime;
67
+ }
68
+
69
+ const hashHex = hash.toString(16).toUpperCase();
70
+
71
+ // -----------------------------
72
+ // 2. Stateless mode (no registry)
73
+ // -----------------------------
74
+ if (!registry) {
75
+ const offset = Number(hash % range),
76
+ codePoint = start + offset;
77
+ return formatResult(name, codePoint, hashHex, false);
78
+ }
79
+
80
+ // -----------------------------
81
+ // 3. Registry mode (0 collision)
82
+ // -----------------------------
83
+
84
+ // Already assigned → return stable mapping
85
+ if (registry.has(name)) {
86
+ return formatResult(name, registry.get(name)!, hashHex, false);
87
+ }
88
+
89
+ // Initial candidate from hash
90
+ let offset = Number(hash % range),
91
+ codePoint = start + offset,
92
+ collision = false;
93
+
94
+ const used = new Set(registry.values());
95
+
96
+ // Linear probing to resolve collisions
97
+ while (used.has(codePoint)) {
98
+ collision = true;
99
+ offset = (offset + 1) % Number(range);
100
+ codePoint = start + offset;
101
+ }
102
+
103
+ // Commit allocation
104
+ registry.set(name, codePoint);
105
+
106
+ return formatResult(name, codePoint, hashHex, collision);
107
+ };
108
+
109
+
110
+ export default stringToEncodings;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @since Last modified: 2026/02/05 17:06:59
3
+ * Parses a Unicode input (string or number) and converts it into
4
+ * multiple encoding formats including Unicode notation, HTML entities,
5
+ * hexadecimal code, and decimal code point.
6
+ *
7
+ * Supported input formats:
8
+ * - Unicode string: "U+F123"
9
+ * - Hex string: "F123", "0xF123"
10
+ * - Decimal string: "61731"
11
+ * - Numeric code point: 61731
12
+ *
13
+ * This utility is useful for:
14
+ * - Icon font Unicode decoding
15
+ * - HTML entity generation
16
+ * - Unicode debugging and inspection
17
+ * - Glyph mapping and font tooling
18
+ * - PUA (Private Use Area) management workflows
19
+ *
20
+ * @param input - Unicode input as a string or numeric code point
21
+ * @returns An object containing Unicode, hex, decimal, and HTML encodings
22
+ * @throws Error if the input cannot be parsed into a valid Unicode code point
23
+ */
24
+ const unicodeToEncodings = (input) => {
25
+ let codePoint;
26
+ if (typeof input === "number") {
27
+ codePoint = input;
28
+ }
29
+ else {
30
+ const cleaned = input.trim()
31
+ .replace(/^U\+/i, "")
32
+ .replace(/^0x/i, "");
33
+ codePoint = /^[0-9A-F]+$/i.test(cleaned)
34
+ ? parseInt(cleaned, 16)
35
+ : parseInt(cleaned, 10);
36
+ }
37
+ // Validate parsed code point
38
+ if (!Number.isFinite(codePoint)) {
39
+ throw new Error("Invalid Unicode input");
40
+ }
41
+ // Convert code point to uppercase hexadecimal representation
42
+ const hex = codePoint.toString(16).toUpperCase();
43
+ return {
44
+ unicode: `U+${hex}`,
45
+ hex,
46
+ codePoint,
47
+ htmlDec: `&#${codePoint};`,
48
+ htmlHex: `&#x${hex};`,
49
+ };
50
+ };
51
+ export default unicodeToEncodings;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @since Last modified: 2026/02/05 17:06:59
3
+ * Parses a Unicode input (string or number) and converts it into
4
+ * multiple encoding formats including Unicode notation, HTML entities,
5
+ * hexadecimal code, and decimal code point.
6
+ *
7
+ * Supported input formats:
8
+ * - Unicode string: "U+F123"
9
+ * - Hex string: "F123", "0xF123"
10
+ * - Decimal string: "61731"
11
+ * - Numeric code point: 61731
12
+ *
13
+ * This utility is useful for:
14
+ * - Icon font Unicode decoding
15
+ * - HTML entity generation
16
+ * - Unicode debugging and inspection
17
+ * - Glyph mapping and font tooling
18
+ * - PUA (Private Use Area) management workflows
19
+ *
20
+ * @param input - Unicode input as a string or numeric code point
21
+ * @returns An object containing Unicode, hex, decimal, and HTML encodings
22
+ * @throws Error if the input cannot be parsed into a valid Unicode code point
23
+ */
24
+ const unicodeToEncodings = (input: string | number) => {
25
+ let codePoint: number;
26
+
27
+ if (typeof input === "number") {
28
+ codePoint = input;
29
+ } else {
30
+ const cleaned = input.trim()
31
+ .replace(/^U\+/i, "")
32
+ .replace(/^0x/i, "");
33
+
34
+ codePoint = /^[0-9A-F]+$/i.test(cleaned)
35
+ ? parseInt(cleaned, 16)
36
+ : parseInt(cleaned, 10);
37
+ }
38
+
39
+ // Validate parsed code point
40
+ if (!Number.isFinite(codePoint)) {
41
+ throw new Error("Invalid Unicode input");
42
+ }
43
+
44
+ // Convert code point to uppercase hexadecimal representation
45
+ const hex = codePoint.toString(16).toUpperCase();
46
+
47
+ return {
48
+ unicode: `U+${hex}`,
49
+ hex,
50
+ codePoint,
51
+ htmlDec: `&#${codePoint};`,
52
+ htmlHex: `&#x${hex};`,
53
+ };
54
+ };
55
+ export default unicodeToEncodings;
@@ -1,5 +0,0 @@
1
- const arrayMutableMethods = [
2
- 'push', 'pop', 'shift', 'unshift', 'splice',
3
- 'sort', 'reverse', 'copyWithin', 'fill'
4
- ];
5
- export default arrayMutableMethods;
@@ -1,2 +0,0 @@
1
- const COMMA = ',';
2
- export default COMMA;
@@ -1,47 +0,0 @@
1
- /**
2
- * @since Last modified: 2025/12/16 14:43:51
3
- * Deep clone an array or object to a JSON-compatible structure.
4
- * Converts non-serializable types like functions, symbols, Date, RegExp to plain values.
5
- *
6
- * @template T
7
- * @param {T} data - Array or object to clone
8
- * @returns {T} - New object with different memory address, in a JSON-compatible structure
9
- *
10
- * @example
11
- * const obj = { a: '', b: 0, c: [] };
12
- * const cloned = deepCloneToJSON(obj);
13
- *
14
- * @example
15
- * const arr = [{}, {}, {}];
16
- * const cloned = deepCloneToJSON(arr);
17
- */
18
- 'use strict';
19
- import getDataType from './getDataType';
20
- const deepCloneToJSON = (data) => {
21
- const dataType = getDataType(data);
22
- // Handle objects
23
- if (dataType === 'Object') {
24
- const newObj = {};
25
- // Clone regular properties
26
- for (const key in data) {
27
- newObj[key] = deepCloneToJSON(data[key]);
28
- }
29
- for (const key in newObj) {
30
- newObj[key] === undefined && Reflect.deleteProperty(newObj, key);
31
- }
32
- return newObj;
33
- // Handle arrays
34
- }
35
- else if (dataType === 'Array') {
36
- let tmp = data.map((item, index) => deepCloneToJSON(item)).filter(item => item !== undefined);
37
- return tmp;
38
- // Handle Date objects
39
- }
40
- else if (!['Number', 'String', 'Boolean', 'Null'].includes(dataType)) {
41
- return undefined;
42
- }
43
- else {
44
- return data;
45
- }
46
- };
47
- export default deepCloneToJSON;
@@ -1,78 +0,0 @@
1
- /**
2
- * @since Last modified: 2025/12/24 17:05:22
3
- * @function deepMergeMaps
4
- * Deeply merges two Maps, with flexible options to replace, concatenate, or merge entries.
5
- * @param target The target Map to merge into
6
- * @param source The source Map to merge from
7
- * @param options Configuration options for merging
8
- * @returns The deeply merged Map
9
- */
10
- 'use strict';
11
- import getDataType from './getDataType';
12
- import deepMergeObjects from './deepMergeObjects';
13
- import deepMergeArrays from './deepMergeArrays';
14
- import deepMergeSets from './deepMergeSets';
15
- const deepMergeMaps = (target, source, options = { itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }) => {
16
- // Ensure both target and source are Maps
17
- if (!(target instanceof Map) || !(source instanceof Map))
18
- return target;
19
- // Merge options, with default values
20
- const opts = Object.assign({ itemMode: 'merge', propAppend: true, targetClone: false, useEnable: true }, options),
21
- // If cloning is enabled, create a deep copy of the target Map
22
- result = opts.targetClone ? new Map(target) : target;
23
- // Handle different merge strategies based on itemMode
24
- if (opts.itemMode === 'replace') {
25
- // Replace mode: clear the target Map and add all entries from the source Map
26
- result.clear();
27
- source.forEach((value, key) => result.set(key, value));
28
- return result;
29
- }
30
- else if (opts.itemMode === 'concat') {
31
- // Concatenate mode: add all entries from the source Map to the target Map
32
- source.forEach((value, key) => result.set(key, value));
33
- return result;
34
- }
35
- else {
36
- // Default "merge" mode: recursively merge entries in the Maps
37
- source.forEach((value, key) => {
38
- // Check if the key already exists in the target Map
39
- if (result.has(key)) {
40
- const targetValue = result.get(key), sourceValue = value;
41
- const targetType = getDataType(targetValue), sourceType = getDataType(sourceValue);
42
- // If both target and source values are of the same type, merge them
43
- if (targetType === sourceType) {
44
- if (targetType === 'Object') {
45
- // If both target and source are objects, merge them recursively
46
- result.set(key, deepMergeObjects(targetValue, sourceValue, opts));
47
- }
48
- else if (targetType === 'Array') {
49
- // If both target and source are arrays, merge them recursively
50
- deepMergeArrays(targetValue, sourceValue, opts);
51
- }
52
- else if (targetType === 'Map') {
53
- // If both target and source are Maps, merge them recursively
54
- deepMergeMaps(targetValue, sourceValue, opts);
55
- }
56
- else if (targetType === 'Set') {
57
- // If both target and source are Sets, merge them recursively
58
- deepMergeSets(targetValue, sourceValue, opts);
59
- }
60
- else {
61
- // For simple values, overwrite the target value with the source value
62
- result.set(key, sourceValue);
63
- }
64
- }
65
- else {
66
- // If types don't match, overwrite the target value with the source value
67
- result.set(key, sourceValue);
68
- }
69
- }
70
- else {
71
- // If the key doesn't exist in the target, add the entry from the source Map
72
- result.set(key, value);
73
- }
74
- });
75
- return result;
76
- }
77
- };
78
- export default deepMergeMaps;
@@ -1,29 +0,0 @@
1
- /**
2
- * @function escapeHTML
3
- * @description Escapes HTML characters in a string to prevent XSS attacks.
4
- * This function replaces special characters such as <, >, &, ", ', etc. with their corresponding HTML entities.
5
- *
6
- * @param {string} str - The input string to be escaped.
7
- * @returns {string} - The escaped string with special HTML characters replaced by HTML entities.
8
- *
9
- * @example
10
- * escapeHTML("<div>Hello & 'world'</div>");
11
- * // returns "&lt;div&gt;Hello &amp; &#39;world&#39;&lt;/div&gt;"
12
- */
13
- export const escapeHTML = (str) => {
14
- // Mapping of HTML special characters to their corresponding HTML entities
15
- const escapes = {
16
- '&': '&amp;',
17
- '<': '&lt;',
18
- '>': '&gt;',
19
- '"': '&quot;',
20
- "'": '&#39;',
21
- '`': '&#96;',
22
- '=': '&#61;',
23
- '/': '&#47;'
24
- };
25
- // Replace each special character in the input string with its corresponding HTML entity
26
- return str.replace(/[&<>"'`=\/]/g, (match) => {
27
- return escapes[match];
28
- });
29
- };
@@ -1,38 +0,0 @@
1
- /**
2
- * @since Last modified: 2025/12/16 09:00:41
3
- * @function getDataType
4
- * @description Get object type.Can detect object types such as Array, Object, Function,
5
- * Window,Location,History,Navigator,XMLHttpRequest, WebSocket,FileReader,MediaStream
6
- * Class , String, Number, Boolean, Date, Symbol ,File ,Blob,
7
- * Error,Promise,ArrayBuffer,TypedArray, Set, weakSet, Map, weakMap, Null, Undefined,
8
- * Text, DocumentFragment,Comment, XMLDocument, ProcessingInstruction, Range, TreeWalker,
9
- * NodeIterator,SVGSVGElement,MathMLElement, HTMLxxxElement (Dom nodes all contain HTML),Promise,AsyncFunction and Instance.
10
- * @param {*} obj - Can be any object
11
- * @returns {string} - Returns the name of the data type.
12
- */
13
- 'use strict';
14
- const getDataType = (obj) => {
15
- let tmp = Object.prototype.toString.call(obj).slice(8, -1), result;
16
- if (tmp === 'Function' && /^\s*class\s+/.test(obj.toString())) {
17
- result = 'Class';
18
- }
19
- else if (tmp === 'Object' && Object.getPrototypeOf(obj) !== Object.prototype) {
20
- result = 'Instance';
21
- }
22
- else {
23
- result = tmp;
24
- }
25
- return result;
26
- //document.createElement -> HTMLxxxElement
27
- //document.createDocumentFragment() -> DocumentFragment
28
- //document.createComment() -> Comment
29
- //document.createTextNode -> Text
30
- //document.createCDATASection() -> XMLDocument
31
- //document.createProcessingInstruction() -> ProcessingInstruction
32
- //document.createRange() -> Range
33
- //document.createTreeWalker() -> TreeWalker
34
- //document.createNodeIterator() -> NodeIterator
35
- //document.createElementNS('http://www.w3.org/2000/svg', 'svg'); -> SVGSVGElement
36
- //document.createElementNS('http://www.w3.org/1998/Math/MathML', 'math'); -> MathMLElement
37
- };
38
- export default getDataType;
@@ -1,45 +0,0 @@
1
- /**
2
- * @since Last modified: 2025/12/25 14:22:26
3
- * @function isEmpty
4
- * @description Determine whether it is empty data.The data itself is empty data: 0| ''|false|undefined|null; <br>empty function: function () {}|() => {}; <br>empty array and empty objects: []|{}| [null]| [ undefined]| ['']| [""];<br> empty symbol object: symbol()|symbol.For(), will be judged as empty.
5
- * @param {*} data - Can be any data
6
- * @returns {boolean} - Return true or false
7
- * @example
8
- * ax.isEmpty([null]);
9
- * <!--return true-->
10
- */
11
- //简介:判断是否为空数据。本身为空的数据:0|''|false|undefined|null;空函数:function(){}|()=>{};空数组和空对象:[]|{}|[null]|[undefined]|['']|[""];空Symbol对象:Symbol()|Symbol.for(),都将判断为空。
12
- //返回:true或false
13
- 'use strict';
14
- import getDataType from './getDataType';
15
- const isEmpty = (data) => {
16
- let type = getDataType(data), flag;
17
- if (!data) {
18
- //0,'',false,undefined,null
19
- flag = true;
20
- }
21
- else {
22
- //function(){}|()=>{}
23
- //[null]|[undefined]|['']|[""]
24
- //[]|{}
25
- //Symbol()|Symbol.for()
26
- //Set,Map
27
- //Date/Regex
28
- flag = (type === 'Object') ? (Object.keys(data).length === 0) :
29
- (type === 'Array') ? data.join('') === '' :
30
- (type === 'Function') ? (data.toString().replace(/\s+/g, '').match(/{.*}/g)[0] === '{}') :
31
- (type === 'Symbol') ? (data.toString().replace(/\s+/g, '').match(/\(.*\)/g)[0] === '()') :
32
- (type === 'Set' || type === 'Map') ? data.size === 0 :
33
- type === 'Date' ? isNaN(data.getTime()) :
34
- type === 'RegExp' ? data.source === '' :
35
- type === 'ArrayBuffer' ? data.byteLength === 0 :
36
- (type === 'NodeList' || type === 'HTMLCollection') ? data.length === 0 :
37
- ('length' in data && typeof data.length === 'number') ? data.length === 0 :
38
- ('size' in data && typeof data.size === 'number') ? data.size === 0 :
39
- (type === 'Error' || data instanceof Error) ? data.message === '' :
40
- (type.includes('Array') && (['Uint8Array', 'Int8Array', 'Uint16Array', 'Int16Array', 'Uint32Array', 'Int32Array', 'Float32Array', 'Float64Array'].includes(type))) ? data.length === 0 :
41
- false;
42
- }
43
- return flag;
44
- };
45
- export default isEmpty;
@@ -1,5 +0,0 @@
1
- /**
2
- * Available Map mutation methods
3
- */
4
- export const mapMutableMethods = ['set', 'delete', 'clear'];
5
- export default mapMutableMethods;
@@ -1,5 +0,0 @@
1
- /**
2
- * Available Set mutation methods
3
- */
4
- export const setMutableMethods = ['add', 'delete', 'clear'];
5
- export default setMutableMethods;
@@ -1,119 +0,0 @@
1
- /**
2
- * @since Last modified: 2025/12/22 16:37:23
3
- * Wraps Map objects with mutation tracking capabilities.
4
- * Provides hooks for before/after mutation events and captures detailed context
5
- * for undo/redo or change tracking purposes.
6
- *
7
- * @template K - Map key type
8
- * @template V - Map value type
9
- * @param {MapMutationOptions<K, V>} options - Configuration object containing target Map and callbacks
10
- * @returns {MapMutationMethods<K, V>} - Object containing wrapped Map mutation methods
11
- *
12
- * @example
13
- * // Basic usage with tracking
14
- * const map = new Map([['key1', 'value1']]);
15
- * const methods = wrapMap({
16
- * target: map,
17
- * onAfterMutate: (patch) => {
18
- * console.log(`Map ${patch.method} called`, patch.context);
19
- * }
20
- * });
21
- *
22
- * methods.set('key2', 'value2'); // Tracked
23
- * methods.delete('key1'); // Tracked
24
- * methods.clear(); // Tracked
25
- *
26
- * @example
27
- * // With undo/redo support
28
- * const history: MapMutationPatch<any, any>[] = [];
29
- * const map = new Map<string, number>();
30
- *
31
- * const methods = wrapMap({
32
- * target: map,
33
- * onBeforeMutate: (context) => {
34
- * console.log('Before mutation:', context);
35
- * },
36
- * onAfterMutate: (patch) => {
37
- * history.push(patch);
38
- * console.log('Mutation recorded:', patch.method, 'History size:', history.length);
39
- * }
40
- * });
41
- */
42
- 'use strict';
43
- /**
44
- * Available Map mutation methods
45
- */
46
- export const mapMutableMethods = ['set', 'delete', 'clear'];
47
- /**
48
- * Creates wrapped mutation methods for Map with tracking capabilities
49
- *
50
- * @param options Configuration options
51
- * @returns Object containing wrapped Map mutation methods
52
- * @throws TypeError if target is not a Map
53
- */
54
- const wrapMap = ({ target, onBeforeMutate = () => { }, onAfterMutate = () => { }, allowList = mapMutableMethods, props = {}, }) => {
55
- // Validation: Ensure target is a Map
56
- if (!(target instanceof Map)) {
57
- throw new TypeError("The 'target' parameter must be a Map.");
58
- }
59
- // Create method wrappers
60
- const methods = {};
61
- // Helper to create wrapped method
62
- const createWrappedMethod = (method) => {
63
- return function (...args) {
64
- const context = {};
65
- // Capture pre-mutation context based on method
66
- switch (method) {
67
- case 'set': {
68
- const [key, newValue] = args;
69
- context.key = key;
70
- context.newValue = newValue;
71
- context.hadKey = target.has(key);
72
- context.oldValue = context.hadKey ? target.get(key) : undefined;
73
- break;
74
- }
75
- case 'delete': {
76
- const [key] = args;
77
- context.key = key;
78
- context.existed = target.has(key);
79
- break;
80
- }
81
- case 'clear': {
82
- context.clearedEntries = Array.from(target.entries());
83
- context.previousSize = target.size;
84
- break;
85
- }
86
- }
87
- // Execute before mutation callback
88
- onBeforeMutate(context);
89
- // Execute the native Map method
90
- const result = target[method].apply(target, args);
91
- // Construct patch object
92
- const patch = {
93
- method,
94
- result,
95
- args,
96
- context,
97
- target,
98
- ...props
99
- };
100
- // Execute after mutation callback
101
- onAfterMutate(patch);
102
- return result;
103
- };
104
- };
105
- // Wrap allowed methods
106
- for (const method of allowList) {
107
- if (mapMutableMethods.includes(method)) {
108
- methods[method] = createWrappedMethod(method);
109
- }
110
- }
111
- // Add target reference
112
- Object.defineProperty(methods, 'target', {
113
- get: () => target,
114
- enumerable: false,
115
- configurable: false
116
- });
117
- return methods;
118
- };
119
- export default wrapMap;