storion 0.9.0 → 0.10.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 (77) hide show
  1. package/CHANGELOG.md +62 -4
  2. package/README.md +65 -2004
  3. package/dist/async/abortable.d.ts +295 -0
  4. package/dist/async/abortable.d.ts.map +1 -0
  5. package/dist/async/async.d.ts +86 -5
  6. package/dist/async/async.d.ts.map +1 -1
  7. package/dist/async/context.d.ts +15 -0
  8. package/dist/async/context.d.ts.map +1 -0
  9. package/dist/async/index.d.ts +16 -3
  10. package/dist/async/index.d.ts.map +1 -1
  11. package/dist/async/index.js +407 -137
  12. package/dist/async/safe.d.ts +221 -0
  13. package/dist/async/safe.d.ts.map +1 -0
  14. package/dist/async/types.d.ts +77 -29
  15. package/dist/async/types.d.ts.map +1 -1
  16. package/dist/async/wrappers.d.ts +217 -0
  17. package/dist/async/wrappers.d.ts.map +1 -0
  18. package/dist/core/effect.d.ts +34 -26
  19. package/dist/core/effect.d.ts.map +1 -1
  20. package/dist/core/equality.d.ts +25 -0
  21. package/dist/core/equality.d.ts.map +1 -1
  22. package/dist/core/focus.d.ts +20 -0
  23. package/dist/core/focus.d.ts.map +1 -0
  24. package/dist/core/focusHelpers.d.ts +258 -0
  25. package/dist/core/focusHelpers.d.ts.map +1 -0
  26. package/dist/core/middleware.d.ts +4 -4
  27. package/dist/core/store.d.ts.map +1 -1
  28. package/dist/core/storeContext.d.ts +2 -9
  29. package/dist/core/storeContext.d.ts.map +1 -1
  30. package/dist/dev.d.ts +0 -10
  31. package/dist/dev.d.ts.map +1 -1
  32. package/dist/{index-C8B6Mo8r.js → effect-BDQU8Voz.js} +1241 -583
  33. package/dist/errors.d.ts +6 -0
  34. package/dist/errors.d.ts.map +1 -1
  35. package/dist/index.d.ts +5 -4
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/network/index.d.ts +69 -0
  38. package/dist/network/index.d.ts.map +1 -0
  39. package/dist/network/retry.d.ts +53 -0
  40. package/dist/network/retry.d.ts.map +1 -0
  41. package/dist/network/services.d.ts +58 -0
  42. package/dist/network/services.d.ts.map +1 -0
  43. package/dist/network/store.d.ts +36 -0
  44. package/dist/network/store.d.ts.map +1 -0
  45. package/dist/network/utils.d.ts +9 -0
  46. package/dist/network/utils.d.ts.map +1 -0
  47. package/dist/persist/index.d.ts +1 -1
  48. package/dist/persist/index.d.ts.map +1 -1
  49. package/dist/persist/index.js +11 -9
  50. package/dist/persist/persist.d.ts +14 -14
  51. package/dist/persist/persist.d.ts.map +1 -1
  52. package/dist/pool.d.ts +77 -0
  53. package/dist/pool.d.ts.map +1 -0
  54. package/dist/react/index.d.ts +2 -2
  55. package/dist/react/index.d.ts.map +1 -1
  56. package/dist/react/index.js +245 -244
  57. package/dist/react/stable.d.ts +27 -0
  58. package/dist/react/stable.d.ts.map +1 -0
  59. package/dist/react/useStore.d.ts +38 -13
  60. package/dist/react/useStore.d.ts.map +1 -1
  61. package/dist/react/withStore.d.ts.map +1 -1
  62. package/dist/storion.js +911 -37
  63. package/dist/trigger.d.ts +12 -7
  64. package/dist/trigger.d.ts.map +1 -1
  65. package/dist/types.d.ts +133 -22
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/utils/storeTuple.d.ts +7 -0
  68. package/dist/utils/storeTuple.d.ts.map +1 -0
  69. package/package.json +5 -1
  70. package/dist/collection.d.ts +0 -34
  71. package/dist/collection.d.ts.map +0 -1
  72. package/dist/core/proxy.d.ts +0 -47
  73. package/dist/core/proxy.d.ts.map +0 -1
  74. package/dist/effect-C6h0PDDI.js +0 -446
  75. package/dist/isPromiseLike-bFkfHAbm.js +0 -6
  76. package/dist/react/useLocalStore.d.ts +0 -48
  77. package/dist/react/useLocalStore.d.ts.map +0 -1
@@ -1,11 +1,12 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { memo, useRef, useMemo, createElement, createContext, useContext, useReducer, useEffect, useLayoutEffect, useState, forwardRef } from "react";
5
- import { c as container, i as isSpec, S as STORION_TYPE, r as resolveEquality, s as store } from "../index-C8B6Mo8r.js";
6
- import { n, m, l, u, q, o, g, a, h, d, f, k, e, j, b, z, p, t, v, w, y, A, x } from "../index-C8B6Mo8r.js";
7
- import { P as ProviderMissingError, L as LocalStoreDependencyError, w as withHooks, A as AsyncFunctionError } from "../effect-C6h0PDDI.js";
8
- import { E, H, I, c, a as a2, d as d2, S, b as b2, e as e2, u as u2 } from "../effect-C6h0PDDI.js";
4
+ import { memo, useRef, useMemo, createElement, createContext, useContext, useId, useReducer, useState, useEffect, useLayoutEffect, forwardRef } from "react";
5
+ import { container } from "../storion.js";
6
+ import { append, applyExcept, applyFor, clamp, decrement, divide, forStores, increment, list, map, merge, multiply, prepend, reset, resolver, toggle, trigger, withMeta } from "../storion.js";
7
+ import { P as ProviderMissingError, w as withHooks, d as AsyncFunctionError, t as tryStabilize, h as strictEqual, j as ScopedOutsideSelectorError, s as storeTuple, i as isSpec, k as STORION_TYPE, l as resolveEquality, a as store } from "../effect-BDQU8Voz.js";
8
+ import { E, H, I, L, z, S, y, x, n, v, e, o, m, p, B, q, u } from "../effect-BDQU8Voz.js";
9
+ import { e as emitter } from "../emitter-j4rC71vY.js";
9
10
  import { jsx } from "react/jsx-runtime";
10
11
  import { m as m2 } from "../meta-40r-AZfe.js";
11
12
  const StoreContext = createContext(null);
@@ -35,185 +36,28 @@ function useContainer() {
35
36
  }
36
37
  return ctx;
37
38
  }
38
- function checkIsDev() {
39
- if (typeof __DEV__ !== "undefined") {
40
- return __DEV__;
41
- }
42
- try {
43
- return process.env.NODE_ENV !== "production";
44
- } catch {
45
- return false;
46
- }
47
- }
48
- function dev(fn) {
49
- const isDev = checkIsDev();
50
- if (fn) {
51
- if (isDev) {
52
- fn();
53
- }
54
- return isDev;
55
- }
56
- return isDev;
57
- }
58
- ((dev2) => {
59
- function log(message, ...args) {
60
- if (checkIsDev()) {
61
- console.log(`[rextive] ${message}`, ...args);
62
- }
63
- }
64
- dev2.log = log;
65
- function warn(message, ...args) {
66
- if (checkIsDev()) {
67
- console.warn(`[rextive] ${message}`, ...args);
68
- }
69
- }
70
- dev2.warn = warn;
71
- function error(message, ...args) {
72
- if (checkIsDev()) {
73
- console.error(`[rextive] ${message}`, ...args);
74
- }
75
- }
76
- dev2.error = error;
77
- function assert(condition, message) {
78
- if (checkIsDev()) {
79
- if (!condition) {
80
- throw new Error(`[rextive] Assertion failed: ${message}`);
81
- }
82
- }
83
- }
84
- dev2.assert = assert;
85
- })(dev || (dev = {}));
86
- const isServer = typeof window === "undefined";
87
- const useIsomorphicLayoutEffect = isServer ? useEffect : useLayoutEffect;
88
- const shouldScheduleDispose = !isServer && typeof useLayoutEffect === "function" && dev();
89
- function useLocalStore(spec) {
90
- var _a;
91
- const [, forceUpdate] = useReducer((x2) => x2 + 1, 0);
92
- const prevControllerRef = useRef(null);
93
- const controller = useMemo(() => new LocalStoreController(spec), [spec]);
94
- if (prevControllerRef.current !== controller) {
95
- (_a = prevControllerRef.current) == null ? void 0 : _a.dispose();
96
- prevControllerRef.current = controller;
97
- }
98
- const store2 = controller.getStore();
99
- useIsomorphicLayoutEffect(() => {
100
- controller.commit();
101
- const unsubscribe = store2.subscribe(() => forceUpdate());
102
- return () => {
103
- unsubscribe();
104
- controller.uncommit();
105
- };
106
- }, [controller, store2]);
107
- return useMemo(
108
- () => [
109
- store2.state,
110
- store2.actions,
111
- { dirty: store2.dirty, reset: store2.reset }
112
- ],
113
- [store2]
114
- );
115
- }
116
- class LocalStoreController {
117
- constructor(spec) {
118
- /** Whether the effect has committed (is active) */
119
- __publicField(this, "_committed", false);
120
- /** Whether this controller has been disposed */
121
- __publicField(this, "_disposed", false);
122
- /** Isolated container for this local store */
123
- __publicField(this, "_container");
124
- /** The store instance */
125
- __publicField(this, "_store");
126
- /**
127
- * Dispose the controller and its store.
128
- * Safe to call multiple times.
129
- */
130
- __publicField(this, "dispose", () => {
131
- var _a, _b;
132
- if (this._disposed) return;
133
- this._disposed = true;
134
- (_a = this._store) == null ? void 0 : _a.dispose();
135
- (_b = this._container) == null ? void 0 : _b.clear();
136
- this._store = void 0;
137
- this._container = void 0;
138
- });
139
- /**
140
- * Schedule disposal check via microtask.
141
- *
142
- * This deferred check is crucial for StrictMode:
143
- * - StrictMode runs cleanup then effect again synchronously
144
- * - The microtask runs AFTER the re-commit, so store survives
145
- * - On real unmount, no re-commit happens, so disposal proceeds
146
- */
147
- __publicField(this, "_disposeIfUnused", () => {
148
- if (this._committed || this._disposed) return;
149
- if (shouldScheduleDispose) {
150
- Promise.resolve().then(() => {
151
- if (!this._committed) {
152
- this.dispose();
153
- }
154
- });
155
- } else {
156
- this.dispose();
157
- }
158
- });
159
- /**
160
- * Get or create the store instance.
161
- * Throws if the store has dependencies (local stores must be isolated).
162
- */
163
- __publicField(this, "getStore", () => {
164
- var _a, _b;
165
- if (this._store) return this._store;
166
- if (!this._container) {
167
- this._container = container();
168
- }
169
- this._store = this._container.get(this.spec);
170
- const depsCount = ((_b = (_a = this._store) == null ? void 0 : _a.deps) == null ? void 0 : _b.length) ?? 0;
171
- if (depsCount > 0) {
172
- this.dispose();
173
- throw new LocalStoreDependencyError(
174
- this.spec.displayName,
175
- depsCount
176
- );
177
- }
178
- if (shouldScheduleDispose) {
179
- this._disposeIfUnused();
180
- }
181
- return this._store;
182
- });
183
- /**
184
- * Mark as committed (effect is active).
185
- * Called at the start of useLayoutEffect.
186
- */
187
- __publicField(this, "commit", () => {
188
- this._committed = true;
189
- });
190
- /**
191
- * Mark as uncommitted (effect cleaned up).
192
- * Schedules deferred disposal check.
193
- */
194
- __publicField(this, "uncommit", () => {
195
- this._committed = false;
196
- this._disposeIfUnused();
197
- });
198
- this.spec = spec;
199
- }
200
- }
201
39
  function useStoreWithContainer(selector, container2) {
40
+ const id = useId();
202
41
  const [, forceUpdate] = useReducer((x2) => x2 + 1, 0);
203
42
  const [refs] = useState(() => ({
204
- fresh: void 0,
205
- stableFns: /* @__PURE__ */ new Map(),
43
+ prevValues: /* @__PURE__ */ new Map(),
206
44
  trackedDeps: /* @__PURE__ */ new Map(),
207
45
  subscriptions: /* @__PURE__ */ new Map(),
208
- id: {},
46
+ id,
209
47
  onceRan: false
210
48
  }));
49
+ const [scopeController] = useState(
50
+ () => new ScopeController(container2)
51
+ );
52
+ const [selectorExecution] = useState(() => ({ active: false }));
53
+ const [scheduledEffects] = useState(() => []);
211
54
  refs.trackedDeps.clear();
212
55
  const shouldRunOnce = !refs.onceRan;
213
56
  const selectorContext = useMemo(() => {
214
57
  const ctx = {
215
58
  [STORION_TYPE]: "selector.context",
216
59
  id: refs.id,
60
+ container: container2,
217
61
  // Implementation handles both StoreSpec and Factory overloads
218
62
  get(specOrFactory) {
219
63
  if (!isSpec(specOrFactory)) {
@@ -226,10 +70,6 @@ function useStoreWithContainer(selector, container2) {
226
70
  actions: instance.actions
227
71
  });
228
72
  },
229
- // Create a fresh instance from a parameterized factory (bypasses cache)
230
- create(factory, ...args) {
231
- return container2.create(factory, ...args);
232
- },
233
73
  mixin(mixin, ...args) {
234
74
  return mixin(ctx, ...args);
235
75
  },
@@ -237,42 +77,65 @@ function useStoreWithContainer(selector, container2) {
237
77
  if (shouldRunOnce) {
238
78
  callback();
239
79
  }
80
+ },
81
+ scoped(spec) {
82
+ if (!selectorExecution.active) {
83
+ throw new ScopedOutsideSelectorError();
84
+ }
85
+ const instance = scopeController.get(spec);
86
+ return storeTuple(instance);
240
87
  }
241
88
  };
242
89
  return ctx;
243
- }, [container2, shouldRunOnce]);
244
- const result = withHooks(
245
- {
246
- onRead: (event) => {
247
- refs.trackedDeps.set(event.key, event);
90
+ }, [container2, shouldRunOnce, scopeController]);
91
+ let output;
92
+ selectorExecution.active = true;
93
+ try {
94
+ output = withHooks(
95
+ {
96
+ onRead: (event) => {
97
+ refs.trackedDeps.set(event.key, event);
98
+ },
99
+ // Collect effects to run in useEffect (not immediately)
100
+ // This enables effect() calls in selector to access component scope
101
+ scheduleEffect: (runEffect) => {
102
+ scheduledEffects.push(runEffect);
103
+ }
104
+ },
105
+ () => {
106
+ const result = selector(selectorContext);
107
+ if (result && typeof result.then === "function") {
108
+ throw new AsyncFunctionError(
109
+ "useStore selector",
110
+ "Do not return a Promise from the selector function."
111
+ );
112
+ }
113
+ const out = Array.isArray(result) ? [] : {};
114
+ for (const [key, value] of Object.entries(result)) {
115
+ const prev = refs.prevValues.get(key);
116
+ const [stableValue] = tryStabilize(prev, value, strictEqual);
117
+ out[key] = stableValue;
118
+ if (prev) {
119
+ prev.value = stableValue;
120
+ } else {
121
+ refs.prevValues.set(key, { value: stableValue });
122
+ }
123
+ }
124
+ return out;
248
125
  }
249
- },
250
- () => selector(selectorContext)
251
- );
252
- if (shouldRunOnce) {
253
- refs.onceRan = true;
254
- }
255
- if (result && typeof result.then === "function") {
256
- throw new AsyncFunctionError(
257
- "useStore selector",
258
- "Do not return a Promise from the selector function."
259
126
  );
127
+ } finally {
128
+ selectorExecution.active = false;
260
129
  }
261
- refs.fresh = result;
262
- const output = Array.isArray(result) ? [] : {};
263
- for (const [key, value] of Object.entries(result)) {
264
- if (typeof value === "function") {
265
- if (!refs.stableFns.has(key)) {
266
- refs.stableFns.set(key, (...args) => {
267
- var _a, _b;
268
- return (_b = (_a = refs.fresh) == null ? void 0 : _a[key]) == null ? void 0 : _b.call(_a, ...args);
269
- });
270
- }
271
- output[key] = refs.stableFns.get(key);
272
- } else {
273
- output[key] = value;
274
- }
130
+ if (shouldRunOnce) {
131
+ refs.onceRan = true;
275
132
  }
133
+ useIsomorphicLayoutEffect(() => {
134
+ scopeController.commit();
135
+ return () => {
136
+ scopeController.uncommit();
137
+ };
138
+ }, [scopeController]);
276
139
  const trackedKeysToken = [...refs.trackedDeps.keys()].sort().join("|");
277
140
  useEffect(() => {
278
141
  var _a;
@@ -297,15 +160,107 @@ function useStoreWithContainer(selector, container2) {
297
160
  refs.subscriptions.clear();
298
161
  };
299
162
  }, [trackedKeysToken]);
163
+ useEffect(() => {
164
+ const disposers = emitter();
165
+ try {
166
+ for (const runEffect of scheduledEffects) {
167
+ disposers.on(runEffect());
168
+ }
169
+ } catch (ex) {
170
+ disposers.emitAndClear();
171
+ throw ex;
172
+ }
173
+ return () => {
174
+ disposers.emitAndClear();
175
+ };
176
+ });
300
177
  return output;
301
178
  }
302
- function useStore(selectorOrSpec) {
303
- const isSpec2 = typeof selectorOrSpec === "object" && selectorOrSpec !== null && "options" in selectorOrSpec;
304
- if (isSpec2) {
305
- return useLocalStore(selectorOrSpec);
306
- }
179
+ function useStore(selector) {
307
180
  const container2 = useContainer();
308
- return useStoreWithContainer(selectorOrSpec, container2);
181
+ return useStoreWithContainer(selector, container2);
182
+ }
183
+ const isServer = typeof window === "undefined";
184
+ const useIsomorphicLayoutEffect = isServer ? useEffect : useLayoutEffect;
185
+ const shouldScheduleDispose = !isServer && typeof useLayoutEffect === "function";
186
+ class ScopeController {
187
+ constructor(container2) {
188
+ /** Whether the effect has committed (is active) */
189
+ __publicField(this, "_committed", false);
190
+ /** Whether this controller has been disposed */
191
+ __publicField(this, "_disposed", false);
192
+ /** Whether a disposal check microtask is pending */
193
+ __publicField(this, "_pendingDisposalCheck", false);
194
+ __publicField(this, "_stores", /* @__PURE__ */ new Map());
195
+ /**
196
+ * Dispose the controller and its store.
197
+ * Safe to call multiple times.
198
+ */
199
+ __publicField(this, "dispose", () => {
200
+ if (this._disposed) return;
201
+ this._disposed = true;
202
+ for (const store2 of this._stores.values()) {
203
+ store2.dispose();
204
+ }
205
+ this._stores.clear();
206
+ });
207
+ /**
208
+ * Schedule disposal check via microtask.
209
+ *
210
+ * This deferred check is crucial for StrictMode:
211
+ * - StrictMode runs cleanup then effect again synchronously
212
+ * - The microtask runs AFTER the re-commit, so store survives
213
+ * - On real unmount, no re-commit happens, so disposal proceeds
214
+ */
215
+ __publicField(this, "_disposeIfUnused", () => {
216
+ if (this._committed || this._disposed || this._pendingDisposalCheck) return;
217
+ if (shouldScheduleDispose) {
218
+ this._pendingDisposalCheck = true;
219
+ Promise.resolve().then(() => {
220
+ this._pendingDisposalCheck = false;
221
+ if (!this._committed) {
222
+ this.dispose();
223
+ }
224
+ });
225
+ } else {
226
+ this.dispose();
227
+ }
228
+ });
229
+ /**
230
+ * Get or create the store instance.
231
+ */
232
+ __publicField(this, "get", (spec) => {
233
+ if (this._disposed) {
234
+ throw new Error("ScopeController has been disposed");
235
+ }
236
+ let store2 = this._stores.get(spec);
237
+ if (!store2) {
238
+ store2 = this.container.create(spec);
239
+ this._stores.set(spec, store2);
240
+ }
241
+ if (shouldScheduleDispose && !this._committed) {
242
+ this._disposeIfUnused();
243
+ }
244
+ return store2;
245
+ });
246
+ /**
247
+ * Mark as committed (effect is active).
248
+ * Called at the start of useLayoutEffect.
249
+ */
250
+ __publicField(this, "commit", () => {
251
+ this._committed = true;
252
+ this._pendingDisposalCheck = false;
253
+ });
254
+ /**
255
+ * Mark as uncommitted (effect cleaned up).
256
+ * Schedules deferred disposal check.
257
+ */
258
+ __publicField(this, "uncommit", () => {
259
+ this._committed = false;
260
+ this._disposeIfUnused();
261
+ });
262
+ this.container = container2;
263
+ }
309
264
  }
310
265
  function createWithStore(useContextHook) {
311
266
  return function boundWithStore2(hook, renderOrOptions, maybeOptions) {
@@ -380,7 +335,9 @@ function createWithStore(useContextHook) {
380
335
  return hoc;
381
336
  };
382
337
  }
383
- const boundWithStore = createWithStore(useStore);
338
+ const boundWithStore = createWithStore(
339
+ useStore
340
+ );
384
341
  function withStore(hook, renderOrOptions, maybeOptions) {
385
342
  return boundWithStore(hook, renderOrOptions, maybeOptions);
386
343
  }
@@ -403,52 +360,96 @@ function create(storeOptions, containerOptions) {
403
360
  const withStore2 = createWithStore(useCreatedStoreContext);
404
361
  return [instance, useCreatedStore, withStore2];
405
362
  }
363
+ function stable(Component, customEquality) {
364
+ const isForwardRefComponent = Component.$$typeof === Symbol.for("react.forward_ref");
365
+ const expectsRef = isForwardRefComponent || typeof Component === "function" && Component.length >= 2;
366
+ const StableComponent = forwardRef((props, ref) => {
367
+ const inputProps = props;
368
+ const [refs] = useState(() => ({
369
+ prevValues: /* @__PURE__ */ new Map(),
370
+ equalityFns: /* @__PURE__ */ new Map()
371
+ }));
372
+ const stableProps = {};
373
+ for (const key of Object.keys(inputProps)) {
374
+ const keyStr = key;
375
+ const value = inputProps[key];
376
+ if (!refs.equalityFns.has(keyStr)) {
377
+ const equalityConfig = customEquality == null ? void 0 : customEquality[key];
378
+ refs.equalityFns.set(keyStr, resolveEquality(equalityConfig));
379
+ }
380
+ const equalityFn = refs.equalityFns.get(keyStr);
381
+ const prev = refs.prevValues.get(keyStr);
382
+ const [stableValue] = tryStabilize(prev, value, equalityFn);
383
+ stableProps[key] = stableValue;
384
+ if (prev) {
385
+ prev.value = stableValue;
386
+ } else {
387
+ refs.prevValues.set(keyStr, { value: stableValue });
388
+ }
389
+ }
390
+ if (isForwardRefComponent) {
391
+ return /* @__PURE__ */ jsx(Component, { ...stableProps, ref });
392
+ }
393
+ if (expectsRef) {
394
+ return Component(
395
+ stableProps,
396
+ ref
397
+ );
398
+ }
399
+ const Comp = Component;
400
+ return /* @__PURE__ */ jsx(Comp, { ...stableProps });
401
+ });
402
+ const componentName = Component.displayName || Component.name || "Component";
403
+ StableComponent.displayName = `Stable(${componentName})`;
404
+ return StableComponent;
405
+ }
406
406
  export {
407
407
  AsyncFunctionError,
408
408
  E as EffectRefreshError,
409
409
  H as HooksContextError,
410
410
  I as InvalidActionError,
411
- c as LifetimeMismatchError,
412
- LocalStoreDependencyError,
411
+ L as LifetimeMismatchError,
412
+ z as LocalStoreDependencyError,
413
413
  ProviderMissingError,
414
414
  STORION_TYPE,
415
- a2 as SetupPhaseError,
416
- d2 as StoreDisposedError,
415
+ S as SetupPhaseError,
416
+ y as StoreDisposedError,
417
417
  StoreProvider,
418
- S as StorionError,
419
- n as applyExcept,
420
- m as applyFor,
421
- b2 as batch,
418
+ x as StorionError,
419
+ append,
420
+ applyExcept,
421
+ applyFor,
422
+ n as batch,
423
+ clamp,
422
424
  container,
423
425
  create,
424
- l as createResolver,
425
- createWithStore,
426
- u as deepEqual,
427
- e2 as effect,
428
- q as equality,
429
- o as forStores,
430
- g as getKind,
431
- a as is,
432
- h as isAction,
433
- d as isContainer,
434
- f as isFocus,
435
- k as isSelectorContext,
436
- isSpec,
437
- e as isStore,
438
- j as isStoreContext,
439
- b as isStorion,
440
- z as isWrappedFn,
426
+ decrement,
427
+ v as deepEqual,
428
+ divide,
429
+ e as effect,
430
+ o as equality,
431
+ forStores,
432
+ increment,
433
+ m as is,
434
+ list,
435
+ map,
436
+ merge,
441
437
  m2 as meta,
438
+ multiply,
442
439
  p as pick,
443
- t as shallowEqual,
440
+ B as pool,
441
+ prepend,
442
+ reset,
443
+ resolver,
444
+ q as shallowEqual,
445
+ stable,
444
446
  store,
445
- v as strictEqual,
446
- w as trigger,
447
- u2 as untrack,
448
- y as unwrapFn,
447
+ strictEqual,
448
+ toggle,
449
+ trigger,
450
+ u as untrack,
449
451
  useContainer,
450
452
  useStore,
451
- A as withMeta,
452
- withStore,
453
- x as wrapFn
453
+ withMeta,
454
+ withStore
454
455
  };
@@ -0,0 +1,27 @@
1
+ import { ComponentType, ForwardRefExoticComponent, RefAttributes, ForwardRefRenderFunction } from 'react';
2
+ import { Equality } from '../types';
3
+
4
+ /**
5
+ * Configuration for per-prop equality checking.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * stable(Component, {
10
+ * data: "shallow", // Use shallow equality
11
+ * items: "deep", // Use deep equality
12
+ * config: (a, b) => a.id === b.id, // Custom equality
13
+ * })
14
+ * ```
15
+ */
16
+ export type PropEqualityConfig<TProps extends object> = {
17
+ [K in keyof TProps]?: Equality<TProps[K]>;
18
+ };
19
+ /**
20
+ * Wrap a component with automatic prop stabilization.
21
+ *
22
+ * @param Component - The component to wrap (function component, forwardRef, or render function)
23
+ * @param customEquality - Optional per-prop equality configuration
24
+ * @returns A forwardRef component with stabilized props
25
+ */
26
+ export declare function stable<TProps extends object, TRef = unknown>(Component: ComponentType<TProps> | ForwardRefRenderFunction<TRef, TProps>, customEquality?: PropEqualityConfig<NoInfer<TProps>>): ForwardRefExoticComponent<TProps & RefAttributes<TRef>>;
27
+ //# sourceMappingURL=stable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stable.d.ts","sourceRoot":"","sources":["../../src/react/stable.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6EG;AAEH,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,yBAAyB,EAC9B,KAAK,aAAa,EAClB,KAAK,wBAAwB,EAE9B,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAOzC;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,kBAAkB,CAAC,MAAM,SAAS,MAAM,IAAI;KACrD,CAAC,IAAI,MAAM,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;CAC1C,CAAC;AAiBF;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,MAAM,SAAS,MAAM,EAAE,IAAI,GAAG,OAAO,EAC1D,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,wBAAwB,CAAC,IAAI,EAAE,MAAM,CAAC,EACzE,cAAc,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GACnD,yBAAyB,CAAC,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CA+EzD"}
@@ -1,5 +1,4 @@
1
- import { StateBase, ActionsBase, StoreSpec, StoreContainer, Selector, StableResult } from '../types';
2
- import { LocalStoreResult } from './useLocalStore';
1
+ import { StoreContainer, Selector, StableResult } from '../types';
3
2
 
4
3
  /**
5
4
  * Core hook implementation that accepts container as parameter.
@@ -9,24 +8,50 @@ export declare function useStoreWithContainer<T extends object>(selector: Select
9
8
  /**
10
9
  * React hook to consume stores with automatic optimization.
11
10
  *
12
- * @overload With selector - Uses the container from React context (StoreProvider).
13
- * Access multiple stores, compute derived values, with fine-grained re-renders.
11
+ * Features:
12
+ * - Multi-store access via `get()` for global stores
13
+ * - Component-local stores via `scoped()` (auto-disposed on unmount)
14
+ * - Component-scoped effects via `effect()` with access to external values
15
+ * - Auto-stable functions (never cause re-renders)
16
+ * - Fine-grained updates (only re-renders when selected values change)
14
17
  *
15
- * @overload With spec - Creates a component-local store instance (like useLocalStore).
16
- * The store is isolated, disposed on unmount, and cannot have dependencies.
17
- *
18
- * @example
18
+ * @example Basic usage
19
19
  * ```tsx
20
- * // With selector - access global stores
21
20
  * const { count, increment } = useStore(({ get }) => {
22
- * const [state, actions] = get(counterSpec);
21
+ * const [state, actions] = get(counterStore);
23
22
  * return { count: state.count, increment: actions.increment };
24
23
  * });
24
+ * ```
25
+ *
26
+ * @example Component-local stores
27
+ * ```tsx
28
+ * const { form } = useStore(({ scoped }) => {
29
+ * const [formState, formActions] = scoped(formStore);
30
+ * return { form: { ...formState, ...formActions } };
31
+ * });
32
+ * ```
33
+ *
34
+ * @example Effects with access to external values (refs, props, hooks)
35
+ * ```tsx
36
+ * function SearchPage() {
37
+ * const inputRef = useRef<HTMLInputElement>(null);
38
+ * const location = useLocation();
39
+ *
40
+ * const { query } = useStore(({ get }) => {
41
+ * const [state] = get(searchStore);
42
+ *
43
+ * // Effect runs in useEffect - has access to refs, props, and hooks
44
+ * // Auto-tracks store state, re-runs when tracked values change
45
+ * effect(() => {
46
+ * if (location.pathname === '/search' && state.isReady) {
47
+ * inputRef.current?.focus();
48
+ * }
49
+ * });
25
50
  *
26
- * // With spec - local store (shorthand for useLocalStore)
27
- * const [state, actions, { dirty, reset }] = useStore(formSpec);
51
+ * return { query: state.query };
52
+ * });
53
+ * }
28
54
  * ```
29
55
  */
30
56
  export declare function useStore<T extends object>(selector: Selector<T>): StableResult<T>;
31
- export declare function useStore<TState extends StateBase, TActions extends ActionsBase>(spec: StoreSpec<TState, TActions>): LocalStoreResult<TState, TActions>;
32
57
  //# sourceMappingURL=useStore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useStore.d.ts","sourceRoot":"","sources":["../../src/react/useStore.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,cAAc,EAEnB,KAAK,QAAQ,EACb,KAAK,YAAY,EAClB,MAAM,UAAU,CAAC;AAIlB,OAAO,EAAiB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAuBvE;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,EACpD,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,SAAS,EAAE,cAAc,GACxB,YAAY,CAAC,CAAC,CAAC,CAmJjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EACvC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GACpB,YAAY,CAAC,CAAC,CAAC,CAAC;AACnB,wBAAgB,QAAQ,CACtB,MAAM,SAAS,SAAS,EACxB,QAAQ,SAAS,WAAW,EAC5B,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC"}
1
+ {"version":3,"file":"useStore.d.ts","sourceRoot":"","sources":["../../src/react/useStore.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,OAAO,EAKL,KAAK,cAAc,EAEnB,KAAK,QAAQ,EACb,KAAK,YAAY,EAElB,MAAM,UAAU,CAAC;AA6BlB;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,EACpD,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,SAAS,EAAE,cAAc,GACxB,YAAY,CAAC,CAAC,CAAC,CAyNjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EACvC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GACpB,YAAY,CAAC,CAAC,CAAC,CAKjB"}