@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.
@@ -0,0 +1,215 @@
1
+ /**
2
+ * @since Last modified: 2025/12/22 17:19:21
3
+ * Wraps Set 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 T - Set element type
8
+ * @param {SetMutationOptions<T>} options - Configuration object containing target Set and callbacks
9
+ * @returns {SetMutationMethods<T>} - Object containing wrapped Set mutation methods
10
+ *
11
+ * @example
12
+ * // Basic usage with tracking
13
+ * const set = new Set([1, 2, 3]);
14
+ * const methods = wrapSet({
15
+ * target: set,
16
+ * onAfterMutate: (patch) => {
17
+ * console.log(`Set ${patch.method} called`, patch.context);
18
+ * }
19
+ * });
20
+ *
21
+ * methods.add(4); // Tracked
22
+ * methods.delete(2); // Tracked
23
+ * methods.clear(); // Tracked
24
+ *
25
+ * @example
26
+ * // With change detection for UI updates
27
+ * const set = new Set<string>();
28
+ * const methods = wrapSet({
29
+ * target: set,
30
+ * onAfterMutate: (patch) => {
31
+ * // Trigger UI update when Set changes
32
+ * if (patch.method === 'add' || patch.method === 'delete') {
33
+ * updateUI(Array.from(patch.target));
34
+ * }
35
+ * }
36
+ * });
37
+ */
38
+ 'use strict';
39
+
40
+ import setMutableMethods, { SetMutationNames } from "./setMutableMethods";
41
+
42
+ /**
43
+ * Context captured before Set mutation for tracking purposes
44
+ */
45
+ export interface SetMutationContext<T = any> {
46
+ /** For add method: the value being added */
47
+ addedItem?: T;
48
+ /** For add / delete method: whether the value already existed */
49
+ existed?: boolean;
50
+ /** For delete method: the value being deleted */
51
+ deletedItem?: T;
52
+ /** For clear method: snapshot of all values before clearing */
53
+ clearedItems?: T[];
54
+ /** For clear method: size of the Set before clearing */
55
+ previousSize?: number;
56
+ }
57
+
58
+ /**
59
+ * Data object passed to callback after Set mutation
60
+ */
61
+ export interface SetMutationPatch<T = any> {
62
+ /** The mutation method called */
63
+ method: SetMutationNames;
64
+ /** The result returned by the mutation method */
65
+ result: any;
66
+ /** Arguments passed to the mutation method */
67
+ args: any[];
68
+ /** Context captured before mutation */
69
+ context: SetMutationContext<T>;
70
+ /** Reference to the mutated Set */
71
+ target: Set<T>;
72
+ /** Additional custom properties */
73
+ [key: string]: any;
74
+ }
75
+
76
+ /**
77
+ * Configuration options for wrapSet function
78
+ */
79
+ export interface SetMutationOptions<T = any> {
80
+ /** The Set to wrap with mutation tracking */
81
+ target: Set<T>;
82
+ /** Callback triggered before mutation */
83
+ onBeforeMutate?: (context: SetMutationContext<T>) => void;
84
+ /** Callback triggered after mutation */
85
+ onAfterMutate?: (patch: SetMutationPatch<T>) => void;
86
+ /** Optional list of allowed mutation methods (default: all Set methods) */
87
+ allowList?: SetMutationNames[];
88
+ /** Additional properties to attach to the patch */
89
+ props?: Record<string, any>;
90
+ }
91
+
92
+ /**
93
+ * Wrapped mutation methods for Set
94
+ */
95
+ export interface SetMutationMethods<T = any> {
96
+ /**
97
+ * Adds a value to the Set.
98
+ * @param value The value to add
99
+ * @returns The Set object
100
+ */
101
+ add: (value: T) => Set<T>;
102
+
103
+ /**
104
+ * Deletes a value from the Set.
105
+ * @param value The value to delete
106
+ * @returns true if the value existed and has been removed, false otherwise
107
+ */
108
+ delete: (value: T) => boolean;
109
+
110
+ /**
111
+ * Clears all values from the Set.
112
+ * @returns void
113
+ */
114
+ clear: () => void;
115
+
116
+ /**
117
+ * Gets the original Set instance for direct access
118
+ */
119
+ readonly target: Set<T>;
120
+ }
121
+
122
+ /**
123
+ * Creates wrapped mutation methods for Set with tracking capabilities
124
+ *
125
+ * @param options Configuration options
126
+ * @returns Object containing wrapped Set mutation methods
127
+ * @throws TypeError if target is not a Set
128
+ */
129
+ const wrapSetMethods = <T = any>({
130
+ target,
131
+ onBeforeMutate = () => { },
132
+ onAfterMutate = () => { },
133
+ allowList = setMutableMethods,
134
+ props = {},
135
+ }: SetMutationOptions<T>): SetMutationMethods<T> => {
136
+
137
+ // Validation: Ensure target is a Set
138
+ if (!(target instanceof Set)) {
139
+ throw new TypeError("The 'target' parameter must be a Set.");
140
+ }
141
+
142
+ // Create method wrappers
143
+ const methods: Partial<SetMutationMethods<T>> = {};
144
+
145
+ // Helper to create wrapped method
146
+ const createWrappedMethod = (method: SetMutationNames) => {
147
+ return function (...args: any[]) {
148
+ const context: SetMutationContext<T> = {};
149
+
150
+ // Capture pre-mutation context based on method
151
+ switch (method) {
152
+ case 'add': {
153
+ const [value] = args as [T];
154
+ context.addedItem = value;
155
+ //context.existed=true,说明值重复
156
+ context.existed = target.has(value);
157
+ break;
158
+ }
159
+
160
+ case 'delete': {
161
+ const [value] = args as [T];
162
+ context.existed = target.has(value);
163
+ context.deletedItem = context.existed ? value : undefined;
164
+ break;
165
+ }
166
+
167
+ case 'clear': {
168
+ context.clearedItems = Array.from(target);
169
+ //用来做验证
170
+ context.previousSize = target.size;
171
+ break;
172
+ }
173
+ }
174
+
175
+ // Execute before mutation callback
176
+ onBeforeMutate(context);
177
+
178
+ // Execute the native Set method
179
+ const result = (target as any)[method].apply(target, args);
180
+
181
+ // Construct patch object
182
+ const patch: SetMutationPatch<T> = {
183
+ method,
184
+ result,
185
+ args,
186
+ context,
187
+ target,
188
+ ...props
189
+ };
190
+
191
+ // Execute after mutation callback
192
+ onAfterMutate(patch);
193
+
194
+ return result;
195
+ };
196
+ };
197
+
198
+ // Wrap allowed methods
199
+ for (const method of allowList) {
200
+ if (setMutableMethods.includes(method)) {
201
+ (methods as any)[method] = createWrappedMethod(method);
202
+ }
203
+ }
204
+
205
+ // Add target reference
206
+ Object.defineProperty(methods, 'target', {
207
+ get: () => target,
208
+ enumerable: false,
209
+ configurable: false
210
+ });
211
+
212
+ return methods as SetMutationMethods<T>;
213
+ };
214
+
215
+ export default wrapSetMethods;