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 CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Subscriber, Unsubscriber } from './types';
2
- export type Invalidator<T> = (value?: T) => void;
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>, invalidate?: Invalidator<T>): Unsubscriber;
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, initialValue?: T): Derived<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 = noop) {
68
- subscribers.set(subscriber, invalidate);
69
- if (subscribers.size === 1) {
70
- stop = start(atom) || noop;
71
- }
72
- invalidate();
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, initialValue) {
94
- const dependencies = new Map();
95
- return readable(initialValue, set => {
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 && inited && sync();
106
+ const subscriber = () => --pending === 0 && sync();
99
107
  const invalidate = () => pending++;
100
108
  const sync = () => {
101
- const [oldDeps, newDeps] = trackDependencies(new Set(dependencies.keys()), () => {
102
- const result = fn();
103
- set(result);
104
- });
105
- oldDeps.forEach(dep => {
106
- dependencies.get(dep)();
107
- dependencies.delete(dep);
108
- });
109
- inited = false;
110
- newDeps.forEach(atom => {
111
- const unsubscribe = atom.subscribe(subscriber, invalidate);
112
- dependencies.set(atom, unsubscribe);
113
- });
114
- inited = true;
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 function stop() {
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":"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"}
1
+ {"version":3,"file":"atom.js","sourceRoot":"","sources":["../src/atom.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easy-signal",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "A package with 2 types of signals, an small and simple event signal, and a reactive value-based signal called atoms.",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
package/src/atom.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Subscriber, Unsubscriber } from './types';
2
- export type Invalidator<T> = (value?: T) => void;
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>, invalidate?: Invalidator<T>): Unsubscriber;
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<T>>();
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
- if (root.trackingDependencies) {
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: Invalidator<T> = noop): Unsubscriber {
108
- subscribers.set(subscriber, invalidate);
109
- if (subscribers.size === 1) {
110
- stop = start(atom as Atom<T>) || noop;
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
- return () => {
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, initialValue?: T): Derived<T> {
138
- const dependencies = new Map<Derived<any>, Unsubscriber>();
157
+ export function derived<T>(fn: (priorValue: T) => T, value?: T): Derived<T> {
158
+ let unsubscribes = new Set<Unsubscriber>();
139
159
 
140
- return readable(initialValue, set => {
141
- let inited = false;
160
+ return readable(value, set => {
142
161
  let pending = 0;
143
- const subscriber = () => --pending === 0 && inited && sync();
162
+ const subscriber = () => --pending === 0 && sync();
144
163
  const invalidate = () => pending++;
145
164
 
146
165
  const sync = () => {
147
- const [oldDeps, newDeps] = trackDependencies(new Set(dependencies.keys()), () => {
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
- sync();
168
+ // Set the context for the derived function
169
+ root.context = { subscriber, invalidate, unsubscribes: new Set() };
166
170
 
167
- return function stop() {
168
- dependencies.forEach(unsub => unsub());
169
- dependencies.clear();
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
- function trackDependencies(existing: Dependencies, fn: () => void): [Dependencies, Dependencies] {
175
- const priorDependencies = root.trackingDependencies;
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
- try {
179
- fn();
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
- const oldDeps = new Set(existing);
184
- newDeps.forEach(oldDeps.delete, oldDeps);
185
- existing.forEach(newDeps.delete, newDeps);
186
- return [oldDeps, newDeps];
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
  }