@plitzi/nexus 0.32.4 → 0.32.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @plitzi/nexus
2
2
 
3
+ ## 0.32.6
4
+
5
+ ### Patch Changes
6
+
7
+ - v0.32.6
8
+
9
+ ## 0.32.5
10
+
11
+ ### Patch Changes
12
+
13
+ - v0.32.4
14
+
3
15
  ## 0.32.4
4
16
 
5
17
  ### Patch Changes
@@ -34,6 +46,13 @@
34
46
  `useStoreHistory`. `vue` is an optional peer dependency (`^3.4`).
35
47
  - Added runnable examples (`examples/`) and integration docs (`docs/integrations/`) for React, Next.js, Astro 6 (LTS),
36
48
  Astro 7, Vue and Svelte.
49
+ - **`unmount` write option.** Every write now takes an optional third argument `SetStateOptions = { canPropagate?;
50
+ unmount? }`. `set(path, undefined, { unmount: true })` **deletes** the key at `path` (splicing array indices) instead
51
+ of leaving a dead `undefined`, so registries keyed by dynamic id leave no stale entries. Nested deletes keep siblings
52
+ with structural sharing, interceptors can veto them, and writing a value back recreates the path.
53
+ - **Read-only paths.** `createStore(init, { readOnly: [...] })` freezes paths: a write to a read-only path — or to an
54
+ ancestor/descendant of one — throws in development and no-ops in production. Prefix-safe matching, enforced per scope
55
+ on that scope's own writes, zero cost when unused.
37
56
 
38
57
  ### Breaking Changes
39
58
 
package/README.md CHANGED
@@ -52,9 +52,12 @@ store.get('user.name'); // 'Carlos' (one path, no full merge)
52
52
  store.get(); // entire state
53
53
  store.set('user.name', 'Ada'); // typed dot-path write
54
54
  store.set('count', n => n + 1); // updater form
55
+ store.set('user.name', undefined, { unmount: true }); // DELETE the key (see "Removing a path")
55
56
  const off = store.watch('user.name', name => render(name)); // fires only for this path
56
57
  ```
57
58
 
59
+ Every write takes an optional third argument, `SetStateOptions = { canPropagate?; unmount? }` — see [Removing a path](#removing-a-path-unmount). To freeze paths against writes, see [Read-only paths](#read-only-paths).
60
+
58
61
  ## What each piece is for
59
62
 
60
63
  ### Core — the 90%
@@ -129,6 +132,9 @@ const store = createStore<MyState>({ count: 0 }, {
129
132
 
130
133
  // With an id, so descendants can target it by id (see "Named stores" below)
131
134
  const store = createStore<MyState>({ count: 0 }, { id: 'root' });
135
+
136
+ // Freeze paths against writes (see "Read-only paths" below)
137
+ const store = createStore<MyState>({ count: 0 }, { readOnly: ['config.theme'] });
132
138
  ```
133
139
 
134
140
  `StoreApi` exposes `getState`, `getPath`, `setState`, `subscribe`, `subscribePath`, `subscribeChange`,
@@ -162,12 +168,20 @@ Wraps children with a store context. Creates a new store by default; pass `store
162
168
  <StoreProvider id="root" value={builderState}>
163
169
  <App />
164
170
  </StoreProvider>
171
+
172
+ // Label it for devtools — cosmetic only, unlike `id` it carries no addressing and never collides
173
+ <StoreProvider name="Form:hero" inherit="live" value={{ record }}>
174
+ <Child />
175
+ </StoreProvider>
165
176
  ```
166
177
 
167
178
  > **`inherit` modes:** `'snapshot'` copies parent keys **once** at init — isolated thereafter: writes stay
168
179
  > local and parent updates do **not** propagate (use for draft/diverge editors). `'live'` keeps a **live
169
180
  > link** — inherited keys stay in sync and writes delegate to the owning scope.
170
181
 
182
+ > **`name`** is a human label of where the store comes from, shown by the devtools store picker (see
183
+ > [DevTools integration](#devtools-integration)). Purely cosmetic — stripped from production.
184
+
171
185
  ## Scoped stores (live scope chain)
172
186
 
173
187
  A store can have a `parent`. Reads resolve through the chain and writes target the owning scope. This
@@ -193,6 +207,17 @@ const item = createStore<S>({ record }, { parent: root });
193
207
  const item = createStore<S>({ runtime: { sources: { record } } }, { parent: root });
194
208
  item.getState().runtime.sources; // { variables: { a: 1 }, record } ← merged, not shadowed
195
209
  ```
210
+ - **Own layer vs merged view (`getOwnState`)** — `getState()` returns the full **merged** view (own +
211
+ inherited). `getOwnState()` returns **only this scope's own layer** — the keys it seeds/writes locally,
212
+ without the parent fall-through. For a root store the two are identical; for a scoped store `getOwnState()`
213
+ isolates the scope's contribution (what a devtools inspector shows to tell nested scopes apart). Both are
214
+ cached and reference-stable between changes.
215
+ ```ts
216
+ const root = createStore<S>({ user, theme: 'dark' });
217
+ const item = createStore<S>({ record }, { parent: root });
218
+ item.getState(); // { user, theme: 'dark', record } ← merged
219
+ item.getOwnState(); // { record } ← own layer only
220
+ ```
196
221
  - **Shadowing** — a scope's own **leaf** value hides the parent's at that path (reads and subscriptions);
197
222
  sibling leaves under a shared object branch are preserved (see deep-merge above).
198
223
  ```ts
@@ -371,6 +396,44 @@ setFlat(`${id}.attributes`, attrs); // sets schema.flat.${id}.attributes
371
396
  setFlat(undefined, flatObj); // replaces schema.flat
372
397
  ```
373
398
 
399
+ ## Removing a path (`unmount`)
400
+
401
+ Every write takes an optional third argument: `SetStateOptions = { canPropagate?; unmount? }`. `canPropagate: false` commits silently (no subscriber wakes); `unmount: true` **deletes** the key at `path` instead of writing `undefined`, so the path leaves no dead entry — an object key or array slot that would otherwise linger as `undefined`.
402
+
403
+ Reach for it whenever you register/unregister things under a dynamic key (a source registry, a per-id cache) and don't want `Object.keys` / `Object.values` to see stale `undefined` holes.
404
+
405
+ ```ts
406
+ store.set('sources.abc', { id: 'abc', meta }); // register
407
+ store.set('sources.abc', undefined, { unmount: true }); // unregister — key is gone
408
+ Object.hasOwn(store.get('sources'), 'abc'); // false
409
+ ```
410
+
411
+ - **Nested paths** delete only the leaf and keep siblings, with structural sharing (`a.b` removed → `a` is a new object; untouched subtrees are shared by reference).
412
+ - A numeric leaf on an **array** is **spliced** out (later indices shift down), never left as a hole.
413
+ - Removing an **absent** key is a **no-op** — no change is emitted, no subscriber wakes.
414
+ - Writing a value back later **recreates** the path, rebuilding any container the delete had emptied. In a scope chain the re-write delegates to the owning scope just like the original write.
415
+ - `beforeChange` interceptors still run (with `value: undefined`), so a validation or [read-only](#read-only-paths) guard can veto the removal.
416
+ - `unmount` is **ignored** for a whole-state write (`set(undefined, next)`).
417
+
418
+ ## Read-only paths
419
+
420
+ Pass `readOnly` to `createStore` to freeze one or more paths. A write to a read-only path — or to an **ancestor** or **descendant** of one — **throws in development** (surfacing the bug at its source) and is **silently dropped in production** (so it never crashes a render).
421
+
422
+ ```ts
423
+ const store = createStore<State>(initial, { readOnly: ['config.theme', 'license'] });
424
+
425
+ store.set('config.theme', 'dark'); // dev: throws · prod: no-op
426
+ store.set('config', { theme: 'x' }); // ancestor — also blocked (would replace the frozen subtree)
427
+ store.set('config.theme.shade', 'x'); // descendant — also blocked
428
+ store.set('config.other', 'ok'); // sibling — allowed
429
+ ```
430
+
431
+ - **Prefix-safe matching**: `readOnly: ['config']` blocks `config` and `config.*` but never `configuration`; `['a.b']` never blocks `a.bc`.
432
+ - The **updater form** throws *before* the updater runs; an `unmount` of a protected path is blocked too.
433
+ - A **whole-state replace** (`set(undefined, next)`) is checked per read-only path by **reference** — blocked if the value at that path changes identity (rebuilding an equal-but-new object still counts as a change).
434
+ - Enforcement is **per scope, on that scope's own writes**. A child that delegates a write up is rejected by the parent's `readOnly`; a key the child owns locally is not governed by an ancestor's `readOnly`.
435
+ - The check is **skipped entirely** when `readOnly` is omitted — zero cost by default.
436
+
374
437
  ## `createStoreHook`
375
438
 
376
439
  Factory that binds `TState` at the module level, giving fully-typed hooks without repeating the generic at every call site.
@@ -667,6 +730,40 @@ function HistoryPanel() {
667
730
 
668
731
  `<StoreProvider history>` (or `history="schema"` to scope it to a subtree) starts recording from mount.
669
732
 
733
+ ## DevTools integration
734
+
735
+ A **dev-only** global registry lets a devtools panel enumerate and inspect every store — including scoped stores mounted **below** the panel, where React context can't reach them. Everything here is stripped from production builds (guarded by `isDev`), so it carries no runtime cost when shipped.
736
+
737
+ Every `StoreProvider` registers its store on mount and removes it on unmount. Read the registry with `useSyncExternalStore`:
738
+
739
+ ```ts
740
+ import { subscribeDevStores, getDevStoresSnapshot } from '@plitzi/nexus';
741
+ import type { DevStoreEntry } from '@plitzi/nexus';
742
+
743
+ // snapshot is a stable array (identity only changes when a store is added/removed)
744
+ const useDevStores = (): ReadonlyArray<DevStoreEntry> =>
745
+ useSyncExternalStore(subscribeDevStores, getDevStoresSnapshot, getDevStoresSnapshot);
746
+ ```
747
+
748
+ Each `DevStoreEntry` is `{ uid, store, scopeId?, name? }`:
749
+
750
+ - **`uid`** — a stable id for selection in a picker.
751
+ - **`store`** — the `StoreApi`; inspect it with `getState()` (merged view) or `getOwnState()` (this scope's own layer — see [Scoped stores](#scoped-stores-live-scope-chain)).
752
+ - **`name`** — the `<StoreProvider name>` label, e.g. `"Form:hero"` — a human hint of where the store comes from.
753
+ - **`scopeId`** — an app-defined grouping tag (see `DevStoreScopeContext` below), e.g. the app/SDK instance the store belongs to, so a panel can group stores by origin.
754
+
755
+ **`DevStoreScopeContext`** tags every store registered beneath it with a `scopeId`. Wrap a subtree to attribute its stores to one origin (a panel then groups by it):
756
+
757
+ ```tsx
758
+ import { DevStoreScopeContext } from '@plitzi/nexus/react';
759
+
760
+ <DevStoreScopeContext value={instanceId}>
761
+ <App /> {/* every StoreProvider below registers with scopeId = instanceId */}
762
+ </DevStoreScopeContext>
763
+ ```
764
+
765
+ Combine the three — `name` (where it comes from), `getOwnState()` (what it uniquely holds) and `scopeId` (which app owns it) — to build a store picker that tells otherwise-identical nested scopes apart. This is exactly what the Plitzi `sdk-dev-tools` Store tab does.
766
+
670
767
  ## CSP & `new Function`
671
768
 
672
769
  By default `@plitzi/nexus` uses a compiled codegen path (`new Function`) for structural-sharing writes, which is orders of magnitude faster than the recursive fallback for deep paths. When a strict [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) blocks `new Function`, the store auto-detects the error at first use and falls back to a recursive writer with identical behaviour — you **don't need to do anything** for CSP environments to work.
@@ -925,8 +1022,9 @@ All types live in `src/types/StoreTypes.ts`:
925
1022
  | `PathSetter<T, P>` | Setter function for a single path |
926
1023
  | `PathSetters<T, Paths>` | Tuple of setters for an array of paths |
927
1024
  | `MultiPathReturn<T, Paths>` | `[values, ...setters]` tuple |
928
- | `StoreApi<T>` | `{ getState, getPath, setState, subscribe, subscribePath, subscribeChange, destroy? }` |
1025
+ | `StoreApi<T>` | `{ getState, getOwnState, getPath, setState, subscribe, subscribePath, subscribeChange, destroy? }` |
929
1026
  | `StoreChange<T>` | `{ path, prev, next }` — payload of `subscribeChange` / middleware `onChange` |
1027
+ | `DevStoreEntry` | `{ uid, store, scopeId?, name? }` — a store registered in the dev-only registry (see [DevTools integration](#devtools-integration)) |
930
1028
  | `StoreMiddleware<T>` | `(api) => { beforeChange?, onChange? } \| void` — a middleware factory |
931
1029
  | `WriteContext<T>` | `{ path, value, prev }` — payload of a `beforeChange` interceptor |
932
1030
  | `WriteInterceptor<T>` | `(ctx: WriteContext<T>) => unknown` — return a value, `CANCEL`, or `undefined` |
@@ -5,6 +5,7 @@ export type CreateStoreOptions<TState extends object> = {
5
5
  parent?: StoreApi<TState>;
6
6
  exclusive?: ReadonlyArray<string>;
7
7
  middlewares?: StoreMiddleware<TState>[];
8
+ readOnly?: ReadonlyArray<string>;
8
9
  /** When true, hydrate handlers are collected but NOT run during creation.
9
10
  * Call `store.hydrate()` manually (StoreProvider does this in a useEffect).
10
11
  * Defaults to false (backward-compatible: standalone usage hydrates synchronously). */
@@ -31,11 +31,15 @@ function l(l, u) {
31
31
  mutateOwnKey: (e, t) => {
32
32
  d[e] = t, O(), f = void 0;
33
33
  },
34
+ deleteOwnKey: (e) => {
35
+ delete d[e], O(), f = void 0;
36
+ },
34
37
  parent: x,
35
38
  listeners: p,
36
39
  pathListeners: h,
37
40
  changeListeners: m,
38
41
  interceptors: v,
42
+ readOnly: u?.readOnly ?? [],
39
43
  reportError: C,
40
44
  invalidateDescendants: _,
41
45
  onDelegateToParent: t && x ? (e) => x.scopeClaims?.claimDelegatedWrite(e, S) : void 0
@@ -56,13 +60,14 @@ function l(l, u) {
56
60
  id: u?.id,
57
61
  scopePath: u?.scopePath,
58
62
  getState: E,
63
+ getOwnState: T,
59
64
  getPath: D,
60
65
  setState: F,
61
66
  get: ((e) => e === void 0 ? E() : D(e)),
62
67
  set: F,
63
68
  watch: ((e, t) => typeof e == "function" ? H(e) : U(e, t)),
64
69
  withBase: (e) => {
65
- let t = (t, n) => F(t === void 0 ? e : `${e}.${t}`, n);
70
+ let t = (t, n, r) => F(t === void 0 ? e : `${e}.${t}`, n, r);
66
71
  return {
67
72
  getState: (t) => {
68
73
  let n = D(e);
@@ -6,11 +6,13 @@ export type SetStateDeps<TState extends object> = {
6
6
  getOwnSnapshot: () => TState;
7
7
  setOwnState: (next: TState) => void;
8
8
  mutateOwnKey: (key: string, value: unknown) => void;
9
+ deleteOwnKey: (key: string) => void;
9
10
  parent: StoreApi<TState> | undefined;
10
11
  listeners: Subscribers<Listener>;
11
12
  changeListeners: Subscribers<ChangeListener<TState>>;
12
13
  pathListeners: PathTrie;
13
14
  interceptors: WriteInterceptor<TState>[];
15
+ readOnly: ReadonlyArray<string>;
14
16
  reportError: StoreErrorReporter<TState>;
15
17
  invalidateDescendants: () => void;
16
18
  onDelegateToParent?: (path: string) => void;
@@ -1,18 +1,19 @@
1
1
  import e from "../../helpers/parsePath.mjs";
2
2
  import t from "../../helpers/getByPath.mjs";
3
- import { UNCHANGED as n, writeByPath as r } from "./writeByPath.mjs";
4
- import i from "../../helpers/setByPath.mjs";
5
- import { CANCEL as a } from "../../types/StoreTypes.mjs";
3
+ import { isDev as n } from "../../env.mjs";
4
+ import { UNCHANGED as r, deleteByPath as i, writeByPath as a } from "./writeByPath.mjs";
5
+ import o from "../../helpers/setByPath.mjs";
6
+ import { CANCEL as s } from "../../types/StoreTypes.mjs";
6
7
  //#region src/createStore/helpers/createSetState.ts
7
- var o = Symbol("no-error"), s = (e, t, n) => {
8
- let r = 0, i = o;
8
+ var c = Symbol("no-error"), l = (e, t, n) => {
9
+ let r = 0, i = c;
9
10
  for (; r < n;) try {
10
11
  for (; r < n; r++) e[r](t);
11
12
  } catch (e) {
12
- i === o && (i = e), r++;
13
+ i === c && (i = e), r++;
13
14
  }
14
15
  return i;
15
- }, c = "__proto__", l = (e, t) => {
16
+ }, u = "__proto__", d = (e, t) => {
16
17
  let n = e;
17
18
  for (let e = 0, r = t.length; e < r; e++) {
18
19
  if (typeof n != "object" || !n || !Object.hasOwn(n, t[e])) return !1;
@@ -20,137 +21,175 @@ var o = Symbol("no-error"), s = (e, t, n) => {
20
21
  }
21
22
  return !0;
22
23
  };
23
- function u(u) {
24
- let { getOwnState: d, getOwnSnapshot: f, setOwnState: p, mutateOwnKey: m, parent: h, listeners: g, pathListeners: _, changeListeners: v, interceptors: y, reportError: b, invalidateDescendants: x, onDelegateToParent: S } = u, C = (e, t, n) => {
24
+ function f(f) {
25
+ let { getOwnState: p, getOwnSnapshot: m, setOwnState: h, mutateOwnKey: g, deleteOwnKey: _, parent: v, listeners: y, pathListeners: b, changeListeners: x, interceptors: S, readOnly: C, reportError: w, invalidateDescendants: T, onDelegateToParent: E } = f, D = (e) => {
26
+ for (let t = 0, n = C.length; t < n; t++) {
27
+ let n = C[t];
28
+ if (e === n || e.startsWith(`${n}.`) || n.startsWith(`${e}.`)) return !0;
29
+ }
30
+ return !1;
31
+ }, O = (e) => {
32
+ if (C.length === 0 || !D(e)) return !1;
33
+ if (n) throw Error(`@plitzi/nexus: refused to write to read-only path "${e}"`);
34
+ return !0;
35
+ }, k = (e, t, n) => {
25
36
  let r = t;
26
- for (let t = 0, i = y.length; t < i; t++) {
37
+ for (let t = 0, i = S.length; t < i; t++) {
27
38
  let i;
28
39
  try {
29
- i = y[t]({
40
+ i = S[t]({
30
41
  path: e,
31
42
  value: r,
32
43
  prev: n
33
44
  });
34
45
  } catch (t) {
35
- return b(t, "beforeChange", e), a;
46
+ return w(t, "beforeChange", e), s;
36
47
  }
37
- if (i === a) return a;
48
+ if (i === s) return s;
38
49
  i !== void 0 && (r = i);
39
50
  }
40
51
  return r;
41
- }, w = 0, T = /* @__PURE__ */ new Set(), E = (e, t) => {
52
+ }, A = 0, j = /* @__PURE__ */ new Set(), M = (e, t) => {
42
53
  let { items: n } = e;
43
- if (w > 0) {
44
- for (let e = 0, t = n.length; e < t; e++) T.add(n[e]);
54
+ if (A > 0) {
55
+ for (let e = 0, t = n.length; e < t; e++) j.add(n[e]);
45
56
  return;
46
57
  }
47
58
  if (n.length !== 0) {
48
59
  e.begin();
49
60
  try {
50
- let e = s(n, t, n.length);
51
- e !== o && b(e, "notify", t);
61
+ let e = l(n, t, n.length);
62
+ e !== c && w(e, "notify", t);
52
63
  } finally {
53
64
  e.end();
54
65
  }
55
66
  }
56
- }, D = (e) => {
57
- w++;
67
+ }, N = (e) => {
68
+ A++;
58
69
  try {
59
70
  return e();
60
71
  } finally {
61
- if (w--, w === 0 && T.size > 0) {
62
- let e = [...T];
63
- T.clear();
64
- let t = s(e, void 0, e.length);
65
- t !== o && b(t, "notify", void 0);
72
+ if (A--, A === 0 && j.size > 0) {
73
+ let e = [...j];
74
+ j.clear();
75
+ let t = l(e, void 0, e.length);
76
+ t !== c && w(t, "notify", void 0);
66
77
  }
67
78
  }
68
- }, O = (e, n, r) => {
79
+ }, P = (e, n, r) => {
69
80
  let i = {
70
81
  path: e,
71
82
  prev: n,
72
83
  next: r,
73
84
  prevValue: e === void 0 ? n : t(n, e),
74
85
  nextValue: e === void 0 ? r : t(r, e)
75
- }, { items: a } = v;
76
- v.begin();
86
+ }, { items: a } = x;
87
+ x.begin();
77
88
  try {
78
- let t = s(a, i, a.length);
79
- t !== o && b(t, "onChange", e);
89
+ let t = l(a, i, a.length);
90
+ t !== c && w(t, "onChange", e);
80
91
  } finally {
81
- v.end();
92
+ x.end();
82
93
  }
83
- }, k = (e, t) => {
84
- _.walkAncestors(t, (t) => E(t, e));
85
- }, A = (n, r, i, a) => {
86
- let o = _.getDescendants(n);
94
+ }, F = (e, t) => {
95
+ b.walkAncestors(t, (t) => M(t, e));
96
+ }, I = (n, r, i, a) => {
97
+ let o = b.getDescendants(n);
87
98
  if (o) for (let s of o) {
88
- let o = _.direct.get(s);
99
+ let o = b.direct.get(s);
89
100
  if (!o) continue;
90
101
  let c = e(s).slice(a);
91
- t(r, c) !== t(i, c) && E(o, n);
102
+ t(r, c) !== t(i, c) && M(o, n);
92
103
  }
93
- }, j = (e, n, r, o) => {
94
- let s = e ? t(r, e) : void 0, c = e ? typeof n == "function" ? n(s) : n : void 0;
95
- if (e && y.length > 0) {
96
- let t = C(e, c, s);
97
- if (t === a) return;
98
- c = t;
104
+ }, L = (e, r, i, a) => {
105
+ let c = e ? t(i, e) : void 0, l = e ? typeof r == "function" ? r(c) : r : void 0;
106
+ if (e && S.length > 0) {
107
+ let t = k(e, l, c);
108
+ if (t === s) return;
109
+ l = t;
99
110
  }
100
- if (e && s === c) return;
101
- let l = e ? i(r, e, c) : typeof n == "function" ? n(r) : {
102
- ...r,
103
- ...n
111
+ if (e && c === l) return;
112
+ let u = e ? o(i, e, l) : typeof r == "function" ? r(i) : {
113
+ ...i,
114
+ ...r
104
115
  };
105
- if (!e && y.length > 0) {
106
- let e = C(void 0, l, r);
107
- if (e === a) return;
108
- l = e;
116
+ if (!e && S.length > 0) {
117
+ let e = k(void 0, u, i);
118
+ if (e === s) return;
119
+ u = e;
120
+ }
121
+ if (u !== i) {
122
+ if (C.length > 0) for (let e = 0, r = C.length; e < r; e++) {
123
+ let r = C[e];
124
+ if (t(i, r) !== t(u, r)) {
125
+ if (n) throw Error(`@plitzi/nexus: refused to write to read-only path "${C[e]}"`);
126
+ return;
127
+ }
128
+ }
129
+ h(u), x.length > 0 && P(e, i, u), T(), a && (M(y, e), b.size > 0 && b.direct.forEach((n, r) => {
130
+ t(i, r) !== t(u, r) && M(n, e);
131
+ }));
109
132
  }
110
- l !== r && (p(l), v.length > 0 && O(e, r, l), x(), o && (E(g, e), _.size > 0 && _.direct.forEach((n, i) => {
111
- t(r, i) !== t(l, i) && E(n, e);
112
- })));
113
133
  };
114
134
  return {
115
- setState: (i, o, s = !0) => {
116
- let u = d();
117
- if (typeof i == "string" && i.indexOf(c) !== -1 && e(i).indexOf(c) !== -1) throw Error(`@plitzi/nexus: refused to write to unsafe path "${i}" (\`__proto__\` segment)`);
118
- if (h && typeof i == "string" && !(i.indexOf(".") === -1 ? Object.hasOwn(u, i) : l(u, e(i)))) {
119
- S?.(i), h.setState(i, o, s);
135
+ setState: (n, o, c = {}) => {
136
+ let { canPropagate: l = !0, unmount: f = !1 } = c, C = p();
137
+ if (typeof n == "string" && n.indexOf(u) !== -1 && e(n).indexOf(u) !== -1) throw Error(`@plitzi/nexus: refused to write to unsafe path "${n}" (\`__proto__\` segment)`);
138
+ if (typeof n == "string" && O(n)) return;
139
+ if (v && typeof n == "string" && !(n.indexOf(".") === -1 ? Object.hasOwn(C, n) : d(C, e(n)))) {
140
+ E?.(n), v.setState(n, o, {
141
+ canPropagate: l,
142
+ unmount: f
143
+ });
120
144
  return;
121
145
  }
122
- if (typeof i != "string") {
123
- j(i, o, u, s);
146
+ if (typeof n != "string") {
147
+ L(n, o, C, l);
124
148
  return;
125
149
  }
126
- if (i.indexOf(".") === -1) {
127
- let e = u[i], t = typeof o == "function" ? o(e) : o, n = t;
128
- if (y.length > 0) {
129
- let r = C(i, t, e);
130
- if (r === a) return;
131
- n = r;
150
+ if (n.indexOf(".") === -1) {
151
+ if (f) {
152
+ if (!Object.hasOwn(C, n)) return;
153
+ let e = C[n];
154
+ if (S.length > 0 && k(n, void 0, e) === s) return;
155
+ let t = x.length > 0 ? m() : void 0;
156
+ if (_(n), t !== void 0 && P(n, t, m()), l && (M(y, n), b.size > 0)) {
157
+ let t = b.direct.get(n);
158
+ t && M(t, n), I(n, e, void 0, 1);
159
+ }
160
+ T();
161
+ return;
162
+ }
163
+ let e = C[n], t = typeof o == "function" ? o(e) : o, r = t;
164
+ if (S.length > 0) {
165
+ let i = k(n, t, e);
166
+ if (i === s) return;
167
+ r = i;
132
168
  }
133
- if (e === n) return;
134
- let r = v.length > 0 ? f() : void 0;
135
- if (m(i, n), r !== void 0 && O(i, r, f()), s && (E(g, i), _.size > 0)) {
136
- let t = _.direct.get(i);
137
- t && E(t, i), A(i, e, n, 1);
169
+ if (e === r) return;
170
+ let i = x.length > 0 ? m() : void 0;
171
+ if (g(n, r), i !== void 0 && P(n, i, m()), l && (M(y, n), b.size > 0)) {
172
+ let t = b.direct.get(n);
173
+ t && M(t, n), I(n, e, r, 1);
138
174
  }
139
- x();
175
+ T();
140
176
  return;
141
177
  }
142
- let b = e(i), w;
143
- if (y.length > 0) {
144
- let e = t(u, i), n = typeof o == "function" ? o(e) : o, s = C(i, n, e);
145
- if (s === a) return;
146
- w = r(u, i, b, s, !1);
147
- } else w = r(u, i, b, o, typeof o == "function");
148
- if (w === n) return;
149
- let T = w;
150
- p(T), v.length > 0 && O(i, u, T), s && (E(g, i), _.size > 0 && (k(i, b), A(i, u, T, 0))), x();
178
+ let w = e(n), D;
179
+ if (f) {
180
+ if (S.length > 0 && k(n, void 0, t(C, n)) === s) return;
181
+ D = i(C, w);
182
+ } else if (S.length > 0) {
183
+ let e = t(C, n), r = typeof o == "function" ? o(e) : o, i = k(n, r, e);
184
+ if (i === s) return;
185
+ D = a(C, n, w, i, !1);
186
+ } else D = a(C, n, w, o, typeof o == "function");
187
+ if (D === r) return;
188
+ let A = D;
189
+ h(A), x.length > 0 && P(n, C, A), l && (M(y, n), b.size > 0 && (F(n, w), I(n, C, A, 0))), T();
151
190
  },
152
- batch: D
191
+ batch: N
153
192
  };
154
193
  }
155
194
  //#endregion
156
- export { u as createSetState };
195
+ export { f as createSetState };
@@ -1,14 +1,16 @@
1
+ import { isTest as e } from "../../env.mjs";
1
2
  //#region src/createStore/helpers/scopeCollisions.ts
2
- var e = () => {
3
- let e = /* @__PURE__ */ new Map();
4
- return { claimDelegatedWrite(t, n) {
5
- let r = e.get(t);
6
- if (r !== void 0 && r !== n) {
7
- console.warn(`@plitzi/nexus: scope collision — two sibling scopes under the same parent both delegate a write to "${t}", clobbering each other through the shared parent. Write to a path each scope owns, or move the shared value into the parent and update it there.`);
3
+ var t = (e) => `@plitzi/nexus: scope collision — two sibling scopes under the same parent both delegate a write to "${e}", clobbering each other through the shared parent. Write to a path each scope owns, or move the shared value into the parent and update it there.`, n = () => {
4
+ let n = /* @__PURE__ */ new Map();
5
+ return { claimDelegatedWrite(r, i) {
6
+ let a = n.get(r);
7
+ if (a !== void 0 && a !== i) {
8
+ if (e) throw Error(t(r));
9
+ console.warn(t(r));
8
10
  return;
9
11
  }
10
- e.set(t, n);
12
+ n.set(r, i);
11
13
  } };
12
14
  };
13
15
  //#endregion
14
- export { e as createScopeClaims };
16
+ export { n as createScopeClaims };
@@ -8,4 +8,5 @@ export declare const UNCHANGED: unique symbol;
8
8
  * - `true` — force codegen even if the probe fails (useful for testing).
9
9
  */
10
10
  export declare const setCodegenEnabled: (enabled: boolean | undefined) => void;
11
+ export declare const deleteByPath: (root: unknown, segments: readonly string[]) => Record<string, unknown> | typeof UNCHANGED;
11
12
  export declare const writeByPath: (root: unknown, path: string, segments: readonly string[], value: unknown, isFn: boolean) => Record<string, unknown> | typeof UNCHANGED;
@@ -40,7 +40,19 @@ var e = Symbol("unchanged"), t = (e) => /^\d+$/.test(e), n = (e) => Array.isArra
40
40
  if (n.some(t)) return;
41
41
  let i = s(n);
42
42
  return f.size >= p && f.delete(f.keys().next().value), f.set(e, i), i;
43
- }, h = (t, n, i, a, o) => {
43
+ }, h = (t, r, i) => {
44
+ if (typeof t != "object" || !t) return e;
45
+ let a = t, o = r[i];
46
+ if (!Object.hasOwn(a, o)) return e;
47
+ if (i === r.length - 1) {
48
+ let e = n(a);
49
+ return Array.isArray(e) ? e.splice(Number(o), 1) : delete e[o], e;
50
+ }
51
+ let s = h(a[o], r, i + 1);
52
+ if (s === e) return e;
53
+ let c = n(a);
54
+ return c[o] = s, c;
55
+ }, g = (e, t) => h(e, t, 0), _ = (t, n, i, a, o) => {
44
56
  let s = m(n, i);
45
57
  if (s) {
46
58
  let n = s(t, a, o);
@@ -49,4 +61,4 @@ var e = Symbol("unchanged"), t = (e) => /^\d+$/.test(e), n = (e) => Array.isArra
49
61
  return r(t, i, 0, a, o);
50
62
  };
51
63
  //#endregion
52
- export { e as UNCHANGED, d as setCodegenEnabled, h as writeByPath };
64
+ export { e as UNCHANGED, g as deleteByPath, d as setCodegenEnabled, _ as writeByPath };
@@ -0,0 +1,11 @@
1
+ import { StoreApi } from './types';
2
+ export type DevStore = StoreApi<Record<string, unknown>>;
3
+ export type DevStoreEntry = {
4
+ uid: string;
5
+ store: DevStore;
6
+ scopeId?: string;
7
+ name?: string;
8
+ };
9
+ export declare const registerDevStore: (store: StoreApi<any>, scopeId?: string, name?: string) => (() => void);
10
+ export declare const subscribeDevStores: (listener: () => void) => (() => void);
11
+ export declare const getDevStoresSnapshot: () => ReadonlyArray<DevStoreEntry>;
@@ -0,0 +1,19 @@
1
+ //#region src/devStoreRegistry.ts
2
+ var e = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new Set(), n = 0, r = [], i = () => {
3
+ r = [...e.values()];
4
+ for (let e of t) e();
5
+ }, a = (t, r, a) => {
6
+ let o = t;
7
+ return e.set(o, {
8
+ uid: `dev-store-${++n}`,
9
+ store: o,
10
+ scopeId: r,
11
+ name: a
12
+ }), i(), () => {
13
+ e.delete(o), i();
14
+ };
15
+ }, o = (e) => (t.add(e), () => {
16
+ t.delete(e);
17
+ }), s = () => r;
18
+ //#endregion
19
+ export { s as getDevStoresSnapshot, a as registerDevStore, o as subscribeDevStores };
package/dist/index.d.ts CHANGED
@@ -12,6 +12,8 @@ export { createRecorder } from './middleware/recorderMiddleware';
12
12
  export { cascade } from './middleware/cascade';
13
13
  export { getStoreHistory } from './middleware/historyMiddleware';
14
14
  export { createServerSnapshot, isServerSnapshot } from './rsc';
15
+ export { registerDevStore, subscribeDevStores, getDevStoresSnapshot } from './devStoreRegistry';
16
+ export type { DevStore, DevStoreEntry } from './devStoreRegistry';
15
17
  export * from './types';
16
18
  export type { CreateStoreOptions } from './createStore/createStore';
17
19
  export type { AsyncOptions, AsyncResource, AsyncSnapshot, AsyncStatus } from './async/createAsync';
package/dist/index.mjs CHANGED
@@ -12,4 +12,5 @@ import { CANCEL as d } from "./types/StoreTypes.mjs";
12
12
  import f from "./createStore/createStore.mjs";
13
13
  import { createRecorder as p } from "./middleware/recorderMiddleware.mjs";
14
14
  import { createServerSnapshot as m, isServerSnapshot as h } from "./rsc/index.mjs";
15
- export { d as CANCEL, l as cascade, e as createAsync, t as createDerived, n as createEntityAdapter, r as createEntityStore, p as createRecorder, m as createServerSnapshot, f as createStore, i as getStoreHistory, a as historyMiddleware, h as isServerSnapshot, o as loggerMiddleware, s as persistMiddleware, c as reduxDevToolsMiddleware, u as setCodegenEnabled };
15
+ import { getDevStoresSnapshot as g, registerDevStore as _, subscribeDevStores as v } from "./devStoreRegistry.mjs";
16
+ export { d as CANCEL, l as cascade, e as createAsync, t as createDerived, n as createEntityAdapter, r as createEntityStore, p as createRecorder, m as createServerSnapshot, f as createStore, g as getDevStoresSnapshot, i as getStoreHistory, a as historyMiddleware, h as isServerSnapshot, o as loggerMiddleware, s as persistMiddleware, c as reduxDevToolsMiddleware, _ as registerDevStore, u as setCodegenEnabled, v as subscribeDevStores };
@@ -0,0 +1,2 @@
1
+ declare const DevStoreScopeContext: import('react').Context<string | undefined>;
2
+ export default DevStoreScopeContext;
@@ -0,0 +1,6 @@
1
+ import { createContext as e } from "react";
2
+ //#region src/react/DevStoreScopeContext.ts
3
+ var t = e(void 0);
4
+ t.displayName = "DevStoreScopeContext";
5
+ //#endregion
6
+ export { t as default };
@@ -19,8 +19,9 @@ export type StoreProviderProps<TState extends object = any> = {
19
19
  inherit?: 'snapshot' | 'live';
20
20
  autoSync?: boolean;
21
21
  middlewares?: StoreMiddleware<TState>[];
22
+ name?: string;
22
23
  children?: ReactNode;
23
24
  };
24
- declare const StoreProvider: <TState extends object = any>({ store, id, segment, isolate, path, value, inherit, autoSync, middlewares, children }: StoreProviderProps<TState>) => import("react").JSX.Element;
25
+ declare const StoreProvider: <TState extends object = any>({ store, id, segment, isolate, path, value, inherit, autoSync, middlewares, name, children }: StoreProviderProps<TState>) => import("react").JSX.Element;
25
26
  export { StoreContext, StoreRegistryContext };
26
27
  export default StoreProvider;
@@ -1,62 +1,66 @@
1
1
  import { isDev as e } from "../env.mjs";
2
2
  import t from "../createStore/index.mjs";
3
3
  import { isServerSnapshot as n, stripServerFlag as r } from "../rsc/index.mjs";
4
- import { StoreContext as i, StoreRegistryContext as a, findStoreInRegistry as o } from "./StoreContext.mjs";
5
- import s from "./hooks/useStoreSync.mjs";
6
- import { createContext as c, useContext as l, useEffect as u, useMemo as d, useRef as f } from "react";
7
- import { jsx as p } from "react/jsx-runtime";
4
+ import { registerDevStore as i } from "../devStoreRegistry.mjs";
5
+ import a from "./DevStoreScopeContext.mjs";
6
+ import { StoreContext as o, StoreRegistryContext as s, findStoreInRegistry as c } from "./StoreContext.mjs";
7
+ import l from "./hooks/useStoreSync.mjs";
8
+ import { createContext as u, useContext as d, useEffect as f, useMemo as p, useRef as m } from "react";
9
+ import { jsx as h } from "react/jsx-runtime";
8
10
  //#region src/react/StoreProvider.tsx
9
- var m = c(void 0), h = (e) => e.cascade === !0, g = ({ store: c, id: g, segment: _, isolate: v, path: y, value: b, inherit: x, autoSync: S = !0, middlewares: C, children: w }) => {
10
- let T = l(i), E = l(m), D = l(a), O = T?.scopePath ?? "", k = d(() => _ === void 0 ? O : O ? `${O}/${_}` : _, [O, _]), A = f(void 0), j = x === "live", M = d(() => {
11
- let e = x === "snapshot" && T ? T.getState() : {}, t = typeof b == "function" ? b(e) : {
11
+ var g = u(void 0), _ = (e) => e.cascade === !0, v = ({ store: u, id: v, segment: y, isolate: b, path: x, value: S, inherit: C, autoSync: w = !0, middlewares: T, name: E, children: D }) => {
12
+ let O = d(o), k = d(g), A = d(s), j = d(a), M = O?.scopePath ?? "", N = p(() => y === void 0 ? M : M ? `${M}/${y}` : y, [M, y]), P = m(void 0), F = C === "live", I = p(() => {
13
+ let e = C === "snapshot" && O ? O.getState() : {}, t = typeof S == "function" ? S(e) : {
12
14
  ...e,
13
- ...b
15
+ ...S
14
16
  };
15
17
  return n(t) ? r(t) : t;
16
18
  }, [
17
- x,
18
- T,
19
- b
20
- ]), N = C ?? [], P = E ? [...E, ...N] : N, F = d(() => {
21
- let e = N.filter(h);
22
- return e.length === 0 ? E : [...E ?? [], ...e];
23
- }, [E, C]);
24
- A.current ||= c ?? t(() => M, {
25
- id: g,
26
- scopePath: k,
27
- exclusive: j ? v : void 0,
28
- parent: j ? T : void 0,
29
- middlewares: P.length > 0 ? P : void 0,
19
+ C,
20
+ O,
21
+ S
22
+ ]), L = T ?? [], R = k ? [...k, ...L] : L, z = p(() => {
23
+ let e = L.filter(_);
24
+ return e.length === 0 ? k : [...k ?? [], ...e];
25
+ }, [k, T]);
26
+ P.current ||= u ?? t(() => I, {
27
+ id: v,
28
+ scopePath: N,
29
+ exclusive: F ? b : void 0,
30
+ parent: F ? O : void 0,
31
+ middlewares: R.length > 0 ? R : void 0,
30
32
  deferHydrate: !0
31
33
  });
32
- let I = d(() => g ? {
33
- id: g,
34
- store: A.current,
35
- parent: D
36
- } : D, [g, D]), L = f(!1);
37
- u(() => {
38
- e && g && !L.current && o(D, g) && (L.current = !0, console.warn(`@plitzi/nexus: duplicate StoreProvider id "${g}" — it shadows an ancestor provider with the same id, so a descendant's { storeId: "${g}" } or useStoreById("${g}") resolves to this (nearer) store. Use a distinct id, or remove one of the providers.`));
39
- }, [g, D]), u(() => (j && !c && A.current?.reconnect?.(), () => {
40
- j && !c && A.current?.destroy?.();
41
- }), [j, c]);
42
- let R = f(!1);
43
- u(() => {
44
- !R.current && A.current?.hydrate && (A.current.hydrate(), R.current = !0);
45
- }, []), s(y, y ? b : M, {
46
- enabled: !!b && S,
47
- store: A.current
34
+ let B = p(() => v ? {
35
+ id: v,
36
+ store: P.current,
37
+ parent: A
38
+ } : A, [v, A]), V = m(!1);
39
+ f(() => {
40
+ e && v && !V.current && c(A, v) && (V.current = !0, console.warn(`@plitzi/nexus: duplicate StoreProvider id "${v}" — it shadows an ancestor provider with the same id, so a descendant's { storeId: "${v}" } or useStoreById("${v}") resolves to this (nearer) store. Use a distinct id, or remove one of the providers.`));
41
+ }, [v, A]), f(() => (F && !u && P.current?.reconnect?.(), () => {
42
+ F && !u && P.current?.destroy?.();
43
+ }), [F, u]), f(() => {
44
+ if (e) return i(P.current, j, E);
45
+ }, [j, E]);
46
+ let H = m(!1);
47
+ f(() => {
48
+ !H.current && P.current?.hydrate && (P.current.hydrate(), H.current = !0);
49
+ }, []), l(x, x ? S : I, {
50
+ enabled: !!S && w,
51
+ store: P.current
48
52
  });
49
- let z = /* @__PURE__ */ p(i, {
50
- value: A.current,
51
- children: w
53
+ let U = /* @__PURE__ */ h(o, {
54
+ value: P.current,
55
+ children: D
52
56
  });
53
- return I !== D && (z = /* @__PURE__ */ p(a, {
54
- value: I,
55
- children: z
56
- })), F !== E && (z = /* @__PURE__ */ p(m, {
57
- value: F,
58
- children: z
59
- })), z;
57
+ return B !== A && (U = /* @__PURE__ */ h(s, {
58
+ value: B,
59
+ children: U
60
+ })), z !== k && (U = /* @__PURE__ */ h(g, {
61
+ value: z,
62
+ children: U
63
+ })), U;
60
64
  };
61
65
  //#endregion
62
- export { i as StoreContext, a as StoreRegistryContext, g as default };
66
+ export { o as StoreContext, s as StoreRegistryContext, v as default };
@@ -29,11 +29,11 @@ function a(e, i, a, o) {
29
29
  if (m.current = a, s) e.setState(void 0, (e) => ({
30
30
  ...e,
31
31
  ...a
32
- }), t);
32
+ }), { canPropagate: t });
33
33
  else if (c) {
34
34
  let n = i(e.getState());
35
- e.setState(n, a, t);
36
- } else e.setState(i, a, t);
35
+ e.setState(n, a, { canPropagate: t });
36
+ } else e.setState(i, a, { canPropagate: t });
37
37
  };
38
38
  p === "render" ? v && y() : (v && g && y(!1), n(() => {
39
39
  v && !g && y();
@@ -1,4 +1,5 @@
1
1
  export { default as StoreProvider, StoreContext, StoreRegistryContext } from './StoreProvider';
2
+ export { default as DevStoreScopeContext } from './DevStoreScopeContext';
2
3
  export { findStoreInRegistry } from './StoreContext';
3
4
  export { createStoreHook } from './createStoreHook';
4
5
  export { default as useStore, defaultMultiEqualityFn } from './hooks/useStore';
@@ -12,6 +13,7 @@ export { useDerived } from './hooks/useDerived';
12
13
  export { useStoreHistory } from './hooks/useStoreHistory';
13
14
  export { useEntity, useEntityOne, useEntityIds, useEntityAll } from './hooks/useEntity';
14
15
  export { default as useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
16
+ export type { StoreApi } from '../types';
15
17
  export type { StoreProviderProps } from './StoreProvider';
16
18
  export type { StoreRegistry } from './StoreContext';
17
19
  export type { UseStoreHistoryReturn } from './hooks/useStoreHistory';
@@ -1,16 +1,17 @@
1
- import { StoreContext as e, StoreRegistryContext as t, findStoreInRegistry as n } from "./StoreContext.mjs";
2
- import { defaultMultiEqualityFn as r } from "./hooks/shared.mjs";
3
- import i from "./useIsomorphicLayoutEffect.mjs";
4
- import a from "./hooks/useStoreSync.mjs";
5
- import o from "./StoreProvider.mjs";
6
- import s from "./hooks/useStore.mjs";
7
- import c from "./hooks/useStoreGetter.mjs";
8
- import l from "./hooks/useStoreSetter.mjs";
9
- import { createStoreHook as u } from "./createStoreHook.mjs";
10
- import d from "./hooks/useStoreById.mjs";
11
- import { useAsync as f } from "./hooks/useAsync.mjs";
12
- import { useAsyncValue as p } from "./hooks/useAsyncValue.mjs";
13
- import { useDerived as m } from "./hooks/useDerived.mjs";
14
- import { useStoreHistory as h } from "./hooks/useStoreHistory.mjs";
15
- import { useEntity as g, useEntityAll as _, useEntityIds as v, useEntityOne as y } from "./hooks/useEntity.mjs";
16
- export { e as StoreContext, o as StoreProvider, t as StoreRegistryContext, u as createStoreHook, r as defaultMultiEqualityFn, n as findStoreInRegistry, f as useAsync, p as useAsyncValue, m as useDerived, g as useEntity, _ as useEntityAll, v as useEntityIds, y as useEntityOne, i as useIsomorphicLayoutEffect, s as useStore, d as useStoreById, c as useStoreGetter, h as useStoreHistory, l as useStoreSetter, a as useStoreSync };
1
+ import e from "./DevStoreScopeContext.mjs";
2
+ import { StoreContext as t, StoreRegistryContext as n, findStoreInRegistry as r } from "./StoreContext.mjs";
3
+ import { defaultMultiEqualityFn as i } from "./hooks/shared.mjs";
4
+ import a from "./useIsomorphicLayoutEffect.mjs";
5
+ import o from "./hooks/useStoreSync.mjs";
6
+ import s from "./StoreProvider.mjs";
7
+ import c from "./hooks/useStore.mjs";
8
+ import l from "./hooks/useStoreGetter.mjs";
9
+ import u from "./hooks/useStoreSetter.mjs";
10
+ import { createStoreHook as d } from "./createStoreHook.mjs";
11
+ import f from "./hooks/useStoreById.mjs";
12
+ import { useAsync as p } from "./hooks/useAsync.mjs";
13
+ import { useAsyncValue as m } from "./hooks/useAsyncValue.mjs";
14
+ import { useDerived as h } from "./hooks/useDerived.mjs";
15
+ import { useStoreHistory as g } from "./hooks/useStoreHistory.mjs";
16
+ import { useEntity as _, useEntityAll as v, useEntityIds as y, useEntityOne as b } from "./hooks/useEntity.mjs";
17
+ export { e as DevStoreScopeContext, t as StoreContext, s as StoreProvider, n as StoreRegistryContext, d as createStoreHook, i as defaultMultiEqualityFn, r as findStoreInRegistry, p as useAsync, m as useAsyncValue, h as useDerived, _ as useEntity, v as useEntityAll, y as useEntityIds, b as useEntityOne, a as useIsomorphicLayoutEffect, c as useStore, f as useStoreById, l as useStoreGetter, g as useStoreHistory, u as useStoreSetter, o as useStoreSync };
@@ -56,9 +56,13 @@ export type MiddlewareOptions<T extends object> = {
56
56
  enabled?: boolean | ((state: T) => boolean);
57
57
  };
58
58
  export type Listener = (changedPath?: Path) => void;
59
+ export type SetStateOptions = {
60
+ canPropagate?: boolean;
61
+ unmount?: boolean;
62
+ };
59
63
  export type SetState<T> = {
60
- (path: undefined, value: T | ((prev: T) => T), canPropagate?: boolean): void;
61
- <P extends PathOf<T>>(path: P, value: PathValue<T, P> | ((prev: PathValue<T, P>) => PathValue<T, P>), canPropagate?: boolean): void;
64
+ (path: undefined, value: T | ((prev: T) => T), options?: SetStateOptions): void;
65
+ <P extends PathOf<T>>(path: P, value: PathValue<T, P> | ((prev: PathValue<T, P>) => PathValue<T, P>), options?: SetStateOptions): void;
62
66
  };
63
67
  export type GetState<T> = () => T;
64
68
  export type BoundStore<TBase> = {
@@ -90,6 +94,7 @@ export type StoreApi<T> = {
90
94
  id?: string;
91
95
  scopePath?: string;
92
96
  getState: GetState<T>;
97
+ getOwnState: () => T;
93
98
  getPath: <P extends PathOf<T>>(path: P) => PathValue<T, P> | undefined;
94
99
  setState: SetState<T>;
95
100
  get: {
@@ -165,12 +170,12 @@ export type GetterTuple<TState extends object, Entries extends ReadonlyArray<Pat
165
170
  };
166
171
  export type UseStoreGetterOptions<TState extends object = object> = StoreHookBaseOptions<TState>;
167
172
  export type SetStateFn<TState extends object> = {
168
- (path: undefined, value: TState | ((prev: TState) => TState)): void;
169
- <P extends PathOf<TState>>(path: P, value: PathValue<TState, P> | ((prev: PathValue<TState, P>) => PathValue<TState, P>)): void;
173
+ (path: undefined, value: TState | ((prev: TState) => TState), options?: SetStateOptions): void;
174
+ <P extends PathOf<TState>>(path: P, value: PathValue<TState, P> | ((prev: PathValue<TState, P>) => PathValue<TState, P>), options?: SetStateOptions): void;
170
175
  };
171
176
  export type SetFromBaseFn<TBase> = TBase extends object ? {
172
- (subPath: undefined, value: TBase | ((prev: TBase) => TBase)): void;
173
- <SubP extends PathOf<TBase>>(subPath: SubP, value: PathValue<TBase, SubP> | ((prev: PathValue<TBase, SubP>) => PathValue<TBase, SubP>)): void;
174
- } : (subPath: undefined, value: TBase) => void;
177
+ (subPath: undefined, value: TBase | ((prev: TBase) => TBase), options?: SetStateOptions): void;
178
+ <SubP extends PathOf<TBase>>(subPath: SubP, value: PathValue<TBase, SubP> | ((prev: PathValue<TBase, SubP>) => PathValue<TBase, SubP>), options?: SetStateOptions): void;
179
+ } : (subPath: undefined, value: TBase, options?: SetStateOptions) => void;
175
180
  export type UseStoreSetterOptions<TState extends object = object> = StoreHookBaseOptions<TState>;
176
181
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plitzi/nexus",
3
- "version": "0.32.4",
3
+ "version": "0.32.6",
4
4
  "license": "Apache-2.0",
5
5
  "files": [
6
6
  "dist",
@@ -23,13 +23,13 @@
23
23
  "type": "module",
24
24
  "sideEffects": false,
25
25
  "devDependencies": {
26
- "eslint": "^9.39.4",
27
- "react": "^19.2.7",
28
- "react-dom": "^19.2.7",
26
+ "eslint": "^9.39.5",
27
+ "react": "^19.2.8",
28
+ "react-dom": "^19.2.8",
29
29
  "typescript": "^6.0.3",
30
- "vite": "^8.0.16",
31
- "vitest": "^4.1.9",
32
- "vue": "^3.5.13"
30
+ "vite": "^8.1.5",
31
+ "vitest": "^4.1.10",
32
+ "vue": "^3.5.40"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "react": "^18.0.0 || ^19.0.0",
@@ -128,6 +128,10 @@
128
128
  "types": "./dist/derived/index.d.ts",
129
129
  "import": "./dist/derived/index.mjs"
130
130
  },
131
+ "./devStoreRegistry": {
132
+ "types": "./dist/devStoreRegistry.d.ts",
133
+ "import": "./dist/devStoreRegistry.mjs"
134
+ },
131
135
  "./entities": {
132
136
  "types": "./dist/entities/index.d.ts",
133
137
  "import": "./dist/entities/index.mjs"
@@ -232,6 +236,10 @@
232
236
  "types": "./dist/react/index.d.ts",
233
237
  "import": "./dist/react/index.mjs"
234
238
  },
239
+ "./react/DevStoreScopeContext": {
240
+ "types": "./dist/react/DevStoreScopeContext.d.ts",
241
+ "import": "./dist/react/DevStoreScopeContext.mjs"
242
+ },
235
243
  "./react/StoreContext": {
236
244
  "types": "./dist/react/StoreContext.d.ts",
237
245
  "import": "./dist/react/StoreContext.mjs"