@vielzeug/stateit 2.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +60 -290
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.d.ts +1 -19
  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 +68 -0
  9. package/dist/stateit.d.ts.map +1 -0
  10. package/dist/stateit.js +294 -228
  11. package/dist/stateit.js.map +1 -1
  12. package/package.json +6 -5
  13. package/dist/batch.cjs +0 -2
  14. package/dist/batch.cjs.map +0 -1
  15. package/dist/batch.d.ts +0 -5
  16. package/dist/batch.d.ts.map +0 -1
  17. package/dist/batch.js +0 -28
  18. package/dist/batch.js.map +0 -1
  19. package/dist/computed.cjs +0 -2
  20. package/dist/computed.cjs.map +0 -1
  21. package/dist/computed.d.ts +0 -58
  22. package/dist/computed.d.ts.map +0 -1
  23. package/dist/computed.js +0 -65
  24. package/dist/computed.js.map +0 -1
  25. package/dist/effect.cjs +0 -2
  26. package/dist/effect.cjs.map +0 -1
  27. package/dist/effect.d.ts +0 -31
  28. package/dist/effect.d.ts.map +0 -1
  29. package/dist/effect.js +0 -53
  30. package/dist/effect.js.map +0 -1
  31. package/dist/runtime.cjs +0 -2
  32. package/dist/runtime.cjs.map +0 -1
  33. package/dist/runtime.d.ts +0 -40
  34. package/dist/runtime.d.ts.map +0 -1
  35. package/dist/runtime.js +0 -47
  36. package/dist/runtime.js.map +0 -1
  37. package/dist/signal.cjs +0 -2
  38. package/dist/signal.cjs.map +0 -1
  39. package/dist/signal.d.ts +0 -26
  40. package/dist/signal.d.ts.map +0 -1
  41. package/dist/signal.js +0 -40
  42. package/dist/signal.js.map +0 -1
  43. package/dist/store.cjs +0 -2
  44. package/dist/store.cjs.map +0 -1
  45. package/dist/store.d.ts +0 -32
  46. package/dist/store.d.ts.map +0 -1
  47. package/dist/store.js +0 -51
  48. package/dist/store.js.map +0 -1
  49. package/dist/types.cjs +0 -2
  50. package/dist/types.cjs.map +0 -1
  51. package/dist/types.d.ts +0 -39
  52. package/dist/types.d.ts.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,107 @@
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
32
  ```
95
33
 
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
34
+ ## Store As Small Recipe
115
35
 
116
- ### Signals
36
+ `store()` is a thin object helper over `signal()` with immutable-style updates. Stores expose the readable signal contract (`value`, `peek`, `subscribe`) plus object-focused mutation helpers.
117
37
 
118
- ```typescript
119
- import { signal, readonly } from '@vielzeug/stateit';
38
+ ```ts
39
+ import { store } from '@vielzeug/stateit';
120
40
 
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
41
+ const user = store({ profile: { name: 'Ada' }, count: 0 });
125
42
 
126
- // Narrow to read-only view (no proxy overhead)
127
- const ro = readonly(count);
43
+ user.patch({ count: 1 });
44
+ user.update((state) => ({ ...state, count: state.count + 1 }));
45
+ user.reset();
128
46
  ```
129
47
 
130
- ### Computed
131
-
132
- ```typescript
133
- import { signal, computed } from '@vielzeug/stateit';
48
+ ## Watching Derived Values
134
49
 
135
- const first = signal('Ada');
136
- const last = signal('Lovelace');
137
- const full = computed(() => `${first.value} ${last.value}`);
50
+ Use `watch` with a getter function to watch any derived value directly:
138
51
 
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
- ```
52
+ ```ts
53
+ import { store, watch } from '@vielzeug/stateit';
148
54
 
149
- ### Effects
55
+ const cart = store({ count: 0, label: 'x' });
150
56
 
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
57
+ const stop = watch(() => cart.value.count, (next, prev) => {
58
+ console.log(prev, '->', next);
159
59
  });
160
60
 
161
- name.value = 'Grace'; // logs 'cleanup' then 'Hello, Grace'
162
- sub.dispose(); // logs 'cleanup', effect is removed
61
+ stop();
163
62
  ```
164
63
 
165
- ### Watch
166
-
167
- ```typescript
168
- import { signal, store, watch } from '@vielzeug/stateit';
169
-
170
- const count = signal(0);
64
+ ## Interop Helpers
171
65
 
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));
179
-
180
- // Options
181
- watch(count, (v) => console.log(v), { immediate: true, once: true });
182
-
183
- sub.dispose();
184
- ```
66
+ Stateit stays framework-agnostic, but the core exports are designed to plug directly into common reactive contracts.
185
67
 
186
- ### Stores
68
+ ```ts
69
+ import { computed, readonly, signal, toObservable, toStore } from '@vielzeug/stateit';
187
70
 
188
- ```typescript
189
- import { store, watch, batch } from '@vielzeug/stateit';
190
-
191
- const cart = store({ items: [] as string[], total: 0 });
192
-
193
- // Partial patch
194
- cart.patch({ total: 42 });
195
-
196
- // Updater function
197
- cart.update((s) => ({ ...s, items: [...s.items, 'apple'] }));
198
-
199
- // Derived slice
200
- const totalSignal = cart.select((s) => s.total);
201
-
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;
225
- },
226
- );
227
-
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');
71
+ const count = signal(0);
72
+ const doubled = computed(() => count.value * 2);
247
73
 
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'));
74
+ const publicCount = readonly(count);
75
+ const svelteStore = toStore(doubled);
76
+ const observable = toObservable(publicCount);
252
77
  ```
253
78
 
254
- ### Custom Equality
255
-
256
- ```typescript
257
- import { signal, computed } from '@vielzeug/stateit';
79
+ ## Strict Runtime Rules
258
80
 
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
- ```
81
+ - `onCleanup()` throws when called outside an active effect.
82
+ - Reading a disposed computed signal throws.
83
+ - Store constructor/patch misuse (non-object values) throws.
265
84
 
266
85
  ## API
267
86
 
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 |
87
+ ```ts
88
+ type ObservableObserver<T> = { next(value: T): void };
89
+ type ObservableLike<T> = {
90
+ subscribe(observer: ObservableObserver<T> | ((value: T) => void)): { unsubscribe(): void };
91
+ };
92
+
93
+ signal<T>(initial: T, options?: { equals?: (a: T, b: T) => boolean }): Signal<T>;
94
+ computed<T>(compute: () => T, options?: { equals?: (a: T, b: T) => boolean }): ComputedSignal<T>;
95
+ effect(fn: () => void | (() => void)): Subscription;
96
+ watch<T>(source: ReadonlySignal<T>, cb: (value: T, prev: T) => void, options?: WatchOptions<T>): Subscription;
97
+ watch<T>(source: () => T, cb: (value: T, prev: T) => void, options?: WatchOptions<T>): Subscription;
98
+ batch<T>(fn: () => T): T;
99
+ untrack<T>(fn: () => T): T;
100
+ readonly<T>(source: ReadonlySignal<T>): ReadonlySignal<T>;
101
+ toStore<T>(source: ReadonlySignal<T>): { subscribe(run: (value: T) => void): Subscription };
102
+ toObservable<T>(source: ReadonlySignal<T>): ObservableLike<T>;
103
+ onCleanup(fn: () => void): void;
104
+ scope(setup?: () => void): Scope;
105
+ store<T extends object>(initial: T): Store<T>;
106
+ isSignal<T>(value: unknown): value is ReadonlySignal<T>;
107
+ ```
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.observableSymbol=e.observableSymbol,exports.onCleanup=e.onCleanup,exports.readonly=e.readonly,exports.scope=e.scope,exports.signal=e.signal,exports.store=e.store,exports.toObservable=e.toObservable,exports.toStore=e.toStore,exports.toSubscription=e.toSubscription,exports.untrack=e.untrack,exports.watch=e.watch;
package/dist/index.d.ts CHANGED
@@ -1,20 +1,2 @@
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';
18
- export * from './types';
19
- export * from './watch';
1
+ export * from './stateit';
20
2
  //# 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,cAAc,WAAW,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, observableSymbol as i, onCleanup as a, readonly as o, scope as s, signal as c, store as l, toObservable as u, toStore as d, toSubscription as f, untrack as p, watch as m } from "./stateit.js";
2
+ export { e as batch, t as computed, n as effect, r as isSignal, i as observableSymbol, a as onCleanup, o as readonly, s as scope, c as signal, l as store, u as toObservable, d as toStore, f as toSubscription, p as untrack, m as watch };
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=100,t=Symbol(`stateit.is-signal`),n=Symbol.observable??Symbol.for(`observable`),r=n,i=e=>Object.assign(e,{dispose:e,[Symbol.dispose]:e}),a=new WeakMap,o=e=>e instanceof Error?e:Error(String(e)),s=(e,t)=>{if(typeof e!=`object`||!e||Array.isArray(e))throw TypeError(t)},c=(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}`)},l=(e,t,n)=>{let r=o(e);throw t.length===0?r:AggregateError([r,...t.map(o)],`[stateit] ${n}`,{cause:r})},u=e=>{try{return c(e,`cleanup errors`),[]}catch(e){return e instanceof AggregateError?e.errors:[e]}},d=null,f=null,p=null,m=null,h=(e,t,n,r,i)=>{let a=d,o=f,s=p,c=m;d=e,f=t,p=n,m=r;try{return i()}finally{d=a,f=o,p=s,m=c}},g=0,_=new Set,v=new Set,y=e=>{let t=new Set(e),n=new Map;for(let t of e)n.set(t,0);for(let r of e)for(let e of r.computedSubscribers())t.has(e)&&n.set(e,(n.get(e)??0)+1);let r=[];for(let t of e)(n.get(t)??0)===0&&r.push(t);let i=[];for(;r.length>0;){let e=r.shift();i.push(e);for(let i of e.computedSubscribers()){if(!t.has(i))continue;let e=(n.get(i)??0)-1;n.set(i,e),e===0&&r.push(i)}}if(i.length<e.length)for(let t of e)i.includes(t)||i.push(t);return i},b=e=>{let t=[...e.computedSubscribers()],n=new Set;for(let t of e.subscribers())_.add(t);for(;t.length>0;){let e=t.pop();if(!n.has(e)&&(n.add(e),e.markDirty())){v.add(e);for(let n of e.computedSubscribers())t.push(n)}}},x=()=>{for(;v.size>0;){let e=y([...v]);v.clear();for(let t of e)if(t.hasAnySubscribers()&&t.refreshIfDirty()){for(let e of t.subscribers())_.add(e);for(let e of t.computedSubscribers())e.markDirty()&&v.add(e)}}},S=()=>{let t=0;for(;_.size>0||v.size>0;){if(++t>e)throw Error(`[stateit] infinite flush loop (> ${e} iterations)`);if(v.size>0&&x(),_.size===0)continue;let n=[..._];_.clear(),c(n,`subscriber errors`)}},C=e=>{e.hasAnySubscribers()&&(b(e),g===0&&S())},w=class{computedSubs_=new Set;subscribers_=new Set;track(){if(p){if(f!==null){let e=f;this.computedSubs_.add(e),p.add(()=>this.computedSubs_.delete(e));return}if(d!==null){let e=d;this.subscribers_.add(e),p.add(()=>this.subscribers_.delete(e))}}}notify(){C(this)}hasAnySubscribers(){return this.computedSubs_.size>0||this.subscribers_.size>0}computedSubscribers(){return this.computedSubs_}subscribers(){return this.subscribers_}subscribe(e){return this.subscribers_.add(e),i(()=>{this.subscribers_.delete(e)})}},T=class extends w{value_;equals_;[t]=!0;constructor(e,t){super(),this.value_=e,this.equals_=t??Object.is}get value(){return this.track(),this.value_}peek(){return this.value_}subscribe=e=>super.subscribe(e);update(e){this.value=e(this.value_)}set value(e){this.equals_(this.value_,e)||(this.value_=e,this.notify())}},E=class extends w{hasValue_=!1;value_;dirty_=!0;computing_=!1;disposed_=!1;deps_=new Set;compute_;equals_;[t]=!0;constructor(e,t){super(),this.compute_=e,this.equals_=t??Object.is}markDirty(){return this.disposed_||this.dirty_?!1:(this.dirty_=!0,!0)}refreshIfDirty(){return this.dirty_?this.recompute():!1}recompute(){if(this.computing_)throw Error(`[stateit] computed cycle detected`);for(let e of this.deps_)e();this.deps_.clear(),this.computing_=!0;try{let e=h(null,this,this.deps_,null,this.compute_);return this.dirty_=!1,!this.hasValue_||!this.equals_(this.value_,e)?(this.hasValue_=!0,this.value_=e,!0):!1}catch(e){let t=u(this.deps_);return this.deps_.clear(),l(e,t,`computed failed dependency cleanup errors`)}finally{this.computing_=!1}}get value(){if(this.disposed_)throw Error(`[stateit] Cannot read disposed computed signal`);return this.refreshIfDirty(),this.track(),this.value_}peek(){if(this.disposed_)throw Error(`[stateit] Cannot read disposed computed signal`);return this.refreshIfDirty(),this.value_}subscribe=e=>{if(this.disposed_)throw Error(`[stateit] Cannot subscribe to a disposed computed signal`);return this.refreshIfDirty(),super.subscribe(e)};dispose(){if(!this.disposed_){this.disposed_=!0;for(let e of this.deps_)e();this.deps_.clear()}}[Symbol.dispose](){this.dispose()}},D=(e,t)=>new T(e,t?.equals),O=(e,t)=>{let n=new E(e,t?.equals);return m!==null&&F(()=>n.dispose()),n},k=e=>{g++;let t,n;try{t=e()}catch(e){n=e}if(g--,g===0)try{S()}catch(e){throw n===void 0?o(e):AggregateError([n,e],`[stateit] batch error with flush errors`,{cause:e})}if(n!==void 0)throw o(n);return t},A=t=>{let n,r=new Set,a=!1,o=!1,s=!1,d=()=>{if(!n&&r.size===0)return;let e=n?[n,...r]:[...r];n=void 0,r.clear(),c(e,`effect teardown errors`)},f=()=>{if(!s){if(a){o=!0;return}a=!0;try{let i=0;do{if(++i>e)throw Error(`[stateit] infinite effect loop (> ${e} iterations)`);o=!1,d();let a=[],s;try{s=h(f,null,r,a,t)}catch(e){let t=[...u(r),...u(a)];r.clear(),l(e,t,`effect failure with cleanup errors`)}typeof s==`function`&&a.push(s),n=a.length>0?()=>{c(a,`effect cleanup errors`)}:void 0}while(o&&!s)}finally{a=!1}}};return f(),i(()=>{s||(s=!0,d())})},j=e=>h(null,null,null,null,e),M=e=>{let t=e,n=a.get(t);if(n)return n;let r={peek(){return e.peek()},subscribe(t){return e.subscribe(t)},get value(){return e.value}};return a.set(t,r),r},N=e=>({subscribe(t){return t(e.value),e.subscribe(()=>t(e.value))}}),P=e=>{let t=t=>{let n=typeof t==`function`?{next:t}:t;n.next?.(e.value);let r=e.subscribe(()=>n.next?.(e.value));return{unsubscribe(){r()}}},r={[n](){return r},subscribe:t};return r},F=e=>{if(m===null)throw Error(`[stateit] onCleanup() must be called from within an active effect or scope.`);m.push(e)},I=e=>{let t=[],n=!1,r=e=>{if(n)throw Error(`[stateit] Cannot run inside a disposed scope.`);return h(null,null,null,t,e)},i=()=>{n||(n=!0,c([...t].reverse(),`scope cleanup errors`),t.length=0)},a={dispose:i,run:r,[Symbol.dispose]:i};return e&&r(e),a};function L(e,t,n){let r=typeof e==`function`?e:()=>e.value,i=n?.equals??Object.is,a=!1,o;return n?.immediate&&(o=j(r),a=!0,t(o,o)),A(()=>{let e=r();if(!a){a=!0,o=e;return}if(i(o,e))return;let n=o;o=e,t(e,n)})}var R=e=>{s(e,`[stateit] store() requires a plain object initial state.`);let n=structuredClone(e),r=D(structuredClone(e));return{[t]:!0,patch(e){s(e,`[stateit] store.patch() requires a plain object partial.`);let t=j(()=>r.value);Object.keys(e).some(n=>!Object.is(t[n],e[n]))&&(r.value={...t,...e})},peek(){return r.peek()},reset(){r.value=structuredClone(n)},subscribe(e){return r.subscribe(e)},update(e){r.update(e)},get value(){return r.value}}},z=e=>typeof e==`object`&&!!e&&!!e[t];exports.batch=k,exports.computed=O,exports.effect=A,exports.isSignal=z,exports.observableSymbol=r,exports.onCleanup=F,exports.readonly=M,exports.scope=I,exports.signal=D,exports.store=R,exports.toObservable=P,exports.toStore=N,exports.toSubscription=i,exports.untrack=j,exports.watch=L;
2
2
  //# sourceMappingURL=stateit.cjs.map