ustor 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -18,6 +18,7 @@ A tiny, dependency-free reactive store for JavaScript objects and arrays. UStor
18
18
  - [Installation](#installation)
19
19
  - [Quickstart](#quickstart)
20
20
  - [Common use cases](#common-use-cases)
21
+ - [Examples](#examples)
21
22
  - [API reference](#api-reference)
22
23
  - [Arrays](#arrays)
23
24
  - [Supported adapters](#supported-adapters)
@@ -53,10 +54,10 @@ import { signal, computed, effect } from "@preact/signals-core";
53
54
 
54
55
  Object.assign(api, {
55
56
  signal,
56
- memo: computed,
57
+ computed,
57
58
  effect,
58
59
  get: (value) => value.value,
59
- getMemo: (value) => value.value,
60
+ getComputed: (value) => value.value,
60
61
  set: (target, value) => { target.value = value; },
61
62
  is: (value) => typeof value?.peek === "function",
62
63
  });
@@ -147,6 +148,10 @@ ui.theme = "dark";
147
148
 
148
149
  Create request- or component-specific stores when the state must not be shared. See [SSR and Next.js](#ssr-and-nextjs).
149
150
 
151
+ ## Examples
152
+
153
+ The [source example collection](examples/README.md) contains runnable Node programs for counters, carts, validation, async race handling, undo history, Vue, and RxJS; React and Next.js components; a browser import-map demo; and TypeScript examples. Run `npm run examples` to build, execute the isolated Node examples, and type-check the component/TypeScript sources.
154
+
150
155
  ## API reference
151
156
 
152
157
  ### `store(values, proto?)`
@@ -190,7 +195,7 @@ is(null); // false
190
195
 
191
196
  ### `state.$property`
192
197
 
193
- Exposes the underlying signal for a data property, or the underlying memo for a memoized getter.
198
+ Exposes the underlying signal for a data property, or the underlying computed value for a computed getter.
194
199
 
195
200
  ```javascript
196
201
  const state = store({ count: 0 });
@@ -199,7 +204,7 @@ state.$count.value = 2; // Preact signal example
199
204
  console.log(state.count); // 2
200
205
  ```
201
206
 
202
- Aliases are non-enumerable. Getters without `api.memo` do not have a `$` alias. If an input explicitly defines a conflicting key such as `$count`, the user-defined property takes precedence.
207
+ Aliases are non-enumerable. Getters without `api.computed` do not have a `$` alias. If an input explicitly defines a conflicting key such as `$count`, the user-defined property takes precedence.
203
208
 
204
209
  ### Adapter API
205
210
 
@@ -216,11 +221,11 @@ Optional operations:
216
221
 
217
222
  | Operation | Purpose |
218
223
  | --- | --- |
219
- | `api.memo(fn)` | Create a lazy computed value. |
220
- | `api.getMemo(memo)` | Read a memo; defaults to `api.get`. |
224
+ | `api.computed(fn)` | Create a lazy computed value. |
225
+ | `api.getComputed(computed)` | Read a computed value; defaults to `api.get`. |
221
226
  | `api.effect(fn)` | Required by `ustor/react`; must auto-track synchronously and return a cleanup function. |
222
227
 
223
- Without `api.memo`, getters execute on each access and still participate in compatible effects. With `api.memo`, getters are lazy and cached while their dependencies remain unchanged.
228
+ Without `api.computed`, getters execute on each access and still participate in compatible effects. With `api.computed`, getters are lazy and cached while their dependencies remain unchanged.
224
229
 
225
230
  ## Arrays
226
231
 
@@ -253,35 +258,35 @@ The configurations in `adapters.spec.js` are executable documentation and includ
253
258
 
254
259
  | Package | Getter support |
255
260
  | --- | --- |
256
- | `@preact/signals-core` | Automatic memoization |
257
- | `solid-js` | Automatic memoization |
258
- | `oby` | Automatic memoization |
259
- | `sinuous/observable` | Automatic memoization |
260
- | `ulive` | Automatic memoization |
261
- | `usignal` | Automatic memoization |
262
- | `@vue/reactivity` | Automatic memoization using `shallowRef` |
263
- | `alien-signals` | Automatic memoization |
264
- | `s-js` | Automatic memoization using the provided branded factory |
265
- | `mobx` | Automatic memoization using shallow observable boxes |
266
- | `@tanstack/store` | Automatic memoization using the provided value wrapper |
267
- | `@webreflection/signal` | Automatic memoization |
268
- | `@webreflection/signals` | Automatic memoization |
269
- | `@webreflection/alien-signals` | Automatic memoization |
261
+ | `@preact/signals-core` | Automatic computed values |
262
+ | `solid-js` | Automatic computed values |
263
+ | `oby` | Automatic computed values |
264
+ | `sinuous/observable` | Automatic computed values |
265
+ | `ulive` | Automatic computed values |
266
+ | `usignal` | Automatic computed values |
267
+ | `@vue/reactivity` | Automatic computed values using `shallowRef` |
268
+ | `alien-signals` | Automatic computed values |
269
+ | `s-js` | Automatic computed values using the provided branded factory |
270
+ | `mobx` | Automatic computed values using shallow observable boxes |
271
+ | `@tanstack/store` | Automatic computed values using the provided value wrapper |
272
+ | `@webreflection/signal` | Automatic computed values |
273
+ | `@webreflection/signals` | Automatic computed values |
274
+ | `@webreflection/alien-signals` | Automatic computed values |
270
275
  | `rxjs` | Basic: `BehaviorSubject` values and direct getters |
271
276
  | `nanostores` | Basic: atoms and direct getters |
272
277
 
273
- **Basic adapters** support synchronous values, nested objects, arrays, and getters evaluated on access. RxJS and Nanostores do not automatically discover arbitrary getter dependencies, so automatic memoization is not advertised for them.
278
+ **Basic adapters** support synchronous values, nested objects, arrays, and getters evaluated on access. RxJS and Nanostores do not automatically discover arbitrary getter dependencies, so automatic computed values are not advertised for them.
274
279
 
275
280
  Adapter-specific notes:
276
281
 
277
282
  - Vue and MobX use shallow containers to avoid wrapping UStor's proxies twice.
278
283
  - TanStack Store treats bare functions as computed values; its tested adapter wraps writable values.
279
- - S.js has no built-in signal brand; use the tested factory and create memoized stores inside an S.js root.
280
- - Solid.js, Vue, S.js, and similar libraries may require their own owner/scope lifecycle for short-lived memos.
284
+ - S.js has no built-in signal brand; use the tested factory and create stores with computed values inside an S.js root.
285
+ - Solid.js, Vue, S.js, and similar libraries may require their own owner/scope lifecycle for short-lived computed values.
281
286
 
282
287
  ## Porting any signal library
283
288
 
284
- You normally **do not need to modify UStor's core**. Port a library by translating its synchronous read/write API into four required functions, then add optional memo/effect functions when supported.
289
+ You normally **do not need to modify UStor's core**. Port a library by translating its synchronous read/write API into four required functions, then add optional computed/effect functions when supported.
285
290
 
286
291
  ### 1. Map the four required operations
287
292
 
@@ -370,20 +375,20 @@ api.is = (value) => signals.has(value);
370
375
 
371
376
  Create external signals through `api.signal` when using this approach. The S.js configuration in `adapters.spec.js` demonstrates it.
372
377
 
373
- ### 4. Add memoization only when dependencies are automatic
378
+ ### 4. Add computed values only when dependencies are automatic
374
379
 
375
380
  If the library can compute a value by automatically tracking signals read inside `fn`, add:
376
381
 
377
382
  ```javascript
378
383
  Object.assign(api, {
379
- memo: (fn) => library.computed(fn),
380
- getMemo: (memo) => library.read(memo),
384
+ computed: (fn) => library.computed(fn),
385
+ getComputed: (computed) => library.read(computed),
381
386
  });
382
387
  ```
383
388
 
384
- The memo may be lazy or eager, but it must invalidate when any dependency changes, including conditional dependencies.
389
+ The computed value may be lazy or eager, but it must invalidate when any dependency changes, including conditional dependencies.
385
390
 
386
- If the library requires an explicit dependency list—as with RxJS or Nanostores—**omit `memo` and `getMemo`**. UStor then uses direct getters evaluated on access. Do not emulate automatic memoization incorrectly.
391
+ If the library requires an explicit dependency list—as with RxJS or Nanostores—**omit `computed` and `getComputed`**. UStor then uses direct getters evaluated on access. Do not emulate automatic dependency tracking incorrectly.
387
392
 
388
393
  ### 5. Add effects for React integration only when compatible
389
394
 
@@ -406,11 +411,11 @@ The core store does not require `api.effect`. Basic adapters without automatic d
406
411
 
407
412
  ### 6. Configure once and manage lifecycle
408
413
 
409
- Configure `api` before creating stores and keep the same implementation for their lifetime. If your library requires an owner, scope, root, or disposer for memos/effects, create short-lived stores in that lifecycle and dispose them with their owner.
414
+ Configure `api` before creating stores and keep the same implementation for their lifetime. If your library requires an owner, scope, root, or disposer for computed values/effects, create short-lived stores in that lifecycle and dispose them with their owner.
410
415
 
411
416
  ### 7. Validate the adapter
412
417
 
413
- This Node script checks the required behavior, function preservation, nested updates, arrays, and optional memoization:
418
+ This Node script checks the required behavior, function preservation, nested updates, arrays, and optional computed values:
414
419
 
415
420
  ```javascript
416
421
  import assert from "node:assert/strict";
@@ -444,15 +449,15 @@ assert.equal(state.total, 11);
444
449
  state.list[0] = 5;
445
450
  assert.equal(state.total, 15);
446
451
 
447
- if (api.memo) {
448
- assert.equal((api.getMemo || api.get)(state.$total), 15);
452
+ if (api.computed) {
453
+ assert.equal((api.getComputed || api.get)(state.$total), 15);
449
454
  }
450
455
  ```
451
456
 
452
457
  A production-quality adapter should also verify:
453
458
 
454
459
  - Whole-array replacement, deletion, and length changes.
455
- - Conditional memo dependencies and no-op writes.
460
+ - Conditional computed dependencies and no-op writes.
456
461
  - Effect cleanup and behavior under nested/batched updates.
457
462
  - React StrictMode and unmount behavior when used with `ustor/react`.
458
463
  - Owner/scope disposal and long-running memory behavior.
@@ -591,7 +596,7 @@ const total = useStore(() => state.list.reduce((sum, value) => sum + value, 0));
591
596
 
592
597
  Exported type utilities:
593
598
 
594
- - `SignalApi<Signal, Memo>` for custom adapters.
599
+ - `SignalApi<Signal, Computed>` for custom adapters.
595
600
  - `Store<T>` for reactive store inference.
596
601
  - `SignalValue<Signal>` for React signal-value inference.
597
602
 
@@ -605,7 +610,7 @@ npm run typecheck
605
610
 
606
611
  The profiler uses Preact Signals and measures:
607
612
 
608
- - High-frequency memoized property updates.
613
+ - High-frequency computed property updates.
609
614
  - Batched property updates.
610
615
  - Bounded array mutation workloads.
611
616
  - Repeated store/effect creation, updates, and disposal with heap/RSS samples.
@@ -672,6 +677,7 @@ npm test # build, type tests, unit/adapter/React/SSR tests, pack
672
677
  npm run typecheck # strict TypeScript declaration tests
673
678
  npm run test:coverage # enforced 100% runtime coverage
674
679
  npm run test:watch
680
+ npm run examples # isolated Node examples and JSX/TypeScript checks
675
681
  npm run profile:smoke
676
682
  npm run build
677
683
  ```
package/dist/index.cjs CHANGED
@@ -76,14 +76,14 @@ const objectStore = (values, proto) => {
76
76
  const desc = Object.getOwnPropertyDescriptor(values, key),
77
77
  accessor = desc.get || desc.set,
78
78
  getter = desc.get?.bind(state),
79
- memoize = getter && api.memo,
79
+ createComputed = getter && api.computed,
80
80
  signal = accessor ? undefined : api.is(desc.value) ? desc.value : api.signal(resolve(desc.value));
81
- let memo;
82
- const getSignal = () => getter ? (memo ||= memoize(getter)) : signal;
81
+ let computed;
82
+ const getSignal = () => getter ? (computed ||= createComputed(getter)) : signal;
83
83
  Object.defineProperty(state, key, {
84
84
  get: accessor
85
85
  ? getter
86
- ? () => memoize ? (api.getMemo || api.get)(getSignal()) : getter()
86
+ ? () => createComputed ? (api.getComputed || api.get)(getSignal()) : getter()
87
87
  : undefined
88
88
  : () => api.get(signal),
89
89
  set: accessor
@@ -94,7 +94,7 @@ const objectStore = (values, proto) => {
94
94
  enumerable: desc.enumerable,
95
95
  });
96
96
  if (
97
- (!accessor || memoize) &&
97
+ (!accessor || createComputed) &&
98
98
  typeof key === "string" &&
99
99
  !key.startsWith("$") &&
100
100
  !Object.hasOwn(values, "$" + key)
package/dist/index.es.js CHANGED
@@ -74,14 +74,14 @@ const objectStore = (values, proto) => {
74
74
  const desc = Object.getOwnPropertyDescriptor(values, key),
75
75
  accessor = desc.get || desc.set,
76
76
  getter = desc.get?.bind(state),
77
- memoize = getter && api.memo,
77
+ createComputed = getter && api.computed,
78
78
  signal = accessor ? undefined : api.is(desc.value) ? desc.value : api.signal(resolve(desc.value));
79
- let memo;
80
- const getSignal = () => getter ? (memo ||= memoize(getter)) : signal;
79
+ let computed;
80
+ const getSignal = () => getter ? (computed ||= createComputed(getter)) : signal;
81
81
  Object.defineProperty(state, key, {
82
82
  get: accessor
83
83
  ? getter
84
- ? () => memoize ? (api.getMemo || api.get)(getSignal()) : getter()
84
+ ? () => createComputed ? (api.getComputed || api.get)(getSignal()) : getter()
85
85
  : undefined
86
86
  : () => api.get(signal),
87
87
  set: accessor
@@ -92,7 +92,7 @@ const objectStore = (values, proto) => {
92
92
  enumerable: desc.enumerable,
93
93
  });
94
94
  if (
95
- (!accessor || memoize) &&
95
+ (!accessor || createComputed) &&
96
96
  typeof key === "string" &&
97
97
  !key.startsWith("$") &&
98
98
  !Object.hasOwn(values, "$" + key)
package/dist/index.min.js CHANGED
@@ -1 +1 @@
1
- var ustor=function(e){const t={},r=Symbol(),n=new WeakMap,s=/^(copyWithin|fill|pop|push|reverse|shift|sort|splice|unshift)$/,o=e=>!!e?.[r],i=e=>{const t=e&&Object.getPrototypeOf(e);return null!=e&&(null===t||t?.constructor===Object)},c=e=>Array.isArray(e)||i(e)?a(e):e,a=(e,a)=>{if(o(e))return e;if(!Array.isArray(e)&&!i(e))return e;const l=n.get(e);if(l)return l;if(!(t.signal&&t.get&&t.set&&t.is))throw new Error("ustor: configure api.signal, api.get, api.set and api.is");return Array.isArray(e)?(e=>{const o=t.signal(0);let i=0,a=!1;const l=()=>t.get(o),u=()=>{a=!1,t.set(o,t.get(o)+1)},f=new Proxy(e,{get:(e,t,n)=>{if(t===r)return!0;l();const o=Reflect.get(e,t,n);return"string"==typeof t&&s.test(t)&&"function"==typeof o?(...e)=>{i++;try{return o.apply(n,e)}finally{! --i&&a&&u()}}:o},set:(e,t,r)=>{const n=c(r);return!!Object.is(e[t],n)||!!Reflect.set(e,t,n)&&(i?a=!0:u(),!0)},deleteProperty:(e,t)=>!Object.hasOwn(e,t)||!!Reflect.deleteProperty(e,t)&&(i?a=!0:u(),!0),ownKeys:e=>(l(),Reflect.ownKeys(e)),has:(e,t)=>(l(),Reflect.has(e,t))});return n.set(e,f),e.forEach(((t,r)=>{e[r]=c(t)})),f})(e):((e,s)=>{const o=Object.create(void 0===s?Object.getPrototypeOf(e):s);return n.set(e,o),Reflect.ownKeys(e).forEach((r=>{const n=Object.getOwnPropertyDescriptor(e,r),s=n.get||n.set,i=n.get?.bind(o),a=i&&t.memo,l=s?void 0:t.is(n.value)?n.value:t.signal(c(n.value));let u;const f=()=>i?u||=a(i):l;Object.defineProperty(o,r,{get:s?i?()=>a?(t.getMemo||t.get)(f()):i():void 0:()=>t.get(l),set:s?n.set?.bind(o):!1===n.writable?void 0:e=>t.set(l,c(e)),enumerable:n.enumerable}),s&&!a||"string"!=typeof r||r.startsWith("$")||Object.hasOwn(e,"$"+r)||Object.defineProperty(o,"$"+r,{get:f,enumerable:!1})})),Object.defineProperty(o,r,{value:!0}),Object.seal(o)})(e,a)};return e.api=t,e.is=o,e.store=a,e}({});
1
+ var ustor=function(e){const t={},r=Symbol(),n=new WeakMap,s=/^(copyWithin|fill|pop|push|reverse|shift|sort|splice|unshift)$/,o=e=>!!e?.[r],i=e=>{const t=e&&Object.getPrototypeOf(e);return null!=e&&(null===t||t?.constructor===Object)},c=e=>Array.isArray(e)||i(e)?a(e):e,a=(e,a)=>{if(o(e))return e;if(!Array.isArray(e)&&!i(e))return e;const l=n.get(e);if(l)return l;if(!(t.signal&&t.get&&t.set&&t.is))throw new Error("ustor: configure api.signal, api.get, api.set and api.is");return Array.isArray(e)?(e=>{const o=t.signal(0);let i=0,a=!1;const l=()=>t.get(o),u=()=>{a=!1,t.set(o,t.get(o)+1)},f=new Proxy(e,{get:(e,t,n)=>{if(t===r)return!0;l();const o=Reflect.get(e,t,n);return"string"==typeof t&&s.test(t)&&"function"==typeof o?(...e)=>{i++;try{return o.apply(n,e)}finally{! --i&&a&&u()}}:o},set:(e,t,r)=>{const n=c(r);return!!Object.is(e[t],n)||!!Reflect.set(e,t,n)&&(i?a=!0:u(),!0)},deleteProperty:(e,t)=>!Object.hasOwn(e,t)||!!Reflect.deleteProperty(e,t)&&(i?a=!0:u(),!0),ownKeys:e=>(l(),Reflect.ownKeys(e)),has:(e,t)=>(l(),Reflect.has(e,t))});return n.set(e,f),e.forEach((t,r)=>{e[r]=c(t)}),f})(e):((e,s)=>{const o=Object.create(void 0===s?Object.getPrototypeOf(e):s);return n.set(e,o),Reflect.ownKeys(e).forEach(r=>{const n=Object.getOwnPropertyDescriptor(e,r),s=n.get||n.set,i=n.get?.bind(o),a=i&&t.computed,l=s?void 0:t.is(n.value)?n.value:t.signal(c(n.value));let u;const f=()=>i?u||=a(i):l;Object.defineProperty(o,r,{get:s?i?()=>a?(t.getComputed||t.get)(f()):i():void 0:()=>t.get(l),set:s?n.set?.bind(o):!1===n.writable?void 0:e=>t.set(l,c(e)),enumerable:n.enumerable}),s&&!a||"string"!=typeof r||r.startsWith("$")||Object.hasOwn(e,"$"+r)||Object.defineProperty(o,"$"+r,{get:f,enumerable:!1})}),Object.defineProperty(o,r,{value:!0}),Object.seal(o)})(e,a)};return e.api=t,e.is=o,e.store=a,e}({});
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).ustor={})}(this,(function(e){const t={},r=Symbol(),n=new WeakMap,s=/^(copyWithin|fill|pop|push|reverse|shift|sort|splice|unshift)$/,o=e=>!!e?.[r],i=e=>{const t=e&&Object.getPrototypeOf(e);return null!=e&&(null===t||t?.constructor===Object)},c=e=>Array.isArray(e)||i(e)?a(e):e,a=(e,a)=>{if(o(e))return e;if(!Array.isArray(e)&&!i(e))return e;const f=n.get(e);if(f)return f;if(!(t.signal&&t.get&&t.set&&t.is))throw new Error("ustor: configure api.signal, api.get, api.set and api.is");return Array.isArray(e)?(e=>{const o=t.signal(0);let i=0,a=!1;const f=()=>t.get(o),l=()=>{a=!1,t.set(o,t.get(o)+1)},u=new Proxy(e,{get:(e,t,n)=>{if(t===r)return!0;f();const o=Reflect.get(e,t,n);return"string"==typeof t&&s.test(t)&&"function"==typeof o?(...e)=>{i++;try{return o.apply(n,e)}finally{! --i&&a&&l()}}:o},set:(e,t,r)=>{const n=c(r);return!!Object.is(e[t],n)||!!Reflect.set(e,t,n)&&(i?a=!0:l(),!0)},deleteProperty:(e,t)=>!Object.hasOwn(e,t)||!!Reflect.deleteProperty(e,t)&&(i?a=!0:l(),!0),ownKeys:e=>(f(),Reflect.ownKeys(e)),has:(e,t)=>(f(),Reflect.has(e,t))});return n.set(e,u),e.forEach(((t,r)=>{e[r]=c(t)})),u})(e):((e,s)=>{const o=Object.create(void 0===s?Object.getPrototypeOf(e):s);return n.set(e,o),Reflect.ownKeys(e).forEach((r=>{const n=Object.getOwnPropertyDescriptor(e,r),s=n.get||n.set,i=n.get?.bind(o),a=i&&t.memo,f=s?void 0:t.is(n.value)?n.value:t.signal(c(n.value));let l;const u=()=>i?l||=a(i):f;Object.defineProperty(o,r,{get:s?i?()=>a?(t.getMemo||t.get)(u()):i():void 0:()=>t.get(f),set:s?n.set?.bind(o):!1===n.writable?void 0:e=>t.set(f,c(e)),enumerable:n.enumerable}),s&&!a||"string"!=typeof r||r.startsWith("$")||Object.hasOwn(e,"$"+r)||Object.defineProperty(o,"$"+r,{get:u,enumerable:!1})})),Object.defineProperty(o,r,{value:!0}),Object.seal(o)})(e,a)};e.api=t,e.is=o,e.store=a}));
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).ustor={})}(this,function(e){const t={},r=Symbol(),n=new WeakMap,s=/^(copyWithin|fill|pop|push|reverse|shift|sort|splice|unshift)$/,o=e=>!!e?.[r],i=e=>{const t=e&&Object.getPrototypeOf(e);return null!=e&&(null===t||t?.constructor===Object)},c=e=>Array.isArray(e)||i(e)?a(e):e,a=(e,a)=>{if(o(e))return e;if(!Array.isArray(e)&&!i(e))return e;const f=n.get(e);if(f)return f;if(!(t.signal&&t.get&&t.set&&t.is))throw new Error("ustor: configure api.signal, api.get, api.set and api.is");return Array.isArray(e)?(e=>{const o=t.signal(0);let i=0,a=!1;const f=()=>t.get(o),l=()=>{a=!1,t.set(o,t.get(o)+1)},u=new Proxy(e,{get:(e,t,n)=>{if(t===r)return!0;f();const o=Reflect.get(e,t,n);return"string"==typeof t&&s.test(t)&&"function"==typeof o?(...e)=>{i++;try{return o.apply(n,e)}finally{! --i&&a&&l()}}:o},set:(e,t,r)=>{const n=c(r);return!!Object.is(e[t],n)||!!Reflect.set(e,t,n)&&(i?a=!0:l(),!0)},deleteProperty:(e,t)=>!Object.hasOwn(e,t)||!!Reflect.deleteProperty(e,t)&&(i?a=!0:l(),!0),ownKeys:e=>(f(),Reflect.ownKeys(e)),has:(e,t)=>(f(),Reflect.has(e,t))});return n.set(e,u),e.forEach((t,r)=>{e[r]=c(t)}),u})(e):((e,s)=>{const o=Object.create(void 0===s?Object.getPrototypeOf(e):s);return n.set(e,o),Reflect.ownKeys(e).forEach(r=>{const n=Object.getOwnPropertyDescriptor(e,r),s=n.get||n.set,i=n.get?.bind(o),a=i&&t.computed,f=s?void 0:t.is(n.value)?n.value:t.signal(c(n.value));let l;const u=()=>i?l||=a(i):f;Object.defineProperty(o,r,{get:s?i?()=>a?(t.getComputed||t.get)(u()):i():void 0:()=>t.get(f),set:s?n.set?.bind(o):!1===n.writable?void 0:e=>t.set(f,c(e)),enumerable:n.enumerable}),s&&!a||"string"!=typeof r||r.startsWith("$")||Object.hasOwn(e,"$"+r)||Object.defineProperty(o,"$"+r,{get:u,enumerable:!1})}),Object.defineProperty(o,r,{value:!0}),Object.seal(o)})(e,a)};e.api=t,e.is=o,e.store=a});
package/dist/react.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("react");const r={},t=t=>{const c=e.useCallback((e=>{if(!r.effect)throw new Error("ustor/react: configure api.effect with cleanup support");let c=!0;return r.effect((()=>{t(),c?c=!1:e()}))}),[t]);return e.useSyncExternalStore(c,t,t)};exports.useSignal=c=>{const s=e.useCallback((()=>r.get(c)),[c]);return t(s)},exports.useStore=t;
1
+ "use strict";var e=require("react");const r={},t=t=>{const c=e.useCallback(e=>{if(!r.effect)throw new Error("ustor/react: configure api.effect with cleanup support");let c=!0;return r.effect(()=>{t(),c?c=!1:e()})},[t]);return e.useSyncExternalStore(c,t,t)};exports.useSignal=c=>{const s=e.useCallback(()=>r.get(c),[c]);return t(s)},exports.useStore=t;
package/dist/react.es.js CHANGED
@@ -1 +1 @@
1
- import{useCallback as t,useSyncExternalStore as r}from"react";const e={},o=o=>{const c=t((t=>{if(!e.effect)throw new Error("ustor/react: configure api.effect with cleanup support");let r=!0;return e.effect((()=>{o(),r?r=!1:t()}))}),[o]);return r(c,o,o)},c=r=>{const c=t((()=>e.get(r)),[r]);return o(c)};export{c as useSignal,o as useStore};
1
+ import{useCallback as t,useSyncExternalStore as r}from"react";const e={},o=o=>{const c=t(t=>{if(!e.effect)throw new Error("ustor/react: configure api.effect with cleanup support");let r=!0;return e.effect(()=>{o(),r?r=!1:t()})},[o]);return r(c,o,o)},c=r=>{const c=t(()=>e.get(r),[r]);return o(c)};export{c as useSignal,o as useStore};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ustor",
3
3
  "description": "A tiny, dependency-free reactive store with pluggable signal adapters and optional React hooks.",
4
- "version": "0.2.0",
4
+ "version": "0.3.0",
5
5
  "type": "module",
6
6
  "source": "./src/index.js",
7
7
  "types": "./types/index.d.ts",
@@ -40,6 +40,9 @@
40
40
  "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.nodenext.json",
41
41
  "test:watch": "vitest",
42
42
  "test:coverage": "vitest --run --coverage",
43
+ "examples": "npm run examples:node && npm run examples:typecheck",
44
+ "examples:node": "npm run build && node scripts/run-examples.mjs",
45
+ "examples:typecheck": "tsc -p tsconfig.examples.json",
43
46
  "profile": "node --expose-gc scripts/profile.mjs",
44
47
  "profile:smoke": "node --expose-gc scripts/profile.mjs --updates=5000 --array-updates=500 --seconds=0.5 --cycles=500 --sample-ms=100",
45
48
  "prepack": "npm run build"
@@ -48,6 +51,7 @@
48
51
  "@preact/signals-core": "^1.14.4",
49
52
  "@rollup/plugin-terser": "^1.0.2",
50
53
  "@tanstack/store": "^0.11.2",
54
+ "@types/react": "^19.3.0",
51
55
  "@vitest/coverage-v8": "^5.0.5",
52
56
  "@vue/reactivity": "^3.5.44",
53
57
  "@webreflection/alien-signals": "^0.4.2",
package/types/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- export interface SignalApi<Signal = any, Memo = any> {
1
+ export interface SignalApi<Signal = any, Computed = any> {
2
2
  signal(value: any): Signal;
3
3
  get(signal: Signal): any;
4
4
  set(signal: Signal, value: any): void;
5
5
  is(value: unknown): boolean;
6
- memo?(fn: () => any): Memo;
7
- getMemo?(memo: Memo): any;
6
+ computed?(fn: () => any): Computed;
7
+ getComputed?(computed: Computed): any;
8
8
  effect?(fn: () => void): () => void;
9
9
  [key: string]: unknown;
10
10
  }