@vielzeug/stateit 2.0.0 → 2.1.0

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.
Files changed (60) hide show
  1. package/README.md +54 -295
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.d.ts +1 -18
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +2 -9
  6. package/dist/stateit.cjs +1 -1
  7. package/dist/stateit.cjs.map +1 -1
  8. package/dist/stateit.d.ts +17 -0
  9. package/dist/stateit.d.ts.map +1 -0
  10. package/dist/stateit.js +180 -236
  11. package/dist/stateit.js.map +1 -1
  12. package/dist/types.d.ts +19 -20
  13. package/dist/types.d.ts.map +1 -1
  14. package/package.json +4 -3
  15. package/dist/batch.cjs +0 -2
  16. package/dist/batch.cjs.map +0 -1
  17. package/dist/batch.d.ts +0 -5
  18. package/dist/batch.d.ts.map +0 -1
  19. package/dist/batch.js +0 -28
  20. package/dist/batch.js.map +0 -1
  21. package/dist/computed.cjs +0 -2
  22. package/dist/computed.cjs.map +0 -1
  23. package/dist/computed.d.ts +0 -58
  24. package/dist/computed.d.ts.map +0 -1
  25. package/dist/computed.js +0 -65
  26. package/dist/computed.js.map +0 -1
  27. package/dist/effect.cjs +0 -2
  28. package/dist/effect.cjs.map +0 -1
  29. package/dist/effect.d.ts +0 -31
  30. package/dist/effect.d.ts.map +0 -1
  31. package/dist/effect.js +0 -53
  32. package/dist/effect.js.map +0 -1
  33. package/dist/runtime.cjs +0 -2
  34. package/dist/runtime.cjs.map +0 -1
  35. package/dist/runtime.d.ts +0 -40
  36. package/dist/runtime.d.ts.map +0 -1
  37. package/dist/runtime.js +0 -47
  38. package/dist/runtime.js.map +0 -1
  39. package/dist/signal.cjs +0 -2
  40. package/dist/signal.cjs.map +0 -1
  41. package/dist/signal.d.ts +0 -26
  42. package/dist/signal.d.ts.map +0 -1
  43. package/dist/signal.js +0 -40
  44. package/dist/signal.js.map +0 -1
  45. package/dist/store.cjs +0 -2
  46. package/dist/store.cjs.map +0 -1
  47. package/dist/store.d.ts +0 -32
  48. package/dist/store.d.ts.map +0 -1
  49. package/dist/store.js +0 -51
  50. package/dist/store.js.map +0 -1
  51. package/dist/types.cjs +0 -2
  52. package/dist/types.cjs.map +0 -1
  53. package/dist/types.js +0 -6
  54. package/dist/types.js.map +0 -1
  55. package/dist/watch.cjs +0 -2
  56. package/dist/watch.cjs.map +0 -1
  57. package/dist/watch.d.ts +0 -36
  58. package/dist/watch.d.ts.map +0 -1
  59. package/dist/watch.js +0 -32
  60. package/dist/watch.js.map +0 -1
package/README.md CHANGED
@@ -1,337 +1,96 @@
1
1
  # @vielzeug/stateit
2
2
 
3
- > Tiny, zero-dependency reactive state signals, computed values, effects, and typed object stores
4
-
5
- [![npm version](https://img.shields.io/npm/v/@vielzeug/stateit)](https://www.npmjs.com/package/@vielzeug/stateit) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
-
7
- **Stateit** is a tiny, zero-dependency reactive library with two complementary primitives:
8
-
9
- - **Signals** — fine-grained reactive atoms with `signal()`, `computed()`, `effect()`, `watch()`, `batch()`, and more
10
- - **Stores** — typed reactive object containers with partial patching, updater functions, selectors, and freeze
11
-
12
- A `Store<T>` extends `Signal<T>`, so every signal primitive — `effect`, `watch`, `computed`, `batch` — works seamlessly on stores.
3
+ Tiny reactive state with direct singleton primitives.
13
4
 
14
5
  ## Installation
15
6
 
16
7
  ```sh
17
8
  pnpm add @vielzeug/stateit
18
- # npm install @vielzeug/stateit
19
- # yarn add @vielzeug/stateit
20
9
  ```
21
10
 
22
- ## Quick Start
11
+ ## Usage
23
12
 
24
- ### Signals
13
+ Stateit exports singleton primitives directly.
25
14
 
26
- ```typescript
27
- import { signal, computed, effect, watch, batch } from '@vielzeug/stateit';
15
+ ```ts
16
+ import { batch, computed, effect, signal } from '@vielzeug/stateit';
28
17
 
29
18
  const count = signal(0);
30
19
  const doubled = computed(() => count.value * 2);
31
20
 
32
- // effect runs immediately and re-runs on every dependency change
33
- const sub = effect(() => {
34
- console.log(`count=${count.value}, doubled=${doubled.value}`);
21
+ const stop = effect(() => {
22
+ console.log(count.value, doubled.value);
35
23
  });
36
- // → count=0, doubled=0
37
24
 
38
- count.value = 3;
39
- // → count=3, doubled=6
40
-
41
- // Update via function — shorthand for count.value = count.value + n
42
- count.update((n) => n + 1);
43
- // → count=4, doubled=8
44
-
45
- // Watch explicitly (does not fire immediately by default)
46
- const stopWatch = watch(count, (next, prev) => console.log(prev, '→', next));
47
- count.value = 5; // → 4 → 5
48
-
49
- // Batch coalesces notifications into one flush
50
25
  batch(() => {
51
- count.value = 10;
52
- count.value = 20;
26
+ count.value = 1;
27
+ count.value = 2;
53
28
  });
54
- // One notification: → 5 → 20
55
29
 
56
- sub.dispose(); // or: sub() — direct call also disposes
30
+ stop();
57
31
  doubled.dispose();
58
- stopWatch.dispose();
59
- ```
60
-
61
- ### Stores
62
-
63
- ```typescript
64
- import { store, watch, batch } from '@vielzeug/stateit';
65
-
66
- const counter = store({ count: 0, user: null as User | null });
67
-
68
- // Read
69
- console.log(counter.value); // { count: 0, user: null }
70
-
71
- // Partial patch
72
- counter.patch({ count: 1 });
73
-
74
- // Updater function
75
- counter.update((s) => ({ ...s, count: s.count + 1 }));
76
-
77
- // Watch a slice — compose store.select() with watch()
78
- const countSignal = counter.select((s) => s.count);
79
- const stopWatch = watch(countSignal, (count, prev) => {
80
- console.log('count:', prev, '→', count);
81
- });
82
-
83
- // Batch updates into one notification
84
- batch(() => {
85
- counter.patch({ count: 10 });
86
- counter.patch({ user: currentUser });
87
- });
88
-
89
- // Reset to initial state
90
- counter.reset();
91
-
92
- stopWatch.dispose();
93
- counter.freeze();
94
- ```
95
-
96
- ## Features
97
-
98
- - ✅ **Signals** — reactive atoms with `.value` getter/setter, `.update(fn)`, and `.peek()` for untracked reads
99
- - ✅ **Computed** — lazy derived signals; call `.dispose()` to stop tracking dependencies
100
- - ✅ **Effects** — run immediately and re-run when dependencies change; support cleanup returns; return a `Subscription`
101
- - ✅ **Watch** — explicit subscriptions with `{ immediate?, once?, equals? }` options; returns a `Subscription`
102
- - ✅ **Batch** — coalesce multiple writes into a single downstream notification
103
- - ✅ **Untrack** — read signals without registering reactive dependencies
104
- - ✅ **derived** — multi-source computed from an array of signals
105
- - ✅ **nextValue** — `Promise` that resolves on the next matching signal emission
106
- - ✅ **Custom equality** — `signal/computed/watch/store` all accept a custom `equals` function
107
- - ✅ **Stores** — object state with `patch(partial)`, `update(fn)`, `reset()`, `select()`, and `freeze()`
108
- - ✅ **Writable computed** — bidirectional computed with `writable(get, set)`
109
- - ✅ **Symbol.dispose** — all dispose handles support `using` declarations (TC39 explicit resource management)
110
- - ✅ **Type guards** — `isSignal(v)` and `isStore(v)`
111
- - ✅ **Zero dependencies** — no supply-chain risk, minimal bundle size
112
- - ✅ **Framework agnostic** — works in React, Vue, Svelte, or plain TypeScript
113
-
114
- ## Usage
115
-
116
- ### Signals
117
-
118
- ```typescript
119
- import { signal, readonly } from '@vielzeug/stateit';
120
-
121
- const count = signal(0);
122
- count.value = 5;
123
- count.peek(); // read without tracking
124
- count.update((n) => n + 1); // derive next value in place
125
-
126
- // Narrow to read-only view (no proxy overhead)
127
- const ro = readonly(count);
128
- ```
129
-
130
- ### Computed
131
-
132
- ```typescript
133
- import { signal, computed } from '@vielzeug/stateit';
134
-
135
- const first = signal('Ada');
136
- const last = signal('Lovelace');
137
- const full = computed(() => `${first.value} ${last.value}`);
138
-
139
- console.log(full.value); // 'Ada Lovelace'
140
- full.stale; // false — just computed
141
- first.value = 'Grace';
142
- full.stale; // true — deps changed, not yet re-read
143
-
144
- // Dispose (stop tracking dependencies)
145
- full.dispose();
146
- // or: using full = computed(...) — TC39 using declaration
147
- ```
148
-
149
- ### Effects
150
-
151
- ```typescript
152
- import { signal, effect, onCleanup } from '@vielzeug/stateit';
153
-
154
- const name = signal('Ada');
155
-
156
- const sub = effect(() => {
157
- console.log('Hello,', name.value);
158
- return () => console.log('cleanup'); // optional teardown
159
- });
160
-
161
- name.value = 'Grace'; // logs 'cleanup' then 'Hello, Grace'
162
- sub.dispose(); // logs 'cleanup', effect is removed
163
32
  ```
164
33
 
165
- ### Watch
34
+ ## Store As Small Recipe
166
35
 
167
- ```typescript
168
- import { signal, store, watch } from '@vielzeug/stateit';
36
+ `store()` is a thin object helper over `signal()` with immutable-style updates.
169
37
 
170
- const count = signal(0);
171
-
172
- // Plain watch
173
- const sub = watch(count, (next, prev) => console.log(prev, '→', next));
174
-
175
- // Slice watch — compose with store.select()
176
- const user = store({ name: 'Ada', age: 30 });
177
- const nameSig = user.select((s) => s.name);
178
- watch(nameSig, (name) => console.log('name:', name));
38
+ ```ts
39
+ import { store } from '@vielzeug/stateit';
179
40
 
180
- // Options
181
- watch(count, (v) => console.log(v), { immediate: true, once: true });
41
+ const user = store({ profile: { name: 'Ada' }, count: 0 });
182
42
 
183
- sub.dispose();
43
+ user.patch({ count: 1 });
44
+ user.update((state) => ({ ...state, count: state.count + 1 }));
45
+ user.reset();
184
46
  ```
185
47
 
186
- ### Stores
187
-
188
- ```typescript
189
- import { store, watch, batch } from '@vielzeug/stateit';
190
-
191
- const cart = store({ items: [] as string[], total: 0 });
48
+ ## Watch Selector Overload
192
49
 
193
- // Partial patch
194
- cart.patch({ total: 42 });
50
+ No intermediate selector signal is required.
195
51
 
196
- // Updater function
197
- cart.update((s) => ({ ...s, items: [...s.items, 'apple'] }));
52
+ ```ts
53
+ import { store, watch } from '@vielzeug/stateit';
198
54
 
199
- // Derived slice
200
- const totalSignal = cart.select((s) => s.total);
55
+ const cart = store({ count: 0, label: 'x' });
201
56
 
202
- // Watch slice
203
- watch(totalSignal, (total) => console.log('total:', total));
204
-
205
- // Batch
206
- batch(() => {
207
- cart.patch({ total: 0 });
208
- cart.update((s) => ({ ...s, items: [] }));
209
- });
210
-
211
- cart.reset();
212
- cart.freeze();
213
- ```
214
-
215
- ### Writable Computed
216
-
217
- ```typescript
218
- import { signal, writable } from '@vielzeug/stateit';
219
-
220
- const celsius = signal(0);
221
- const fahrenheit = writable(
222
- () => (celsius.value * 9) / 5 + 32,
223
- (f) => {
224
- celsius.value = ((f - 32) * 5) / 9;
57
+ const stop = watch(
58
+ cart,
59
+ (state) => state.count,
60
+ (next, prev) => {
61
+ console.log(prev, '->', next);
225
62
  },
226
63
  );
227
64
 
228
- fahrenheit.value = 212;
229
- console.log(celsius.value); // 100
230
-
231
- fahrenheit.update((f) => f + 10); // increment via fn
232
- fahrenheit.dispose();
233
- ```
234
-
235
- ### `nextValue` — Async Watch
236
-
237
- ```typescript
238
- import { signal, nextValue } from '@vielzeug/stateit';
239
-
240
- const status = signal<'idle' | 'loading' | 'done'>('idle');
241
-
242
- // Wait for the next change (any value)
243
- const next = await nextValue(status);
244
-
245
- // Wait for a specific condition
246
- const done = await nextValue(status, (v) => v === 'done');
247
-
248
- // Abortable wait
249
- const controller = new AbortController();
250
- const p = nextValue(status, (v) => v === 'done', { signal: controller.signal });
251
- controller.abort(new Error('cancelled'));
65
+ stop();
252
66
  ```
253
67
 
254
- ### Custom Equality
255
-
256
- ```typescript
257
- import { signal, computed } from '@vielzeug/stateit';
68
+ ## Strict Runtime Rules
258
69
 
259
- // Signal only notifies when the array reference AND contents differ
260
- const tags = signal(['ts'], { equals: (a, b) => JSON.stringify(a) === JSON.stringify(b) });
261
-
262
- // Computed suppresses downstream when result is structurally unchanged
263
- const sorted = computed(() => [...tags.value].sort(), { equals: (a, b) => a.join() === b.join() });
264
- ```
70
+ - `onCleanup()` throws when called outside an active effect.
71
+ - Reading a disposed computed signal throws.
72
+ - Store misuse (non-object values) throws.
265
73
 
266
74
  ## API
267
75
 
268
- ### Signal Functions
269
-
270
- | Export | Signature | Returns |
271
- | ------------------ | -------------------------------------------------------- | ------------------------ |
272
- | `signal` | `signal(initial, options?)` | `Signal<T>` |
273
- | `computed` | `computed(fn, options?)` | `ComputedSignal<T>` |
274
- | `writable` | `writable(get, set, options?)` | `WritableSignal<T>` |
275
- | `derived` | `derived(sources, fn, options?)` | `ComputedSignal<R>` |
276
- | `effect` | `effect(fn, options?)` | `Subscription` |
277
- | `watch` | `watch(source, cb, options?)` | `Subscription` |
278
- | `nextValue` | `nextValue(source, predicate?, { signal? }?)` | `Promise<T>` |
279
- | `batch` | `batch(fn)` | `T` |
280
- | `untrack` | `untrack(fn)` | `T` |
281
- | `onCleanup` | `onCleanup(fn)` | `void` |
282
- | `readonly` | `readonly(sig)` | `ReadonlySignal<T>` |
283
- | `toValue` | `toValue(v)` | `T` |
284
- | `peekValue` | `peekValue(v)` | `T` |
285
- | `isSignal` | `isSignal(v)` | `v is ReadonlySignal<T>` |
286
- | `isStore` | `isStore(v)` | `v is Store<T>` |
287
- | `shallowEqual` | `shallowEqual(a, b)` | `boolean` |
288
- | `configureStateit` | `configureStateit(opts)` | `void` |
289
- | `_resetContextForTesting` | `_resetContextForTesting()` | `void` |
290
-
291
- ### Store Functions
292
-
293
- | Export | Signature | Returns |
294
- | ------- | -------------------------- | ---------- |
295
- | `store` | `store(initial, options?)` | `Store<T>` |
296
-
297
- ### `Store<T>` Methods
298
-
299
- | Member | Description |
300
- | ----------------------------- | ----------------------------------------------------------------------- |
301
- | `.value` | Read the current state (tracked) |
302
- | `.peek()` | Read the current state (untracked) |
303
- | `.update(fn)` | Derive next state via `fn(copy) => next` |
304
- | `.patch(partial)` | Shallow-merge a `Partial<T>` into state |
305
- | `.reset()` | Restore the original initial state |
306
- | `.select(selector, options?)` | Lazily derived `ComputedSignal<U>` from a slice; compose with `watch()` |
307
- | `.frozen` | `true` after `freeze()` has been called |
308
- | `.freeze()` | Freeze the store; further writes are silently ignored |
309
-
310
- ### Types
311
-
312
- | Type | Description |
313
- | -------------------- | ------------------------------------------------------------------------------------ |
314
- | `Signal<T>` | Readable/writable reactive atom with `.update(fn)` |
315
- | `ReadonlySignal<T>` | Read-only signal (no setter) |
316
- | `ComputedSignal<T>` | `ReadonlySignal<T> & Disposable` — has `.stale`, `.dispose()` |
317
- | `WritableSignal<T>` | `Signal<T> & Disposable` — has `.stale`, `.update(fn)`, `.dispose()` |
318
- | `Store<T>` | Object store extending `Signal<T>` |
319
- | `Subscription` | Callable + `.dispose()` + `[Symbol.dispose]` — returned by `effect`/`watch` |
320
- | `Disposable` | `.dispose()` + `[Symbol.dispose]` — implemented by `ComputedSignal`/`WritableSignal` |
321
- | `CleanupFn` | `() => void` |
322
- | `EffectCallback` | `() => CleanupFn \| void` |
323
- | `EffectOptions` | `{ maxIterations?, onError? }` |
324
- | `EqualityFn<T>` | `(a: T, b: T) => boolean` |
325
- | `ReactiveOptions<T>` | `{ equals?: EqualityFn<T> }` |
326
- | `WatchOptions<T>` | `{ immediate?, once?, equals? }` |
327
- | `StoreOptions<T>` | `{ equals?: EqualityFn<T> }` |
328
-
329
- ## Documentation
330
-
331
- Full docs at **[vielzeug.dev/stateit](https://vielzeug.dev/stateit)**
332
-
333
- | | |
334
- | ------------------------------------------------- | -------------------------------------- |
335
- | [Usage Guide](https://vielzeug.dev/stateit/usage) | Concepts, patterns, and best practices |
336
- | [API Reference](https://vielzeug.dev/stateit/api) | Complete type signatures |
337
- | [Examples](https://vielzeug.dev/stateit/examples) | Framework integrations and recipes |
76
+ ```ts
77
+ signal<T>(initial: T, options?: { equals?: (a: T, b: T) => boolean }): Signal<T>;
78
+ computed<T>(compute: () => T, options?: { equals?: (a: T, b: T) => boolean }): ComputedSignal<T>;
79
+ effect(fn: () => void | (() => void), options?: { maxIterations?: number }): Subscription;
80
+ watch<T>(source: ReadonlySignal<T>, cb: (value: T, prev: T) => void, options?: WatchOptions<T>): Subscription;
81
+ watch<S, T>(
82
+ source: ReadonlySignal<S>,
83
+ selector: (value: S) => T,
84
+ cb: (value: T, prev: T) => void,
85
+ options?: WatchOptions<T>,
86
+ ): Subscription;
87
+ batch<T>(fn: () => T): T;
88
+ untrack<T>(fn: () => T): T;
89
+ onCleanup(fn: () => void): void;
90
+ store<T extends object>(initial: T, options?: { equals?: (a: T, b: T) => boolean }): Store<T>;
91
+ readonly<T>(input: ReadonlySignal<T>): ReadonlySignal<T>;
92
+ writable<T>(input: Signal<T>): Signal<T>;
93
+ isSignal<T>(value: unknown): value is ReadonlySignal<T>;
94
+ peekValue<T>(input: T | ReadonlySignal<T>): T;
95
+ toValue<T>(input: T | ReadonlySignal<T> | (() => T)): T;
96
+ ```
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./runtime.cjs`),t=require(`./batch.cjs`),n=require(`./types.cjs`),r=require(`./computed.cjs`),i=require(`./effect.cjs`),a=require(`./signal.cjs`),o=require(`./store.cjs`),s=require(`./watch.cjs`);exports.ComputedNode=r.ComputedNode,exports.SignalImpl=a.SignalImpl,exports.WritableNode=r.WritableNode,exports._SIGNAL_BRAND=n._SIGNAL_BRAND,exports._STORE_BRAND=n._STORE_BRAND,exports._flushPending=t._flushPending,exports._resetContextForTesting=e._resetContextForTesting,exports.batch=t.batch,exports.computed=r.computed,exports.configureStateit=e.configureStateit,exports.derived=r.derived,exports.effect=i.effect,exports.isSignal=a.isSignal,exports.isStore=o.isStore,exports.nextValue=s.nextValue,exports.onCleanup=i.onCleanup,exports.peekValue=a.peekValue,exports.readonly=a.readonly,exports.shallowEqual=o.shallowEqual,exports.signal=a.signal,exports.store=o.store,exports.toValue=a.toValue,exports.untrack=i.untrack,exports.watch=s.watch,exports.writable=r.writable;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./stateit.cjs`);exports.batch=e.batch,exports.computed=e.computed,exports.effect=e.effect,exports.isSignal=e.isSignal,exports.onCleanup=e.onCleanup,exports.peekValue=e.peekValue,exports.readonly=e.readonly,exports.signal=e.signal,exports.store=e.store,exports.toValue=e.toValue,exports.untrack=e.untrack,exports.watch=e.watch,exports.writable=e.writable;
package/dist/index.d.ts CHANGED
@@ -1,20 +1,3 @@
1
- /** stateit -- Lightweight reactive state
2
- *
3
- * Reactive primitives:
4
- * Signal<T> -- synchronous, fine-grained reactive atom
5
- * ReadonlySignal<T> -- read-only view of a signal
6
- * Store<T> -- object-state store with set/freeze/reset/select
7
- *
8
- * All primitives interoperate: computed, derived, effect, watch, batch,
9
- * untrack, onCleanup, toValue, configureStateit work uniformly
10
- * on Signal<T> and Store<T>.
11
- */
12
- export * from './batch';
13
- export * from './computed';
14
- export * from './effect';
15
- export { _resetContextForTesting, configureStateit } from './runtime';
16
- export * from './signal';
17
- export * from './store';
1
+ export { batch, computed, effect, isSignal, onCleanup, peekValue, readonly, signal, store, toValue, untrack, watch, writable, } from './stateit';
18
2
  export * from './types';
19
- export * from './watch';
20
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACtE,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,EACR,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,KAAK,EACL,QAAQ,GACT,MAAM,WAAW,CAAC;AAEnB,cAAc,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -1,9 +1,2 @@
1
- import { _resetContextForTesting as e, configureStateit as t } from "./runtime.js";
2
- import { _flushPending as n, batch as r } from "./batch.js";
3
- import { _SIGNAL_BRAND as i, _STORE_BRAND as a } from "./types.js";
4
- import { ComputedNode as o, WritableNode as s, computed as c, derived as l, writable as u } from "./computed.js";
5
- import { effect as d, onCleanup as f, untrack as p } from "./effect.js";
6
- import { SignalImpl as m, isSignal as h, peekValue as g, readonly as _, signal as v, toValue as y } from "./signal.js";
7
- import { isStore as b, shallowEqual as x, store as S } from "./store.js";
8
- import { nextValue as C, watch as w } from "./watch.js";
9
- export { o as ComputedNode, m as SignalImpl, s as WritableNode, i as _SIGNAL_BRAND, a as _STORE_BRAND, n as _flushPending, e as _resetContextForTesting, r as batch, c as computed, t as configureStateit, l as derived, d as effect, h as isSignal, b as isStore, C as nextValue, f as onCleanup, g as peekValue, _ as readonly, x as shallowEqual, v as signal, S as store, y as toValue, p as untrack, w as watch, u as writable };
1
+ import { batch as e, computed as t, effect as n, isSignal as r, onCleanup as i, peekValue as a, readonly as o, signal as s, store as c, toValue as l, untrack as u, watch as d, writable as f } from "./stateit.js";
2
+ export { e as batch, t as computed, n as effect, r as isSignal, i as onCleanup, a as peekValue, o as readonly, s as signal, c as store, l as toValue, u as untrack, d as watch, f as writable };
package/dist/stateit.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Symbol(`stateit.uninitialized`),t={cleanups:null,deps:null,effect:null},n={depth:0,pending:new Set},r=100,i=e=>{e.maxEffectIterations!==void 0&&(r=e.maxEffectIterations)},a=()=>{n.depth=0,t.deps=null,t.effect=null,t.cleanups=null,n.pending.clear()},o=class{#e=new Set;_track(){if(t.deps!==null&&t.effect!==null){let e=t.effect;this.#e.add(e),t.deps.add(()=>this.#e.delete(e))}}_notify(){if(this.#e.size===0)return;if(n.depth>0){for(let e of this.#e)n.pending.add(e);return}let e=[];for(let t of[...this.#e])try{t()}catch(t){e.push(t)}if(e.length)throw e.length===1?e[0]:AggregateError(e,`[stateit] multiple subscriber errors`)}},s=(e,n,r,i)=>{let a=t.effect,o=t.deps,s=t.cleanups;t.effect=e,t.deps=n,t.cleanups=r;try{return i()}finally{t.effect=a,t.deps=o,t.cleanups=s}},c=()=>{let e=[...n.pending];n.pending.clear();let t=[];for(let n of e)try{n()}catch(e){t.push(e)}if(t.length)throw t.length===1?t[0]:AggregateError(t,`[stateit] batch errors`)},l=e=>{n.depth++;try{let t=e();return--n.depth===0&&c(),t}catch(e){if(--n.depth===0)try{c()}catch{}throw e}},u=Symbol(`stateit.signal`),d=Symbol(`stateit.store`),f=class extends o{[u]=!0;#e;#t=e;#n=!0;#r=!1;#i;#a=new Set;#o=()=>{this.#n||(this.#n=!0,this._notify())};constructor(e,t){super(),this.#e=e,this.#i=t?.equals??Object.is,t?.lazy||this.#s()}#s(){for(let e of this.#a)e();this.#a.clear();let t=s(this.#o,this.#a,null,this.#e);this.#n=!1,(this.#t===e||!this.#i(this.#t,t))&&(this.#t=t)}get value(){return this.#r?this.#t:(this.#n&&this.#s(),this._track(),this.#t)}peek(){if(this.#t===e){if(this.#r)return;this.#s()}return this.#t}get stale(){return this.#n||this.#r}dispose(){if(!this.#r){this.#r=!0;for(let e of this.#a)e();this.#a.clear()}}[Symbol.dispose](){this.dispose()}},p=(e,t)=>new f(e,t),m=class extends f{#e;constructor(e,t,n){super(e,n),this.#e=t}get value(){return super.value}set value(e){this.#e(e)}update(e){this.#e(e(this.peek()))}},h=(e,t,n)=>new m(e,t,n),g=(e,t,n)=>p(()=>t(...e.map(e=>e.value)),n),_=(e,t)=>{let n,i=new Set,a=!1,o=!1,c=!1,l=t?.maxIterations??r,u=()=>{n?.(),n=void 0;for(let e of i)e();i.clear()},d=()=>{o=!1,u();let r=[],a,l=!1;try{s(f,i,r,()=>{let t=e();typeof t==`function`&&r.push(t)})}catch(e){if(t?.onError)l=!0,a=e;else throw e}return n=r.length>0?()=>{for(let e of r)e()}:void 0,l&&(u(),t.onError(a),c=!0),l},f=()=>{if(c||a){a&&(o=!0);return}a=!0;try{let e=0;do{if(++e>l)throw Error(`[stateit] effect: possible infinite reactive loop (> ${l} iterations)`);if(d())break}while(o)}finally{a=!1}};f();let p=()=>{c||(c=!0,u())};return Object.assign(p,{dispose:p,[Symbol.dispose]:p})},v=e=>s(null,null,null,e),y=e=>{t.cleanups?.push(e)},b=class extends o{[u]=!0;#e;#t;constructor(e,t){super(),this.#e=e,this.#t=t?.equals??Object.is}get value(){return this._track(),this.#e}set value(e){this.#t(this.#e,e)||(this.#e=e,this._notify())}update(e){this.value=e(this.#e)}peek(){return this.#e}},x=(e,t)=>new b(e,t),S=new WeakMap,C=e=>{let t=S.get(e);if(t)return t;let n={get[u](){return!0},peek:()=>e.peek(),get value(){return e.value}};return S.set(e,n),n},w=e=>typeof e==`object`&&!!e&&u in e,T=e=>w(e)?e.value:e,E=e=>w(e)?e.peek():e,D=(e,t)=>{if(e===t)return!0;if(e==null||t==null)return e===t;if(typeof e!=`object`||typeof t!=`object`)return!1;let n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(let r of n)if(!Object.is(e[r],t[r]))return!1;return!0},O=class extends b{[d]=!0;#e;#t=!1;constructor(e,t){super(e,{equals:t?.equals??D}),this.#e={...e}}get frozen(){return this.#t}get value(){return super.value}set value(e){this.#t||(super.value=e)}patch(e){this.value={...this.peek(),...e}}update(e){this.value=e({...this.peek()})}reset(){this.value={...this.#e}}select(e,t){return p(()=>e(this.value),t)}freeze(){this.#t=!0}},k=(e,t)=>new O(e,t),A=e=>typeof e==`object`&&!!e&&d in e,j=(e,t,n)=>{let r=n?.equals??Object.is,i=e.peek();n?.immediate&&t(i,i);let a;return a=_(()=>{let o=e.value;if(!r(i,o)){let e=i;i=o,t(o,e),n?.once&&a()}}),a},M=(e,t,n)=>new Promise((r,i)=>{let a=n?.signal;try{a?.throwIfAborted()}catch(e){i(e);return}let o=j(e,e=>{(!t||t(e))&&(o(),a?.removeEventListener(`abort`,s),r(e))}),s=()=>{o(),i(a.reason)};a?.addEventListener(`abort`,s,{once:!0})});exports.ComputedNode=f,exports.SignalImpl=b,exports.WritableNode=m,exports._SIGNAL_BRAND=u,exports._STORE_BRAND=d,exports._flushPending=c,exports._resetContextForTesting=a,exports.batch=l,exports.computed=p,exports.configureStateit=i,exports.derived=g,exports.effect=_,exports.isSignal=w,exports.isStore=A,exports.nextValue=M,exports.onCleanup=y,exports.peekValue=E,exports.readonly=C,exports.shallowEqual=D,exports.signal=x,exports.store=k,exports.toValue=T,exports.untrack=v,exports.watch=j,exports.writable=h;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Symbol(`stateit.uninitialized`),t=100,n=e=>Object.assign(e,{dispose:e,[Symbol.dispose]:e}),r=(e,t)=>{if(typeof e!=`object`||!e||Array.isArray(e))throw TypeError(t)},i=e=>structuredClone(e),a=(e,t)=>{let n=[];for(let t of e)try{t()}catch(e){n.push(e)}if(n.length===1)throw n[0];if(n.length>1)throw AggregateError(n,`[stateit] ${t}`)},o=e=>{let t=[];for(let n of[...e])try{n()}catch(e){t.push(e)}if(t.length===1)throw t[0];if(t.length>1)throw AggregateError(t,`[stateit] subscriber errors`)},s={cleanups:null,deps:null,effect:null},c=(e,t,n,r)=>{let i=s;s={cleanups:n,deps:t,effect:e};try{return r()}finally{s=i}},l=0,u=new Set,d=()=>{for(;u.size>0;){let e=[...u];u.clear(),o(new Set(e))}},f=class{subscribers=new Set;track(){!s.effect||!s.deps||(this.subscribers.add(s.effect),s.deps.add(()=>this.subscribers.delete(s.effect)))}hasSubscribers(){return this.subscribers.size>0}notify(){if(this.subscribers.size!==0){if(l>0){for(let e of this.subscribers)u.add(e);return}o(this.subscribers)}}},p=class extends f{value_;equals_;constructor(e,t){super(),this.value_=e,this.equals_=t??Object.is}get value(){return this.track(),this.value_}set value(e){this.equals_(this.value_,e)||(this.value_=e,this.notify())}update(e){this.value=e(this.value_)}},m=class extends f{value_=e;dirty_=!0;disposed_=!1;deps_=new Set;compute_;equals_;onDepChange=()=>{if(!this.disposed_){if(this.subscribers.size===0){this.dirty_=!0;return}this.recompute()&&this.notify()}};constructor(e,t){super(),this.compute_=e,this.equals_=t??Object.is,this.recompute()}recompute(){for(let e of this.deps_)e();this.deps_.clear();let t=c(this.onDepChange,this.deps_,null,this.compute_);return this.dirty_=!1,this.value_===e||!this.equals_(this.value_,t)?(this.value_=t,!0):!1}get value(){if(this.disposed_)throw Error(`[stateit] Cannot read disposed computed signal`);return this.dirty_&&this.recompute(),this.track(),this.value_}get stale(){return this.dirty_}dispose(){if(!this.disposed_){this.disposed_=!0,this.dirty_=!0;for(let e of this.deps_)e();this.deps_.clear()}}[Symbol.dispose](){this.dispose()}},h=(e,t)=>new p(e,t?.equals),g=(e,t)=>{let n=new m(e,t?.equals);return s.effect!==null&&s.cleanups!==null&&b(()=>n.dispose()),n},_=e=>{l++;try{let t=e();return--l===0&&d(),t}catch(e){throw--l===0&&d(),e}},v=(e,r)=>{let i,o=new Set,s=!1,l=!1,u=!1,d=r?.maxIterations??t,f=()=>{let e=[];i&&e.push(i),i=void 0;for(let t of o)e.push(t);o.clear(),a(e,`effect teardown errors`)},p=()=>{if(!u){if(s){l=!0;return}s=!0;try{let t=0;do{if(++t>d)throw Error(`[stateit] infinite effect loop (> ${d} iterations)`);l=!1,f();let n=[],r=c(p,o,n,e);r&&n.push(r),n.length>0&&(i=()=>{a(n,`effect cleanup errors`)})}while(l&&!u)}finally{s=!1}}};return p(),n(()=>{u||(u=!0,f())})},y=e=>c(null,null,null,e),b=e=>{if(s.cleanups===null)throw Error(`[stateit] onCleanup() must be called from inside an active effect.`);s.cleanups.push(e)},x=(e,t,n)=>{let r=n?.equals??Object.is,i=y(()=>e.value);n?.immediate&&t(i,i);let a=v(()=>{let o=e.value;if(r(i,o))return;let s=i;i=o,t(o,s),n?.once&&a()});return a};function S(e,t,r,i){if(typeof r==`function`){let a=t,o=r,s=g(()=>a(e.value)),c=x(s,o,i);return n(()=>{c(),s.dispose()})}return x(e,t,r)}var C=(e,t)=>{r(e,`[stateit] store() requires a plain object initial state.`);let n=i(e),a=h(i(e),t);return{patch(e){if(typeof e!=`object`||!e||Array.isArray(e))throw TypeError(`[stateit] store.patch() requires a plain object partial.`);a.value={...a.value,...e}},reset(){a.value=i(n)},update(e){let t=e(a.value);r(t,`[stateit] store.update() must return a plain object.`),a.value=t},get value(){return a.value},set value(e){r(e,`[stateit] Store value must always be a plain object.`),a.value=e}}},w=e=>e,T=e=>e,E=e=>typeof e!=`object`||!e?!1:`value`in e,D=e=>E(e)?e.value:e,O=e=>E(e)?e.value:typeof e==`function`?e():e;exports.batch=_,exports.computed=g,exports.effect=v,exports.isSignal=E,exports.onCleanup=b,exports.peekValue=D,exports.readonly=w,exports.signal=h,exports.store=C,exports.toValue=O,exports.untrack=y,exports.watch=S,exports.writable=T;
2
2
  //# sourceMappingURL=stateit.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"stateit.cjs","names":["#subscribers","#onDepChange","#dirty","#compute","#equals","#recompute","#deps","#value","#disposed","#set","#value","#equals","#initial","#frozen"],"sources":["../src/runtime.ts","../src/batch.ts","../src/types.ts","../src/computed.ts","../src/effect.ts","../src/signal.ts","../src/store.ts","../src/watch.ts"],"sourcesContent":["import type { CleanupFn, EffectCallback } from './types';\n\n/** @internal True outside of production builds; gates dev-only warnings. */\nexport const _DEV = import.meta.env.DEV;\n\nexport const _UNINITIALIZED = Symbol('stateit.uninitialized');\n\n/** @internal Active reactive tracking scope — null outside any effect/computed. */\nexport const scope = {\n cleanups: null as CleanupFn[] | null,\n deps: null as Set<CleanupFn> | null,\n effect: null as EffectCallback | null,\n};\n\n/** @internal Deferred notification queue — active only during batch(). */\nexport const queue = {\n depth: 0,\n pending: new Set<EffectCallback>(),\n};\n\nexport let _maxEffectIterations = 100;\n\n/**\n * Configures global stateit behaviour.\n * @example configureStateit({ maxEffectIterations: 200 });\n */\nexport const configureStateit = (opts: { maxEffectIterations?: number }): void => {\n if (opts.maxEffectIterations !== undefined) _maxEffectIterations = opts.maxEffectIterations;\n};\n\n/**\n * Resets the shared reactive context. Use in test setup/teardown to prevent state\n * leaks between tests when a batch or effect throws without being fully cleaned up.\n */\nexport const _resetContextForTesting = (): void => {\n queue.depth = 0;\n scope.deps = null;\n scope.effect = null;\n scope.cleanups = null;\n queue.pending.clear();\n};\n\n/** @internal Base reactive node: manages a subscriber set and batch-aware notification. */\nexport class ReactiveNode {\n #subscribers = new Set<EffectCallback>();\n\n /** Register the current tracking scope as a subscriber and store an unsubscribe\n * function in scope.deps for automatic cleanup when the effect re-runs. */\n protected _track(): void {\n if (scope.deps !== null && scope.effect !== null) {\n const fn = scope.effect;\n\n this.#subscribers.add(fn);\n scope.deps.add(() => this.#subscribers.delete(fn));\n }\n }\n\n /** Notify all subscribers. Respects batch queue depth. */\n protected _notify(): void {\n if (this.#subscribers.size === 0) return;\n\n if (queue.depth > 0) {\n for (const fn of this.#subscribers) queue.pending.add(fn);\n\n return;\n }\n\n const errors: unknown[] = [];\n\n for (const fn of [...this.#subscribers]) {\n try {\n fn();\n } catch (e) {\n errors.push(e);\n }\n }\n\n if (errors.length) {\n throw errors.length === 1 ? errors[0] : new AggregateError(errors, '[stateit] multiple subscriber errors');\n }\n }\n}\n\n/** @internal Save scope fields, swap to new tracking context, call fn, restore. */\nexport const _withCtx = <T>(\n eff: EffectCallback | null,\n deps: Set<CleanupFn> | null,\n cleanups: CleanupFn[] | null,\n fn: () => T,\n): T => {\n const pe = scope.effect;\n const pd = scope.deps;\n const pc = scope.cleanups;\n\n scope.effect = eff;\n scope.deps = deps;\n scope.cleanups = cleanups;\n\n try {\n return fn();\n } finally {\n scope.effect = pe;\n scope.deps = pd;\n scope.cleanups = pc;\n }\n};\n","import { _DEV, queue } from './runtime';\n\n/** @internal Drain and run all pending effects from the batch queue, collecting errors. */\nexport const _flushPending = (): void => {\n const toFlush = [...queue.pending];\n\n queue.pending.clear();\n\n const errors: unknown[] = [];\n\n for (const f of toFlush) {\n try {\n f();\n } catch (e) {\n errors.push(e);\n }\n }\n\n if (errors.length) throw errors.length === 1 ? errors[0] : new AggregateError(errors, '[stateit] batch errors');\n};\n\n/** Runs fn and defers all Signal notifications until fn returns, then flushes once. */\nexport const batch = <T>(fn: () => T): T => {\n queue.depth++;\n\n try {\n const result = fn();\n\n if (--queue.depth === 0) _flushPending();\n\n return result;\n } catch (e) {\n if (--queue.depth === 0) {\n try {\n _flushPending();\n } catch (flushErr) {\n if (_DEV)\n console.error(\n '[stateit] batch: a secondary flush error was suppressed (callback error takes precedence)',\n flushErr,\n );\n }\n }\n\n throw e;\n }\n};\n","/** A function that tears down a subscription or effect. */\nexport type CleanupFn = () => void;\n\n/** The shape of a function passed to `effect()`. Can return an optional cleanup. */\nexport type EffectCallback = () => CleanupFn | void;\n\nexport type EqualityFn<T> = (a: T, b: T) => boolean;\n\n/** Options accepted by signal(), computed(), writable(), and store(). */\nexport type ReactiveOptions<T> = { equals?: EqualityFn<T> };\n\n/** Shared interface for disposable reactive values (computed, writable). Supports the TC39 `using` declaration. */\nexport interface Disposable {\n dispose(): void;\n [Symbol.dispose](): void;\n}\n\n/**\n * A callable subscription handle returned by `effect()` and `watch()`.\n * Can be called directly or via `.dispose()` to stop the subscription.\n * Supports the TC39 `using` declaration via `[Symbol.dispose]`.\n */\nexport interface Subscription {\n (): void;\n dispose(): void;\n [Symbol.dispose](): void;\n}\n\nexport const _SIGNAL_BRAND = Symbol('stateit.signal');\nexport const _STORE_BRAND = Symbol('stateit.store');\n\n/** Public read-only signal interface. */\nexport interface ReadonlySignal<T> {\n readonly [_SIGNAL_BRAND]: true;\n readonly value: T;\n peek(): T;\n}\n\n/** Public read/write signal interface. */\nexport interface Signal<T> extends ReadonlySignal<T> {\n value: T;\n /** Update the value by applying a function to the current value. */\n update(fn: (current: T) => T): void;\n}\n","import { ReactiveNode, _DEV, _UNINITIALIZED, _withCtx } from './runtime';\nimport {\n type CleanupFn,\n type Disposable,\n type EffectCallback,\n type EqualityFn,\n type ReactiveOptions,\n type ReadonlySignal,\n type Signal,\n _SIGNAL_BRAND,\n} from './types';\n\n/** A derived read-only signal with an explicit dispose method. */\nexport interface ComputedSignal<T> extends ReadonlySignal<T>, Disposable {\n /** True when the computed value is stale (deps changed but not yet re-read) or disposed. */\n readonly stale: boolean;\n}\n\n/** @internal\n * Lazy computed node. Seeded once at construction so .peek() is immediately valid\n * (unless { lazy: true } is passed, in which case first compute defers to first read).\n * - On dep change: marks dirty, notifies downstream (no recompute yet).\n * - On .value read: recomputes if dirty, re-tracks fresh deps.\n * - After dispose: _track() is skipped to prevent leaking outer effects.\n */\nexport class ComputedNode<T> extends ReactiveNode implements ComputedSignal<T> {\n readonly [_SIGNAL_BRAND] = true as const;\n #compute: () => T;\n #value: T | typeof _UNINITIALIZED = _UNINITIALIZED;\n #dirty = true;\n #disposed = false;\n #equals: EqualityFn<T>;\n #deps = new Set<CleanupFn>();\n\n readonly #onDepChange: EffectCallback = () => {\n if (!this.#dirty) {\n this.#dirty = true;\n this._notify();\n }\n };\n\n constructor(compute: () => T, options?: ReactiveOptions<T> & { lazy?: boolean }) {\n super();\n this.#compute = compute;\n this.#equals = options?.equals ?? Object.is;\n\n if (!options?.lazy) this.#recompute(); // seed: ensures peek() is valid immediately\n }\n\n #recompute(): void {\n for (const unsub of this.#deps) unsub();\n this.#deps.clear();\n\n const result = _withCtx(this.#onDepChange, this.#deps, null, this.#compute);\n\n this.#dirty = false;\n\n if (this.#value === _UNINITIALIZED || !this.#equals(this.#value as T, result)) {\n this.#value = result;\n }\n }\n\n get value(): T {\n if (this.#disposed) {\n if (_DEV) console.warn('[stateit] Reading a disposed ComputedSignal returns a stale value.');\n\n return this.#value as T;\n }\n\n if (this.#dirty) this.#recompute();\n\n this._track();\n\n return this.#value as T;\n }\n\n peek(): T {\n if (this.#value === _UNINITIALIZED) {\n if (this.#disposed) {\n if (_DEV)\n console.warn(\n '[stateit] peek() called on a disposed lazy ComputedSignal that was never read — returning undefined.',\n );\n\n return undefined as unknown as T;\n }\n\n this.#recompute();\n }\n\n return this.#value as T;\n }\n\n get stale(): boolean {\n return this.#dirty || this.#disposed;\n }\n\n dispose(): void {\n if (this.#disposed) return;\n\n this.#disposed = true;\n for (const unsub of this.#deps) unsub();\n this.#deps.clear();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n\n/** Creates a derived read-only Signal whose value is recomputed lazily on `.value` read\n * when dependencies have changed. Call `.dispose()` to stop tracking and free dependencies.\n * Pass `{ lazy: true }` to defer the initial computation until the first `.value` read. */\nexport const computed = <T>(compute: () => T, options?: ReactiveOptions<T> & { lazy?: boolean }): ComputedSignal<T> =>\n new ComputedNode(compute, options);\n\n/** A bidirectional computed Signal with an explicit dispose method. */\nexport interface WritableSignal<T> extends Signal<T>, Disposable {\n /** True when the backing computed value is stale (deps changed but not yet re-read) or disposed. */\n readonly stale: boolean;\n}\n\n/** @internal */\nexport class WritableNode<T> extends ComputedNode<T> implements WritableSignal<T> {\n readonly #set: (v: T) => void;\n\n constructor(get: () => T, set: (v: T) => void, options?: ReactiveOptions<T>) {\n super(get, options);\n this.#set = set;\n }\n\n override get value(): T {\n return super.value;\n }\n\n set value(v: T) {\n this.#set(v);\n }\n\n update(fn: (current: T) => T): void {\n this.#set(fn(this.peek()));\n }\n}\n\n/** Creates a bidirectional computed Signal. Reads track the getter reactively;\n * writes are forwarded to `set`. Call `.dispose()` to stop tracking and free dependencies. */\nexport const writable = <T>(get: () => T, set: (value: T) => void, options?: ReactiveOptions<T>): WritableSignal<T> =>\n new WritableNode(get, set, options);\n\n/**\n * Creates a derived ComputedSignal by combining multiple source signals through a projector\n * function. Each source is passed as a positional argument to `fn`; the projector is\n * re-evaluated whenever any source changes.\n *\n * @example\n * const total = derived([price, quantity, discount], (p, q, d) => p * q * (1 - d));\n */\nexport const derived = <const Srcs extends ReadonlyArray<ReadonlySignal<unknown>>, R>(\n sources: Srcs,\n fn: (...values: { [K in keyof Srcs]: Srcs[K] extends ReadonlySignal<infer V> ? V : never }) => R,\n options?: ReactiveOptions<R>,\n): ComputedSignal<R> => computed(() => (fn as (...args: any[]) => R)(...sources.map((s) => s.value)), options);\n","import { _DEV, _maxEffectIterations, _withCtx, scope } from './runtime';\nimport { type CleanupFn, type EffectCallback, type Subscription } from './types';\n\n/** Options for the `effect()` primitive. */\nexport type EffectOptions = {\n /**\n * Maximum re-entrant iterations before throwing to prevent infinite loops.\n * Overrides the global value set via `configureStateit` for this specific effect.\n */\n maxIterations?: number;\n /**\n * Called when the effect function throws. When provided, the effect is automatically\n * disposed after the first error — it won't re-run on subsequent dependency changes.\n */\n onError?: (error: unknown) => void;\n};\n\n/** Runs fn immediately and re-runs it whenever any Signal read inside it changes. Returns a dispose handle. */\nexport const effect = (fn: EffectCallback, options?: EffectOptions): Subscription => {\n let cleanup: CleanupFn | undefined;\n const deps = new Set<CleanupFn>();\n let running = false;\n let dirty = false;\n let disposed = false;\n const maxIter = options?.maxIterations ?? _maxEffectIterations;\n\n /** Tears down the current run: calls cleanup and unsubscribes all deps. */\n const teardown = (): void => {\n cleanup?.();\n cleanup = undefined;\n for (const unsub of deps) unsub();\n deps.clear();\n };\n\n /** Runs a single iteration: tears down previous deps/cleanup, re-executes fn, registers fresh deps.\n * Returns true if onError handled a throw (caller should break the loop). */\n const runIteration = (): boolean => {\n dirty = false;\n teardown();\n\n const cleanups: CleanupFn[] = [];\n let thrownError: unknown;\n let threw = false;\n\n try {\n _withCtx(runner, deps, cleanups, () => {\n const result = fn();\n\n if (typeof result === 'function') cleanups.push(result);\n });\n } catch (e) {\n if (options?.onError) {\n threw = true;\n thrownError = e;\n } else throw e;\n }\n\n cleanup =\n cleanups.length > 0\n ? () => {\n for (const c of cleanups) c();\n }\n : undefined;\n\n if (threw) {\n teardown(); // flush onCleanup registrations from the throwing run\n options!.onError!(thrownError);\n disposed = true;\n }\n\n return threw;\n };\n\n const runner: EffectCallback = () => {\n if (disposed || running) {\n if (running) dirty = true;\n\n return;\n }\n\n running = true;\n\n try {\n let iterations = 0;\n\n do {\n if (++iterations > maxIter)\n throw new Error(`[stateit] effect: possible infinite reactive loop (> ${maxIter} iterations)`);\n\n if (runIteration()) break;\n } while (dirty);\n } finally {\n running = false;\n }\n };\n\n runner();\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n teardown();\n };\n\n return Object.assign(dispose, { dispose, [Symbol.dispose]: dispose }) as Subscription;\n};\n\n/** Runs fn without registering any reactive dependencies. */\nexport const untrack = <T>(fn: () => T): T => _withCtx(null, null, null, fn);\n\n/**\n * Registers a cleanup function within the currently running effect.\n * Called before the effect re-runs and on final dispose.\n * Allows nested helpers to register teardown without needing the effect's return value.\n *\n * @example\n * effect(() => {\n * const id = setInterval(() => { ... }, 1000);\n * onCleanup(() => clearInterval(id));\n * });\n */\nexport const onCleanup = (fn: CleanupFn): void => {\n if (_DEV && scope.cleanups === null) {\n console.warn('[stateit] onCleanup() called outside of an active effect — the cleanup will never run.');\n }\n\n scope.cleanups?.push(fn);\n};\n","import { ReactiveNode } from './runtime';\nimport { type EqualityFn, type ReactiveOptions, type ReadonlySignal, type Signal, _SIGNAL_BRAND } from './types';\n\n/** @internal */\nexport class SignalImpl<T> extends ReactiveNode implements Signal<T> {\n readonly [_SIGNAL_BRAND] = true as const;\n #value: T;\n #equals: EqualityFn<T>;\n\n constructor(initial: T, options?: ReactiveOptions<T>) {\n super();\n this.#value = initial;\n this.#equals = options?.equals ?? Object.is;\n }\n\n get value(): T {\n this._track();\n\n return this.#value;\n }\n\n set value(next: T) {\n if (this.#equals(this.#value, next)) return;\n\n this.#value = next;\n this._notify();\n }\n\n update(fn: (current: T) => T): void {\n this.value = fn(this.#value);\n }\n\n peek(): T {\n return this.#value;\n }\n}\n\n/** Creates a reactive signal holding a single value. */\nexport const signal = <T>(initial: T, options?: ReactiveOptions<T>): Signal<T> => new SignalImpl(initial, options);\n\nconst _readonlyCache = new WeakMap<object, ReadonlySignal<unknown>>();\n\n/**\n * Returns a stable read-only view of a signal. Hides the setter at both type and runtime\n * level. The wrapper is cached — repeated calls with the same signal return the same reference.\n */\nexport const readonly = <T>(sig: ReadonlySignal<T>): ReadonlySignal<T> => {\n const cached = _readonlyCache.get(sig as object);\n\n if (cached) return cached as ReadonlySignal<T>;\n\n const wrapper: ReadonlySignal<T> = {\n get [_SIGNAL_BRAND]() {\n return true as const;\n },\n peek: () => sig.peek(),\n get value() {\n return sig.value;\n },\n };\n\n _readonlyCache.set(sig as object, wrapper as ReadonlySignal<unknown>);\n\n return wrapper;\n};\n\n/** Type guard -- identifies Signal instances (works through composition and subclasses). */\nexport const isSignal = <T = unknown>(value: unknown): value is ReadonlySignal<T> =>\n typeof value === 'object' && value !== null && _SIGNAL_BRAND in (value as object);\n\n/** Unwraps a plain value or Signal to its current value. Reads are tracked if called inside an effect. */\nexport const toValue = <T>(v: T | ReadonlySignal<T>): T => (isSignal<T>(v) ? v.value : v);\n\n/** Unwraps a plain value or Signal to its current value without registering a reactive subscription. */\nexport const peekValue = <T>(v: T | ReadonlySignal<T>): T => (isSignal<T>(v) ? v.peek() : v);\n","import { type ComputedSignal, computed } from './computed';\nimport { _DEV } from './runtime';\nimport { SignalImpl } from './signal';\nimport { type EqualityFn, type ReactiveOptions, type Signal, _STORE_BRAND } from './types';\n\n/**\n * Shallow structural equality — compares own enumerable keys by reference.\n * This is the default equality function used by `store()`. Export it to avoid\n * reimplementation when composing custom `StoreOptions.equals`.\n */\nexport const shallowEqual: EqualityFn<unknown> = (a, b) => {\n if (a === b) return true;\n\n if (a == null || b == null) return a === b;\n\n if (typeof a !== 'object' || typeof b !== 'object') return false;\n\n const keysA = Object.keys(a as object);\n const keysB = Object.keys(b as object);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!Object.is((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])) return false;\n }\n\n return true;\n};\n\nexport type StoreOptions<T extends object> = {\n /** Custom equality for top-level change detection. Default: shallowEqual */\n equals?: EqualityFn<T>;\n};\n\n/** Reactive store for object state. Implements Signal<T> so all signal primitives work natively. */\nexport interface Store<T extends object> extends Signal<T> {\n readonly frozen: boolean;\n /** Shallow-merge a partial object into the current state. */\n patch(partial: Partial<T>): void;\n /** Derive the next state from the current state via an updater function.\n * Receives a shallow copy of the current state — mutations in the updater are safe. */\n update(fn: (s: T) => T): void;\n /** Reset to the original initial state. */\n reset(): void;\n /** Create a lazily recomputed derived signal from a slice of this store's state. */\n select<U>(selector: (s: T) => U, options?: ReactiveOptions<U>): ComputedSignal<U>;\n /** Freeze the store. Further writes via patch/update/reset are silently ignored. */\n freeze(): void;\n}\n\n/** @internal */\nclass StoreImpl<T extends object> extends SignalImpl<T> implements Store<T> {\n readonly [_STORE_BRAND] = true as const;\n\n readonly #initial: T;\n #frozen = false;\n\n constructor(initial: T, options?: StoreOptions<T>) {\n super(initial, { equals: options?.equals ?? (shallowEqual as EqualityFn<T>) });\n this.#initial = { ...initial }; // defensive copy — external mutation cannot corrupt reset()\n }\n\n get frozen(): boolean {\n return this.#frozen;\n }\n\n override get value(): T {\n return super.value;\n }\n\n override set value(next: T) {\n if (this.#frozen) {\n if (_DEV) console.warn('[stateit] store is frozen — write ignored.');\n\n return;\n }\n\n super.value = next;\n }\n\n patch(partial: Partial<T>): void {\n this.value = { ...this.peek(), ...partial };\n }\n\n update(fn: (s: T) => T): void {\n this.value = fn({ ...this.peek() });\n }\n\n reset(): void {\n this.value = { ...this.#initial };\n }\n\n select<U>(selector: (s: T) => U, options?: ReactiveOptions<U>): ComputedSignal<U> {\n return computed(() => selector(this.value), options);\n }\n\n freeze(): void {\n this.#frozen = true;\n }\n}\n\n/** Creates a reactive store for the object state. */\nexport const store = <T extends object>(initial: T, options?: StoreOptions<T>): Store<T> =>\n new StoreImpl(initial, options);\n\n/** Type guard -- identifies Store instances. */\nexport const isStore = <T extends object = Record<string, unknown>>(value: unknown): value is Store<T> =>\n typeof value === 'object' && value !== null && _STORE_BRAND in value;\n","import { effect } from './effect';\nimport { type EqualityFn, type ReadonlySignal, type Subscription } from './types';\n\nexport type WatchOptions<T> = {\n /** Custom equality; suppresses the callback when old and new values are equal. Default: Object.is */\n equals?: EqualityFn<T>;\n /** Fire the callback immediately with the current value on subscribe. */\n immediate?: boolean;\n /**\n * Auto-unsubscribe after the first *change* invocation.\n * When combined with `immediate`, the immediate call does not count against this quota —\n * the callback may fire up to twice total.\n */\n once?: boolean;\n};\n\n/**\n * Watches a Signal and calls cb when its value changes. Returns a dispose handle.\n * Does not fire on initial subscription unless `{ immediate: true }` is passed.\n * To watch a derived slice, compose with `store.select()` or `computed()`:\n *\n * @example\n * const stop = watch(count, (next, prev) => console.log(prev, '->', next));\n * const stop = watch(userStore.select(s => s.name), (name) => ...);\n */\nexport const watch = <T>(\n source: ReadonlySignal<T>,\n cb: (value: T, prev: T) => void,\n options?: WatchOptions<T>,\n): Subscription => {\n const eq: EqualityFn<T> = options?.equals ?? Object.is;\n let prev = source.peek();\n\n if (options?.immediate) cb(prev, prev);\n\n // Use `let` so the stop reference is readable inside the callback without TDZ risk.\n let stop: Subscription;\n\n // eslint-disable-next-line prefer-const\n stop = effect(() => {\n const next = source.value;\n\n if (!eq(prev, next)) {\n const old = prev;\n\n prev = next;\n cb(next, old);\n\n if (options?.once) stop();\n }\n });\n\n return stop;\n};\n\n/**\n * Returns a Promise that resolves with the next value of `source` that satisfies the optional predicate.\n * Disposes automatically after one emission — no cleanup needed.\n * Pass `{ signal: AbortSignal }` to cancel early; the promise rejects with the abort reason.\n *\n * @example\n * const name = await nextValue(userStore.select(s => s.name));\n * const nonZero = await nextValue(count, v => v > 0, { signal: AbortSignal.timeout(5000) });\n */\nexport const nextValue = <T>(\n source: ReadonlySignal<T>,\n predicate?: (v: T) => boolean,\n options?: { signal?: AbortSignal },\n): Promise<T> =>\n new Promise<T>((resolve, reject) => {\n const signal = options?.signal;\n\n try {\n signal?.throwIfAborted();\n } catch (err) {\n reject(err);\n\n return;\n }\n\n const stop = watch(source, (v) => {\n if (!predicate || predicate(v)) {\n stop();\n signal?.removeEventListener('abort', onAbort);\n resolve(v);\n }\n });\n\n const onAbort = () => {\n stop();\n reject(signal!.reason);\n };\n\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"stateit.cjs","names":[],"sources":["../src/stateit.ts"],"sourcesContent":["import type {\n CleanupFn,\n ComputedSignal,\n EffectCallback,\n EffectOptions,\n EqualityFn,\n ReactiveOptions,\n ReadonlySignal,\n Signal,\n Store,\n Subscription,\n WatchOptions,\n} from './types';\n\n// === CONSTANTS ===\nconst UNINITIALIZED = Symbol('stateit.uninitialized');\nconst DEFAULT_MAX_ITERATIONS = 100;\n\n// === HELPERS ===\nconst toSubscription = (dispose: () => void): Subscription =>\n Object.assign(dispose, { dispose, [Symbol.dispose]: dispose }) as Subscription;\n\nconst ensureObject = (value: unknown, message: string): void => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new TypeError(message);\n }\n};\n\nconst clone = <T>(value: T): T => structuredClone(value);\n\n// === UNIFIED ERROR HANDLING ===\nconst runCallbacks = (callbacks: CleanupFn[], context: string): void => {\n const errors: unknown[] = [];\n\n for (const callback of callbacks) {\n try {\n callback();\n } catch (e) {\n errors.push(e);\n }\n }\n\n if (errors.length === 1) throw errors[0];\n\n if (errors.length > 1) {\n throw new AggregateError(errors, `[stateit] ${context}`);\n }\n};\n\nconst runSubscribers = (subscribers: Set<EffectCallback>): void => {\n const errors: unknown[] = [];\n\n for (const fn of [...subscribers]) {\n try {\n fn();\n } catch (e) {\n errors.push(e);\n }\n }\n\n if (errors.length === 1) throw errors[0];\n\n if (errors.length > 1) {\n throw new AggregateError(errors, '[stateit] subscriber errors');\n }\n};\n\n// === GLOBAL REACTIVE GRAPH ===\ninterface EffectScope {\n effect: EffectCallback | null;\n deps: Set<CleanupFn> | null;\n cleanups: CleanupFn[] | null;\n}\n\nlet currentScope: EffectScope = {\n cleanups: null,\n deps: null,\n effect: null,\n};\n\nconst withScope = <T>(\n effect: EffectCallback | null,\n deps: Set<CleanupFn> | null,\n cleanups: CleanupFn[] | null,\n fn: () => T,\n): T => {\n const prev = currentScope;\n\n currentScope = { cleanups, deps, effect };\n\n try {\n return fn();\n } finally {\n currentScope = prev;\n }\n};\n\n// === BATCH QUEUE ===\nlet batchDepth = 0;\nconst batchQueue = new Set<EffectCallback>();\n\nconst flushBatch = (): void => {\n while (batchQueue.size > 0) {\n const pending = [...batchQueue];\n\n batchQueue.clear();\n\n runSubscribers(new Set(pending));\n }\n};\n\n// === BASE REACTIVE NODE ===\nclass ReactiveNode {\n protected subscribers = new Set<EffectCallback>();\n\n protected track(): void {\n if (!currentScope.effect || !currentScope.deps) return;\n\n this.subscribers.add(currentScope.effect);\n currentScope.deps.add(() => this.subscribers.delete(currentScope.effect!));\n }\n\n protected hasSubscribers(): boolean {\n return this.subscribers.size > 0;\n }\n\n protected notify(): void {\n if (this.subscribers.size === 0) return;\n\n if (batchDepth > 0) {\n for (const fn of this.subscribers) batchQueue.add(fn);\n\n return;\n }\n\n runSubscribers(this.subscribers);\n }\n}\n\n// === SIGNAL IMPLEMENTATION ===\nclass SignalImpl<T> extends ReactiveNode implements Signal<T> {\n private value_: T;\n private equals_: EqualityFn<T>;\n\n constructor(initial: T, equals?: EqualityFn<T>) {\n super();\n this.value_ = initial;\n this.equals_ = equals ?? Object.is;\n }\n\n get value(): T {\n this.track();\n\n return this.value_;\n }\n\n set value(next: T) {\n if (this.equals_(this.value_, next)) return;\n\n this.value_ = next;\n this.notify();\n }\n\n update(fn: (current: T) => T): void {\n this.value = fn(this.value_);\n }\n}\n\n// === COMPUTED IMPLEMENTATION ===\nclass ComputedImpl<T> extends ReactiveNode implements ComputedSignal<T> {\n private value_: T | typeof UNINITIALIZED = UNINITIALIZED;\n private dirty_ = true;\n private disposed_ = false;\n private deps_ = new Set<CleanupFn>();\n private compute_: () => T;\n private equals_: EqualityFn<T>;\n\n private readonly onDepChange: EffectCallback = () => {\n if (this.disposed_) return;\n\n if (this.subscribers.size === 0) {\n this.dirty_ = true;\n\n return;\n }\n\n if (this.recompute()) this.notify();\n };\n\n constructor(compute: () => T, equals?: EqualityFn<T>) {\n super();\n this.compute_ = compute;\n this.equals_ = equals ?? Object.is;\n this.recompute();\n }\n\n private recompute(): boolean {\n for (const unsub of this.deps_) unsub();\n this.deps_.clear();\n\n const next = withScope(this.onDepChange, this.deps_, null, this.compute_);\n\n this.dirty_ = false;\n\n if (this.value_ === UNINITIALIZED || !this.equals_(this.value_ as T, next)) {\n this.value_ = next;\n\n return true;\n }\n\n return false;\n }\n\n get value(): T {\n if (this.disposed_) {\n throw new Error('[stateit] Cannot read disposed computed signal');\n }\n\n if (this.dirty_) this.recompute();\n\n this.track();\n\n return this.value_ as T;\n }\n\n get stale(): boolean {\n return this.dirty_;\n }\n\n dispose(): void {\n if (this.disposed_) return;\n\n this.disposed_ = true;\n this.dirty_ = true;\n for (const unsub of this.deps_) unsub();\n this.deps_.clear();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n\n// === CORE API ===\n\nexport const signal = <T>(initial: T, options?: ReactiveOptions<T>): Signal<T> =>\n new SignalImpl(initial, options?.equals);\n\nexport const computed = <T>(compute: () => T, options?: ReactiveOptions<T>): ComputedSignal<T> => {\n const comp = new ComputedImpl(compute, options?.equals);\n\n // Auto-dispose when created inside effect\n if (currentScope.effect !== null && currentScope.cleanups !== null) {\n onCleanup(() => comp.dispose());\n }\n\n return comp;\n};\n\nexport const batch = <T>(fn: () => T): T => {\n batchDepth++;\n\n try {\n const result = fn();\n\n if (--batchDepth === 0) flushBatch();\n\n return result;\n } catch (e) {\n if (--batchDepth === 0) flushBatch();\n\n throw e;\n }\n};\n\nexport const effect = (fn: EffectCallback, options?: EffectOptions): Subscription => {\n let cleanup: CleanupFn | undefined;\n const deps = new Set<CleanupFn>();\n let isRunning = false;\n let isDirty = false;\n let isDisposed = false;\n const maxIterations = options?.maxIterations ?? DEFAULT_MAX_ITERATIONS;\n\n const teardown = (): void => {\n const callbacks: CleanupFn[] = [];\n\n if (cleanup) callbacks.push(cleanup);\n\n cleanup = undefined;\n\n for (const unsub of deps) callbacks.push(unsub);\n deps.clear();\n\n runCallbacks(callbacks, 'effect teardown errors');\n };\n\n const run = (): void => {\n if (isDisposed) return;\n\n if (isRunning) {\n isDirty = true;\n\n return;\n }\n\n isRunning = true;\n\n try {\n let iterations = 0;\n\n do {\n if (++iterations > maxIterations) {\n throw new Error(`[stateit] infinite effect loop (> ${maxIterations} iterations)`);\n }\n\n isDirty = false;\n teardown();\n\n const localCleanups: CleanupFn[] = [];\n const returnedCleanup = withScope(run, deps, localCleanups, fn);\n\n if (returnedCleanup) localCleanups.push(returnedCleanup);\n\n if (localCleanups.length > 0) {\n cleanup = () => {\n runCallbacks(localCleanups, 'effect cleanup errors');\n };\n }\n } while (isDirty && !isDisposed);\n } finally {\n isRunning = false;\n }\n };\n\n run();\n\n return toSubscription(() => {\n if (isDisposed) return;\n\n isDisposed = true;\n teardown();\n });\n};\n\nexport const untrack = <T>(fn: () => T): T => {\n return withScope(null, null, null, fn);\n};\n\nexport const onCleanup = (fn: CleanupFn): void => {\n if (currentScope.cleanups === null) {\n throw new Error('[stateit] onCleanup() must be called from inside an active effect.');\n }\n\n currentScope.cleanups.push(fn);\n};\n\nconst watchBase = <T>(\n source: ReadonlySignal<T>,\n cb: (value: T, prev: T) => void,\n watchOptions?: WatchOptions<T>,\n): Subscription => {\n const equals = watchOptions?.equals ?? Object.is;\n let prev = untrack(() => source.value);\n\n if (watchOptions?.immediate) cb(prev, prev);\n\n const stop = effect(() => {\n const next = source.value;\n\n if (equals(prev, next)) return;\n\n const old = prev;\n\n prev = next;\n cb(next, old);\n\n if (watchOptions?.once) stop();\n });\n\n return stop;\n};\n\nfunction watch<T>(\n source: ReadonlySignal<T>,\n cb: (value: T, prev: T) => void,\n watchOptions?: WatchOptions<T>,\n): Subscription;\nfunction watch<S, T>(\n source: ReadonlySignal<S>,\n selector: (value: S) => T,\n cb: (value: T, prev: T) => void,\n watchOptions?: WatchOptions<T>,\n): Subscription;\nfunction watch<S, T>(\n source: ReadonlySignal<S>,\n selectorOrCb: ((value: S) => T) | ((value: S, prev: S) => void),\n maybeCb?: ((value: T, prev: T) => void) | WatchOptions<S>,\n maybeOptions?: WatchOptions<T>,\n): Subscription {\n if (typeof maybeCb === 'function') {\n const selector = selectorOrCb as (value: S) => T;\n const cb = maybeCb as (value: T, prev: T) => void;\n const selected = computed(() => selector(source.value));\n const stop = watchBase(selected, cb, maybeOptions);\n\n return toSubscription(() => {\n stop();\n selected.dispose();\n });\n }\n\n const cb = selectorOrCb as (value: S, prev: S) => void;\n\n return watchBase(source, cb, maybeCb as WatchOptions<S> | undefined);\n}\n\nexport { watch };\n\nexport const store = <T extends object>(initial: T, storeOptions?: ReactiveOptions<T>): Store<T> => {\n ensureObject(initial, '[stateit] store() requires a plain object initial state.');\n\n const initialSnapshot = clone(initial);\n const state = signal(clone(initial), storeOptions);\n\n return {\n patch(partial: Partial<T>): void {\n if (typeof partial !== 'object' || partial === null || Array.isArray(partial)) {\n throw new TypeError('[stateit] store.patch() requires a plain object partial.');\n }\n\n state.value = { ...state.value, ...partial };\n },\n reset(): void {\n state.value = clone(initialSnapshot);\n },\n update(fn: (current: T) => T): void {\n const next = fn(state.value);\n\n ensureObject(next, '[stateit] store.update() must return a plain object.');\n state.value = next;\n },\n get value(): T {\n return state.value;\n },\n set value(next: T) {\n ensureObject(next, '[stateit] Store value must always be a plain object.');\n state.value = next;\n },\n };\n};\n\n// === TYPE HELPERS ===\n\nexport const readonly = <T>(input: ReadonlySignal<T>): ReadonlySignal<T> => input;\n\nexport const writable = <T>(input: Signal<T>): Signal<T> => input;\n\nexport const isSignal = <T = unknown>(value: unknown): value is ReadonlySignal<T> => {\n if (typeof value !== 'object' || value === null) return false;\n\n const candidate = value as Partial<ReadonlySignal<T>>;\n\n return 'value' in candidate;\n};\n\nexport const peekValue = <T>(input: T | ReadonlySignal<T>): T => (isSignal<T>(input) ? input.value : input);\n\nexport const toValue = <T>(input: T | ReadonlySignal<T> | (() => T)): T => {\n if (isSignal<T>(input)) return input.value;\n\n if (typeof input === 'function') return (input as () => T)();\n\n return input;\n};\n"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { CleanupFn, ComputedSignal, EffectCallback, EffectOptions, ReactiveOptions, ReadonlySignal, Signal, Store, Subscription, WatchOptions } from './types';
2
+ export declare const signal: <T>(initial: T, options?: ReactiveOptions<T>) => Signal<T>;
3
+ export declare const computed: <T>(compute: () => T, options?: ReactiveOptions<T>) => ComputedSignal<T>;
4
+ export declare const batch: <T>(fn: () => T) => T;
5
+ export declare const effect: (fn: EffectCallback, options?: EffectOptions) => Subscription;
6
+ export declare const untrack: <T>(fn: () => T) => T;
7
+ export declare const onCleanup: (fn: CleanupFn) => void;
8
+ declare function watch<T>(source: ReadonlySignal<T>, cb: (value: T, prev: T) => void, watchOptions?: WatchOptions<T>): Subscription;
9
+ declare function watch<S, T>(source: ReadonlySignal<S>, selector: (value: S) => T, cb: (value: T, prev: T) => void, watchOptions?: WatchOptions<T>): Subscription;
10
+ export { watch };
11
+ export declare const store: <T extends object>(initial: T, storeOptions?: ReactiveOptions<T>) => Store<T>;
12
+ export declare const readonly: <T>(input: ReadonlySignal<T>) => ReadonlySignal<T>;
13
+ export declare const writable: <T>(input: Signal<T>) => Signal<T>;
14
+ export declare const isSignal: <T = unknown>(value: unknown) => value is ReadonlySignal<T>;
15
+ export declare const peekValue: <T>(input: T | ReadonlySignal<T>) => T;
16
+ export declare const toValue: <T>(input: T | ReadonlySignal<T> | (() => T)) => T;
17
+ //# sourceMappingURL=stateit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stateit.d.ts","sourceRoot":"","sources":["../src/stateit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,cAAc,EACd,cAAc,EACd,aAAa,EAEb,eAAe,EACf,cAAc,EACd,MAAM,EACN,KAAK,EACL,YAAY,EACZ,YAAY,EACb,MAAM,SAAS,CAAC;AAyOjB,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,SAAS,CAAC,EAAE,UAAU,eAAe,CAAC,CAAC,CAAC,KAAG,MAAM,CAAC,CAAC,CACnC,CAAC;AAE3C,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,SAAS,MAAM,CAAC,EAAE,UAAU,eAAe,CAAC,CAAC,CAAC,KAAG,cAAc,CAAC,CAAC,CAS5F,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,IAAI,MAAM,CAAC,KAAG,CActC,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,IAAI,cAAc,EAAE,UAAU,aAAa,KAAG,YAmEpE,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,CAAC,EAAE,IAAI,MAAM,CAAC,KAAG,CAExC,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,IAAI,SAAS,KAAG,IAMzC,CAAC;AA4BF,iBAAS,KAAK,CAAC,CAAC,EACd,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,EACzB,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,IAAI,EAC/B,YAAY,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GAC7B,YAAY,CAAC;AAChB,iBAAS,KAAK,CAAC,CAAC,EAAE,CAAC,EACjB,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,EACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,EACzB,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,IAAI,EAC/B,YAAY,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GAC7B,YAAY,CAAC;AAwBhB,OAAO,EAAE,KAAK,EAAE,CAAC;AAEjB,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,MAAM,EAAE,SAAS,CAAC,EAAE,eAAe,eAAe,CAAC,CAAC,CAAC,KAAG,KAAK,CAAC,CAAC,CA+B9F,CAAC;AAIF,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,OAAO,cAAc,CAAC,CAAC,CAAC,KAAG,cAAc,CAAC,CAAC,CAAU,CAAC;AAElF,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,OAAO,MAAM,CAAC,CAAC,CAAC,KAAG,MAAM,CAAC,CAAC,CAAU,CAAC;AAElE,eAAO,MAAM,QAAQ,GAAI,CAAC,GAAG,OAAO,EAAE,OAAO,OAAO,KAAG,KAAK,IAAI,cAAc,CAAC,CAAC,CAM/E,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,OAAO,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,KAAG,CAA+C,CAAC;AAE5G,eAAO,MAAM,OAAO,GAAI,CAAC,EAAE,OAAO,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,KAAG,CAMrE,CAAC"}