@fynixorg/ui 1.0.11 → 1.0.13
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/LICENSE +21 -0
- package/dist/README.md +36 -0
- package/dist/context/context.d.ts +19 -0
- package/dist/context/context.d.ts.map +1 -0
- package/dist/context/context.js +3 -11
- package/dist/context/context.js.map +3 -3
- package/dist/custom/button.d.ts +2 -0
- package/dist/custom/button.d.ts.map +1 -0
- package/dist/custom/button.js +2 -9
- package/dist/custom/button.js.map +3 -3
- package/dist/custom/index.d.ts +3 -0
- package/dist/custom/index.d.ts.map +1 -0
- package/dist/custom/index.js +2 -7
- package/dist/custom/index.js.map +3 -3
- package/dist/custom/path.d.ts +14 -0
- package/dist/custom/path.d.ts.map +1 -0
- package/dist/custom/path.js +17 -34
- package/dist/custom/path.js.map +3 -3
- package/dist/error/errorOverlay.d.ts +3 -0
- package/dist/error/errorOverlay.d.ts.map +1 -0
- package/dist/error/errorOverlay.js +82 -91
- package/dist/error/errorOverlay.js.map +3 -3
- package/dist/fynix/index.d.ts +5 -0
- package/dist/fynix/index.d.ts.map +1 -0
- package/dist/fynix/index.js +2 -7
- package/dist/fynix/index.js.map +3 -3
- package/dist/hooks/nixAsync.d.ts +14 -0
- package/dist/hooks/nixAsync.d.ts.map +1 -0
- package/dist/hooks/nixAsync.js +38 -43
- package/dist/hooks/nixAsync.js.map +3 -3
- package/dist/hooks/nixAsyncCache.d.ts +14 -0
- package/dist/hooks/nixAsyncCache.d.ts.map +1 -0
- package/dist/hooks/nixAsyncCache.js +57 -59
- package/dist/hooks/nixAsyncCache.js.map +3 -3
- package/dist/hooks/nixAsyncDebounce.d.ts +22 -0
- package/dist/hooks/nixAsyncDebounce.d.ts.map +1 -0
- package/dist/hooks/nixAsyncDebounce.js +74 -85
- package/dist/hooks/nixAsyncDebounce.js.map +3 -3
- package/dist/hooks/nixAsyncQuery.d.ts +16 -0
- package/dist/hooks/nixAsyncQuery.d.ts.map +1 -0
- package/dist/hooks/nixAsyncQuery.js +85 -79
- package/dist/hooks/nixAsyncQuery.js.map +3 -3
- package/dist/hooks/nixCallback.d.ts +2 -0
- package/dist/hooks/nixCallback.d.ts.map +1 -0
- package/dist/hooks/nixCallback.js +30 -40
- package/dist/hooks/nixCallback.js.map +3 -3
- package/dist/hooks/nixComputed.d.ts +16 -0
- package/dist/hooks/nixComputed.d.ts.map +1 -0
- package/dist/hooks/nixComputed.js +166 -198
- package/dist/hooks/nixComputed.js.map +4 -4
- package/dist/hooks/nixDebounce.d.ts +11 -0
- package/dist/hooks/nixDebounce.d.ts.map +1 -0
- package/dist/hooks/nixDebounce.js +53 -58
- package/dist/hooks/nixDebounce.js.map +3 -3
- package/dist/hooks/nixEffect.d.ts +4 -0
- package/dist/hooks/nixEffect.d.ts.map +1 -0
- package/dist/hooks/nixEffect.js +65 -75
- package/dist/hooks/nixEffect.js.map +3 -3
- package/dist/hooks/nixForm.d.ts +33 -0
- package/dist/hooks/nixForm.d.ts.map +1 -0
- package/dist/hooks/nixForm.js +110 -120
- package/dist/hooks/nixForm.js.map +3 -3
- package/dist/hooks/nixFormAsync.d.ts +42 -0
- package/dist/hooks/nixFormAsync.d.ts.map +1 -0
- package/dist/hooks/nixFormAsync.js +158 -167
- package/dist/hooks/nixFormAsync.js.map +3 -3
- package/dist/hooks/nixInterval.d.ts +2 -0
- package/dist/hooks/nixInterval.d.ts.map +1 -0
- package/dist/hooks/nixInterval.js +21 -27
- package/dist/hooks/nixInterval.js.map +3 -3
- package/dist/hooks/nixLazy.d.ts +8 -0
- package/dist/hooks/nixLazy.d.ts.map +1 -0
- package/dist/hooks/nixLazy.js +53 -58
- package/dist/hooks/nixLazy.js.map +3 -3
- package/dist/hooks/nixLazyAsync.d.ts +10 -0
- package/dist/hooks/nixLazyAsync.d.ts.map +1 -0
- package/dist/hooks/nixLazyAsync.js +65 -71
- package/dist/hooks/nixLazyAsync.js.map +3 -3
- package/dist/hooks/nixLazyFormAsync.d.ts +50 -0
- package/dist/hooks/nixLazyFormAsync.d.ts.map +1 -0
- package/dist/hooks/nixLazyFormAsync.js +209 -213
- package/dist/hooks/nixLazyFormAsync.js.map +3 -3
- package/dist/hooks/nixLocalStorage.d.ts +5 -0
- package/dist/hooks/nixLocalStorage.d.ts.map +1 -0
- package/dist/hooks/nixLocalStorage.js +21 -25
- package/dist/hooks/nixLocalStorage.js.map +3 -3
- package/dist/hooks/nixMemo.d.ts +2 -0
- package/dist/hooks/nixMemo.d.ts.map +1 -0
- package/dist/hooks/nixMemo.js +27 -31
- package/dist/hooks/nixMemo.js.map +3 -3
- package/dist/hooks/nixPrevious.d.ts +2 -0
- package/dist/hooks/nixPrevious.d.ts.map +1 -0
- package/dist/hooks/nixPrevious.js +13 -19
- package/dist/hooks/nixPrevious.js.map +3 -3
- package/dist/hooks/nixRef.d.ts +4 -0
- package/dist/hooks/nixRef.d.ts.map +1 -0
- package/dist/hooks/nixRef.js +14 -20
- package/dist/hooks/nixRef.js.map +3 -3
- package/dist/hooks/nixState.d.ts +15 -0
- package/dist/hooks/nixState.d.ts.map +1 -0
- package/dist/hooks/nixState.js +120 -173
- package/dist/hooks/nixState.js.map +3 -3
- package/dist/hooks/nixStore.d.ts +7 -0
- package/dist/hooks/nixStore.d.ts.map +1 -0
- package/dist/hooks/nixStore.js +48 -54
- package/dist/hooks/nixStore.js.map +3 -3
- package/dist/package.json +213 -0
- package/dist/plugins/vite-plugin-res.d.ts +41 -0
- package/dist/plugins/vite-plugin-res.d.ts.map +1 -0
- package/dist/plugins/vite-plugin-res.js +620 -36
- package/dist/plugins/vite-plugin-res.js.map +4 -4
- package/dist/router/router.d.ts +35 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/router.js +520 -486
- package/dist/router/router.js.map +3 -3
- package/dist/runtime.d.ts +62 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +833 -820
- package/dist/runtime.js.map +4 -4
- package/package.json +227 -44
- package/types/fnx.d.ts +72 -0
- package/types/fynix-ui.d.ts +323 -0
- package/types/global.d.ts +46 -6
- package/types/index.d.ts +37 -0
- package/types/vite-env.d.ts +553 -0
- package/runtime.d.ts +0 -83
- package/types/jsx.d.ts +0 -692
|
@@ -1,204 +1,172 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
1
|
import { activeContext, setActiveContext } from "../context/context";
|
|
4
|
-
function nixComputed(computeFn) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
_subscriptionCleanups: []
|
|
27
|
-
};
|
|
28
|
-
const prevContext = activeContext;
|
|
29
|
-
try {
|
|
30
|
-
setActiveContext(trackingContext);
|
|
31
|
-
cachedValue = computeFn();
|
|
32
|
-
const oldDeps = Array.from(dependencies);
|
|
33
|
-
oldDeps.forEach((dep) => {
|
|
34
|
-
if (!trackingContext._accessedStates.has(dep)) {
|
|
35
|
-
if (unsubscribers.has(dep)) {
|
|
36
|
-
try {
|
|
37
|
-
unsubscribers.get(dep)();
|
|
38
|
-
} catch (e) {
|
|
39
|
-
console.error("[nixComputed] Error unsubscribing from old dependency:", e);
|
|
40
|
-
}
|
|
41
|
-
unsubscribers.delete(dep);
|
|
2
|
+
export function nixComputed(computeFn) {
|
|
3
|
+
const ctx = activeContext;
|
|
4
|
+
if (!ctx)
|
|
5
|
+
throw new Error("nixComputed() called outside component");
|
|
6
|
+
if (typeof computeFn !== "function") {
|
|
7
|
+
throw new TypeError("[nixComputed] First argument must be a function");
|
|
8
|
+
}
|
|
9
|
+
const idx = ctx.hookIndex++;
|
|
10
|
+
if (!ctx.hooks[idx]) {
|
|
11
|
+
const subscribers = new Set();
|
|
12
|
+
const dependencies = new Set();
|
|
13
|
+
const unsubscribers = new Map();
|
|
14
|
+
let cachedValue;
|
|
15
|
+
let isStale = true;
|
|
16
|
+
let isDestroyed = false;
|
|
17
|
+
let isComputing = false;
|
|
18
|
+
function compute() {
|
|
19
|
+
if (isDestroyed)
|
|
20
|
+
return cachedValue;
|
|
21
|
+
if (isComputing) {
|
|
22
|
+
console.error("[nixComputed] Circular dependency detected");
|
|
23
|
+
return cachedValue;
|
|
42
24
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (isDestroyed) {
|
|
113
|
-
console.warn("[nixComputed] Cannot subscribe to destroyed computed state");
|
|
114
|
-
return () => {
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
const MAX_SUBSCRIBERS = 1e3;
|
|
118
|
-
if (subscribers.size >= MAX_SUBSCRIBERS) {
|
|
119
|
-
console.error("[nixComputed] Maximum subscriber limit reached");
|
|
120
|
-
return () => {
|
|
121
|
-
};
|
|
25
|
+
isComputing = true;
|
|
26
|
+
const trackingContext = {
|
|
27
|
+
_accessedStates: new Set(),
|
|
28
|
+
hookIndex: 0,
|
|
29
|
+
hooks: [],
|
|
30
|
+
_subscriptions: new Set(),
|
|
31
|
+
_subscriptionCleanups: [],
|
|
32
|
+
effects: [],
|
|
33
|
+
cleanups: [],
|
|
34
|
+
_vnode: null,
|
|
35
|
+
version: 0,
|
|
36
|
+
props: {},
|
|
37
|
+
stateCleanups: [],
|
|
38
|
+
parent: null,
|
|
39
|
+
context: {},
|
|
40
|
+
rerender: () => { },
|
|
41
|
+
Component: null,
|
|
42
|
+
_isMounted: false,
|
|
43
|
+
_isRerendering: false,
|
|
44
|
+
};
|
|
45
|
+
const prevContext = activeContext;
|
|
46
|
+
try {
|
|
47
|
+
setActiveContext(trackingContext);
|
|
48
|
+
cachedValue = computeFn();
|
|
49
|
+
const oldDeps = Array.from(dependencies);
|
|
50
|
+
oldDeps.forEach((dep) => {
|
|
51
|
+
if (!trackingContext._accessedStates.has(dep)) {
|
|
52
|
+
if (unsubscribers.has(dep)) {
|
|
53
|
+
try {
|
|
54
|
+
unsubscribers.get(dep)();
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
console.error("[nixComputed] Error unsubscribing from old dependency:", e);
|
|
58
|
+
}
|
|
59
|
+
unsubscribers.delete(dep);
|
|
60
|
+
}
|
|
61
|
+
dependencies.delete(dep);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
trackingContext._accessedStates.forEach((state) => {
|
|
65
|
+
if (!dependencies.has(state)) {
|
|
66
|
+
const unsub = state.subscribe(() => {
|
|
67
|
+
isStale = true;
|
|
68
|
+
const subsArray = Array.from(subscribers);
|
|
69
|
+
subsArray.forEach((fn) => {
|
|
70
|
+
try {
|
|
71
|
+
fn(s.value);
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
console.error("[nixComputed] Subscriber error:", e);
|
|
75
|
+
subscribers.delete(fn);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
unsubscribers.set(state, unsub);
|
|
80
|
+
dependencies.add(state);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
isStale = false;
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
console.error("[nixComputed] Compute error:", err);
|
|
87
|
+
isStale = false;
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
setActiveContext(prevContext);
|
|
91
|
+
isComputing = false;
|
|
92
|
+
}
|
|
93
|
+
return cachedValue;
|
|
122
94
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
95
|
+
const s = {
|
|
96
|
+
get value() {
|
|
97
|
+
if (isDestroyed) {
|
|
98
|
+
console.warn("[nixComputed] Accessing destroyed computed state");
|
|
99
|
+
return cachedValue;
|
|
100
|
+
}
|
|
101
|
+
if (isStale) {
|
|
102
|
+
compute();
|
|
103
|
+
}
|
|
104
|
+
if (activeContext && activeContext._accessedStates) {
|
|
105
|
+
activeContext._accessedStates.add(s);
|
|
106
|
+
}
|
|
107
|
+
return cachedValue;
|
|
108
|
+
},
|
|
109
|
+
subscribe(fn) {
|
|
110
|
+
if (typeof fn !== "function") {
|
|
111
|
+
console.error("[nixComputed] subscribe() requires a function");
|
|
112
|
+
return () => { };
|
|
113
|
+
}
|
|
114
|
+
if (isDestroyed) {
|
|
115
|
+
console.warn("[nixComputed] Cannot subscribe to destroyed computed state");
|
|
116
|
+
return () => { };
|
|
117
|
+
}
|
|
118
|
+
const MAX_SUBSCRIBERS = 1000;
|
|
119
|
+
if (subscribers.size >= MAX_SUBSCRIBERS) {
|
|
120
|
+
console.error("[nixComputed] Maximum subscriber limit reached");
|
|
121
|
+
return () => { };
|
|
122
|
+
}
|
|
123
|
+
subscribers.add(fn);
|
|
124
|
+
return () => {
|
|
125
|
+
subscribers.delete(fn);
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
cleanup() {
|
|
129
|
+
if (isDestroyed)
|
|
130
|
+
return;
|
|
131
|
+
isDestroyed = true;
|
|
132
|
+
unsubscribers.forEach((unsub) => {
|
|
133
|
+
try {
|
|
134
|
+
unsub();
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
console.error("[nixComputed] Cleanup error:", e);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
unsubscribers.clear();
|
|
141
|
+
dependencies.clear();
|
|
142
|
+
subscribers.clear();
|
|
143
|
+
cachedValue = null;
|
|
144
|
+
console.log("[nixComputed] Computed state cleaned up");
|
|
145
|
+
},
|
|
146
|
+
getSubscriberCount() {
|
|
147
|
+
return subscribers.size;
|
|
148
|
+
},
|
|
149
|
+
getDependencyCount() {
|
|
150
|
+
return dependencies.size;
|
|
151
|
+
},
|
|
152
|
+
isDestroyed() {
|
|
153
|
+
return isDestroyed;
|
|
154
|
+
},
|
|
155
|
+
getDependencyInfo() {
|
|
156
|
+
return Array.from(dependencies).map((state) => ({
|
|
157
|
+
state,
|
|
158
|
+
hasCleanup: unsubscribers.has(state),
|
|
159
|
+
isComputed: !!state._isComputed,
|
|
160
|
+
}));
|
|
161
|
+
},
|
|
162
|
+
_isNixState: true,
|
|
163
|
+
_isComputed: true,
|
|
126
164
|
};
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
* @example
|
|
133
|
-
* nixEffect(() => {
|
|
134
|
-
* return () => myComputed.cleanup();
|
|
135
|
-
* }, []);
|
|
136
|
-
*/
|
|
137
|
-
cleanup() {
|
|
138
|
-
if (isDestroyed)
|
|
139
|
-
return;
|
|
140
|
-
isDestroyed = true;
|
|
141
|
-
unsubscribers.forEach((unsub, dep) => {
|
|
142
|
-
try {
|
|
143
|
-
unsub();
|
|
144
|
-
} catch (e) {
|
|
145
|
-
console.error("[nixComputed] Cleanup error:", e);
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
unsubscribers.clear();
|
|
149
|
-
dependencies.clear();
|
|
150
|
-
subscribers.clear();
|
|
151
|
-
cachedValue = null;
|
|
152
|
-
console.log("[nixComputed] Computed state cleaned up");
|
|
153
|
-
},
|
|
154
|
-
/**
|
|
155
|
-
* Get the number of active subscribers (useful for debugging)
|
|
156
|
-
* @returns {number} Number of active subscribers
|
|
157
|
-
*/
|
|
158
|
-
getSubscriberCount() {
|
|
159
|
-
return subscribers.size;
|
|
160
|
-
},
|
|
161
|
-
/**
|
|
162
|
-
* Get the number of tracked dependencies (useful for debugging)
|
|
163
|
-
* @returns {number} Number of dependencies
|
|
164
|
-
*/
|
|
165
|
-
getDependencyCount() {
|
|
166
|
-
return dependencies.size;
|
|
167
|
-
},
|
|
168
|
-
/**
|
|
169
|
-
* Check if computed state has been destroyed
|
|
170
|
-
* @returns {boolean} True if state is destroyed
|
|
171
|
-
*/
|
|
172
|
-
isDestroyed() {
|
|
173
|
-
return isDestroyed;
|
|
174
|
-
},
|
|
175
|
-
/**
|
|
176
|
-
* Get information about current dependencies (debugging)
|
|
177
|
-
* @returns {Array<{state: Object, hasCleanup: boolean}>} Dependency info
|
|
178
|
-
*/
|
|
179
|
-
getDependencyInfo() {
|
|
180
|
-
return Array.from(dependencies).map((dep) => ({
|
|
181
|
-
state: dep,
|
|
182
|
-
hasCleanup: unsubscribers.has(dep),
|
|
183
|
-
isComputed: !!dep._isComputed
|
|
184
|
-
}));
|
|
185
|
-
},
|
|
186
|
-
// Internal flags
|
|
187
|
-
_isNixState: true,
|
|
188
|
-
// Computed states behave like states
|
|
189
|
-
_isComputed: true
|
|
190
|
-
// Flag to identify computed states
|
|
191
|
-
};
|
|
192
|
-
compute();
|
|
193
|
-
ctx.hooks[idx] = s;
|
|
194
|
-
if (ctx.stateCleanups) {
|
|
195
|
-
ctx.stateCleanups.push(() => s.cleanup());
|
|
165
|
+
compute();
|
|
166
|
+
ctx.hooks[idx] = s;
|
|
167
|
+
if (ctx.stateCleanups) {
|
|
168
|
+
ctx.stateCleanups.push(() => s.cleanup());
|
|
169
|
+
}
|
|
196
170
|
}
|
|
197
|
-
|
|
198
|
-
return ctx.hooks[idx];
|
|
171
|
+
return ctx.hooks[idx];
|
|
199
172
|
}
|
|
200
|
-
__name(nixComputed, "nixComputed");
|
|
201
|
-
export {
|
|
202
|
-
nixComputed
|
|
203
|
-
};
|
|
204
|
-
//# sourceMappingURL=nixComputed.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../hooks/nixComputed.
|
|
4
|
-
"sourcesContent": ["/* ----------------------\r\n nixComputed - Computed/Derived State\r\n Memory Leaks & Security Issues Resolved\r\n---------------------- */\r\nimport { activeContext, setActiveContext } from \"../context/context\";\r\n\r\n/**\r\n * @template T\r\n * @typedef {Object} ComputedState\r\n * @property {T} value - Get the computed value (read-only)\r\n * @property {(fn: (value: T) => void) => (() => void)} subscribe - Subscribe to computed value changes\r\n * @property {() => void} cleanup - Cleanup all subscriptions and dependencies\r\n * @property {() => number} getSubscriberCount - Get number of active subscribers (debugging)\r\n * @property {() => number} getDependencyCount - Get number of tracked dependencies (debugging)\r\n * @property {() => boolean} isDestroyed - Check if computed state has been destroyed\r\n * @property {boolean} _isNixState - Internal flag (computed states behave like states)\r\n * @property {boolean} _isComputed - Internal flag to identify computed states\r\n */\r\n\r\n/**\r\n * Create a derived/computed state from other states.\r\n * Automatically tracks dependencies and updates when any dependency changes.\r\n * \r\n * @template T\r\n * @param {() => T} computeFn - Function that computes the derived value\r\n * @returns {ComputedState<T>} A reactive state object with the computed value\r\n * \r\n * @example\r\n * const count = nixState(5);\r\n * const doubled = nixComputed(() => count.value * 2);\r\n * console.log(doubled.value); // 10\r\n * count.value = 10;\r\n * console.log(doubled.value); // 20\r\n * \r\n * @example\r\n * // Multiple dependencies\r\n * const a = nixState(5);\r\n * const b = nixState(10);\r\n * const sum = nixComputed(() => a.value + b.value);\r\n * console.log(sum.value); // 15\r\n * \r\n * @example\r\n * // Conditional dependencies\r\n * const flag = nixState(true);\r\n * const x = nixState(1);\r\n * const y = nixState(2);\r\n * const result = nixComputed(() => flag.value ? x.value : y.value);\r\n * \r\n * @example\r\n * // With cleanup\r\n * const MyComponent = () => {\r\n * const count = nixState(0);\r\n * const doubled = nixComputed(() => count.value * 2);\r\n * \r\n * nixEffect(() => {\r\n * return () => {\r\n * doubled.cleanup();\r\n * count.cleanup();\r\n * };\r\n * }, []);\r\n * };\r\n * \r\n * @throws {Error} If called outside a component context\r\n * @throws {TypeError} If computeFn is not a function\r\n */\r\nexport function nixComputed(computeFn) {\r\n const ctx = activeContext;\r\n if (!ctx) throw new Error(\"nixComputed() called outside component\");\r\n\r\n // Security: Validate compute function\r\n if (typeof computeFn !== 'function') {\r\n throw new TypeError('[nixComputed] First argument must be a function');\r\n }\r\n\r\n const idx = ctx.hookIndex++;\r\n if (!ctx.hooks[idx]) {\r\n const subscribers = new Set();\r\n const dependencies = new Set();\r\n const unsubscribers = new Map(); // Map<dependency, unsubscribe function>\r\n let cachedValue;\r\n let isStale = true;\r\n let isDestroyed = false;\r\n let isComputing = false; // Prevent infinite loops\r\n\r\n /**\r\n * Compute the value and track dependencies\r\n */\r\n function compute() {\r\n if (isDestroyed) return cachedValue;\r\n\r\n // Prevent infinite computation loops\r\n if (isComputing) {\r\n console.error('[nixComputed] Circular dependency detected');\r\n return cachedValue;\r\n }\r\n\r\n isComputing = true;\r\n\r\n // Create a mock context for dependency tracking\r\n const trackingContext = {\r\n _accessedStates: new Set(),\r\n hookIndex: 0,\r\n hooks: [],\r\n _subscriptions: new Set(),\r\n _subscriptionCleanups: [],\r\n };\r\n\r\n // Save the previous context\r\n const prevContext = activeContext;\r\n\r\n try {\r\n // Temporarily set the tracking context\r\n // @ts-ignore - Internal tracking context doesn't need full ComponentContext properties\r\n setActiveContext(trackingContext);\r\n\r\n // Compute the value - this will trigger state.value getters\r\n // which will add themselves to trackingContext._accessedStates\r\n cachedValue = computeFn();\r\n\r\n // Find dependencies that are no longer needed\r\n const oldDeps = Array.from(dependencies);\r\n oldDeps.forEach(dep => {\r\n if (!trackingContext._accessedStates.has(dep)) {\r\n // This dependency is no longer accessed - unsubscribe\r\n if (unsubscribers.has(dep)) {\r\n try {\r\n unsubscribers.get(dep)();\r\n } catch (e) {\r\n console.error('[nixComputed] Error unsubscribing from old dependency:', e);\r\n }\r\n unsubscribers.delete(dep);\r\n }\r\n dependencies.delete(dep);\r\n }\r\n });\r\n\r\n // Subscribe to new dependencies\r\n trackingContext._accessedStates.forEach(state => {\r\n if (!dependencies.has(state)) {\r\n // New dependency found - subscribe to it\r\n const unsub = state.subscribe(() => {\r\n // Mark computed value as stale when dependency changes\r\n isStale = true;\r\n\r\n // Notify computed subscribers\r\n const subsArray = Array.from(subscribers);\r\n subsArray.forEach(fn => {\r\n try {\r\n fn(s.value);\r\n } catch (e) {\r\n console.error('[nixComputed] Subscriber error:', e);\r\n subscribers.delete(fn);\r\n }\r\n });\r\n });\r\n\r\n // Store unsubscribe function\r\n unsubscribers.set(state, unsub);\r\n dependencies.add(state);\r\n }\r\n });\r\n\r\n isStale = false;\r\n } catch (err) {\r\n console.error('[nixComputed] Compute error:', err);\r\n isStale = false; // Don't retry immediately\r\n } finally {\r\n // Restore the previous context\r\n setActiveContext(prevContext);\r\n isComputing = false;\r\n }\r\n\r\n return cachedValue;\r\n }\r\n\r\n const s = {\r\n /**\r\n * Get the computed value (read-only).\r\n * Automatically recomputes if dependencies have changed.\r\n * @returns {any} The computed value\r\n */\r\n get value() {\r\n if (isDestroyed) {\r\n console.warn('[nixComputed] Accessing destroyed computed state');\r\n return cachedValue;\r\n }\r\n\r\n // Recompute if stale\r\n if (isStale) {\r\n compute();\r\n }\r\n\r\n // Track this computed state as a dependency if accessed in another computed\r\n if (activeContext && activeContext._accessedStates) {\r\n activeContext._accessedStates.add(s);\r\n }\r\n\r\n return cachedValue;\r\n },\r\n\r\n /**\r\n * Subscribe to computed value changes.\r\n * @param {(value: T) => void} fn - Callback function\r\n * @returns {() => void} Unsubscribe function\r\n */\r\n subscribe(fn) {\r\n // Security: Validate subscriber function\r\n if (typeof fn !== 'function') {\r\n console.error('[nixComputed] subscribe() requires a function');\r\n return () => { };\r\n }\r\n\r\n // Memory Leak Fix: Don't add subscribers to destroyed state\r\n if (isDestroyed) {\r\n console.warn('[nixComputed] Cannot subscribe to destroyed computed state');\r\n return () => { };\r\n }\r\n\r\n // Security: Limit number of subscribers to prevent DOS\r\n const MAX_SUBSCRIBERS = 1000;\r\n if (subscribers.size >= MAX_SUBSCRIBERS) {\r\n console.error('[nixComputed] Maximum subscriber limit reached');\r\n return () => { };\r\n }\r\n\r\n subscribers.add(fn);\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n subscribers.delete(fn);\r\n };\r\n },\r\n\r\n /**\r\n * Cleanup all subscriptions and dependencies.\r\n * Call this when component unmounts to prevent memory leaks.\r\n * \r\n * @example\r\n * nixEffect(() => {\r\n * return () => myComputed.cleanup();\r\n * }, []);\r\n */\r\n cleanup() {\r\n if (isDestroyed) return;\r\n\r\n // Mark as destroyed to prevent further operations\r\n isDestroyed = true;\r\n\r\n // Unsubscribe from all dependencies\r\n unsubscribers.forEach((unsub, dep) => {\r\n try {\r\n unsub();\r\n } catch (e) {\r\n console.error('[nixComputed] Cleanup error:', e);\r\n }\r\n });\r\n\r\n // Clear all collections\r\n unsubscribers.clear();\r\n dependencies.clear();\r\n subscribers.clear();\r\n\r\n // Clear cached value to help garbage collection\r\n cachedValue = null;\r\n\r\n console.log('[nixComputed] Computed state cleaned up');\r\n },\r\n\r\n /**\r\n * Get the number of active subscribers (useful for debugging)\r\n * @returns {number} Number of active subscribers\r\n */\r\n getSubscriberCount() {\r\n return subscribers.size;\r\n },\r\n\r\n /**\r\n * Get the number of tracked dependencies (useful for debugging)\r\n * @returns {number} Number of dependencies\r\n */\r\n getDependencyCount() {\r\n return dependencies.size;\r\n },\r\n\r\n /**\r\n * Check if computed state has been destroyed\r\n * @returns {boolean} True if state is destroyed\r\n */\r\n isDestroyed() {\r\n return isDestroyed;\r\n },\r\n\r\n /**\r\n * Get information about current dependencies (debugging)\r\n * @returns {Array<{state: Object, hasCleanup: boolean}>} Dependency info\r\n */\r\n getDependencyInfo() {\r\n return Array.from(dependencies).map(dep => ({\r\n state: dep,\r\n hasCleanup: unsubscribers.has(dep),\r\n isComputed: !!dep._isComputed,\r\n }));\r\n },\r\n\r\n // Internal flags\r\n _isNixState: true, // Computed states behave like states\r\n _isComputed: true, // Flag to identify computed states\r\n };\r\n\r\n // Initial computation\r\n compute();\r\n\r\n // Store in hooks\r\n ctx.hooks[idx] = s;\r\n\r\n // Memory Leak Fix: Track for cleanup\r\n if (ctx.stateCleanups) {\r\n ctx.stateCleanups.push(() => s.cleanup());\r\n }\r\n }\r\n\r\n return ctx.hooks[idx];\r\n}"],
|
|
5
|
-
"mappings": ";;
|
|
6
|
-
"names": []
|
|
3
|
+
"sources": ["../../hooks/nixComputed.ts"],
|
|
4
|
+
"sourcesContent": ["/* MIT License\r\n\r\n* Copyright (c) 2026 Resty Gonzales\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n* SOFTWARE.\r\n */\r\n/* ----------------------\r\n nixComputed - Computed/Derived State\r\n Memory Leaks & Security Issues Resolved\r\n---------------------- */\r\nimport { activeContext, setActiveContext } from \"../context/context\";\r\n\r\n/**\r\n * @template T\r\n * @typedef {Object} ComputedState\r\n * @property {T} value - Get the computed value (read-only)\r\n * @property {(fn: (value: T) => void) => (() => void)} subscribe - Subscribe to computed value changes\r\n * @property {() => void} cleanup - Cleanup all subscriptions and dependencies\r\n * @property {() => number} getSubscriberCount - Get number of active subscribers (debugging)\r\n * @property {() => number} getDependencyCount - Get number of tracked dependencies (debugging)\r\n * @property {() => boolean} isDestroyed - Check if computed state has been destroyed\r\n * @property {boolean} _isNixState - Internal flag (computed states behave like states)\r\n * @property {boolean} _isComputed - Internal flag to identify computed states\r\n */\r\n\r\n/**\r\n * Create a derived/computed state from other states.\r\n * Automatically tracks dependencies and updates when any dependency changes.\r\n *\r\n * @template T\r\n * @param {() => T} computeFn - Function that computes the derived value\r\n * @returns {ComputedState<T>} A reactive state object with the computed value\r\n *\r\n * @example\r\n * const count = nixState(5);\r\n * const doubled = nixComputed(() => count.value * 2);\r\n * console.log(doubled.value); // 10\r\n * count.value = 10;\r\n * console.log(doubled.value); // 20\r\n *\r\n * @example\r\n * // Multiple dependencies\r\n * const a = nixState(5);\r\n * const b = nixState(10);\r\n * const sum = nixComputed(() => a.value + b.value);\r\n * console.log(sum.value); // 15\r\n *\r\n * @example\r\n * // Conditional dependencies\r\n * const flag = nixState(true);\r\n * const x = nixState(1);\r\n * const y = nixState(2);\r\n * const result = nixComputed(() => flag.value ? x.value : y.value);\r\n *\r\n * @example\r\n * // With cleanup\r\n * const MyComponent = () => {\r\n * const count = nixState(0);\r\n * const doubled = nixComputed(() => count.value * 2);\r\n *\r\n * nixEffect(() => {\r\n * return () => {\r\n * doubled.cleanup();\r\n * count.cleanup();\r\n * };\r\n * }, []);\r\n * };\r\n *\r\n * @throws {Error} If called outside a component context\r\n * @throws {TypeError} If computeFn is not a function\r\n */\r\nexport function nixComputed<T>(computeFn: () => T): {\r\n value: T;\r\n subscribe: (fn: (value: T) => void) => () => void;\r\n cleanup: () => void;\r\n getSubscriberCount: () => number;\r\n getDependencyCount: () => number;\r\n isDestroyed: () => boolean;\r\n getDependencyInfo: () => Array<{\r\n state: any;\r\n hasCleanup: boolean;\r\n isComputed: boolean;\r\n }>;\r\n _isNixState: true;\r\n _isComputed: true;\r\n} {\r\n const ctx = activeContext as\r\n | (typeof activeContext & {\r\n hookIndex: number;\r\n hooks: Array<any>;\r\n stateCleanups?: Array<() => void>;\r\n })\r\n | undefined;\r\n if (!ctx) throw new Error(\"nixComputed() called outside component\");\r\n\r\n if (typeof computeFn !== \"function\") {\r\n throw new TypeError(\"[nixComputed] First argument must be a function\");\r\n }\r\n\r\n const idx = ctx.hookIndex++;\r\n if (!ctx.hooks[idx]) {\r\n const subscribers: Set<(value: T) => void> = new Set();\r\n const dependencies: Set<any> = new Set();\r\n const unsubscribers: Map<any, () => void> = new Map();\r\n let cachedValue: T;\r\n let isStale = true;\r\n let isDestroyed = false;\r\n let isComputing = false;\r\n\r\n function compute(): T {\r\n if (isDestroyed) return cachedValue;\r\n if (isComputing) {\r\n console.error(\"[nixComputed] Circular dependency detected\");\r\n return cachedValue;\r\n }\r\n isComputing = true;\r\n // Provide all required properties for ComponentContext type\r\n // Use type assertion for safety and future-proofing\r\n const trackingContext = {\r\n _accessedStates: new Set(),\r\n hookIndex: 0,\r\n hooks: [],\r\n _subscriptions: new Set(),\r\n _subscriptionCleanups: [],\r\n effects: [],\r\n cleanups: [],\r\n _vnode: null,\r\n version: 0,\r\n props: {},\r\n stateCleanups: [],\r\n parent: null,\r\n context: {},\r\n rerender: () => {},\r\n Component: null,\r\n _isMounted: false,\r\n _isRerendering: false,\r\n } as any; // Use 'as any' to satisfy ComponentContext type\r\n const prevContext = activeContext;\r\n try {\r\n setActiveContext(trackingContext);\r\n cachedValue = computeFn();\r\n const oldDeps = Array.from(dependencies);\r\n oldDeps.forEach((dep) => {\r\n if (!trackingContext._accessedStates.has(dep)) {\r\n if (unsubscribers.has(dep)) {\r\n try {\r\n unsubscribers.get(dep)!();\r\n } catch (e) {\r\n console.error(\r\n \"[nixComputed] Error unsubscribing from old dependency:\",\r\n e\r\n );\r\n }\r\n unsubscribers.delete(dep);\r\n }\r\n dependencies.delete(dep);\r\n }\r\n });\r\n trackingContext._accessedStates.forEach((state: any) => {\r\n if (!dependencies.has(state)) {\r\n const unsub = state.subscribe(() => {\r\n isStale = true;\r\n const subsArray = Array.from(subscribers);\r\n subsArray.forEach((fn) => {\r\n try {\r\n fn(s.value);\r\n } catch (e) {\r\n console.error(\"[nixComputed] Subscriber error:\", e);\r\n subscribers.delete(fn);\r\n }\r\n });\r\n });\r\n unsubscribers.set(state, unsub);\r\n dependencies.add(state);\r\n }\r\n });\r\n isStale = false;\r\n } catch (err) {\r\n console.error(\"[nixComputed] Compute error:\", err);\r\n isStale = false;\r\n } finally {\r\n setActiveContext(prevContext);\r\n isComputing = false;\r\n }\r\n return cachedValue;\r\n }\r\n\r\n const s = {\r\n get value(): T {\r\n if (isDestroyed) {\r\n console.warn(\"[nixComputed] Accessing destroyed computed state\");\r\n return cachedValue;\r\n }\r\n if (isStale) {\r\n compute();\r\n }\r\n if (activeContext && activeContext._accessedStates) {\r\n activeContext._accessedStates.add(s);\r\n }\r\n return cachedValue;\r\n },\r\n subscribe(fn: (value: T) => void): () => void {\r\n if (typeof fn !== \"function\") {\r\n console.error(\"[nixComputed] subscribe() requires a function\");\r\n return () => {};\r\n }\r\n if (isDestroyed) {\r\n console.warn(\r\n \"[nixComputed] Cannot subscribe to destroyed computed state\"\r\n );\r\n return () => {};\r\n }\r\n const MAX_SUBSCRIBERS = 1000;\r\n if (subscribers.size >= MAX_SUBSCRIBERS) {\r\n console.error(\"[nixComputed] Maximum subscriber limit reached\");\r\n return () => {};\r\n }\r\n subscribers.add(fn);\r\n return () => {\r\n subscribers.delete(fn);\r\n };\r\n },\r\n cleanup(): void {\r\n if (isDestroyed) return;\r\n isDestroyed = true;\r\n unsubscribers.forEach((unsub) => {\r\n try {\r\n unsub();\r\n } catch (e) {\r\n console.error(\"[nixComputed] Cleanup error:\", e);\r\n }\r\n });\r\n unsubscribers.clear();\r\n dependencies.clear();\r\n subscribers.clear();\r\n cachedValue = null as any as T;\r\n console.log(\"[nixComputed] Computed state cleaned up\");\r\n },\r\n getSubscriberCount(): number {\r\n return subscribers.size;\r\n },\r\n getDependencyCount(): number {\r\n return dependencies.size;\r\n },\r\n isDestroyed(): boolean {\r\n return isDestroyed;\r\n },\r\n getDependencyInfo(): Array<{\r\n state: any;\r\n hasCleanup: boolean;\r\n isComputed: boolean;\r\n }> {\r\n return Array.from(dependencies).map((state) => ({\r\n state,\r\n hasCleanup: unsubscribers.has(state),\r\n isComputed: !!state._isComputed,\r\n }));\r\n },\r\n _isNixState: true as const,\r\n _isComputed: true as const,\r\n };\r\n\r\n compute();\r\n ctx.hooks[idx] = s;\r\n if (ctx.stateCleanups) {\r\n ctx.stateCleanups.push(() => s.cleanup());\r\n }\r\n }\r\n return ctx.hooks[idx] as ReturnType<typeof nixComputed<T>>;\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["compute"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface NixDebounceOptions {
|
|
2
|
+
leading?: boolean;
|
|
3
|
+
trailing?: boolean;
|
|
4
|
+
maxWait?: number;
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
}
|
|
7
|
+
export type DebouncedFunction<T extends (...args: any[]) => any> = ((...args: Parameters<T>) => void) & {
|
|
8
|
+
cancel: () => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function nixDebounce<T extends (...args: any[]) => any>(fn: T, delay?: number, options?: NixDebounceOptions): DebouncedFunction<T>;
|
|
11
|
+
//# sourceMappingURL=nixDebounce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nixDebounce.d.ts","sourceRoot":"","sources":["../../hooks/nixDebounce.ts"],"names":[],"mappings":"AAiDA,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC,CAClE,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KACnB,IAAI,CAAC,GAAG;IACX,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF,wBAAgB,WAAW,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAC3D,EAAE,EAAE,CAAC,EACL,KAAK,GAAE,MAAY,EACnB,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAAC,CAAC,CAAC,CA8DtB"}
|
|
@@ -1,60 +1,55 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
lastThis = null;
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
const invoke = /* @__PURE__ */ __name(() => {
|
|
20
|
-
lastInvokeTime = Date.now();
|
|
21
|
-
if (lastArgs) {
|
|
22
|
-
fn.apply(lastThis, lastArgs);
|
|
23
|
-
lastArgs = lastThis = null;
|
|
24
|
-
}
|
|
25
|
-
}, "invoke");
|
|
26
|
-
const debounced = /* @__PURE__ */ __name(function(...args) {
|
|
27
|
-
const now = Date.now();
|
|
28
|
-
lastArgs = args;
|
|
29
|
-
lastThis = this;
|
|
30
|
-
const shouldInvokeLeading = leading && !timerId;
|
|
31
|
-
const timeSinceLastInvoke = now - lastInvokeTime;
|
|
32
|
-
const remainingTime = delay - timeSinceLastInvoke;
|
|
33
|
-
if (maxWait !== void 0 && timeSinceLastInvoke >= maxWait) {
|
|
34
|
-
if (timerId)
|
|
35
|
-
clearTimeout(timerId);
|
|
36
|
-
timerId = null;
|
|
37
|
-
invoke();
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
if (timerId)
|
|
41
|
-
clearTimeout(timerId);
|
|
42
|
-
if (shouldInvokeLeading) {
|
|
43
|
-
invoke();
|
|
1
|
+
export function nixDebounce(fn, delay = 300, options = {}) {
|
|
2
|
+
let timerId = null;
|
|
3
|
+
let lastInvokeTime = 0;
|
|
4
|
+
let lastArgs = null;
|
|
5
|
+
let lastThis = null;
|
|
6
|
+
const { leading = false, trailing = true, maxWait, signal } = options;
|
|
7
|
+
if (signal) {
|
|
8
|
+
signal.addEventListener("abort", () => {
|
|
9
|
+
if (timerId) {
|
|
10
|
+
clearTimeout(timerId);
|
|
11
|
+
timerId = null;
|
|
12
|
+
}
|
|
13
|
+
lastArgs = null;
|
|
14
|
+
lastThis = null;
|
|
15
|
+
});
|
|
44
16
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
17
|
+
const invoke = () => {
|
|
18
|
+
lastInvokeTime = Date.now();
|
|
19
|
+
if (lastArgs) {
|
|
20
|
+
fn.apply(lastThis, lastArgs);
|
|
21
|
+
lastArgs = lastThis = null;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const debounced = function (...args) {
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
lastArgs = args;
|
|
27
|
+
lastThis = this;
|
|
28
|
+
const shouldInvokeLeading = leading && !timerId;
|
|
29
|
+
const timeSinceLastInvoke = now - lastInvokeTime;
|
|
30
|
+
const remainingTime = delay - timeSinceLastInvoke;
|
|
31
|
+
if (maxWait !== undefined && timeSinceLastInvoke >= maxWait) {
|
|
32
|
+
if (timerId)
|
|
33
|
+
clearTimeout(timerId);
|
|
34
|
+
timerId = null;
|
|
35
|
+
invoke();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (timerId)
|
|
39
|
+
clearTimeout(timerId);
|
|
40
|
+
if (shouldInvokeLeading) {
|
|
41
|
+
invoke();
|
|
42
|
+
}
|
|
43
|
+
if (trailing) {
|
|
44
|
+
timerId = setTimeout(invoke, remainingTime > 0 ? remainingTime : delay);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
debounced.cancel = () => {
|
|
48
|
+
if (timerId)
|
|
49
|
+
clearTimeout(timerId);
|
|
50
|
+
timerId = null;
|
|
51
|
+
lastArgs = null;
|
|
52
|
+
lastThis = null;
|
|
53
|
+
};
|
|
54
|
+
return debounced;
|
|
55
55
|
}
|
|
56
|
-
__name(nixDebounce, "nixDebounce");
|
|
57
|
-
export {
|
|
58
|
-
nixDebounce
|
|
59
|
-
};
|
|
60
|
-
//# sourceMappingURL=nixDebounce.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../hooks/nixDebounce.
|
|
4
|
-
"sourcesContent": ["/**\r\n * @fileoverview Debounce utility function with advanced options.\r\n * Supports leading/trailing edge invocation, maxWait, and AbortController.\r\n */\r\n\r\n/**\r\n * Debounce a function with options for leading, trailing, maxWait, and AbortController support.\r\n *\r\n * @param {Function} fn - The function to debounce\r\n * @param {number} [delay=300] - Delay in milliseconds\r\n * @param {Object} [options={}] - Debounce options\r\n * @param {boolean} [options.leading=false] - Invoke on the leading edge\r\n * @param {boolean} [options.trailing=true] - Invoke on the trailing edge\r\n * @param {number} [options.maxWait] - Maximum wait time before forced invocation\r\n * @param {AbortSignal} [options.signal] - Optional AbortSignal to cancel pending calls\r\n * @returns {Function} Debounced function with `.cancel()` method\r\n *\r\n * @example\r\n * const controller = new AbortController();\r\n * const debounced = nixDebounce(() => console.log('Hello'), 500, {\r\n * leading: true,\r\n * maxWait: 2000,\r\n * signal: controller.signal\r\n * });\r\n * debounced();\r\n * controller.abort(); // Cancel pending invocation\r\n */\r\nexport function nixDebounce(fn
|
|
5
|
-
"mappings": ";;
|
|
3
|
+
"sources": ["../../hooks/nixDebounce.ts"],
|
|
4
|
+
"sourcesContent": ["/* MIT License\r\n\r\n* Copyright (c) 2026 Resty Gonzales\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n* SOFTWARE.\r\n */\r\n/**\r\n * @fileoverview Debounce utility function with advanced options.\r\n * Supports leading/trailing edge invocation, maxWait, and AbortController.\r\n */\r\n\r\n/**\r\n * Debounce a function with options for leading, trailing, maxWait, and AbortController support.\r\n *\r\n * @param {Function} fn - The function to debounce\r\n * @param {number} [delay=300] - Delay in milliseconds\r\n * @param {Object} [options={}] - Debounce options\r\n * @param {boolean} [options.leading=false] - Invoke on the leading edge\r\n * @param {boolean} [options.trailing=true] - Invoke on the trailing edge\r\n * @param {number} [options.maxWait] - Maximum wait time before forced invocation\r\n * @param {AbortSignal} [options.signal] - Optional AbortSignal to cancel pending calls\r\n * @returns {Function} Debounced function with `.cancel()` method\r\n *\r\n * @example\r\n * const controller = new AbortController();\r\n * const debounced = nixDebounce(() => console.log('Hello'), 500, {\r\n * leading: true,\r\n * maxWait: 2000,\r\n * signal: controller.signal\r\n * });\r\n * debounced();\r\n * controller.abort(); // Cancel pending invocation\r\n */\r\nexport interface NixDebounceOptions {\r\n leading?: boolean;\r\n trailing?: boolean;\r\n maxWait?: number;\r\n signal?: AbortSignal;\r\n}\r\n\r\nexport type DebouncedFunction<T extends (...args: any[]) => any> = ((\r\n ...args: Parameters<T>\r\n) => void) & {\r\n cancel: () => void;\r\n};\r\n\r\nexport function nixDebounce<T extends (...args: any[]) => any>(\r\n fn: T,\r\n delay: number = 300,\r\n options: NixDebounceOptions = {}\r\n): DebouncedFunction<T> {\r\n let timerId: ReturnType<typeof setTimeout> | null = null;\r\n let lastInvokeTime = 0;\r\n let lastArgs: Parameters<T> | null = null;\r\n let lastThis: any = null;\r\n\r\n const { leading = false, trailing = true, maxWait, signal } = options;\r\n\r\n if (signal) {\r\n signal.addEventListener(\"abort\", () => {\r\n if (timerId) {\r\n clearTimeout(timerId);\r\n timerId = null;\r\n }\r\n lastArgs = null;\r\n lastThis = null;\r\n });\r\n }\r\n\r\n const invoke = () => {\r\n lastInvokeTime = Date.now();\r\n if (lastArgs) {\r\n fn.apply(lastThis, lastArgs);\r\n lastArgs = lastThis = null;\r\n }\r\n };\r\n\r\n const debounced = function (this: unknown, ...args: Parameters<T>) {\r\n const now = Date.now();\r\n lastArgs = args;\r\n lastThis = this;\r\n\r\n const shouldInvokeLeading = leading && !timerId;\r\n const timeSinceLastInvoke = now - lastInvokeTime;\r\n const remainingTime = delay - timeSinceLastInvoke;\r\n\r\n if (maxWait !== undefined && timeSinceLastInvoke >= maxWait) {\r\n if (timerId) clearTimeout(timerId);\r\n timerId = null;\r\n invoke();\r\n return;\r\n }\r\n\r\n if (timerId) clearTimeout(timerId);\r\n\r\n if (shouldInvokeLeading) {\r\n invoke();\r\n }\r\n\r\n if (trailing) {\r\n timerId = setTimeout(invoke, remainingTime > 0 ? remainingTime : delay);\r\n }\r\n } as DebouncedFunction<T>;\r\n\r\n debounced.cancel = () => {\r\n if (timerId) clearTimeout(timerId);\r\n timerId = null;\r\n lastArgs = null;\r\n lastThis = null;\r\n };\r\n\r\n return debounced;\r\n}\r\n"],
|
|
5
|
+
"mappings": ";;AA8DO,SAAS,YACd,IACA,QAAgB,KAChB,UAA8B,CAAC,GACT;AACtB,MAAI,UAAgD;AACpD,MAAI,iBAAiB;AACrB,MAAI,WAAiC;AACrC,MAAI,WAAgB;AAEpB,QAAM,EAAE,UAAU,OAAO,WAAW,MAAM,SAAS,OAAO,IAAI;AAE9D,MAAI,QAAQ;AACV,WAAO,iBAAiB,SAAS,MAAM;AACrC,UAAI,SAAS;AACX,qBAAa,OAAO;AACpB,kBAAU;AAAA,MACZ;AACA,iBAAW;AACX,iBAAW;AAAA,IACb,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,6BAAM;AACnB,qBAAiB,KAAK,IAAI;AAC1B,QAAI,UAAU;AACZ,SAAG,MAAM,UAAU,QAAQ;AAC3B,iBAAW,WAAW;AAAA,IACxB;AAAA,EACF,GANe;AAQf,QAAM,YAAY,mCAA4B,MAAqB;AACjE,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW;AACX,eAAW;AAEX,UAAM,sBAAsB,WAAW,CAAC;AACxC,UAAM,sBAAsB,MAAM;AAClC,UAAM,gBAAgB,QAAQ;AAE9B,QAAI,YAAY,UAAa,uBAAuB,SAAS;AAC3D,UAAI,QAAS,cAAa,OAAO;AACjC,gBAAU;AACV,aAAO;AACP;AAAA,IACF;AAEA,QAAI,QAAS,cAAa,OAAO;AAEjC,QAAI,qBAAqB;AACvB,aAAO;AAAA,IACT;AAEA,QAAI,UAAU;AACZ,gBAAU,WAAW,QAAQ,gBAAgB,IAAI,gBAAgB,KAAK;AAAA,IACxE;AAAA,EACF,GAzBkB;AA2BlB,YAAU,SAAS,MAAM;AACvB,QAAI,QAAS,cAAa,OAAO;AACjC,cAAU;AACV,eAAW;AACX,eAAW;AAAA,EACb;AAEA,SAAO;AACT;AAlEgB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function nixEffect(effect: () => void | (() => void), deps?: any[]): void;
|
|
2
|
+
export declare function nixEffectOnce(effect: () => void | (() => void)): void;
|
|
3
|
+
export declare function nixEffectAlways(effect: () => void | (() => void)): void;
|
|
4
|
+
//# sourceMappingURL=nixEffect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nixEffect.d.ts","sourceRoot":"","sources":["../../hooks/nixEffect.ts"],"names":[],"mappings":"AAkEA,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,EACjC,IAAI,GAAE,GAAG,EAAO,GACf,IAAI,CAyDN;AAgCD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAErE;AAaD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAUvE"}
|