easy-signal 4.0.0 → 4.0.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/dist/atom.d.ts +3 -3
- package/dist/atom.js +46 -51
- package/dist/atom.js.map +1 -1
- package/package.json +1 -1
- package/src/atom.ts +69 -62
package/dist/atom.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Subscriber, Unsubscriber } from './types';
|
|
2
|
-
export type Invalidator
|
|
2
|
+
export type Invalidator = () => void;
|
|
3
3
|
export type StartStopNotifier<T> = (set: Atom<T>) => Unsubscriber | void;
|
|
4
4
|
export type { Subscriber, Unsubscriber };
|
|
5
5
|
export interface Derived<T> {
|
|
@@ -10,7 +10,7 @@ export interface Derived<T> {
|
|
|
10
10
|
/**
|
|
11
11
|
* Subscribe to changes with a callback. Returns an unsubscribe function.
|
|
12
12
|
*/
|
|
13
|
-
subscribe(callback: Subscriber<T
|
|
13
|
+
subscribe(callback: Subscriber<T>): Unsubscriber;
|
|
14
14
|
}
|
|
15
15
|
export interface Atom<T> extends Derived<T> {
|
|
16
16
|
/**
|
|
@@ -37,4 +37,4 @@ export declare function observe<T>(fn: () => T): Unsubscriber;
|
|
|
37
37
|
/**
|
|
38
38
|
* Create a `Readable` atom that derives its value from other atoms and updates when those atoms change.
|
|
39
39
|
*/
|
|
40
|
-
export declare function derived<T>(fn: () => T,
|
|
40
|
+
export declare function derived<T>(fn: (priorValue: T) => T, value?: T): Derived<T>;
|
package/dist/atom.js
CHANGED
|
@@ -5,7 +5,6 @@ const root = globalThis[symbol] ||
|
|
|
5
5
|
(globalThis[symbol] = {
|
|
6
6
|
context: null,
|
|
7
7
|
subscriberQueue: new Map(),
|
|
8
|
-
trackingDependencies: null,
|
|
9
8
|
});
|
|
10
9
|
/**
|
|
11
10
|
* Creates a `Readable` atom that allows reading by subscription.
|
|
@@ -24,6 +23,11 @@ export function atom(value, start = noop) {
|
|
|
24
23
|
const subscribers = new Map();
|
|
25
24
|
function atom(newValue) {
|
|
26
25
|
if (newValue === undefined) {
|
|
26
|
+
if (root.context) {
|
|
27
|
+
const { subscriber, unsubscribes, invalidate } = root.context;
|
|
28
|
+
const unsubscribe = subscribe(subscriber, invalidate);
|
|
29
|
+
unsubscribes.add(unsubscribe);
|
|
30
|
+
}
|
|
27
31
|
if (!subscribers.size && !started) {
|
|
28
32
|
started = true;
|
|
29
33
|
try {
|
|
@@ -33,9 +37,6 @@ export function atom(value, start = noop) {
|
|
|
33
37
|
started = false;
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
|
-
if (root.trackingDependencies) {
|
|
37
|
-
root.trackingDependencies.add(atom);
|
|
38
|
-
}
|
|
39
40
|
return value;
|
|
40
41
|
}
|
|
41
42
|
else if (value !== newValue) {
|
|
@@ -43,9 +44,10 @@ export function atom(value, start = noop) {
|
|
|
43
44
|
if (stop) {
|
|
44
45
|
// atom is ready
|
|
45
46
|
const runQueue = !root.subscriberQueue.size;
|
|
46
|
-
subscribers.forEach((invalidate, subscriber) => {
|
|
47
|
+
subscribers.forEach(([, invalidate], subscriber) => {
|
|
47
48
|
if (!root.subscriberQueue.has(subscriber)) {
|
|
48
|
-
invalidate
|
|
49
|
+
if (invalidate)
|
|
50
|
+
invalidate();
|
|
49
51
|
}
|
|
50
52
|
else {
|
|
51
53
|
// move to the end of the queue
|
|
@@ -64,20 +66,27 @@ export function atom(value, start = noop) {
|
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
|
-
function subscribe(subscriber, invalidate
|
|
68
|
-
subscribers.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
subscriber(value);
|
|
74
|
-
return () => {
|
|
69
|
+
function subscribe(subscriber, invalidate) {
|
|
70
|
+
let unsubscribe = subscribers.get(subscriber)?.[0];
|
|
71
|
+
// If already subscribed, return the existing unsubscribe function
|
|
72
|
+
if (unsubscribe)
|
|
73
|
+
return unsubscribe;
|
|
74
|
+
unsubscribe = () => {
|
|
75
75
|
subscribers.delete(subscriber);
|
|
76
76
|
if (subscribers.size === 0) {
|
|
77
77
|
stop();
|
|
78
78
|
stop = null;
|
|
79
79
|
}
|
|
80
80
|
};
|
|
81
|
+
subscribers.set(subscriber, [unsubscribe, invalidate]);
|
|
82
|
+
if (subscribers.size === 1) {
|
|
83
|
+
stop = start(atom) || noop;
|
|
84
|
+
}
|
|
85
|
+
// If invalidate is provided, this comes from a derived atom and we should not call the subscriber immediately
|
|
86
|
+
if (!invalidate) {
|
|
87
|
+
subscriber(value);
|
|
88
|
+
}
|
|
89
|
+
return unsubscribe;
|
|
81
90
|
}
|
|
82
91
|
return Object.assign(atom, { subscribe, set: (value) => atom(value) });
|
|
83
92
|
}
|
|
@@ -90,48 +99,34 @@ export function observe(fn) {
|
|
|
90
99
|
/**
|
|
91
100
|
* Create a `Readable` atom that derives its value from other atoms and updates when those atoms change.
|
|
92
101
|
*/
|
|
93
|
-
export function derived(fn,
|
|
94
|
-
|
|
95
|
-
return readable(
|
|
96
|
-
let inited = false;
|
|
102
|
+
export function derived(fn, value) {
|
|
103
|
+
let unsubscribes = new Set();
|
|
104
|
+
return readable(value, set => {
|
|
97
105
|
let pending = 0;
|
|
98
|
-
const subscriber = () => --pending === 0 &&
|
|
106
|
+
const subscriber = () => --pending === 0 && sync();
|
|
99
107
|
const invalidate = () => pending++;
|
|
100
108
|
const sync = () => {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
const prior = root.context;
|
|
110
|
+
// Set the context for the derived function
|
|
111
|
+
root.context = { subscriber, invalidate, unsubscribes: new Set() };
|
|
112
|
+
try {
|
|
113
|
+
// Run the effect collecting all the unsubscribes from the signals that are called when it is run
|
|
114
|
+
value = fn(value);
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
// Filter out unchanged unsubscribes, leaving only those which no longer apply
|
|
118
|
+
root.context.unsubscribes.forEach(u => unsubscribes.delete(u));
|
|
119
|
+
// Unsubscribe from all the signals that are no longer needed
|
|
120
|
+
unsubscribes.forEach(u => u());
|
|
121
|
+
// Set the new unsubscribes
|
|
122
|
+
unsubscribes = root.context.unsubscribes;
|
|
123
|
+
// Clear the context
|
|
124
|
+
root.context = prior;
|
|
125
|
+
}
|
|
126
|
+
set(value);
|
|
115
127
|
};
|
|
116
128
|
sync();
|
|
117
|
-
return
|
|
118
|
-
dependencies.forEach(unsub => unsub());
|
|
119
|
-
dependencies.clear();
|
|
120
|
-
};
|
|
129
|
+
return () => unsubscribes.forEach(u => u());
|
|
121
130
|
});
|
|
122
131
|
}
|
|
123
|
-
function trackDependencies(existing, fn) {
|
|
124
|
-
const priorDependencies = root.trackingDependencies;
|
|
125
|
-
const newDeps = (root.trackingDependencies = new Set());
|
|
126
|
-
try {
|
|
127
|
-
fn();
|
|
128
|
-
}
|
|
129
|
-
finally {
|
|
130
|
-
root.trackingDependencies = priorDependencies;
|
|
131
|
-
const oldDeps = new Set(existing);
|
|
132
|
-
newDeps.forEach(oldDeps.delete, oldDeps);
|
|
133
|
-
existing.forEach(newDeps.delete, newDeps);
|
|
134
|
-
return [oldDeps, newDeps];
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
132
|
//# sourceMappingURL=atom.js.map
|
package/dist/atom.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atom.js","sourceRoot":"","sources":["../src/atom.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"atom.js","sourceRoot":"","sources":["../src/atom.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/atom.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Subscriber, Unsubscriber } from './types';
|
|
2
|
-
export type Invalidator
|
|
2
|
+
export type Invalidator = () => void;
|
|
3
3
|
export type StartStopNotifier<T> = (set: Atom<T>) => Unsubscriber | void;
|
|
4
4
|
export type { Subscriber, Unsubscriber };
|
|
5
5
|
|
|
@@ -12,7 +12,7 @@ export interface Derived<T> {
|
|
|
12
12
|
/**
|
|
13
13
|
* Subscribe to changes with a callback. Returns an unsubscribe function.
|
|
14
14
|
*/
|
|
15
|
-
subscribe(callback: Subscriber<T
|
|
15
|
+
subscribe(callback: Subscriber<T>): Unsubscriber;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export interface Atom<T> extends Derived<T> {
|
|
@@ -28,19 +28,24 @@ export interface Atom<T> extends Derived<T> {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
type Dependencies = Set<Derived<any>>;
|
|
31
|
+
type Context = {
|
|
32
|
+
subscriber: Subscriber<any>;
|
|
33
|
+
invalidate: Invalidator;
|
|
34
|
+
unsubscribes: Set<Unsubscriber>;
|
|
35
|
+
};
|
|
36
|
+
type Root = {
|
|
37
|
+
context: Context;
|
|
38
|
+
subscriberQueue: Map<Subscriber<any>, any>;
|
|
39
|
+
};
|
|
31
40
|
|
|
32
41
|
const noop = () => {};
|
|
33
42
|
// Ensure 2 versions of the signal library can work together
|
|
34
43
|
const symbol = Symbol.for('reactiveAtoms');
|
|
35
|
-
const root =
|
|
44
|
+
const root: Root =
|
|
36
45
|
globalThis[symbol] ||
|
|
37
46
|
(globalThis[symbol] = {
|
|
38
47
|
context: null,
|
|
39
48
|
subscriberQueue: new Map(),
|
|
40
|
-
trackingDependencies: null,
|
|
41
|
-
} as {
|
|
42
|
-
subscriberQueue: Map<Subscriber<any>, any>;
|
|
43
|
-
trackingDependencies: Dependencies;
|
|
44
49
|
});
|
|
45
50
|
|
|
46
51
|
/**
|
|
@@ -58,12 +63,18 @@ export function readable<T>(value?: T, start?: StartStopNotifier<T>): Derived<T>
|
|
|
58
63
|
export function atom<T>(value: T, start: StartStopNotifier<T> = noop): Atom<T> {
|
|
59
64
|
let stop: Unsubscriber;
|
|
60
65
|
let started = false;
|
|
61
|
-
const subscribers = new Map<Subscriber<T>, Invalidator
|
|
66
|
+
const subscribers = new Map<Subscriber<T>, [Unsubscriber, Invalidator?]>();
|
|
62
67
|
|
|
63
68
|
function atom(): T;
|
|
64
69
|
function atom(newValue: T): void;
|
|
65
70
|
function atom(newValue?: T): T | void {
|
|
66
71
|
if (newValue === undefined) {
|
|
72
|
+
if (root.context) {
|
|
73
|
+
const { subscriber, unsubscribes, invalidate } = root.context;
|
|
74
|
+
const unsubscribe = subscribe(subscriber, invalidate);
|
|
75
|
+
unsubscribes.add(unsubscribe);
|
|
76
|
+
}
|
|
77
|
+
|
|
67
78
|
if (!subscribers.size && !started) {
|
|
68
79
|
started = true;
|
|
69
80
|
try {
|
|
@@ -72,9 +83,7 @@ export function atom<T>(value: T, start: StartStopNotifier<T> = noop): Atom<T> {
|
|
|
72
83
|
started = false;
|
|
73
84
|
}
|
|
74
85
|
}
|
|
75
|
-
|
|
76
|
-
root.trackingDependencies.add(atom as Derived<any>);
|
|
77
|
-
}
|
|
86
|
+
|
|
78
87
|
return value;
|
|
79
88
|
} else if (value !== newValue) {
|
|
80
89
|
value = newValue;
|
|
@@ -82,9 +91,9 @@ export function atom<T>(value: T, start: StartStopNotifier<T> = noop): Atom<T> {
|
|
|
82
91
|
// atom is ready
|
|
83
92
|
const runQueue = !root.subscriberQueue.size;
|
|
84
93
|
|
|
85
|
-
subscribers.forEach((invalidate, subscriber) => {
|
|
94
|
+
subscribers.forEach(([, invalidate], subscriber) => {
|
|
86
95
|
if (!root.subscriberQueue.has(subscriber)) {
|
|
87
|
-
invalidate();
|
|
96
|
+
if (invalidate) invalidate();
|
|
88
97
|
} else {
|
|
89
98
|
// move to the end of the queue
|
|
90
99
|
root.subscriberQueue.delete(subscriber);
|
|
@@ -104,21 +113,32 @@ export function atom<T>(value: T, start: StartStopNotifier<T> = noop): Atom<T> {
|
|
|
104
113
|
}
|
|
105
114
|
}
|
|
106
115
|
|
|
107
|
-
function subscribe(subscriber: Subscriber<T>, invalidate
|
|
108
|
-
subscribers.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
invalidate();
|
|
113
|
-
subscriber(value);
|
|
116
|
+
function subscribe(subscriber: Subscriber<T>, invalidate?: Invalidator): Unsubscriber {
|
|
117
|
+
let unsubscribe = subscribers.get(subscriber)?.[0];
|
|
118
|
+
|
|
119
|
+
// If already subscribed, return the existing unsubscribe function
|
|
120
|
+
if (unsubscribe) return unsubscribe;
|
|
114
121
|
|
|
115
|
-
|
|
122
|
+
unsubscribe = () => {
|
|
116
123
|
subscribers.delete(subscriber);
|
|
117
124
|
if (subscribers.size === 0) {
|
|
118
125
|
stop();
|
|
119
126
|
stop = null;
|
|
120
127
|
}
|
|
121
128
|
};
|
|
129
|
+
|
|
130
|
+
subscribers.set(subscriber, [unsubscribe, invalidate]);
|
|
131
|
+
|
|
132
|
+
if (subscribers.size === 1) {
|
|
133
|
+
stop = start(atom as Atom<T>) || noop;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// If invalidate is provided, this comes from a derived atom and we should not call the subscriber immediately
|
|
137
|
+
if (!invalidate) {
|
|
138
|
+
subscriber(value);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return unsubscribe;
|
|
122
142
|
}
|
|
123
143
|
|
|
124
144
|
return Object.assign(atom, { subscribe, set: (value: T) => atom(value) });
|
|
@@ -134,55 +154,42 @@ export function observe<T>(fn: () => T): Unsubscriber {
|
|
|
134
154
|
/**
|
|
135
155
|
* Create a `Readable` atom that derives its value from other atoms and updates when those atoms change.
|
|
136
156
|
*/
|
|
137
|
-
export function derived<T>(fn: () => T,
|
|
138
|
-
|
|
157
|
+
export function derived<T>(fn: (priorValue: T) => T, value?: T): Derived<T> {
|
|
158
|
+
let unsubscribes = new Set<Unsubscriber>();
|
|
139
159
|
|
|
140
|
-
return readable(
|
|
141
|
-
let inited = false;
|
|
160
|
+
return readable(value, set => {
|
|
142
161
|
let pending = 0;
|
|
143
|
-
const subscriber = () => --pending === 0 &&
|
|
162
|
+
const subscriber = () => --pending === 0 && sync();
|
|
144
163
|
const invalidate = () => pending++;
|
|
145
164
|
|
|
146
165
|
const sync = () => {
|
|
147
|
-
const
|
|
148
|
-
const result = fn();
|
|
149
|
-
set(result as T);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
oldDeps.forEach(dep => {
|
|
153
|
-
dependencies.get(dep)();
|
|
154
|
-
dependencies.delete(dep);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
inited = false;
|
|
158
|
-
newDeps.forEach(atom => {
|
|
159
|
-
const unsubscribe = atom.subscribe(subscriber, invalidate);
|
|
160
|
-
dependencies.set(atom, unsubscribe);
|
|
161
|
-
});
|
|
162
|
-
inited = true;
|
|
163
|
-
};
|
|
166
|
+
const prior = root.context;
|
|
164
167
|
|
|
165
|
-
|
|
168
|
+
// Set the context for the derived function
|
|
169
|
+
root.context = { subscriber, invalidate, unsubscribes: new Set() };
|
|
166
170
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
});
|
|
172
|
-
}
|
|
171
|
+
try {
|
|
172
|
+
// Run the effect collecting all the unsubscribes from the signals that are called when it is run
|
|
173
|
+
value = fn(value);
|
|
174
|
+
} finally {
|
|
173
175
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const newDeps = (root.trackingDependencies = new Set<Derived<any>>());
|
|
176
|
+
// Filter out unchanged unsubscribes, leaving only those which no longer apply
|
|
177
|
+
root.context.unsubscribes.forEach(u => unsubscribes.delete(u));
|
|
177
178
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
} finally {
|
|
181
|
-
root.trackingDependencies = priorDependencies;
|
|
179
|
+
// Unsubscribe from all the signals that are no longer needed
|
|
180
|
+
unsubscribes.forEach(u => u());
|
|
182
181
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
182
|
+
// Set the new unsubscribes
|
|
183
|
+
unsubscribes = root.context.unsubscribes;
|
|
184
|
+
|
|
185
|
+
// Clear the context
|
|
186
|
+
root.context = prior;
|
|
187
|
+
}
|
|
188
|
+
set(value);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
sync();
|
|
192
|
+
|
|
193
|
+
return () => unsubscribes.forEach(u => u());
|
|
194
|
+
});
|
|
188
195
|
}
|