@hoosk/minisignals 1.1.0 → 1.1.1
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/README.md +1 -1
- package/dist/computed.js +4 -4
- package/dist/effect.d.ts +4 -1
- package/dist/effect.js +16 -15
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -63,4 +63,4 @@ computed-batched-writes-then-read 706.54K ops/s 1.42 µs/op
|
|
|
63
63
|
|
|
64
64
|
Run `npm run bench` to reproduce locally.
|
|
65
65
|
|
|
66
|
-
For full documentation and advanced features, please refer to the [main repository README](
|
|
66
|
+
For full documentation and advanced features, please refer to the [main repository README](https://github.com/Hoosk/minisignals#readme).
|
package/dist/computed.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cleanupSubscriber, createDependencySource, getActiveSubscriber, scheduleSource, trackDependency, trackSubscriber, } from './effect.js';
|
|
1
|
+
import { cleanupSubscriber, createDependencySource, getActiveSubscriber, PENDING, scheduleSource, trackDependency, trackSubscriber, } from './effect.js';
|
|
2
2
|
/**
|
|
3
3
|
* Creates a computed signal that derives its value from other signals.
|
|
4
4
|
* It only calculates and subscribes when its value is read for the first time.
|
|
@@ -18,18 +18,18 @@ export function computed(fn) {
|
|
|
18
18
|
depsHead: null,
|
|
19
19
|
depsTail: null,
|
|
20
20
|
trackId: 0,
|
|
21
|
-
|
|
21
|
+
flags: 0,
|
|
22
22
|
};
|
|
23
23
|
let initialized = false;
|
|
24
24
|
let isDisposed = false;
|
|
25
25
|
let dirty = true;
|
|
26
26
|
let cachedValue;
|
|
27
27
|
function refreshValue() {
|
|
28
|
-
// Clear
|
|
28
|
+
// Clear PENDING so a batch flush after an eager read inside the same batch
|
|
29
29
|
// does not re-trigger notify() on an already-fresh computed.
|
|
30
30
|
// We do NOT call cleanupSubscriber here: trackSubscriber increments trackId
|
|
31
31
|
// and pruneStaleDependencies removes only stale links, reusing stable ones.
|
|
32
|
-
observer.
|
|
32
|
+
observer.flags &= ~PENDING;
|
|
33
33
|
cachedValue = trackSubscriber(observer, fn);
|
|
34
34
|
initialized = true;
|
|
35
35
|
dirty = false;
|
package/dist/effect.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export declare const PENDING: number;
|
|
2
|
+
export declare const RUNNING: number;
|
|
3
|
+
export declare const NEEDS_RERUN: number;
|
|
1
4
|
export interface DependencySource {
|
|
2
5
|
subsHead: DependencyLink | null;
|
|
3
6
|
currentLink: DependencyLink | null;
|
|
@@ -17,7 +20,7 @@ export interface Subscriber {
|
|
|
17
20
|
depsHead: DependencyLink | null;
|
|
18
21
|
depsTail: DependencyLink | null;
|
|
19
22
|
trackId: number;
|
|
20
|
-
|
|
23
|
+
flags: number;
|
|
21
24
|
}
|
|
22
25
|
declare let _activeSubscriber: Subscriber | null;
|
|
23
26
|
export { _activeSubscriber };
|
package/dist/effect.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
// ───
|
|
1
|
+
// ─── Subscriber flags ───────────────────────────────────────────────────────
|
|
2
|
+
export const PENDING = 1 << 0; // queued in _pendingNotifications
|
|
3
|
+
export const RUNNING = 1 << 1; // effect is currently executing
|
|
4
|
+
export const NEEDS_RERUN = 1 << 2; // dependency changed while RUNNING
|
|
2
5
|
let _activeSubscriber = null;
|
|
3
6
|
// Exported so signal.ts can do an inline null-check in the getter hot path,
|
|
4
7
|
// avoiding a function call when there is no active subscriber.
|
|
@@ -120,15 +123,15 @@ export function cleanupSubscriber(subscriber) {
|
|
|
120
123
|
unlinkDependency(link);
|
|
121
124
|
link = next;
|
|
122
125
|
}
|
|
123
|
-
subscriber.
|
|
126
|
+
subscriber.flags &= ~PENDING;
|
|
124
127
|
}
|
|
125
128
|
// ─── Batch scheduling ────────────────────────────────────────────────────────
|
|
126
129
|
let _batchDepth = 0;
|
|
127
130
|
const _pendingNotifications = [];
|
|
128
131
|
function queueSubscriber(subscriber) {
|
|
129
132
|
if (_batchDepth > 0) {
|
|
130
|
-
if (!subscriber.
|
|
131
|
-
subscriber.
|
|
133
|
+
if (!(subscriber.flags & PENDING)) {
|
|
134
|
+
subscriber.flags |= PENDING;
|
|
132
135
|
_pendingNotifications.push(subscriber);
|
|
133
136
|
}
|
|
134
137
|
}
|
|
@@ -154,9 +157,9 @@ export function batch(fn) {
|
|
|
154
157
|
if (_batchDepth === 0) {
|
|
155
158
|
const pending = _pendingNotifications.splice(0);
|
|
156
159
|
for (const sub of pending) {
|
|
157
|
-
if (!sub.
|
|
160
|
+
if (!(sub.flags & PENDING))
|
|
158
161
|
continue;
|
|
159
|
-
sub.
|
|
162
|
+
sub.flags &= ~PENDING;
|
|
160
163
|
sub.notify();
|
|
161
164
|
}
|
|
162
165
|
}
|
|
@@ -183,31 +186,29 @@ const MAX_EFFECT_ITERATIONS = 100;
|
|
|
183
186
|
export function effect(fn) {
|
|
184
187
|
const effectObj = {};
|
|
185
188
|
effectObj.notify = () => {
|
|
186
|
-
if (effectObj.
|
|
187
|
-
effectObj.
|
|
189
|
+
if (effectObj.flags & RUNNING) {
|
|
190
|
+
effectObj.flags |= NEEDS_RERUN;
|
|
188
191
|
return;
|
|
189
192
|
}
|
|
190
|
-
effectObj.
|
|
193
|
+
effectObj.flags |= RUNNING;
|
|
191
194
|
let iterations = 0;
|
|
192
195
|
try {
|
|
193
196
|
do {
|
|
194
197
|
if (++iterations > MAX_EFFECT_ITERATIONS) {
|
|
195
198
|
throw new Error('Circular dependency detected: effect exceeded maximum re-run limit');
|
|
196
199
|
}
|
|
197
|
-
effectObj.
|
|
200
|
+
effectObj.flags &= ~NEEDS_RERUN;
|
|
198
201
|
trackSubscriber(effectObj, fn);
|
|
199
|
-
} while (effectObj.
|
|
202
|
+
} while (effectObj.flags & NEEDS_RERUN);
|
|
200
203
|
}
|
|
201
204
|
finally {
|
|
202
|
-
effectObj.
|
|
205
|
+
effectObj.flags &= ~RUNNING;
|
|
203
206
|
}
|
|
204
207
|
};
|
|
205
208
|
effectObj.depsHead = null;
|
|
206
209
|
effectObj.depsTail = null;
|
|
207
210
|
effectObj.trackId = 0;
|
|
208
|
-
effectObj.
|
|
209
|
-
effectObj.isRunning = false;
|
|
210
|
-
effectObj.needsRerun = false;
|
|
211
|
+
effectObj.flags = 0;
|
|
211
212
|
effectObj.notify();
|
|
212
213
|
return () => cleanupSubscriber(effectObj);
|
|
213
214
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hoosk/minisignals",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Minimal agnostic signals library",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"test": "vitest run",
|
|
23
23
|
"bench": "npm run build && node ./bench/core.bench.mjs"
|
|
24
24
|
},
|
|
25
|
-
"keywords": [],
|
|
25
|
+
"keywords": ["signals", "reactive", "reactivity", "state", "computed", "effect", "signal", "observable", "framework-agnostic"],
|
|
26
26
|
"author": "Hoosk",
|
|
27
27
|
"license": "MIT"
|
|
28
28
|
}
|