@reactra/store 0.1.0-alpha.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Akhil Shastri and the Reactra contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # @reactra/store
2
+
3
+ > Reactra store runtime — scoped, fine-grained, per-field-subscription state for Reactra apps.
4
+
5
+ **Alpha** — published under the npm dist-tag `alpha`. APIs may change before 1.0.
6
+
7
+ ```bash
8
+ npm install @reactra/store@alpha
9
+ ```
10
+
11
+ Part of [Reactra](https://github.com/akhilshastri/reactra) — a compiler-first,
12
+ React-19-compatible framework. See the [documentation](https://reactra-docs.vercel.app) to get
13
+ started.
14
+
15
+ ## License
16
+
17
+ MIT
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Per-store runtime instance. Compiler-emitted factories build one of these
3
+ * via `createStoreInstance` and hand it to the registry. The factory closure
4
+ * owns the state vars; the instance just exposes the subscribe + getSnapshot
5
+ * pair that `useSyncExternalStore` needs.
6
+ */
7
+ export interface StoreInstance<T extends Record<string, unknown> = Record<string, unknown>> {
8
+ /** Subscribe to all changes. Returns an unsubscribe fn. */
9
+ subscribe: (onChange: () => void) => () => void;
10
+ /**
11
+ * Return the current snapshot — a stable object reference until the next
12
+ * `notify()` call. `useSyncExternalStore` relies on referential stability
13
+ * to avoid infinite re-renders.
14
+ *
15
+ * `T` is the store's public surface (state + derived + actions). The
16
+ * compiler-emitted factory infers it from the snapshot composer, which is
17
+ * how `StoreSurface` recovers the precise type for `export type <store>`
18
+ * (Store §2.5). Defaults to `Record<string, unknown>` so hand-written /
19
+ * untyped callers keep working.
20
+ */
21
+ getSnapshot: () => T;
22
+ /**
23
+ * Internal — fan out the current snapshot to all subscribers. Used by
24
+ * `StoreRegistry.replace` during HMR to wake old subscribers after a
25
+ * factory swap so their next render reads the new instance from the
26
+ * registry. User code (and compiler-emitted action bodies) call notify
27
+ * through the closure parameter passed to `createStoreInstance.build`,
28
+ * not via this field.
29
+ */
30
+ notify: () => void;
31
+ }
32
+ /** Lifecycle kind that determines when a store's factory runs. */
33
+ export type StoreBindingKind = "export" | "session" | "route";
34
+ /**
35
+ * The shape a compiler-emitted store binding takes: a `{ name, kind, factory }`
36
+ * tuple the user passes to `configureStores`. The factory's `inputs` parameter
37
+ * is only meaningful for route stores (the router passes URL-derived values
38
+ * mapped via argumented `use storeX(param a, query b)`). Export and session
39
+ * stores ignore it.
40
+ */
41
+ export interface StoreBinding {
42
+ name: string;
43
+ /**
44
+ * "export" / "session" → instantiate eagerly at `configureStores` time.
45
+ * "route" → register the factory but wait for `StoreRegistry.instantiate`
46
+ * to fire on route enter.
47
+ */
48
+ kind: StoreBindingKind;
49
+ /**
50
+ * Wave 3 §2b — `route store ... for subtree "/path"`. When set, this store
51
+ * survives across every route whose pathname starts with the subtree path.
52
+ * `instantiate(name, ...)` is a NO-OP when an instance is already live;
53
+ * `dispose(name, { nextPathname })` is a no-op when `nextPathname` is still
54
+ * inside the subtree. Both checks isolate subtree-aware behaviour to
55
+ * stores that opt in via this field — non-subtree stores keep their
56
+ * existing per-route lifecycle.
57
+ */
58
+ subtreePath?: string;
59
+ /**
60
+ * Builds an instance. `inputs` is route-derived (argumented `inject store`).
61
+ * `restored` (route stores with `preserved state`) carries the values read
62
+ * back from `sessionStorage` — the compiler-emitted factory overwrites the
63
+ * matching `state` fields from it (Store §6). Both optional + ignored by
64
+ * stores that don't use them, so existing factories are unaffected.
65
+ */
66
+ factory: (inputs?: Record<string, unknown>, restored?: Record<string, unknown>) => StoreInstance;
67
+ /**
68
+ * State field names listed under `preserved state X, Y` (Store §6) — the
69
+ * compiler emits this on the binding. The registry persists exactly these
70
+ * fields on route exit and reads them back into `restored` on the next enter.
71
+ */
72
+ preservedFields?: readonly string[];
73
+ /**
74
+ * Stage E (Resource v1 §8) — compiler-emitted prefetch warmer. For a route
75
+ * store with `resource r(deps) => fn(deps)` declarations, the compiler emits
76
+ * a `warm(inputs, signal)` companion that calls `warmResource(name, deps,
77
+ * fn, signal)` once per resource (parallel `Promise.all`). Best-effort: the
78
+ * returned promise resolves silently on any failure (RES015 was deliberately
79
+ * not minted — failed warms are debug-only). Stores without resources omit
80
+ * this field; `StoreRegistry.warm` treats absence as a no-op.
81
+ */
82
+ warm?: (inputs: Record<string, unknown> | undefined, signal: AbortSignal) => Promise<void>;
83
+ }
84
+ /**
85
+ * The public surface type of a compiler-emitted store binding — its state,
86
+ * derived, and action members (inputs are excluded; they are factory
87
+ * parameters, not part of the snapshot). Pass 9 emits, alongside each store
88
+ * binding, `export type <name> = StoreSurface<typeof <name>>` (Store §2.5) so
89
+ * a consumer's `import type { <name> }` resolves the live surface through
90
+ * native module resolution.
91
+ *
92
+ * It stays in lockstep with the store body **by construction**: the type is
93
+ * inferred from the same factory the runtime executes, so it cannot drift the
94
+ * way a hand-maintained interface would. This is what lets store linkage
95
+ * survive package boundaries (the `.reactra.json` sidecar does not — see
96
+ * Compiler §6) and is the prerequisite for retiring the implicit sidecar-index
97
+ * name resolution (backlog 1a).
98
+ */
99
+ export type StoreSurface<B extends {
100
+ factory: (...args: never[]) => StoreInstance;
101
+ }> = ReturnType<B["factory"]> extends StoreInstance<infer T> ? T : never;
102
+ /**
103
+ * Route-entry coordinates that key a preserved-state slot (Store §6 / Router
104
+ * §8.4). Two distinct entries to the same route (different params/query) get
105
+ * distinct slots, so e.g. `/checkout?u=1` and `/checkout?u=2` don't leak.
106
+ */
107
+ export interface PreservedKey {
108
+ pathname: string;
109
+ params: Record<string, unknown>;
110
+ query: Record<string, unknown>;
111
+ }
112
+ /**
113
+ * Internal/diagnostics tier (Runtime §3) — register a store's restore closure.
114
+ * Compiler-emitted (Pass 9); last-wins per name. Mirrors `__listStoreBindings`.
115
+ * The closure returns the **count of state fields it actually wrote** so callers
116
+ * can tell a real re-drive from a no-op (shape-drift) one.
117
+ */
118
+ export declare const __registerStoreRestore: (name: string, restore: (state: Record<string, unknown>) => number) => void;
119
+ /**
120
+ * Internal/diagnostics tier (Runtime §3) — drive a live store back to a past
121
+ * `state` (Devtools time-travel re-drive). Looks up the registered restore
122
+ * closure; when present, fires it (writes the matching state fields + notifies)
123
+ * and returns the **number of fields written** — 0 when no field in `state`
124
+ * matched a declared store `state` (e.g. a post-HMR shape drift), so the caller's
125
+ * "⟲ live" signal can't claim a no-op store moved. Returns 0 when no closure is
126
+ * registered (never instantiated / torn down) — a view-only degrade, never a throw.
127
+ */
128
+ export declare const replayStoreState: (name: string, state: Record<string, unknown>) => number;
129
+ declare class StoreRegistryImpl {
130
+ private instances;
131
+ private factories;
132
+ private kinds;
133
+ private preservedFields;
134
+ private preserveWarned;
135
+ private warmers;
136
+ private subtreePaths;
137
+ /**
138
+ * Register a binding. Export / session stores instantiate immediately so a
139
+ * bare `use storeX` anywhere in the tree resolves. Route stores hold their
140
+ * factory until the router calls `instantiate(name, { inputs })` on enter.
141
+ */
142
+ register: (binding: StoreBinding) => void;
143
+ /**
144
+ * Instantiate a route store with the inputs the router mapped from URL
145
+ * params/query. Idempotent within a single route lifetime — re-entering
146
+ * the same route disposes the previous instance first (router contract).
147
+ */
148
+ instantiate: (name: string, opts?: {
149
+ inputs?: Record<string, unknown>;
150
+ preservedKey?: PreservedKey;
151
+ }) => void;
152
+ /**
153
+ * Persist a route store's `preserved state` fields to `sessionStorage` under
154
+ * the route-entry key (Store §6 / Router §8.4). Called by the route lifecycle
155
+ * immediately before `dispose` on exit. No-op for stores with no preserved
156
+ * fields or no live instance. Quota / serialization failures are swallowed
157
+ * with one `console.warn` per store per session.
158
+ */
159
+ savePreservedState: (name: string, key: PreservedKey) => void;
160
+ /**
161
+ * Remove a live instance but keep its factory so the next route enter can
162
+ * rebuild it. Export / session stores live for the app lifetime and are
163
+ * never disposed by the router.
164
+ *
165
+ * Wave 3 §2b — for stores declared `route store ... for subtree "/path"`,
166
+ * pass the incoming route's pathname in `opts.nextPathname`. The dispose
167
+ * is SKIPPED when the destination is still inside the subtree (the next
168
+ * page's `instantiate` becomes a no-op too, so the same instance flows
169
+ * through the transition). Non-subtree stores ignore the option.
170
+ */
171
+ dispose: (name: string, opts?: {
172
+ nextPathname?: string | null;
173
+ }) => void;
174
+ /**
175
+ * Look up an instance by name. Throws if the store hasn't been instantiated
176
+ * — either configureStores hasn't run, or it's a route store whose route
177
+ * isn't active.
178
+ */
179
+ get: (name: string) => StoreInstance;
180
+ /** True if a live instance exists. Used by tests + router wiring. */
181
+ has: (name: string) => boolean;
182
+ /**
183
+ * Internal tier (Runtime §1) — enumerate every registered binding for
184
+ * diagnostics (the Devtools spec §5 Stores tab). Read-only snapshots;
185
+ * never part of the public contract.
186
+ */
187
+ __list: () => ReadonlyArray<{
188
+ name: string;
189
+ kind: StoreBindingKind;
190
+ instantiated: boolean;
191
+ preservedFields: readonly string[];
192
+ subtreePath?: string;
193
+ }>;
194
+ /**
195
+ * Resource v1 §8.2 — invoke a route store's compiler-emitted `warm(inputs,
196
+ * signal)` companion if present. Pre-fills the resource cache so the
197
+ * consumer mount that follows hits a fresh entry (Stage E hookup with
198
+ * `PrefetchRuntime`). Stores without resources omit the companion; this
199
+ * is a silent no-op. Best-effort: the returned promise resolves even on
200
+ * warmer failure (the warmer itself swallows; see `warmResource` in
201
+ * `@reactra/resource`).
202
+ */
203
+ warm: (name: string, inputs: Record<string, unknown> | undefined, signal: AbortSignal) => Promise<void>;
204
+ /**
205
+ * HMR hot-replace a binding (Compiler §5.3). Compiler-emitted
206
+ * `import.meta.hot.accept(...)` blocks in store-bearing files call this
207
+ * after the module re-evaluates so the new factory takes effect without
208
+ * a full page reload.
209
+ *
210
+ * Semantics:
211
+ * - "export" / "session" — build a fresh instance from the new factory
212
+ * and notify any old subscribers, so React re-renders consumers and
213
+ * their next `useReactraStore` read picks up the new instance from the
214
+ * registry. **State is intentionally lost** (new factory = fresh
215
+ * closure vars). Trade-off: the edited action body actually runs.
216
+ * - "route" — dispose the live instance; the router re-instantiates with
217
+ * inputs on the next route enter, which is the natural picking-up
218
+ * point for the new factory.
219
+ *
220
+ * Idempotent on a name that wasn't registered (treated as a fresh
221
+ * register) — this happens if the HMR fires before configureStores ran,
222
+ * which shouldn't occur in practice but isn't worth crashing over.
223
+ */
224
+ replace: (binding: StoreBinding) => void;
225
+ /**
226
+ * Day 27 / `#5c-followup`: reconcile the registry against a new
227
+ * `STORES` array (typically the freshly-evaluated `newMod.STORES`
228
+ * from a HMR-accept callback on the generated route manifest).
229
+ *
230
+ * Semantics:
231
+ * - **Newcomers** (in `newStores`, not in the registry) → `register`.
232
+ * - **Departures** (in the registry, not in `newStores`) → drop
233
+ * factory + instance + kind. Subscribers that hold a stale
234
+ * instance reference keep that reference until React re-renders;
235
+ * their next `useReactraStore(name)` throws "store not active",
236
+ * which is the right signal — the user removed the store, so
237
+ * consumers should be next to go.
238
+ * - **Existing** (in both) → `replace` (so action-body edits to a
239
+ * re-emitted store take effect via the existing replace path).
240
+ *
241
+ * This is the manifest-level companion to per-file HMR (Day 16 /
242
+ * `#11`'s `import.meta.hot.accept` block emitted alongside store
243
+ * containers). The per-file path handles edits to a stable set of
244
+ * stores; this path handles add/remove of WHOLE store FILES.
245
+ */
246
+ applyDelta: (newStores: readonly StoreBinding[]) => void;
247
+ }
248
+ /**
249
+ * The process-wide store registry singleton. The compiler emits imports of
250
+ * this and the `useReactraStore` hook below; user code typically doesn't
251
+ * touch it directly except through `configureStores`.
252
+ */
253
+ export declare const StoreRegistry: StoreRegistryImpl;
254
+ /**
255
+ * Internal tier (Runtime §1) — registered-binding enumeration for
256
+ * diagnostics. Consumed by `@reactra/devtools` (Devtools spec §5).
257
+ */
258
+ export declare const __listStoreBindings: () => ReadonlyArray<{
259
+ name: string;
260
+ kind: StoreBindingKind;
261
+ instantiated: boolean;
262
+ preservedFields: readonly string[];
263
+ subtreePath?: string;
264
+ }>;
265
+ /**
266
+ * Bootstrap call — invoked from `src/main.tsx` per Runtime v0 §5. Registers
267
+ * every store binding the app cares about. Order matters: `configureStores`
268
+ * must precede `configureRouter`.
269
+ */
270
+ export declare const configureStores: (opts: {
271
+ stores: StoreBinding[];
272
+ }) => void;
273
+ /**
274
+ * React hook used by compiler-emitted `use storeX` bindings. Reads the live
275
+ * snapshot via `useSyncExternalStore`, so the component re-renders when the
276
+ * store calls `notify()`.
277
+ *
278
+ * The compiler destructures the returned object into the bare names the user
279
+ * wrote (state field, derived, action) — see Compiler v2 §7.3 / Store v4.1 §7.3.
280
+ *
281
+ * MVP simplification: snapshot is whole-store; per-field selection (Store
282
+ * §7.3 Selector Optimization) is a v1 improvement.
283
+ */
284
+ export declare const useReactraStore: <T extends Record<string, unknown> = Record<string, unknown>>(name: string) => T;
285
+ /**
286
+ * A2 (Store §7.3 Selector Optimization) — per-FIELD store subscription. Backs
287
+ * the compiler-emitted field-selected binding `inject store X { a, b }`: the
288
+ * component re-renders ONLY when one of the selected SOURCE fields changes, not
289
+ * on every store write.
290
+ *
291
+ * The store's `notify()` still fans out to every listener (subscription is
292
+ * unchanged — C1/C4); the per-field bail-out happens in React via the
293
+ * `selector` + `isEqual` pair of `useSyncExternalStoreWithSelector`. A write to
294
+ * an UNselected field re-runs the selector but produces a selection that is
295
+ * shallow-equal to the previous one, so React skips the re-render.
296
+ *
297
+ * `fields` are the store's SOURCE keys (the names on the store surface), NOT the
298
+ * renamed locals — the compiler keeps the destructure rename in the emitted
299
+ * code; only the projected source keys flow here (Compiler §7.3 C3).
300
+ *
301
+ * The `K extends keyof T = keyof T` generic key-checks `fields` against the
302
+ * surface — a hand-caller typo (`["staus"]`) is a compile error either way,
303
+ * because `K` (whether explicit or defaulted to `keyof T`) constrains the array.
304
+ * Supplying `K` explicitly (`<T, "a" | "b">`) narrows the return to exactly the
305
+ * selected keys. The `= keyof T` default is load-bearing: the compiler emits a
306
+ * SINGLE type arg (`useReactraStoreFields<X>("X", ["a","b"])`), and TS requires
307
+ * every non-defaulted type param when any is given — the default lets that
308
+ * one-arg emitted form compile (it yields `Pick<T, keyof T>` = `T`, the pre-A2
309
+ * whole-surface type; the destructure only names selected fields, so this is
310
+ * harmless). Type-only — emission is byte-identical.
311
+ *
312
+ * **Reassignment invariant (Store §7.3):** the per-field bail-out compares each
313
+ * selected field by `Object.is`, so reactive state must be updated by
314
+ * REASSIGNMENT (`items = [...items, x]`), not in-place mutation
315
+ * (`items.push(x)`). An in-place mutation of a selected object/array field keeps
316
+ * the same reference → `isEqual` returns true → no re-render. (Whole-store /
317
+ * namespace bindings are unaffected — they re-render on any `notify()`.) This
318
+ * matches the framework's reassignment-based reactive model.
319
+ *
320
+ * **Version-cache invariant:** this relies on `createStoreInstance`'s
321
+ * version-cache — `getSnapshot` returns the SAME object reference until
322
+ * `notify()` bumps the version. Every state-changing path MUST call `notify()`
323
+ * (it does today, via every emitted setter); a write that skips it leaves the
324
+ * cached snapshot in place, the selector never re-runs, and the consumer never
325
+ * re-renders.
326
+ *
327
+ * @param name registered store name
328
+ * @param fields source-key names to select + subscribe to (re-render gate)
329
+ * @returns an object with exactly the selected source keys
330
+ */
331
+ export declare const useReactraStoreFields: <T extends Record<string, unknown>, K extends keyof T = keyof T>(name: string, fields: readonly K[]) => Pick<T, K>;
332
+ /**
333
+ * Compiler-emitted factories call this to build a `StoreInstance`. The
334
+ * `build` callback receives a `notify` function the factory must call after
335
+ * every state mutation. The callback returns the snapshot composer — a fn
336
+ * that produces the current snapshot object on demand. The instance handles
337
+ * snapshot caching + listener fan-out.
338
+ *
339
+ * Shape used by emitted code (see Pass 9 codegen):
340
+ *
341
+ * // export store — no inputs
342
+ * createStoreInstance((notify) => {
343
+ * let count = 0
344
+ * const increment = () => { count = count + 1; notify() }
345
+ * return () => ({ count, increment }) // snapshot composer
346
+ * })
347
+ *
348
+ * // route store — inputs come from argumented `use storeX(param a, query b)`
349
+ * // via StoreRegistry.instantiate
350
+ * (inputs) => createStoreInstance((notify) => {
351
+ * const { customerId, returnTo } = inputs ?? {}
352
+ * ...
353
+ * })
354
+ */
355
+ export declare const createStoreInstance: <T extends Record<string, unknown>>(build: (notify: () => void) => () => T) => StoreInstance<T>;
356
+ export {};
357
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,570 @@
1
+ // @reactra/store — Store factories + StoreRegistry (Phase 1 MVP)
2
+ //
3
+ // Owner: reactra-store-spec.md (Spec 3.1 / Discussion v4.1)
4
+ //
5
+ // Phase 1 MVP surface — what's shipped today:
6
+ // StoreRegistry: register / instantiate / dispose / get / has
7
+ // configureStores({ stores })
8
+ // useReactraStore(name) — React hook backed by useSyncExternalStore
9
+ // createStoreInstance — helper consumed by compiler-emitted factories
10
+ //
11
+ // Day 10a addition — route-store lifecycle:
12
+ // StoreBinding.kind = "export" | "session" | "route" so the registry
13
+ // knows which factories to eagerly instantiate at configureStores time
14
+ // (export, session) vs which to defer until the router calls
15
+ // `instantiate(name, { inputs })` (route).
16
+ //
17
+ // Day 16 addition — HMR replace:
18
+ // StoreRegistry.replace(binding) — invoked from compiler-emitted
19
+ // `import.meta.hot.accept(...)` blocks in store-bearing files. Swaps
20
+ // the factory; for export/session also rebuilds the instance and
21
+ // notifies old subscribers so consumers re-render against the new
22
+ // instance. State is intentionally lost (new factory = fresh closure
23
+ // vars); the trade-off is that the action body edit actually takes
24
+ // effect. See Compiler §5.3 (HMR Boundary Per File).
25
+ //
26
+ // Preserved state (Store §6) — SHIPPED:
27
+ // StoreRegistry.savePreservedState(name, key) persists `preserved state`
28
+ // fields to sessionStorage on route exit; instantiate(name, { preservedKey })
29
+ // reads them back into the factory's `restored` arg on the next enter.
30
+ //
31
+ // Stage E (Resource v1 §8) — SHIPPED:
32
+ // StoreBinding.warm? + StoreRegistry.warm(name, inputs, signal) — prefetch
33
+ // resource warming. The runtime contract is live; the compiler-emitted
34
+ // warm(inputs, signal) companion (which calls warmResource per-resource
35
+ // from @reactra/resource) lands in a follow-up compiler stage. Hand-written
36
+ // bindings that set `warm` work today.
37
+ //
38
+ // Phase 1 deferred:
39
+ // per-field subscription (today: whole-store notification on any change)
40
+ // middleware (interceptAction / stateChanged / afterAction)
41
+ // resources / effects / mount / cleanup inside store bodies
42
+ import { useCallback, useMemo, useSyncExternalStore } from "react";
43
+ // A2 (framework-review §A2 / Store §7.3): per-field store subscription. The shim
44
+ // import is Node-portable (no Bun API) and ships with React's own
45
+ // use-sync-external-store package. The selector + equality gate the RE-RENDER;
46
+ // the underlying `subscribe` is unchanged (notify() still fans to all listeners).
47
+ import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector";
48
+ /** `sessionStorage` key: `reactra:preserved:<name>:<pathname>:<params>|<query>` (Store §6.4). */
49
+ /**
50
+ * Wave 3 §2b — true when `pathname` is inside the subtree rooted at
51
+ * `subtreePath`. Path-component atomic — `/checkout` matches `/checkout`
52
+ * and `/checkout/cart`, but NOT `/checkout-summary`. Trailing slashes on
53
+ * `subtreePath` are normalised away.
54
+ */
55
+ const isInsideSubtree = (pathname, subtreePath) => {
56
+ const stripped = subtreePath.endsWith("/") ? subtreePath.slice(0, -1) : subtreePath;
57
+ if (pathname === stripped)
58
+ return true;
59
+ return pathname.startsWith(stripped + "/");
60
+ };
61
+ const preservedStorageKey = (name, k) => `reactra:preserved:${name}:${k.pathname}:${JSON.stringify(k.params)}|${JSON.stringify(k.query)}`;
62
+ /** Read + parse a preserved slot; `undefined` if absent / unavailable / malformed. */
63
+ const readPreserved = (key) => {
64
+ if (typeof sessionStorage === "undefined")
65
+ return undefined;
66
+ try {
67
+ const raw = sessionStorage.getItem(key);
68
+ if (raw == null)
69
+ return undefined;
70
+ const parsed = JSON.parse(raw);
71
+ return parsed != null && typeof parsed === "object"
72
+ ? parsed
73
+ : undefined;
74
+ }
75
+ catch {
76
+ return undefined;
77
+ }
78
+ };
79
+ /** Write a serialized preserved slot. Throws on quota — `savePreservedState` swallows. */
80
+ const writePreserved = (key, json) => {
81
+ if (typeof sessionStorage === "undefined")
82
+ return;
83
+ sessionStorage.setItem(key, json);
84
+ };
85
+ /**
86
+ * Module-private name-keyed registry of compiler-emitted store restore
87
+ * closures (Store §6 / Devtools time-travel). Pass 9 emits, inside each store
88
+ * factory, a call to `__registerStoreRestore(name, restore)` where `restore`
89
+ * writes the `state` fields of a `next` map into the factory's closure `let`s
90
+ * and calls the factory's `notify`. Last-wins per name (a re-instantiated
91
+ * factory overwrites the stale closure). Cleared on instance teardown so a
92
+ * `replayStoreState` against a torn-down store degrades to a view-only no-op.
93
+ *
94
+ * This is a plain module-level map — `@reactra/store` takes NO dependency edge
95
+ * toward devtools/replay (architect condition C2).
96
+ */
97
+ const storeRestores = new Map();
98
+ /**
99
+ * Internal/diagnostics tier (Runtime §3) — register a store's restore closure.
100
+ * Compiler-emitted (Pass 9); last-wins per name. Mirrors `__listStoreBindings`.
101
+ * The closure returns the **count of state fields it actually wrote** so callers
102
+ * can tell a real re-drive from a no-op (shape-drift) one.
103
+ */
104
+ export const __registerStoreRestore = (name, restore) => {
105
+ storeRestores.set(name, restore);
106
+ };
107
+ /**
108
+ * Internal/diagnostics tier (Runtime §3) — drive a live store back to a past
109
+ * `state` (Devtools time-travel re-drive). Looks up the registered restore
110
+ * closure; when present, fires it (writes the matching state fields + notifies)
111
+ * and returns the **number of fields written** — 0 when no field in `state`
112
+ * matched a declared store `state` (e.g. a post-HMR shape drift), so the caller's
113
+ * "⟲ live" signal can't claim a no-op store moved. Returns 0 when no closure is
114
+ * registered (never instantiated / torn down) — a view-only degrade, never a throw.
115
+ */
116
+ export const replayStoreState = (name, state) => {
117
+ const restore = storeRestores.get(name);
118
+ if (restore === undefined)
119
+ return 0;
120
+ return restore(state);
121
+ };
122
+ class StoreRegistryImpl {
123
+ instances = new Map();
124
+ factories = new Map();
125
+ kinds = new Map();
126
+ // `preserved state` field names per store (Store §6). Empty/absent → the
127
+ // save/restore paths are no-ops for that store.
128
+ preservedFields = new Map();
129
+ // One save-failure warning per store per session (quota / serialization).
130
+ preserveWarned = new Set();
131
+ // Stage E (Resource v1 §8) — per-store `warm(inputs, signal)` companions,
132
+ // populated by `register`/`replace`/`applyDelta` from the binding. Stores
133
+ // without resources have no entry; `warm()` no-ops in that case.
134
+ warmers = new Map();
135
+ // Wave 3 §2b — `for subtree "/path"` subtree paths per store. Drives the
136
+ // skip-instantiate + skip-dispose logic so a single instance survives
137
+ // across every page whose pathname starts with the subtree path. No
138
+ // entry → store is a regular per-route store (existing behaviour).
139
+ subtreePaths = new Map();
140
+ /**
141
+ * Register a binding. Export / session stores instantiate immediately so a
142
+ * bare `use storeX` anywhere in the tree resolves. Route stores hold their
143
+ * factory until the router calls `instantiate(name, { inputs })` on enter.
144
+ */
145
+ register = (binding) => {
146
+ if (this.factories.has(binding.name)) {
147
+ throw new Error(`[reactra:store] S001: store "${binding.name}" is already registered`);
148
+ }
149
+ this.factories.set(binding.name, binding.factory);
150
+ this.kinds.set(binding.name, binding.kind);
151
+ if (binding.preservedFields && binding.preservedFields.length > 0) {
152
+ this.preservedFields.set(binding.name, binding.preservedFields);
153
+ }
154
+ if (binding.warm !== undefined) {
155
+ this.warmers.set(binding.name, binding.warm);
156
+ }
157
+ if (binding.subtreePath !== undefined && binding.subtreePath.length > 0) {
158
+ this.subtreePaths.set(binding.name, binding.subtreePath);
159
+ }
160
+ if (binding.kind === "export" || binding.kind === "session") {
161
+ this.instances.set(binding.name, binding.factory());
162
+ }
163
+ };
164
+ /**
165
+ * Instantiate a route store with the inputs the router mapped from URL
166
+ * params/query. Idempotent within a single route lifetime — re-entering
167
+ * the same route disposes the previous instance first (router contract).
168
+ */
169
+ instantiate = (name, opts = {}) => {
170
+ const factory = this.factories.get(name);
171
+ if (!factory) {
172
+ throw new Error(`[reactra:store] cannot instantiate "${name}" — no factory registered`);
173
+ }
174
+ // Wave 3 §2b — for subtree stores, REUSE the existing instance when one
175
+ // is already alive (the previous page in the subtree built it; the new
176
+ // page joins the same lifecycle). Inputs from the new page are
177
+ // ignored — the entry route's inputs are what defined the store, per
178
+ // Router §4.6 "argumented form passes inputs at subtree entry; bare
179
+ // form everywhere else in the subtree". Non-subtree stores keep the
180
+ // existing always-rebuild behaviour.
181
+ if (this.subtreePaths.has(name) && this.instances.has(name)) {
182
+ return;
183
+ }
184
+ // Preserved-state restore (Store §6): if this route store opted into
185
+ // `preserved state` AND the router passed the entry key, read the persisted
186
+ // values back and hand them to the factory, which overwrites the matching
187
+ // `state` fields before the snapshot composer runs — so the restored values
188
+ // are visible on first render (before React subscribes). No-op for stores
189
+ // with no preserved fields or when the entry has nothing stored.
190
+ const fields = this.preservedFields.get(name);
191
+ const restored = fields && fields.length > 0 && opts.preservedKey
192
+ ? readPreserved(preservedStorageKey(name, opts.preservedKey))
193
+ : undefined;
194
+ this.instances.set(name, factory(opts.inputs, restored));
195
+ };
196
+ /**
197
+ * Persist a route store's `preserved state` fields to `sessionStorage` under
198
+ * the route-entry key (Store §6 / Router §8.4). Called by the route lifecycle
199
+ * immediately before `dispose` on exit. No-op for stores with no preserved
200
+ * fields or no live instance. Quota / serialization failures are swallowed
201
+ * with one `console.warn` per store per session.
202
+ */
203
+ savePreservedState = (name, key) => {
204
+ const fields = this.preservedFields.get(name);
205
+ if (!fields || fields.length === 0)
206
+ return;
207
+ const inst = this.instances.get(name);
208
+ if (!inst)
209
+ return;
210
+ try {
211
+ const snap = inst.getSnapshot();
212
+ const out = {};
213
+ for (const f of fields)
214
+ out[f] = snap[f];
215
+ writePreserved(preservedStorageKey(name, key), JSON.stringify(out));
216
+ }
217
+ catch (err) {
218
+ if (!this.preserveWarned.has(name)) {
219
+ this.preserveWarned.add(name);
220
+ console.warn(`[reactra:store] preserved-state save failed for "${name}"`, err);
221
+ }
222
+ }
223
+ };
224
+ /**
225
+ * Remove a live instance but keep its factory so the next route enter can
226
+ * rebuild it. Export / session stores live for the app lifetime and are
227
+ * never disposed by the router.
228
+ *
229
+ * Wave 3 §2b — for stores declared `route store ... for subtree "/path"`,
230
+ * pass the incoming route's pathname in `opts.nextPathname`. The dispose
231
+ * is SKIPPED when the destination is still inside the subtree (the next
232
+ * page's `instantiate` becomes a no-op too, so the same instance flows
233
+ * through the transition). Non-subtree stores ignore the option.
234
+ */
235
+ dispose = (name, opts = {}) => {
236
+ const subtreePath = this.subtreePaths.get(name);
237
+ if (subtreePath !== undefined &&
238
+ typeof opts.nextPathname === "string" &&
239
+ isInsideSubtree(opts.nextPathname, subtreePath)) {
240
+ // Still inside the subtree — keep the instance alive for the next page.
241
+ return;
242
+ }
243
+ this.instances.delete(name);
244
+ // C7: the torn-down instance's restore closure points at dead closure vars;
245
+ // drop it so a stale `replayStoreState(name, ...)` degrades to a view-only
246
+ // no-op (returns 0) instead of writing into the old closure.
247
+ storeRestores.delete(name);
248
+ };
249
+ /**
250
+ * Look up an instance by name. Throws if the store hasn't been instantiated
251
+ * — either configureStores hasn't run, or it's a route store whose route
252
+ * isn't active.
253
+ */
254
+ get = (name) => {
255
+ const inst = this.instances.get(name);
256
+ if (!inst) {
257
+ throw new Error(`[reactra:store] store "${name}" is not active — either configureStores() didn't register it, or it's a route store whose route isn't entered`);
258
+ }
259
+ return inst;
260
+ };
261
+ /** True if a live instance exists. Used by tests + router wiring. */
262
+ has = (name) => this.instances.has(name);
263
+ /**
264
+ * Internal tier (Runtime §1) — enumerate every registered binding for
265
+ * diagnostics (the Devtools spec §5 Stores tab). Read-only snapshots;
266
+ * never part of the public contract.
267
+ */
268
+ __list = () => [...this.factories.keys()].map((name) => ({
269
+ name,
270
+ // kinds is populated in lockstep with factories by register/replace.
271
+ kind: this.kinds.get(name),
272
+ instantiated: this.instances.has(name),
273
+ preservedFields: this.preservedFields.get(name) ?? [],
274
+ ...(this.subtreePaths.has(name) ? { subtreePath: this.subtreePaths.get(name) } : {}),
275
+ }));
276
+ /**
277
+ * Resource v1 §8.2 — invoke a route store's compiler-emitted `warm(inputs,
278
+ * signal)` companion if present. Pre-fills the resource cache so the
279
+ * consumer mount that follows hits a fresh entry (Stage E hookup with
280
+ * `PrefetchRuntime`). Stores without resources omit the companion; this
281
+ * is a silent no-op. Best-effort: the returned promise resolves even on
282
+ * warmer failure (the warmer itself swallows; see `warmResource` in
283
+ * `@reactra/resource`).
284
+ */
285
+ warm = (name, inputs, signal) => {
286
+ const w = this.warmers.get(name);
287
+ if (w === undefined)
288
+ return Promise.resolve();
289
+ return w(inputs, signal).catch(() => {
290
+ // Defensive — the warmer itself is documented best-effort, but a
291
+ // hand-written binding that rejects must not break the route prefetch.
292
+ });
293
+ };
294
+ /**
295
+ * HMR hot-replace a binding (Compiler §5.3). Compiler-emitted
296
+ * `import.meta.hot.accept(...)` blocks in store-bearing files call this
297
+ * after the module re-evaluates so the new factory takes effect without
298
+ * a full page reload.
299
+ *
300
+ * Semantics:
301
+ * - "export" / "session" — build a fresh instance from the new factory
302
+ * and notify any old subscribers, so React re-renders consumers and
303
+ * their next `useReactraStore` read picks up the new instance from the
304
+ * registry. **State is intentionally lost** (new factory = fresh
305
+ * closure vars). Trade-off: the edited action body actually runs.
306
+ * - "route" — dispose the live instance; the router re-instantiates with
307
+ * inputs on the next route enter, which is the natural picking-up
308
+ * point for the new factory.
309
+ *
310
+ * Idempotent on a name that wasn't registered (treated as a fresh
311
+ * register) — this happens if the HMR fires before configureStores ran,
312
+ * which shouldn't occur in practice but isn't worth crashing over.
313
+ */
314
+ replace = (binding) => {
315
+ const oldInst = this.instances.get(binding.name);
316
+ // C7 (HMR): drop the stale restore closure before the new factory runs.
317
+ // For export/session the rebuild below re-emits `__registerStoreRestore`
318
+ // (last-wins); clearing first guarantees a factory edited to drop all
319
+ // `state` fields leaves no stale closure pointing at dead vars. For route
320
+ // stores the live instance is kept, but its closure was rebuilt on the
321
+ // last instantiate — a mid-scrub HMR tolerating the swap drops travel
322
+ // state with no throw (the next instantiate re-registers).
323
+ storeRestores.delete(binding.name);
324
+ this.factories.set(binding.name, binding.factory);
325
+ this.kinds.set(binding.name, binding.kind);
326
+ if (binding.preservedFields && binding.preservedFields.length > 0) {
327
+ this.preservedFields.set(binding.name, binding.preservedFields);
328
+ }
329
+ else {
330
+ this.preservedFields.delete(binding.name);
331
+ }
332
+ if (binding.warm !== undefined) {
333
+ this.warmers.set(binding.name, binding.warm);
334
+ }
335
+ else {
336
+ this.warmers.delete(binding.name);
337
+ }
338
+ if (binding.kind === "route") {
339
+ // Day 18 / `#18b`: keep the live instance. The router naturally
340
+ // re-instantiates on the next route enter, which is the right
341
+ // pickup point for the new factory. Disposing here would throw
342
+ // "store not active" in any mounted consumer the moment we
343
+ // achieve HMR for a file with a live route store.
344
+ return;
345
+ }
346
+ this.instances.set(binding.name, binding.factory());
347
+ if (oldInst)
348
+ oldInst.notify();
349
+ };
350
+ /**
351
+ * Day 27 / `#5c-followup`: reconcile the registry against a new
352
+ * `STORES` array (typically the freshly-evaluated `newMod.STORES`
353
+ * from a HMR-accept callback on the generated route manifest).
354
+ *
355
+ * Semantics:
356
+ * - **Newcomers** (in `newStores`, not in the registry) → `register`.
357
+ * - **Departures** (in the registry, not in `newStores`) → drop
358
+ * factory + instance + kind. Subscribers that hold a stale
359
+ * instance reference keep that reference until React re-renders;
360
+ * their next `useReactraStore(name)` throws "store not active",
361
+ * which is the right signal — the user removed the store, so
362
+ * consumers should be next to go.
363
+ * - **Existing** (in both) → `replace` (so action-body edits to a
364
+ * re-emitted store take effect via the existing replace path).
365
+ *
366
+ * This is the manifest-level companion to per-file HMR (Day 16 /
367
+ * `#11`'s `import.meta.hot.accept` block emitted alongside store
368
+ * containers). The per-file path handles edits to a stable set of
369
+ * stores; this path handles add/remove of WHOLE store FILES.
370
+ */
371
+ applyDelta = (newStores) => {
372
+ const newNames = new Set(newStores.map((s) => s.name));
373
+ // Departures first — drop the old factories+instances before we
374
+ // potentially register a same-named newcomer (defensive; the loops
375
+ // below shouldn't overlap on names but the order keeps it sane).
376
+ for (const name of [...this.factories.keys()]) {
377
+ if (!newNames.has(name)) {
378
+ this.instances.delete(name);
379
+ this.factories.delete(name);
380
+ this.kinds.delete(name);
381
+ this.preservedFields.delete(name);
382
+ this.warmers.delete(name);
383
+ // C7: a departed store's restore closure must not survive the delta.
384
+ storeRestores.delete(name);
385
+ }
386
+ }
387
+ // Newcomers + existing — replace handles both (idempotent on
388
+ // an unknown name; see its own docstring).
389
+ for (const binding of newStores) {
390
+ if (this.factories.has(binding.name)) {
391
+ this.replace(binding);
392
+ }
393
+ else {
394
+ this.register(binding);
395
+ }
396
+ }
397
+ };
398
+ }
399
+ /**
400
+ * The process-wide store registry singleton. The compiler emits imports of
401
+ * this and the `useReactraStore` hook below; user code typically doesn't
402
+ * touch it directly except through `configureStores`.
403
+ */
404
+ export const StoreRegistry = new StoreRegistryImpl();
405
+ /**
406
+ * Internal tier (Runtime §1) — registered-binding enumeration for
407
+ * diagnostics. Consumed by `@reactra/devtools` (Devtools spec §5).
408
+ */
409
+ export const __listStoreBindings = StoreRegistry.__list;
410
+ /**
411
+ * Bootstrap call — invoked from `src/main.tsx` per Runtime v0 §5. Registers
412
+ * every store binding the app cares about. Order matters: `configureStores`
413
+ * must precede `configureRouter`.
414
+ */
415
+ export const configureStores = (opts) => {
416
+ for (const binding of opts.stores)
417
+ StoreRegistry.register(binding);
418
+ };
419
+ /**
420
+ * React hook used by compiler-emitted `use storeX` bindings. Reads the live
421
+ * snapshot via `useSyncExternalStore`, so the component re-renders when the
422
+ * store calls `notify()`.
423
+ *
424
+ * The compiler destructures the returned object into the bare names the user
425
+ * wrote (state field, derived, action) — see Compiler v2 §7.3 / Store v4.1 §7.3.
426
+ *
427
+ * MVP simplification: snapshot is whole-store; per-field selection (Store
428
+ * §7.3 Selector Optimization) is a v1 improvement.
429
+ */
430
+ export const useReactraStore = (name) => {
431
+ const inst = StoreRegistry.get(name);
432
+ // useCallback so React sees stable subscribe/getSnapshot references and
433
+ // doesn't tear them down between renders.
434
+ const subscribe = useCallback((onChange) => inst.subscribe(onChange), [inst]);
435
+ const getSnapshot = useCallback(() => inst.getSnapshot(), [inst]);
436
+ return useSyncExternalStore(subscribe, getSnapshot);
437
+ };
438
+ /**
439
+ * A2 (Store §7.3 Selector Optimization) — per-FIELD store subscription. Backs
440
+ * the compiler-emitted field-selected binding `inject store X { a, b }`: the
441
+ * component re-renders ONLY when one of the selected SOURCE fields changes, not
442
+ * on every store write.
443
+ *
444
+ * The store's `notify()` still fans out to every listener (subscription is
445
+ * unchanged — C1/C4); the per-field bail-out happens in React via the
446
+ * `selector` + `isEqual` pair of `useSyncExternalStoreWithSelector`. A write to
447
+ * an UNselected field re-runs the selector but produces a selection that is
448
+ * shallow-equal to the previous one, so React skips the re-render.
449
+ *
450
+ * `fields` are the store's SOURCE keys (the names on the store surface), NOT the
451
+ * renamed locals — the compiler keeps the destructure rename in the emitted
452
+ * code; only the projected source keys flow here (Compiler §7.3 C3).
453
+ *
454
+ * The `K extends keyof T = keyof T` generic key-checks `fields` against the
455
+ * surface — a hand-caller typo (`["staus"]`) is a compile error either way,
456
+ * because `K` (whether explicit or defaulted to `keyof T`) constrains the array.
457
+ * Supplying `K` explicitly (`<T, "a" | "b">`) narrows the return to exactly the
458
+ * selected keys. The `= keyof T` default is load-bearing: the compiler emits a
459
+ * SINGLE type arg (`useReactraStoreFields<X>("X", ["a","b"])`), and TS requires
460
+ * every non-defaulted type param when any is given — the default lets that
461
+ * one-arg emitted form compile (it yields `Pick<T, keyof T>` = `T`, the pre-A2
462
+ * whole-surface type; the destructure only names selected fields, so this is
463
+ * harmless). Type-only — emission is byte-identical.
464
+ *
465
+ * **Reassignment invariant (Store §7.3):** the per-field bail-out compares each
466
+ * selected field by `Object.is`, so reactive state must be updated by
467
+ * REASSIGNMENT (`items = [...items, x]`), not in-place mutation
468
+ * (`items.push(x)`). An in-place mutation of a selected object/array field keeps
469
+ * the same reference → `isEqual` returns true → no re-render. (Whole-store /
470
+ * namespace bindings are unaffected — they re-render on any `notify()`.) This
471
+ * matches the framework's reassignment-based reactive model.
472
+ *
473
+ * **Version-cache invariant:** this relies on `createStoreInstance`'s
474
+ * version-cache — `getSnapshot` returns the SAME object reference until
475
+ * `notify()` bumps the version. Every state-changing path MUST call `notify()`
476
+ * (it does today, via every emitted setter); a write that skips it leaves the
477
+ * cached snapshot in place, the selector never re-runs, and the consumer never
478
+ * re-renders.
479
+ *
480
+ * @param name registered store name
481
+ * @param fields source-key names to select + subscribe to (re-render gate)
482
+ * @returns an object with exactly the selected source keys
483
+ */
484
+ export const useReactraStoreFields = (name, fields) => {
485
+ const inst = StoreRegistry.get(name);
486
+ const subscribe = useCallback((onChange) => inst.subscribe(onChange), [inst]);
487
+ const getSnapshot = useCallback(() => inst.getSnapshot(), [inst]);
488
+ // Stable selector + equality keyed on the field list. The selector projects
489
+ // the selected source keys; `isEqual` is a shallow Object.is comparison over
490
+ // exactly those keys — the per-field bail-out. We do NOT allocate the selected
491
+ // object outside the selector (the selector's own result is what
492
+ // with-selector memoizes against the previous selection via `isEqual`).
493
+ const key = fields.join(",");
494
+ const { selector, isEqual } = useMemo(() => {
495
+ const sel = (snap) => {
496
+ const out = {};
497
+ for (const f of fields)
498
+ out[f] = snap[f];
499
+ return out;
500
+ };
501
+ const eq = (a, b) => {
502
+ for (const f of fields) {
503
+ if (!Object.is(a[f], b[f])) {
504
+ return false;
505
+ }
506
+ }
507
+ return true;
508
+ };
509
+ return { selector: sel, isEqual: eq };
510
+ // `key` is the stable identity of `fields`; recompute only when it changes.
511
+ // eslint-disable-next-line react-hooks/exhaustive-deps
512
+ }, [key]);
513
+ return useSyncExternalStoreWithSelector(subscribe, getSnapshot,
514
+ // getServerSnapshot: same source as the client snapshot. Correct for CSR;
515
+ // when SSR lands this must return a request-scoped snapshot or hydration
516
+ // will mismatch (ref specs/adr/0001-ssr-scoping.md — singleton registries
517
+ // are the client-only boundary).
518
+ getSnapshot, selector, isEqual);
519
+ };
520
+ /**
521
+ * Compiler-emitted factories call this to build a `StoreInstance`. The
522
+ * `build` callback receives a `notify` function the factory must call after
523
+ * every state mutation. The callback returns the snapshot composer — a fn
524
+ * that produces the current snapshot object on demand. The instance handles
525
+ * snapshot caching + listener fan-out.
526
+ *
527
+ * Shape used by emitted code (see Pass 9 codegen):
528
+ *
529
+ * // export store — no inputs
530
+ * createStoreInstance((notify) => {
531
+ * let count = 0
532
+ * const increment = () => { count = count + 1; notify() }
533
+ * return () => ({ count, increment }) // snapshot composer
534
+ * })
535
+ *
536
+ * // route store — inputs come from argumented `use storeX(param a, query b)`
537
+ * // via StoreRegistry.instantiate
538
+ * (inputs) => createStoreInstance((notify) => {
539
+ * const { customerId, returnTo } = inputs ?? {}
540
+ * ...
541
+ * })
542
+ */
543
+ export const createStoreInstance = (build) => {
544
+ const listeners = new Set();
545
+ let version = 0;
546
+ let cachedSnapshot = null;
547
+ let cachedVersion = -1;
548
+ const notify = () => {
549
+ version++;
550
+ cachedSnapshot = null;
551
+ for (const l of listeners)
552
+ l();
553
+ };
554
+ const compose = build(notify);
555
+ return {
556
+ subscribe: (onChange) => {
557
+ listeners.add(onChange);
558
+ return () => listeners.delete(onChange);
559
+ },
560
+ getSnapshot: () => {
561
+ if (cachedVersion !== version || cachedSnapshot === null) {
562
+ cachedSnapshot = compose();
563
+ cachedVersion = version;
564
+ }
565
+ return cachedSnapshot;
566
+ },
567
+ notify,
568
+ };
569
+ };
570
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@reactra/store",
3
+ "version": "0.1.0-alpha.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ }
11
+ },
12
+ "dependencies": {
13
+ "use-sync-external-store": "^1.5.0"
14
+ },
15
+ "peerDependencies": {
16
+ "react": "^19.2.0"
17
+ },
18
+ "types": "./dist/index.d.ts",
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public",
24
+ "tag": "alpha",
25
+ "provenance": false
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/akhilshastri/reactra.git",
30
+ "directory": "packages/store"
31
+ },
32
+ "homepage": "https://reactra-docs.vercel.app",
33
+ "license": "MIT",
34
+ "engines": {
35
+ "node": ">=22.18"
36
+ },
37
+ "sideEffects": false,
38
+ "description": "Reactra store runtime — scoped, fine-grained, per-field-subscription state for Reactra apps."
39
+ }