@retreon/cells 0.1.1 → 0.2.0-2025-06-23.7d6d015

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 CHANGED
@@ -5,162 +5,150 @@
5
5
 
6
6
  ## Purpose
7
7
 
8
- Retreon Cells is an implementation of Signals.
8
+ Retreon Cells is an implementation of [Signals](https://dev.to/this-is-learning/the-evolution-of-signals-in-javascript-8ob) with first-class support for **[Volatile Functions](https://learn.microsoft.com/en-us/office/dev/add-ins/excel/custom-functions-volatile)**, a primitive for binding external data sources.
9
9
 
10
- The driving force is support for **[Volatile Functions](https://learn.microsoft.com/en-us/office/dev/add-ins/excel/custom-functions-volatile)**, a concept borrowed from spreadsheet programming. Volatile functions are inherently impure - they return different values even with the same inputs. The web platform is full of these: `Date.now()`, `localStorage` reads, `window.innerHeight`, and countless others.
10
+ This is a low-level library. It biases towards power and expressiveness over safety and convenience. As such, several components are out of scope:
11
11
 
12
- Traditional systems struggle with volatile sources. They introduce stale reads, resource leaks, or unusable APIs outside a component context. Retreon Cells addresses this by integrating external sources directly into the computation graph, propagating cache invalidation in pull-based reactivity and upgrading to tracked values in watched contexts.
12
+ - Error caching
13
+ - Cycle detection
14
+ - Effect management
13
15
 
14
- For more information, see [the TC39 Signals proposal discussion](https://github.com/tc39/proposal-signals/issues/237).
16
+ ## Other Projects
15
17
 
16
- ## Design Goals
18
+ There are many high-quality libraries implementing signal-based reactivity:
17
19
 
18
- This is intended as a low-level library. It's designed to be powerful, expressive, lightweight, and performant.
20
+ - [SolidJS Signals](https://github.com/solidjs/signals)
21
+ - [Preact Signals](https://github.com/preactjs/signals)
22
+ - [Alien Signals](https://github.com/stackblitz/alien-signals) (used by Vue, XState, many others)
23
+ - [TC39 Proposal Reference Implementation](https://github.com/proposal-signals/signal-polyfill)
19
24
 
20
- Guardrails and convenience functions are out of scope and better suited to higher-level abstractions.
25
+ `@retreon/cells` exists to explore the space of Volatile Functions and serve as a concrete example of how it might work in practice. It spawned out of a discussion thread [here](https://github.com/tc39/proposal-signals/issues/237).
26
+
27
+ ## Overview
28
+
29
+ - **Cell:** Holds a value. Can only be read or replaced.
30
+ - **Source:** Similar to a cell, but binds to externally owned data.
31
+ - **Formula:** Computes a cached value using cells, sources, or other formulas.
32
+ - **Watcher:** Listens for changes on a cell, source, or formula.
21
33
 
22
34
  ## Installation
23
35
 
24
36
  ```bash
25
- npm install @retreon/cells
37
+ npm install --save @retreon/cells
26
38
  ```
27
39
 
28
- ## Quick Example
40
+ ## API
41
+
42
+ ### `cell`
43
+
44
+ Creates a mutable value container. Like a spreadsheet cell, it holds a value that can be updated.
29
45
 
30
46
  ```ts
31
- import { cell, formula, source, get, batch, watch } from '@retreon/cells';
47
+ const count = cell(0);
48
+ ```
32
49
 
33
- // Mutable state
34
- const quantity = cell(2);
35
- const price = cell(19.99);
50
+ ### `source`
36
51
 
37
- // Computed values
38
- const total = formula(() => get(quantity) * get(price));
52
+ Binds to an untracked data source. Examples are `window.innerHeight`, `document.visibilityState`, `localStorage.getItem()`, or any value that changes over time.
39
53
 
40
- // Volatile source
41
- const timestamp = source(
42
- () => Date.now(),
54
+ ```ts
55
+ const viewport = source(
56
+ () => ({
57
+ width: window.innerWidth,
58
+ height: window.innerHeight,
59
+ }),
43
60
  (onChange) => {
44
- // Timestamp sampled every second.
45
- const interval = setInterval(onChange, 1_000);
46
- return () => clearInterval(interval);
61
+ window.addEventListener('resize', onChange);
62
+ return () => window.removeEventListener('resize', onChange);
47
63
  },
48
64
  );
49
-
50
- // Composed formula using both stable and volatile data
51
- const receipt = formula(() => ({
52
- total: get(total),
53
- time: get(timestamp),
54
- }));
55
-
56
- // Read values
57
- console.log(get(receipt));
58
- // { total: 39.98, time: 1577836800000 }
59
-
60
- // Update state atomically
61
- batch((swap) => {
62
- swap(quantity, 3);
63
- swap(price, 24.99);
64
- });
65
-
66
- // Watch for changes
67
- const dispose = watch(total, () => {
68
- console.log(`Total changed to: ${get(total)}`);
69
- });
70
65
  ```
71
66
 
72
- ## API
67
+ Sources run in two modes:
73
68
 
74
- ### `cell(initialValue)`
69
+ - **Volatile (unwatched):** Every read is fresh, not cached.
70
+ - **Non-Volatile (watched):** Reads are cached and only recomputed when necessary.
75
71
 
76
- Creates a mutable value container. Like a spreadsheet cell, it holds a value that can be updated.
77
-
78
- ```ts
79
- const count = cell(0);
80
- ```
72
+ Sources start volatile until they become watched. If a `subscribe()` handler is provided, the source upgrades to non-volatile and only triggers changes when it calls `onChange`.
81
73
 
82
- ### `formula(compute)`
74
+ ### `formula`
83
75
 
84
- Creates a computed value that automatically updates when its dependencies change. Dependencies are discovered automatically during execution.
76
+ Formulas compute a value using cells and sources. The result is cached until a dependency changes.
85
77
 
86
78
  ```ts
87
79
  const doubled = formula(() => get(count) * 2);
88
80
  ```
89
81
 
90
- ### `source(fetch, subscribe?)`
82
+ Formulas that depend on volatile sources are never cached and will always re-evaluate.
91
83
 
92
- Bridges external data into the reactive system. Without a subscription function, sources remain volatile - recomputing on every read. With a subscription, they become cached while observed.
84
+ ### `get`
93
85
 
94
- ```ts
95
- // Volatile: always fresh
96
- const random = source(() => Math.random());
86
+ Reads the current value of a source, cell, or formula.
97
87
 
98
- // Cached when watched: efficient for event-driven data
99
- const windowSize = source(
100
- () => ({ width: window.innerWidth, height: window.innerHeight }),
101
- (onChange) => {
102
- window.addEventListener('resize', onChange);
103
- return () => window.removeEventListener('resize', onChange);
104
- },
105
- );
88
+ ```ts
89
+ const value = cell(10);
90
+ get(value); // => 10
106
91
  ```
107
92
 
108
- ### `get(signal)`
93
+ When used inside a formula, the value is automatically tracked as a dependency.
109
94
 
110
- Reads the current value of any signal. When used inside a formula, automatically tracks the signal as a dependency.
95
+ ### `untracked`
96
+
97
+ Allows reading cells, sources, and formulas without adding them as dependencies.
111
98
 
112
99
  ```ts
113
- const value = get(doubled); // 2
100
+ const a = cell('a');
101
+ const b = cell('b');
102
+
103
+ // Only `a` is added to the set of dependencies. `b` is ignored.
104
+ const result = formula(() => {
105
+ const trackedValue = get(a);
106
+ const untrackedValue = untracked(() => get(b));
107
+ });
114
108
  ```
115
109
 
116
- ### `batch(fn)`
110
+ ### `batch`
117
111
 
118
- Executes multiple cell updates atomically. Ensures consistency by preventing intermediate states from being observed.
112
+ Updates the value of one or more cells atomically.
119
113
 
120
114
  ```ts
121
115
  batch((swap) => {
122
116
  swap(cellA, 1);
123
117
  swap(cellB, 2);
124
- // Watchers fire after both updates complete
125
118
  });
126
119
  ```
127
120
 
128
- ### `watch(signal, handler)`
121
+ Watchers are only notified after all changes are applied (glitch-free evaluation).
122
+
123
+ ### `watch`
129
124
 
130
- Subscribes to changes in a signal. Returns a cleanup function. For formulas, watches all transitive dependencies.
125
+ Subscribes to a cell, source, or formula.
131
126
 
132
127
  ```ts
133
- const dispose = watch(total, () => {
134
- console.log('Total updated:', get(total));
128
+ const [dispose, renew] = watch(total, () => {
129
+ console.log('Total updated:', renew());
135
130
  });
136
-
137
- // Later: stop watching
138
- dispose();
139
131
  ```
140
132
 
141
- ### `untracked(fn)`
133
+ If the target is a formula, all its recursive dependencies are observed. Any `source()` values used are immediately promoted to non-volatile.
142
134
 
143
- Executes a function without tracking any signal dependencies. When called inside a formula computation, any signals read within the untracked function will not be registered as dependencies.
135
+ Calling `renew()` re-evaluates the value and updates the set of observed dependencies. Calling `dispose()` clears the watcher and releases all values. If this was the only watcher observing a `source()`, it will downgrade to volatile mode.
144
136
 
145
- ```ts
146
- const a = cell(1);
147
- const b = cell(2);
137
+ > [!WARNING]
138
+ > Calling `watch()` does **not** evaluate formulas. If the formula hasn't been evaluated yet, or hasn't been evaluated since dependencies changed, `watch()` will subscribe to stale dependencies.
139
+ >
140
+ > While it provides control over how and when formulas execute, the API is easy to misuse. It's recommended to abstract it with higher-level utilities.
148
141
 
149
- // result only recomputes when 'a' changes, not when 'b' changes
150
- const result = formula(() => {
151
- const trackedValue = get(a); // This creates a dependency
152
- const untrackedValue = untracked(() => get(b)); // This does not
153
- return trackedValue + untrackedValue;
154
- });
155
- ```
156
-
157
- ### `visitDependencies(signal, visitor)`
142
+ ### `visitDependencies`
158
143
 
159
- Visits all signals in the dependency graph. Calls the visitor function for each signal encountered and returns the set of all visited signals.
144
+ Visits all cells, sources, and formulas in a dependency graph.
160
145
 
161
146
  ```ts
162
- const visited = visitDependencies(receipt, (sig) => {
163
- console.log(`Found ${sig.type}`);
147
+ const visited = visitDependencies(expression, (dep) => {
148
+ console.log('Found:', dep.type);
164
149
  });
165
- // Logs: "Found formula", "Found cell", "Found cell", "Found source"
166
150
  ```
151
+
152
+ It returns the set of all dependencies including the value provided.
153
+
154
+ **Note:** like `watch()`, `visitDependencies()` does not evaluate formulas and may return a stale (cached) set of dependencies. You may want to force evaluation before calling this API.
package/dist/cells.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let f=0;function u(){return++f}const y=(t,e)=>{t.w.add(e)},v=(t,e)=>{t.w.delete(e)},W=t=>({t:"c",c:t,v:f,w:new Set});let c=null,o=!1;const b=t=>{c&&F(c,t)},k=(t,e)=>{const n=c;c=t;try{return e()}finally{c=n}},C=t=>{const e=o;o=!0;try{return t()}finally{o=e}},D=t=>{const e=c;c=null;try{return t()}finally{c=e}},E=t=>({t:"f",f:t,c:void 0,v:-1,d:new Map,x:!1}),F=(t,e)=>{t.d.set(e,e.v)},V=t=>{if(t.x)return!0;if(t.v===f)return!1;for(const[e,n]of t.d)if(e.t==="s"&&e.x||e.v!==n)return!0;return t.v===-1},h=t=>{if(!V(t))return t.c;t.d.clear(),t.x=!1;const e=k(t,t.f);for(const n of t.d.keys())if(n.t==="s"&&n.x||n.t==="f"&&n.x){t.x=!0;break}return t.c=e,t.v=f,e};function x(t,e){const n=new Set;function r(i){if(!n.has(i)&&(n.add(i),e==null||e(i),i.t==="f")){h(i);for(const[p]of i.d)r(p)}}return r(t),n}const z=(t,e)=>({s:t,c:e,d:new Set}),l=(t,e=!0)=>{const n=C(()=>x(t.s));n.forEach(r=>{t.d.has(r)||(r.t==="c"?y(r,t):r.t==="s"&&j(r,t))}),t.d.forEach(r=>{n.has(r)||(r.t==="c"?v(r,t):r.t==="s"&&S(r,t))}),t.d=n,e&&(0,t.c)()},M=t=>{t.d.forEach(e=>{e.t==="c"?v(e,t):e.t==="s"&&S(e,t)}),t.d.clear()},N=(t,e)=>{const n=z(t,e);return l(n,!1),()=>M(n)},T=t=>(t.x&&o&&w(t),t.x?(0,t.r)():(t.p||(t.c=t.r(),t.p=!0),t.c)),w=t=>{t.x&&t.s&&(t.x=!1,t.d=t.s(()=>{t.c=void 0,t.p=!1,t.v=u();for(const e of t.w)l(e)}))},j=(t,e)=>{t.w.add(e),t.w.size===1&&w(t)},S=(t,e)=>{t.w.delete(e),t.w.size===0&&(t.x=!0,t.d&&(t.d(),t.d=void 0),t.c=void 0,t.p=!1)},B=(t,e)=>({t:"s",r:t,s:e,c:void 0,v:f,w:new Set,x:!0,p:!1,d:void 0}),I=t=>(b(t),t.t==="c"?t.c:t.t==="f"?h(t):T(t));let s=!1;const a=new Set;function O(t){if(s)return t(d);s=!0,a.clear();try{const e=t(d);for(const n of a)l(n);return e}finally{s=!1,a.clear()}}function d(t,e){if(!s)throw new Error("Cell mutations must occur within a batch()");if(t.c!==e){t.c=e,t.v=u();for(const n of t.w)a.add(n)}}exports.batch=O;exports.cell=W;exports.formula=E;exports.get=I;exports.source=B;exports.untracked=D;exports.visitDependencies=x;exports.watch=N;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let o=0;function h(){return++o}const b=(t,e)=>{t.w.add(e)},w=(t,e)=>{t.w.delete(e)},C=t=>({t:"c",c:t,v:o,w:new Set});let c=null,i=!1;const k=(t,e)=>{const n=c;c=t;try{return e()}finally{c=n}},x=t=>{const e=i;i=!0;try{return t()}finally{i=e}},D=t=>{const e=c;c=null;try{return t()}finally{c=e}},E=t=>({t:"f",f:t,c:void 0,v:-1,d:new Map,x:!1}),F=(t,e)=>{t.d.set(e,e.v)},V=t=>{if(t.x)return!0;if(t.v===o)return!1;for(const[e,n]of t.d)if(e.t==="s"&&e.x||e.v!==n)return!0;return t.v===-1},T=t=>{if(!V(t))return t.c;t.d.clear(),t.x=!1;const e=k(t,t.f);for(const n of t.d.keys())if(n.t==="s"&&n.x||n.t==="f"&&n.x){t.x=!0;break}return t.c=e,t.v=o,e},d={};function S(t,e){const n=new Set;function r(s){if(!n.has(s)&&(n.add(s),e==null||e(s),s.t==="f"))for(const[u]of s.d)r(u)}return r(t),n}const y=t=>(c&&F(c,t),t.t==="c"?t.c:t.t==="f"?T(t):j(t)),z=(t,e)=>({s:t,c:e,d:new Set}),l=(t,e=!0)=>{const n=x(()=>S(t.s));n.forEach(r=>{t.d.has(r)||(r.t==="c"?b(r,t):r.t==="s"&&A(r,t))}),t.d.forEach(r=>{n.has(r)||(r.t==="c"?w(r,t):r.t==="s"&&p(r,t))}),t.d=n,e&&(0,t.c)()},M=t=>{t.d.forEach(e=>{e.t==="c"?w(e,t):e.t==="s"&&p(e,t)}),t.d.clear()},N=(t,e)=>{const n=z(t,e);return l(n,!1),[()=>M(n),()=>{const u=x(()=>y(t));return l(n,!1),u}]},j=t=>(t.x&&i&&W(t),t.x?(0,t.r)():(t.c===d&&(t.c=t.r()),t.c)),W=t=>{t.x&&t.s&&(t.x=!1,t.d=t.s(()=>{t.c=d,t.v=h();for(const e of t.w)l(e)}))},A=(t,e)=>{t.w.add(e),t.w.size===1&&W(t)},p=(t,e)=>{t.w.delete(e),t.w.size===0&&(t.x=!0,t.d&&(t.d(),t.d=void 0),t.c=d)},B=(t,e)=>({t:"s",r:t,s:e,c:d,v:o,w:new Set,x:!0,d:void 0});let f=!1;const a=new Set;function I(t){if(f)return t(v);f=!0,a.clear();try{const e=t(v);for(const n of a)l(n);return e}finally{f=!1,a.clear()}}function v(t,e){if(!f)throw new Error("Cell mutations must occur within a batch()");if(t.c!==e){t.c=e,t.v=h();for(const n of t.w)a.add(n)}}exports.batch=I;exports.cell=C;exports.formula=E;exports.get=y;exports.source=B;exports.untracked=D;exports.visitDependencies=S;exports.watch=N;
package/dist/cells.d.ts CHANGED
@@ -133,9 +133,7 @@ export declare interface Source<T> {
133
133
  /** Disposer for the subscription */
134
134
  d?: Disposer;
135
135
  /** Cached value */
136
- c: T | undefined;
137
- /** Whether the cache is primed */
138
- p: boolean;
136
+ c: T | typeof STALE;
139
137
  /** Volatile flag */
140
138
  x: boolean;
141
139
  /** Version */
@@ -174,6 +172,9 @@ export declare interface Source<T> {
174
172
  */
175
173
  export declare const source: <T>(read: () => T, subscribe?: (onChange: () => void) => () => void) => Source<T>;
176
174
 
175
+ /** Sentinel value indicating cache is empty. */
176
+ declare const STALE: {};
177
+
177
178
  export declare type SwapFunction = <T>(cell: Cell<T>, newValue: T) => void;
178
179
 
179
180
  /**
@@ -230,14 +231,14 @@ export declare function visitDependencies<T>(signal: Signal<T>, visitor?: (signa
230
231
  *
231
232
  * @param signal - The signal to watch
232
233
  * @param onChange - Function to call when the signal changes
233
- * @returns A disposer function that stops watching when called
234
+ * @returns A tuple of [dispose, renew] functions
234
235
  *
235
236
  * @example
236
237
  * ```typescript
237
238
  * const count = cell(0);
238
239
  *
239
- * const dispose = watch(count, () => {
240
- * console.log('Count changed to:', get(count));
240
+ * const [dispose, renew] = watch(count, () => {
241
+ * console.log('Count changed to:', renew());
241
242
  * });
242
243
  *
243
244
  * batch((swap) => {
@@ -247,7 +248,7 @@ export declare function visitDependencies<T>(signal: Signal<T>, visitor?: (signa
247
248
  * dispose(); // Stop watching
248
249
  * ```
249
250
  */
250
- export declare const watch: <T>(signal: Signal<T>, onChange: ChangeHandler) => Disposer;
251
+ export declare const watch: <T>(signal: Signal<T>, onChange: ChangeHandler) => [dispose: Disposer, renew: () => T];
251
252
 
252
253
  declare interface Watcher<T> {
253
254
  /** Signal */
package/dist/cells.js CHANGED
@@ -1,21 +1,19 @@
1
- let f = 0;
2
- function u() {
3
- return ++f;
1
+ let o = 0;
2
+ function x() {
3
+ return ++o;
4
4
  }
5
- const S = (t, e) => {
5
+ const y = (t, e) => {
6
6
  t.w.add(e);
7
- }, v = (t, e) => {
7
+ }, h = (t, e) => {
8
8
  t.w.delete(e);
9
- }, N = (t) => ({
9
+ }, T = (t) => ({
10
10
  t: "c",
11
11
  c: t,
12
- v: f,
12
+ v: o,
13
13
  w: /* @__PURE__ */ new Set()
14
14
  });
15
- let c = null, o = !1;
16
- const y = (t) => {
17
- c && k(c, t);
18
- }, W = (t, e) => {
15
+ let c = null, i = !1;
16
+ const p = (t, e) => {
19
17
  const n = c;
20
18
  c = t;
21
19
  try {
@@ -23,15 +21,15 @@ const y = (t) => {
23
21
  } finally {
24
22
  c = n;
25
23
  }
26
- }, C = (t) => {
27
- const e = o;
28
- o = !0;
24
+ }, w = (t) => {
25
+ const e = i;
26
+ i = !0;
29
27
  try {
30
28
  return t();
31
29
  } finally {
32
- o = e;
30
+ i = e;
33
31
  }
34
- }, B = (t) => {
32
+ }, A = (t) => {
35
33
  const e = c;
36
34
  c = null;
37
35
  try {
@@ -39,119 +37,119 @@ const y = (t) => {
39
37
  } finally {
40
38
  c = e;
41
39
  }
42
- }, I = (t) => ({
40
+ }, B = (t) => ({
43
41
  t: "f",
44
42
  f: t,
45
43
  c: void 0,
46
44
  v: -1,
47
45
  d: /* @__PURE__ */ new Map(),
48
46
  x: !1
49
- }), k = (t, e) => {
47
+ }), C = (t, e) => {
50
48
  t.d.set(e, e.v);
51
- }, D = (t) => {
49
+ }, E = (t) => {
52
50
  if (t.x)
53
51
  return !0;
54
- if (t.v === f)
52
+ if (t.v === o)
55
53
  return !1;
56
54
  for (const [e, n] of t.d)
57
55
  if (e.t === "s" && e.x || e.v !== n)
58
56
  return !0;
59
57
  return t.v === -1;
60
- }, x = (t) => {
61
- if (!D(t))
58
+ }, b = (t) => {
59
+ if (!E(t))
62
60
  return t.c;
63
61
  t.d.clear(), t.x = !1;
64
- const e = W(t, t.f);
62
+ const e = p(t, t.f);
65
63
  for (const n of t.d.keys())
66
64
  if (n.t === "s" && n.x || n.t === "f" && n.x) {
67
65
  t.x = !0;
68
66
  break;
69
67
  }
70
- return t.c = e, t.v = f, e;
71
- };
72
- function b(t, e) {
68
+ return t.c = e, t.v = o, e;
69
+ }, l = {};
70
+ function k(t, e) {
73
71
  const n = /* @__PURE__ */ new Set();
74
- function r(i) {
75
- if (!n.has(i) && (n.add(i), e == null || e(i), i.t === "f")) {
76
- x(i);
77
- for (const [p] of i.d)
78
- r(p);
79
- }
72
+ function r(s) {
73
+ if (!n.has(s) && (n.add(s), e == null || e(s), s.t === "f"))
74
+ for (const [u] of s.d)
75
+ r(u);
80
76
  }
81
77
  return r(t), n;
82
78
  }
83
- const E = (t, e) => ({
79
+ const D = (t) => (c && C(c, t), t.t === "c" ? t.c : t.t === "f" ? b(t) : z(t)), F = (t, e) => ({
84
80
  s: t,
85
81
  c: e,
86
82
  d: /* @__PURE__ */ new Set()
87
83
  }), d = (t, e = !0) => {
88
- const n = C(
89
- () => b(t.s)
84
+ const n = w(
85
+ () => k(t.s)
90
86
  );
91
87
  n.forEach((r) => {
92
- t.d.has(r) || (r.t === "c" ? S(r, t) : r.t === "s" && z(r, t));
88
+ t.d.has(r) || (r.t === "c" ? y(r, t) : r.t === "s" && N(r, t));
93
89
  }), t.d.forEach((r) => {
94
- n.has(r) || (r.t === "c" ? v(r, t) : r.t === "s" && w(r, t));
90
+ n.has(r) || (r.t === "c" ? h(r, t) : r.t === "s" && W(r, t));
95
91
  }), t.d = n, e && (0, t.c)();
96
- }, F = (t) => {
92
+ }, V = (t) => {
97
93
  t.d.forEach((e) => {
98
- e.t === "c" ? v(e, t) : e.t === "s" && w(e, t);
94
+ e.t === "c" ? h(e, t) : e.t === "s" && W(e, t);
99
95
  }), t.d.clear();
100
- }, M = (t, e) => {
101
- const n = E(t, e);
102
- return d(n, !1), () => F(n);
103
- }, V = (t) => (t.x && o && h(t), t.x ? (0, t.r)() : (t.p || (t.c = t.r(), t.p = !0), t.c)), h = (t) => {
96
+ }, I = (t, e) => {
97
+ const n = F(t, e);
98
+ return d(n, !1), [() => V(n), () => {
99
+ const u = w(() => D(t));
100
+ return d(n, !1), u;
101
+ }];
102
+ }, z = (t) => (t.x && i && S(t), t.x ? (0, t.r)() : (t.c === l && (t.c = t.r()), t.c)), S = (t) => {
104
103
  t.x && t.s && (t.x = !1, t.d = t.s(() => {
105
- t.c = void 0, t.p = !1, t.v = u();
104
+ t.c = l, t.v = x();
106
105
  for (const e of t.w)
107
106
  d(e);
108
107
  }));
109
- }, z = (t, e) => {
110
- t.w.add(e), t.w.size === 1 && h(t);
111
- }, w = (t, e) => {
112
- t.w.delete(e), t.w.size === 0 && (t.x = !0, t.d && (t.d(), t.d = void 0), t.c = void 0, t.p = !1);
113
- }, T = (t, e) => ({
108
+ }, N = (t, e) => {
109
+ t.w.add(e), t.w.size === 1 && S(t);
110
+ }, W = (t, e) => {
111
+ t.w.delete(e), t.w.size === 0 && (t.x = !0, t.d && (t.d(), t.d = void 0), t.c = l);
112
+ }, L = (t, e) => ({
114
113
  t: "s",
115
114
  r: t,
116
115
  s: e,
117
- c: void 0,
118
- v: f,
116
+ c: l,
117
+ v: o,
119
118
  w: /* @__PURE__ */ new Set(),
120
119
  x: !0,
121
- p: !1,
122
120
  d: void 0
123
- }), j = (t) => (y(t), t.t === "c" ? t.c : t.t === "f" ? x(t) : V(t));
124
- let s = !1;
121
+ });
122
+ let f = !1;
125
123
  const a = /* @__PURE__ */ new Set();
126
- function q(t) {
127
- if (s)
128
- return t(l);
129
- s = !0, a.clear();
124
+ function M(t) {
125
+ if (f)
126
+ return t(v);
127
+ f = !0, a.clear();
130
128
  try {
131
- const e = t(l);
129
+ const e = t(v);
132
130
  for (const n of a)
133
131
  d(n);
134
132
  return e;
135
133
  } finally {
136
- s = !1, a.clear();
134
+ f = !1, a.clear();
137
135
  }
138
136
  }
139
- function l(t, e) {
140
- if (!s)
137
+ function v(t, e) {
138
+ if (!f)
141
139
  throw new Error("Cell mutations must occur within a batch()");
142
140
  if (t.c !== e) {
143
- t.c = e, t.v = u();
141
+ t.c = e, t.v = x();
144
142
  for (const n of t.w)
145
143
  a.add(n);
146
144
  }
147
145
  }
148
146
  export {
149
- q as batch,
150
- N as cell,
151
- I as formula,
152
- j as get,
153
- T as source,
154
- B as untracked,
155
- b as visitDependencies,
156
- M as watch
147
+ M as batch,
148
+ T as cell,
149
+ B as formula,
150
+ D as get,
151
+ L as source,
152
+ A as untracked,
153
+ k as visitDependencies,
154
+ I as watch
157
155
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@retreon/cells",
4
- "version": "0.1.1",
4
+ "version": "0.2.0-2025-06-23.7d6d015",
5
5
  "description": "A minimal spreadsheet engine",
6
6
  "author": "Jesse Gibson <JesseTheGibson@gmail.com>",
7
7
  "license": "MIT",
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "devDependencies": {
54
54
  "@eslint/js": "^9.17.0",
55
- "@types/eslint__js": "^8.42.3",
55
+ "@types/eslint__js": "^9.0.0",
56
56
  "@types/node": "^24.0.1",
57
57
  "@typescript-eslint/eslint-plugin": "^8.18.2",
58
58
  "keep-a-changelog": "^2.6.2",