@ereo/state 0.1.28 → 0.1.30

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/index.js CHANGED
@@ -3,8 +3,11 @@
3
3
  class Signal {
4
4
  _value;
5
5
  _subscribers = new Set;
6
+ _boundFire;
7
+ _disposers = [];
6
8
  constructor(initialValue) {
7
9
  this._value = initialValue;
10
+ this._boundFire = this._fireSubscribers.bind(this);
8
11
  }
9
12
  get() {
10
13
  return this._value;
@@ -23,11 +26,25 @@ class Signal {
23
26
  return () => this._subscribers.delete(subscriber);
24
27
  }
25
28
  map(fn) {
26
- const computed = new Signal(fn(this._value));
27
- this.subscribe((v) => computed.set(fn(v)));
28
- return computed;
29
+ const mapped = new Signal(fn(this._value));
30
+ const unsub = this.subscribe((v) => mapped.set(fn(v)));
31
+ mapped._disposers.push(unsub);
32
+ return mapped;
33
+ }
34
+ dispose() {
35
+ for (const disposer of this._disposers) {
36
+ disposer();
37
+ }
38
+ this._disposers = [];
39
+ this._subscribers.clear();
29
40
  }
30
41
  _notify() {
42
+ const deferred = _scheduleBatchNotification(this._boundFire);
43
+ if (!deferred) {
44
+ this._fireSubscribers();
45
+ }
46
+ }
47
+ _fireSubscribers() {
31
48
  for (const subscriber of this._subscribers) {
32
49
  try {
33
50
  subscriber(this._value);
@@ -41,20 +58,52 @@ function signal(initialValue) {
41
58
  return new Signal(initialValue);
42
59
  }
43
60
  function computed(fn, deps) {
44
- const computed2 = new Signal(fn());
61
+ const c = new Signal(fn());
45
62
  const update = () => {
46
- computed2.set(fn());
63
+ c.set(fn());
47
64
  };
48
- for (const dep of deps) {
49
- dep.subscribe(update);
65
+ const uniqueDeps = [...new Set(deps)];
66
+ for (const dep of uniqueDeps) {
67
+ const unsub = dep.subscribe(update);
68
+ c._disposers.push(unsub);
50
69
  }
51
- return computed2;
70
+ return c;
52
71
  }
53
72
  function atom(initialValue) {
54
73
  return signal(initialValue);
55
74
  }
75
+ var batchDepth = 0;
76
+ var batchQueue = null;
77
+ function _scheduleBatchNotification(notifier) {
78
+ if (batchDepth > 0) {
79
+ if (!batchQueue)
80
+ batchQueue = new Set;
81
+ batchQueue.add(notifier);
82
+ return true;
83
+ }
84
+ return false;
85
+ }
56
86
  function batch(fn) {
57
- return fn();
87
+ batchDepth++;
88
+ try {
89
+ return fn();
90
+ } finally {
91
+ batchDepth--;
92
+ if (batchDepth === 0 && batchQueue) {
93
+ const queue = batchQueue;
94
+ batchQueue = null;
95
+ let firstError;
96
+ for (const notifier of queue) {
97
+ try {
98
+ notifier();
99
+ } catch (e) {
100
+ firstError ??= e;
101
+ }
102
+ }
103
+ if (firstError)
104
+ throw firstError;
105
+ }
106
+ }
58
107
  }
59
108
 
60
109
  class Store {
@@ -90,7 +139,7 @@ function createStore(initialState) {
90
139
  return new Store(initialState);
91
140
  }
92
141
  // src/react.ts
93
- import { useSyncExternalStore, useCallback } from "react";
142
+ import { useSyncExternalStore, useCallback, useRef } from "react";
94
143
  function useSignal(signal2) {
95
144
  return useSyncExternalStore(useCallback((callback) => signal2.subscribe(callback), [signal2]), () => signal2.get(), () => signal2.get());
96
145
  }
@@ -99,13 +148,29 @@ function useStoreKey(store, key) {
99
148
  return useSignal(signal2);
100
149
  }
101
150
  function useStore(store) {
151
+ const cachedSnapshot = useRef(null);
152
+ const versionRef = useRef(0);
102
153
  return useSyncExternalStore(useCallback((callback) => {
103
154
  const unsubscribers = [];
104
- for (const [key, sig] of store.entries()) {
105
- unsubscribers.push(sig.subscribe(callback));
155
+ for (const [, sig] of store.entries()) {
156
+ unsubscribers.push(sig.subscribe(() => {
157
+ versionRef.current++;
158
+ cachedSnapshot.current = null;
159
+ callback();
160
+ }));
106
161
  }
107
162
  return () => unsubscribers.forEach((unsub) => unsub());
108
- }, [store]), () => store.getSnapshot(), () => store.getSnapshot());
163
+ }, [store]), () => {
164
+ if (cachedSnapshot.current === null) {
165
+ cachedSnapshot.current = store.getSnapshot();
166
+ }
167
+ return cachedSnapshot.current;
168
+ }, () => {
169
+ if (cachedSnapshot.current === null) {
170
+ cachedSnapshot.current = store.getSnapshot();
171
+ }
172
+ return cachedSnapshot.current;
173
+ });
109
174
  }
110
175
  export {
111
176
  useStoreKey,
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAMjD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAC9E,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,GAAG,EAAE,CAAC,GACL,CAAC,CAAC,CAAC,CAAC,CAGN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAY9E"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAMjD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAC9E,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,GAAG,EAAE,CAAC,GACL,CAAC,CAAC,CAAC,CAAC,CAGN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CA6B9E"}
package/dist/signals.d.ts CHANGED
@@ -9,6 +9,10 @@ type Subscriber<T> = (value: T) => void;
9
9
  export declare class Signal<T> {
10
10
  private _value;
11
11
  private _subscribers;
12
+ /** Stable reference for batch deduplication */
13
+ private readonly _boundFire;
14
+ /** Cleanup functions for upstream subscriptions (from map/computed) */
15
+ _disposers: (() => void)[];
12
16
  constructor(initialValue: T);
13
17
  /** Get current value (subscribes in reactive context) */
14
18
  get(): T;
@@ -20,7 +24,10 @@ export declare class Signal<T> {
20
24
  subscribe(subscriber: Subscriber<T>): () => void;
21
25
  /** Create computed signal from this signal */
22
26
  map<U>(fn: (value: T) => U): Signal<U>;
27
+ /** Dispose this signal: unsubscribe from upstream sources and clear subscribers */
28
+ dispose(): void;
23
29
  private _notify;
30
+ private _fireSubscribers;
24
31
  }
25
32
  /** Create a new signal */
26
33
  export declare function signal<T>(initialValue: T): Signal<T>;
@@ -28,7 +35,9 @@ export declare function signal<T>(initialValue: T): Signal<T>;
28
35
  export declare function computed<T>(fn: () => T, deps: Signal<unknown>[]): Signal<T>;
29
36
  /** Atom (alias for signal) */
30
37
  export declare function atom<T>(initialValue: T): Signal<T>;
31
- /** Batch multiple updates */
38
+ /** Schedule a notification (called from Signal._notify) */
39
+ export declare function _scheduleBatchNotification(notifier: () => void): boolean;
40
+ /** Batch multiple updates — notifications are deferred until the batch completes */
32
41
  export declare function batch<T>(fn: () => T): T;
33
42
  /** Store for global state */
34
43
  export declare class Store<T extends Record<string, unknown>> {
@@ -1 +1 @@
1
- {"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,0BAA0B;AAC1B,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAExC,iCAAiC;AACjC,qBAAa,MAAM,CAAC,CAAC;IACnB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,YAAY,CAAiC;gBAEzC,YAAY,EAAE,CAAC;IAI3B,yDAAyD;IACzD,GAAG,IAAI,CAAC;IAIR,2CAA2C;IAC3C,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAOnB,iCAAiC;IACjC,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI;IAItC,2BAA2B;IAC3B,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAKhD,8CAA8C;IAC9C,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAMtC,OAAO,CAAC,OAAO;CAUhB;AAED,0BAA0B;AAC1B,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAEpD;AAED,+BAA+B;AAC/B,wBAAgB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAY3E;AAED,8BAA8B;AAC9B,wBAAgB,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAElD;AAED,6BAA6B;AAC7B,wBAAgB,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAGvC;AAED,6BAA6B;AAC7B,qBAAa,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAElD,OAAO,CAAC,MAAM,CAAuC;gBAEzC,YAAY,EAAE,CAAC;IAM3B,yBAAyB;IACzB,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAI5C,wBAAwB;IACxB,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IASjD,sCAAsC;IACtC,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAItD,yCAAyC;IACzC,WAAW,IAAI,CAAC;CAOjB;AAED,qBAAqB;AACrB,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAExF"}
1
+ {"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,0BAA0B;AAC1B,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAExC,iCAAiC;AACjC,qBAAa,MAAM,CAAC,CAAC;IACnB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,YAAY,CAAiC;IACrD,+CAA+C;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,uEAAuE;IACvE,UAAU,EAAE,CAAC,MAAM,IAAI,CAAC,EAAE,CAAM;gBAEpB,YAAY,EAAE,CAAC;IAK3B,yDAAyD;IACzD,GAAG,IAAI,CAAC;IAIR,2CAA2C;IAC3C,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAOnB,iCAAiC;IACjC,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI;IAItC,2BAA2B;IAC3B,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAKhD,8CAA8C;IAC9C,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAOtC,mFAAmF;IACnF,OAAO,IAAI,IAAI;IAQf,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,gBAAgB;CAUzB;AAED,0BAA0B;AAC1B,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAEpD;AAED,+BAA+B;AAC/B,wBAAgB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAc3E;AAED,8BAA8B;AAC9B,wBAAgB,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAElD;AAOD,2DAA2D;AAC3D,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAOxE;AAED,oFAAoF;AACpF,wBAAgB,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAoBvC;AAED,6BAA6B;AAC7B,qBAAa,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAElD,OAAO,CAAC,MAAM,CAAuC;gBAEzC,YAAY,EAAE,CAAC;IAM3B,yBAAyB;IACzB,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAI5C,wBAAwB;IACxB,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IASjD,sCAAsC;IACtC,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAItD,yCAAyC;IACzC,WAAW,IAAI,CAAC;CAOjB;AAED,qBAAqB;AACrB,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAExF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ereo/state",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "license": "MIT",
5
5
  "author": "Ereo Team",
6
6
  "homepage": "https://ereo.dev",
@@ -33,7 +33,7 @@
33
33
  "typecheck": "tsc --noEmit"
34
34
  },
35
35
  "dependencies": {
36
- "@ereo/core": "^0.1.28"
36
+ "@ereo/core": "workspace:*"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "react": "^18.0.0"