@codebelt/classy-store 0.0.1 → 0.1.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/dist/index.cjs +29 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -66
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +28 -66
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +28 -59
- package/dist/index.mjs.map +1 -1
- package/dist/react/react.cjs +61 -0
- package/dist/react/react.cjs.map +1 -0
- package/dist/react/react.d.cts +26 -0
- package/dist/react/react.d.cts.map +1 -0
- package/dist/react/react.d.mts +26 -0
- package/dist/react/react.d.mts.map +1 -0
- package/dist/react/react.mjs +60 -0
- package/dist/react/react.mjs.map +1 -0
- package/dist/{snapshot-TbHIUjvP.cjs → snapshot-BKVFJLuo.cjs} +27 -50
- package/dist/snapshot-BKVFJLuo.cjs.map +1 -0
- package/dist/{snapshot-fVu34Cr6.mjs → snapshot-P0QPV1ER.mjs} +22 -39
- package/dist/snapshot-P0QPV1ER.mjs.map +1 -0
- package/dist/types-B6RZUB86.d.cts +27 -0
- package/dist/types-B6RZUB86.d.cts.map +1 -0
- package/dist/types-vWYkF3tH.d.mts +27 -0
- package/dist/types-vWYkF3tH.d.mts.map +1 -0
- package/dist/utils/index.cjs +14 -4
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.cts +16 -3
- package/dist/utils/index.d.cts.map +1 -1
- package/dist/utils/index.d.mts +16 -3
- package/dist/utils/index.d.mts.map +1 -1
- package/dist/utils/index.mjs +14 -4
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +30 -6
- package/dist/snapshot-TbHIUjvP.cjs.map +0 -1
- package/dist/snapshot-fVu34Cr6.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { a as subscribe,
|
|
2
|
-
import { createProxy, isChanged } from "proxy-compare";
|
|
3
|
-
import { useCallback, useRef, useSyncExternalStore } from "react";
|
|
1
|
+
import { a as subscribe, i as getVersion, n as createClassyStore, o as PROXYABLE, t as snapshot } from "./snapshot-P0QPV1ER.mjs";
|
|
4
2
|
|
|
5
|
-
//#region src/collections.ts
|
|
3
|
+
//#region src/collections/collections.ts
|
|
6
4
|
/**
|
|
7
|
-
* A Map-like class backed by a plain array so `
|
|
5
|
+
* A Map-like class backed by a plain array so `createClassyStore()` can proxy mutations.
|
|
8
6
|
*
|
|
9
7
|
* Native `Map` uses internal slots that ES6 Proxy can't intercept, so mutations
|
|
10
8
|
* like `.set()` would be invisible to the store. `ReactiveMap` solves this by
|
|
@@ -12,7 +10,7 @@ import { useCallback, useRef, useSyncExternalStore } from "react";
|
|
|
12
10
|
*
|
|
13
11
|
* Usage:
|
|
14
12
|
* ```ts
|
|
15
|
-
* const myStore =
|
|
13
|
+
* const myStore = createClassyStore({ users: reactiveMap<string, User>() });
|
|
16
14
|
* myStore.users.set('id1', { name: 'Alice' }); // reactive
|
|
17
15
|
* ```
|
|
18
16
|
*/
|
|
@@ -81,7 +79,7 @@ var ReactiveMap = class {
|
|
|
81
79
|
}
|
|
82
80
|
};
|
|
83
81
|
/**
|
|
84
|
-
* A Set-like class backed by a plain array so `
|
|
82
|
+
* A Set-like class backed by a plain array so `createClassyStore()` can proxy mutations.
|
|
85
83
|
*
|
|
86
84
|
* Native `Set` uses internal slots that ES6 Proxy can't intercept, so mutations
|
|
87
85
|
* like `.add()` would be invisible to the store. `ReactiveSet` solves this by
|
|
@@ -89,7 +87,7 @@ var ReactiveMap = class {
|
|
|
89
87
|
*
|
|
90
88
|
* Usage:
|
|
91
89
|
* ```ts
|
|
92
|
-
* const myStore =
|
|
90
|
+
* const myStore = createClassyStore({ tags: reactiveSet<string>(['urgent']) });
|
|
93
91
|
* myStore.tags.add('bug'); // reactive
|
|
94
92
|
* ```
|
|
95
93
|
*/
|
|
@@ -150,7 +148,7 @@ var ReactiveSet = class {
|
|
|
150
148
|
};
|
|
151
149
|
/**
|
|
152
150
|
* Creates a reactive Map-like collection backed by a plain array.
|
|
153
|
-
* Wrap the parent object with `
|
|
151
|
+
* Wrap the parent object with `createClassyStore()` for full reactivity.
|
|
154
152
|
*
|
|
155
153
|
* @param initial - Optional iterable of `[key, value]` pairs.
|
|
156
154
|
*/
|
|
@@ -159,7 +157,7 @@ function reactiveMap(initial) {
|
|
|
159
157
|
}
|
|
160
158
|
/**
|
|
161
159
|
* Creates a reactive Set-like collection backed by a plain array.
|
|
162
|
-
* Wrap the parent object with `
|
|
160
|
+
* Wrap the parent object with `createClassyStore()` for full reactivity.
|
|
163
161
|
*
|
|
164
162
|
* @param initial - Optional iterable of values.
|
|
165
163
|
*/
|
|
@@ -168,59 +166,30 @@ function reactiveSet(initial) {
|
|
|
168
166
|
}
|
|
169
167
|
|
|
170
168
|
//#endregion
|
|
171
|
-
//#region src/
|
|
172
|
-
function useStore(proxyStore, selector, isEqual) {
|
|
173
|
-
getInternal(proxyStore);
|
|
174
|
-
const subscribe$1 = useCallback((onStoreChange) => subscribe(proxyStore, onStoreChange), [proxyStore]);
|
|
175
|
-
const snapRef = useRef(void 0);
|
|
176
|
-
const resultRef = useRef(void 0);
|
|
177
|
-
const affected = useRef(/* @__PURE__ */ new WeakMap()).current;
|
|
178
|
-
const proxyCache = useRef(/* @__PURE__ */ new WeakMap()).current;
|
|
179
|
-
const prevSnapRef = useRef(void 0);
|
|
180
|
-
const wrappedRef = useRef(void 0);
|
|
181
|
-
const getSnapshot = () => selector ? getSelectorSnapshot(proxyStore, snapRef, resultRef, selector, isEqual) : getAutoTrackSnapshot(proxyStore, affected, proxyCache, prevSnapRef, wrappedRef);
|
|
182
|
-
return useSyncExternalStore(subscribe$1, getSnapshot, getSnapshot);
|
|
183
|
-
}
|
|
169
|
+
//#region src/utils/equality/equality.ts
|
|
184
170
|
/**
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* Fast-paths when the snapshot reference hasn't changed (O(1)). Otherwise
|
|
188
|
-
* runs the selector against the new snapshot and compares the result to the
|
|
189
|
-
* previous one via `Object.is` (or a custom `isEqual`). Returns the previous
|
|
190
|
-
* result reference when equal, preventing unnecessary React re-renders.
|
|
171
|
+
* Shallow-equal comparison for objects and arrays.
|
|
172
|
+
* Useful as a custom `isEqual` for `useStore` selectors that return objects/arrays.
|
|
191
173
|
*
|
|
192
|
-
*
|
|
174
|
+
* - Primitives compared with `Object.is`.
|
|
175
|
+
* - Arrays: length + element-wise `Object.is`.
|
|
176
|
+
* - Objects: key count + value-wise `Object.is`.
|
|
193
177
|
*/
|
|
194
|
-
function
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
* If the snapshot reference is the same, returns the cached tracking proxy.
|
|
208
|
-
* If the snapshot changed but no tracked property differs (`isChanged` returns
|
|
209
|
-
* false), also returns the cached proxy -- avoiding re-render. Only when a
|
|
210
|
-
* relevant property changed does it create a new `createProxy` wrapper.
|
|
211
|
-
*
|
|
212
|
-
* Pure function -- no hooks, safe to call from `useSyncExternalStore`.
|
|
213
|
-
*/
|
|
214
|
-
function getAutoTrackSnapshot(proxyStore, affected, proxyCache, prevSnapRef, wrappedRef) {
|
|
215
|
-
const nextSnap = snapshot(proxyStore);
|
|
216
|
-
if (prevSnapRef.current === nextSnap) return wrappedRef.current;
|
|
217
|
-
if (prevSnapRef.current !== void 0 && !isChanged(prevSnapRef.current, nextSnap, affected)) return wrappedRef.current;
|
|
218
|
-
prevSnapRef.current = nextSnap;
|
|
219
|
-
const wrapped = createProxy(nextSnap, affected, proxyCache);
|
|
220
|
-
wrappedRef.current = wrapped;
|
|
221
|
-
return wrapped;
|
|
178
|
+
function shallowEqual(a, b) {
|
|
179
|
+
if (Object.is(a, b)) return true;
|
|
180
|
+
if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return false;
|
|
181
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
182
|
+
if (a.length !== b.length) return false;
|
|
183
|
+
for (let i = 0; i < a.length; i++) if (!Object.is(a[i], b[i])) return false;
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
const keysA = Object.keys(a);
|
|
187
|
+
const keysB = Object.keys(b);
|
|
188
|
+
if (keysA.length !== keysB.length) return false;
|
|
189
|
+
for (const key of keysA) if (!Object.hasOwn(b, key) || !Object.is(a[key], b[key])) return false;
|
|
190
|
+
return true;
|
|
222
191
|
}
|
|
223
192
|
|
|
224
193
|
//#endregion
|
|
225
|
-
export { getVersion, reactiveMap, reactiveSet, shallowEqual, snapshot,
|
|
194
|
+
export { createClassyStore, getVersion, reactiveMap, reactiveSet, shallowEqual, snapshot, subscribe };
|
|
226
195
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["subscribe","coreSubscribe"],"sources":["../src/collections.ts","../src/useStore.ts"],"sourcesContent":["import {PROXYABLE} from './utils';\n\n// ── ReactiveMap ───────────────────────────────────────────────────────────────\n\n/**\n * A Map-like class backed by a plain array so `store()` can proxy mutations.\n *\n * Native `Map` uses internal slots that ES6 Proxy can't intercept, so mutations\n * like `.set()` would be invisible to the store. `ReactiveMap` solves this by\n * storing entries in a plain array (`_entries`) that the proxy can track.\n *\n * Usage:\n * ```ts\n * const myStore = store({ users: reactiveMap<string, User>() });\n * myStore.users.set('id1', { name: 'Alice' }); // reactive\n * ```\n */\nexport class ReactiveMap<K, V> {\n static [PROXYABLE] = true;\n\n /** @internal Backing storage — proxied by store(). */\n _entries: [K, V][] = [];\n\n /** Deduplicates initial entries by key (last value wins, matching native `Map`). */\n constructor(initial?: Iterable<[K, V]>) {\n if (initial) {\n for (const [k, v] of initial) {\n const index = this._entries.findIndex(([ek]) => Object.is(ek, k));\n if (index !== -1) {\n this._entries[index] = [k, v];\n } else {\n this._entries.push([k, v]);\n }\n }\n }\n }\n\n /** Returns the number of entries. */\n get size(): number {\n return this._entries.length;\n }\n\n /** Returns the value for `key`, or `undefined`. O(n) linear scan. */\n get(key: K): V | undefined {\n const entry = this._entries.find(([k]) => Object.is(k, key));\n return entry ? entry[1] : undefined;\n }\n\n /** Returns `true` if `key` exists. O(n) linear scan. */\n has(key: K): boolean {\n return this._entries.some(([k]) => Object.is(k, key));\n }\n\n /** Sets `key` to `value`. Updates in-place if key exists, appends otherwise. */\n set(key: K, value: V): this {\n const index = this._entries.findIndex(([k]) => Object.is(k, key));\n if (index !== -1) {\n this._entries[index] = [key, value];\n } else {\n this._entries.push([key, value]);\n }\n return this;\n }\n\n /** Removes the entry for `key`. Returns `true` if found. */\n delete(key: K): boolean {\n const index = this._entries.findIndex(([k]) => Object.is(k, key));\n if (index === -1) return false;\n this._entries.splice(index, 1);\n return true;\n }\n\n /** Removes all entries. Uses splice to trigger proxy notification. */\n clear(): void {\n this._entries.splice(0, this._entries.length);\n }\n\n /** Returns an iterator over the keys. */\n keys(): IterableIterator<K> {\n return this._entries.map(([k]) => k)[Symbol.iterator]();\n }\n\n /** Returns an iterator over the values. */\n values(): IterableIterator<V> {\n return this._entries.map(([, v]) => v)[Symbol.iterator]();\n }\n\n /** Returns an iterator over [key, value] pairs. */\n entries(): IterableIterator<[K, V]> {\n return this._entries.map(([k, v]) => [k, v] as [K, V])[Symbol.iterator]();\n }\n\n /** Calls `callback` for each entry, matching the native `Map.forEach` signature. */\n forEach(callback: (value: V, key: K, map: ReactiveMap<K, V>) => void): void {\n for (const [k, v] of this._entries) {\n callback(v, k, this);\n }\n }\n\n /** Enables `for...of` iteration over [key, value] pairs. */\n [Symbol.iterator](): IterableIterator<[K, V]> {\n return this.entries();\n }\n}\n\n// ── ReactiveSet ───────────────────────────────────────────────────────────────\n\n/**\n * A Set-like class backed by a plain array so `store()` can proxy mutations.\n *\n * Native `Set` uses internal slots that ES6 Proxy can't intercept, so mutations\n * like `.add()` would be invisible to the store. `ReactiveSet` solves this by\n * storing items in a plain array (`_items`) that the proxy can track.\n *\n * Usage:\n * ```ts\n * const myStore = store({ tags: reactiveSet<string>(['urgent']) });\n * myStore.tags.add('bug'); // reactive\n * ```\n */\nexport class ReactiveSet<T> {\n static [PROXYABLE] = true;\n\n /** @internal Backing storage — proxied by store(). */\n _items: T[] = [];\n\n /** Deduplicates initial values using `Object.is` comparison. */\n constructor(initial?: Iterable<T>) {\n if (initial) {\n for (const v of initial) {\n if (!this._items.some((item) => Object.is(item, v))) {\n this._items.push(v);\n }\n }\n }\n }\n\n /** Returns the number of unique items. */\n get size(): number {\n return this._items.length;\n }\n\n /** Returns `true` if `value` exists. O(n) linear scan. */\n has(value: T): boolean {\n return this._items.some((item) => Object.is(item, value));\n }\n\n /** Adds `value` if not already present (no-op for duplicates). */\n add(value: T): this {\n if (!this.has(value)) {\n this._items.push(value);\n }\n return this;\n }\n\n /** Removes `value`. Returns `true` if found. */\n delete(value: T): boolean {\n const index = this._items.findIndex((item) => Object.is(item, value));\n if (index === -1) return false;\n this._items.splice(index, 1);\n return true;\n }\n\n /** Removes all items. Uses splice to trigger proxy notification. */\n clear(): void {\n this._items.splice(0, this._items.length);\n }\n\n /** Returns an iterator over the values (same as `values()`, matching Set API). */\n keys(): IterableIterator<T> {\n return this._items[Symbol.iterator]();\n }\n\n /** Returns an iterator over the values. */\n values(): IterableIterator<T> {\n return this._items[Symbol.iterator]();\n }\n\n /** Returns an iterator over [value, value] pairs, matching the native Set API. */\n entries(): IterableIterator<[T, T]> {\n return this._items.map((v) => [v, v] as [T, T])[Symbol.iterator]();\n }\n\n /** Calls `callback` for each item, matching the native `Set.forEach` signature. */\n forEach(callback: (value: T, key: T, set: ReactiveSet<T>) => void): void {\n for (const v of this._items) {\n callback(v, v, this);\n }\n }\n\n /** Enables `for...of` iteration over values. */\n [Symbol.iterator](): IterableIterator<T> {\n return this.values();\n }\n}\n\n// ── Factory functions ─────────────────────────────────────────────────────────\n\n/**\n * Creates a reactive Map-like collection backed by a plain array.\n * Wrap the parent object with `store()` for full reactivity.\n *\n * @param initial - Optional iterable of `[key, value]` pairs.\n */\nexport function reactiveMap<K, V>(\n initial?: Iterable<[K, V]>,\n): ReactiveMap<K, V> {\n return new ReactiveMap(initial);\n}\n\n/**\n * Creates a reactive Set-like collection backed by a plain array.\n * Wrap the parent object with `store()` for full reactivity.\n *\n * @param initial - Optional iterable of values.\n */\nexport function reactiveSet<T>(initial?: Iterable<T>): ReactiveSet<T> {\n return new ReactiveSet(initial);\n}\n","import {createProxy, isChanged} from 'proxy-compare';\nimport {useCallback, useRef, useSyncExternalStore} from 'react';\nimport {subscribe as coreSubscribe, getInternal} from './core';\nimport {snapshot} from './snapshot';\nimport type {Snapshot} from './types';\n\n// ── Overloads ─────────────────────────────────────────────────────────────────\n\n/**\n * Subscribe to a store proxy with an explicit selector.\n *\n * Re-renders only when the selected value changes (compared via `Object.is`\n * by default, or a custom `isEqual`).\n *\n * @param proxyStore - A reactive proxy created by `store()`.\n * @param selector - Picks data from the immutable snapshot.\n * @param isEqual - Optional custom equality function (default: `Object.is`).\n */\nexport function useStore<T extends object, S>(\n proxyStore: T,\n selector: (snap: Snapshot<T>) => S,\n isEqual?: (a: S, b: S) => boolean,\n): S;\n\n/**\n * Subscribe to a store proxy **without** a selector (auto-tracked mode).\n *\n * Returns a `proxy-compare` tracking proxy over the immutable snapshot.\n * The component only re-renders when a property it actually read changes.\n *\n * @param proxyStore - A reactive proxy created by `store()`.\n */\nexport function useStore<T extends object>(proxyStore: T): Snapshot<T>;\n\n// ── Implementation ────────────────────────────────────────────────────────────\n\nexport function useStore<T extends object, S>(\n proxyStore: T,\n selector?: (snap: Snapshot<T>) => S,\n isEqual?: (a: S, b: S) => boolean,\n): Snapshot<T> | S {\n // Validate that the argument is actually a store proxy (throws if not).\n getInternal(proxyStore);\n\n // Stable subscribe function (internal identity never changes for a given store).\n const subscribe = useCallback(\n (onStoreChange: () => void) => coreSubscribe(proxyStore, onStoreChange),\n [proxyStore],\n );\n\n // ── Refs used by both modes (always allocated to satisfy Rules of Hooks) ──\n\n // Selector mode refs\n const snapRef = useRef<Snapshot<T> | undefined>(undefined);\n const resultRef = useRef<S | undefined>(undefined);\n\n // Auto-track mode refs\n const affected = useRef(new WeakMap<object, unknown>()).current;\n const proxyCache = useRef(new WeakMap<object, unknown>()).current;\n const prevSnapRef = useRef<Snapshot<T> | undefined>(undefined);\n const wrappedRef = useRef<Snapshot<T> | undefined>(undefined);\n\n // ── Single getSnapshot for useSyncExternalStore ───────────────────────────\n\n const getSnapshot = (): Snapshot<T> | S =>\n selector\n ? getSelectorSnapshot(proxyStore, snapRef, resultRef, selector, isEqual)\n : getAutoTrackSnapshot(\n proxyStore,\n affected,\n proxyCache,\n prevSnapRef,\n wrappedRef,\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n// ── Selector mode logic (pure function, no hooks) ─────────────────────────────\n\n/**\n * `getSnapshot` implementation for selector mode.\n *\n * Fast-paths when the snapshot reference hasn't changed (O(1)). Otherwise\n * runs the selector against the new snapshot and compares the result to the\n * previous one via `Object.is` (or a custom `isEqual`). Returns the previous\n * result reference when equal, preventing unnecessary React re-renders.\n *\n * Pure function -- no hooks, safe to call from `useSyncExternalStore`.\n */\nfunction getSelectorSnapshot<T extends object, S>(\n proxyStore: T,\n snapRef: React.RefObject<Snapshot<T> | undefined>,\n resultRef: React.RefObject<S | undefined>,\n selector: (snap: Snapshot<T>) => S,\n isEqual?: (a: S, b: S) => boolean,\n): S {\n const nextSnap = snapshot(proxyStore);\n\n // Fast path: same snapshot reference → same result.\n if (snapRef.current === nextSnap && resultRef.current !== undefined) {\n return resultRef.current;\n }\n\n const nextResult = selector(nextSnap);\n snapRef.current = nextSnap;\n\n // Check equality with previous result.\n if (\n resultRef.current !== undefined &&\n (isEqual\n ? isEqual(resultRef.current, nextResult)\n : Object.is(resultRef.current, nextResult))\n ) {\n return resultRef.current;\n }\n\n resultRef.current = nextResult;\n return nextResult;\n}\n\n// ── Auto-tracked (selectorless) mode logic (pure function, no hooks) ──────────\n\n/**\n * `getSnapshot` implementation for auto-tracked (selectorless) mode.\n *\n * Uses `proxy-compare` to diff only the properties the component actually read.\n * If the snapshot reference is the same, returns the cached tracking proxy.\n * If the snapshot changed but no tracked property differs (`isChanged` returns\n * false), also returns the cached proxy -- avoiding re-render. Only when a\n * relevant property changed does it create a new `createProxy` wrapper.\n *\n * Pure function -- no hooks, safe to call from `useSyncExternalStore`.\n */\nfunction getAutoTrackSnapshot<T extends object>(\n proxyStore: T,\n affected: WeakMap<object, unknown>,\n proxyCache: WeakMap<object, unknown>,\n prevSnapRef: React.RefObject<Snapshot<T> | undefined>,\n wrappedRef: React.RefObject<Snapshot<T> | undefined>,\n): Snapshot<T> {\n const nextSnap = snapshot(proxyStore);\n\n // If the raw snapshot is the same reference, nothing changed.\n if (prevSnapRef.current === nextSnap) {\n return wrappedRef.current as Snapshot<T>;\n }\n\n // Check if any property the component actually read has changed.\n if (\n prevSnapRef.current !== undefined &&\n !isChanged(prevSnapRef.current, nextSnap, affected)\n ) {\n // No property the component cares about changed → return same wrapped proxy.\n return wrappedRef.current as Snapshot<T>;\n }\n\n // Something relevant changed — create a new tracking proxy.\n prevSnapRef.current = nextSnap;\n const wrapped = createProxy(nextSnap, affected, proxyCache) as Snapshot<T>;\n wrappedRef.current = wrapped;\n return wrapped;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiBA,IAAa,cAAb,MAA+B;CAC7B,QAAQ,aAAa;;CAGrB,WAAqB,EAAE;;CAGvB,YAAY,SAA4B;AACtC,MAAI,QACF,MAAK,MAAM,CAAC,GAAG,MAAM,SAAS;GAC5B,MAAM,QAAQ,KAAK,SAAS,WAAW,CAAC,QAAQ,OAAO,GAAG,IAAI,EAAE,CAAC;AACjE,OAAI,UAAU,GACZ,MAAK,SAAS,SAAS,CAAC,GAAG,EAAE;OAE7B,MAAK,SAAS,KAAK,CAAC,GAAG,EAAE,CAAC;;;;CAOlC,IAAI,OAAe;AACjB,SAAO,KAAK,SAAS;;;CAIvB,IAAI,KAAuB;EACzB,MAAM,QAAQ,KAAK,SAAS,MAAM,CAAC,OAAO,OAAO,GAAG,GAAG,IAAI,CAAC;AAC5D,SAAO,QAAQ,MAAM,KAAK;;;CAI5B,IAAI,KAAiB;AACnB,SAAO,KAAK,SAAS,MAAM,CAAC,OAAO,OAAO,GAAG,GAAG,IAAI,CAAC;;;CAIvD,IAAI,KAAQ,OAAgB;EAC1B,MAAM,QAAQ,KAAK,SAAS,WAAW,CAAC,OAAO,OAAO,GAAG,GAAG,IAAI,CAAC;AACjE,MAAI,UAAU,GACZ,MAAK,SAAS,SAAS,CAAC,KAAK,MAAM;MAEnC,MAAK,SAAS,KAAK,CAAC,KAAK,MAAM,CAAC;AAElC,SAAO;;;CAIT,OAAO,KAAiB;EACtB,MAAM,QAAQ,KAAK,SAAS,WAAW,CAAC,OAAO,OAAO,GAAG,GAAG,IAAI,CAAC;AACjE,MAAI,UAAU,GAAI,QAAO;AACzB,OAAK,SAAS,OAAO,OAAO,EAAE;AAC9B,SAAO;;;CAIT,QAAc;AACZ,OAAK,SAAS,OAAO,GAAG,KAAK,SAAS,OAAO;;;CAI/C,OAA4B;AAC1B,SAAO,KAAK,SAAS,KAAK,CAAC,OAAO,EAAE,CAAC,OAAO,WAAW;;;CAIzD,SAA8B;AAC5B,SAAO,KAAK,SAAS,KAAK,GAAG,OAAO,EAAE,CAAC,OAAO,WAAW;;;CAI3D,UAAoC;AAClC,SAAO,KAAK,SAAS,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,EAAE,CAAW,CAAC,OAAO,WAAW;;;CAI3E,QAAQ,UAAoE;AAC1E,OAAK,MAAM,CAAC,GAAG,MAAM,KAAK,SACxB,UAAS,GAAG,GAAG,KAAK;;;CAKxB,CAAC,OAAO,YAAsC;AAC5C,SAAO,KAAK,SAAS;;;;;;;;;;;;;;;;AAmBzB,IAAa,cAAb,MAA4B;CAC1B,QAAQ,aAAa;;CAGrB,SAAc,EAAE;;CAGhB,YAAY,SAAuB;AACjC,MAAI,SACF;QAAK,MAAM,KAAK,QACd,KAAI,CAAC,KAAK,OAAO,MAAM,SAAS,OAAO,GAAG,MAAM,EAAE,CAAC,CACjD,MAAK,OAAO,KAAK,EAAE;;;;CAO3B,IAAI,OAAe;AACjB,SAAO,KAAK,OAAO;;;CAIrB,IAAI,OAAmB;AACrB,SAAO,KAAK,OAAO,MAAM,SAAS,OAAO,GAAG,MAAM,MAAM,CAAC;;;CAI3D,IAAI,OAAgB;AAClB,MAAI,CAAC,KAAK,IAAI,MAAM,CAClB,MAAK,OAAO,KAAK,MAAM;AAEzB,SAAO;;;CAIT,OAAO,OAAmB;EACxB,MAAM,QAAQ,KAAK,OAAO,WAAW,SAAS,OAAO,GAAG,MAAM,MAAM,CAAC;AACrE,MAAI,UAAU,GAAI,QAAO;AACzB,OAAK,OAAO,OAAO,OAAO,EAAE;AAC5B,SAAO;;;CAIT,QAAc;AACZ,OAAK,OAAO,OAAO,GAAG,KAAK,OAAO,OAAO;;;CAI3C,OAA4B;AAC1B,SAAO,KAAK,OAAO,OAAO,WAAW;;;CAIvC,SAA8B;AAC5B,SAAO,KAAK,OAAO,OAAO,WAAW;;;CAIvC,UAAoC;AAClC,SAAO,KAAK,OAAO,KAAK,MAAM,CAAC,GAAG,EAAE,CAAW,CAAC,OAAO,WAAW;;;CAIpE,QAAQ,UAAiE;AACvE,OAAK,MAAM,KAAK,KAAK,OACnB,UAAS,GAAG,GAAG,KAAK;;;CAKxB,CAAC,OAAO,YAAiC;AACvC,SAAO,KAAK,QAAQ;;;;;;;;;AAYxB,SAAgB,YACd,SACmB;AACnB,QAAO,IAAI,YAAY,QAAQ;;;;;;;;AASjC,SAAgB,YAAe,SAAuC;AACpE,QAAO,IAAI,YAAY,QAAQ;;;;;ACrLjC,SAAgB,SACd,YACA,UACA,SACiB;AAEjB,aAAY,WAAW;CAGvB,MAAMA,cAAY,aACf,kBAA8BC,UAAc,YAAY,cAAc,EACvE,CAAC,WAAW,CACb;CAKD,MAAM,UAAU,OAAgC,OAAU;CAC1D,MAAM,YAAY,OAAsB,OAAU;CAGlD,MAAM,WAAW,uBAAO,IAAI,SAA0B,CAAC,CAAC;CACxD,MAAM,aAAa,uBAAO,IAAI,SAA0B,CAAC,CAAC;CAC1D,MAAM,cAAc,OAAgC,OAAU;CAC9D,MAAM,aAAa,OAAgC,OAAU;CAI7D,MAAM,oBACJ,WACI,oBAAoB,YAAY,SAAS,WAAW,UAAU,QAAQ,GACtE,qBACE,YACA,UACA,YACA,aACA,WACD;AAEP,QAAO,qBAAqBD,aAAW,aAAa,YAAY;;;;;;;;;;;;AAelE,SAAS,oBACP,YACA,SACA,WACA,UACA,SACG;CACH,MAAM,WAAW,SAAS,WAAW;AAGrC,KAAI,QAAQ,YAAY,YAAY,UAAU,YAAY,OACxD,QAAO,UAAU;CAGnB,MAAM,aAAa,SAAS,SAAS;AACrC,SAAQ,UAAU;AAGlB,KACE,UAAU,YAAY,WACrB,UACG,QAAQ,UAAU,SAAS,WAAW,GACtC,OAAO,GAAG,UAAU,SAAS,WAAW,EAE5C,QAAO,UAAU;AAGnB,WAAU,UAAU;AACpB,QAAO;;;;;;;;;;;;;AAgBT,SAAS,qBACP,YACA,UACA,YACA,aACA,YACa;CACb,MAAM,WAAW,SAAS,WAAW;AAGrC,KAAI,YAAY,YAAY,SAC1B,QAAO,WAAW;AAIpB,KACE,YAAY,YAAY,UACxB,CAAC,UAAU,YAAY,SAAS,UAAU,SAAS,CAGnD,QAAO,WAAW;AAIpB,aAAY,UAAU;CACtB,MAAM,UAAU,YAAY,UAAU,UAAU,WAAW;AAC3D,YAAW,UAAU;AACrB,QAAO"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/collections/collections.ts","../src/utils/equality/equality.ts"],"sourcesContent":["import {PROXYABLE} from '../utils/internal/internal';\n\n// ── ReactiveMap ───────────────────────────────────────────────────────────────\n\n/**\n * A Map-like class backed by a plain array so `createClassyStore()` can proxy mutations.\n *\n * Native `Map` uses internal slots that ES6 Proxy can't intercept, so mutations\n * like `.set()` would be invisible to the store. `ReactiveMap` solves this by\n * storing entries in a plain array (`_entries`) that the proxy can track.\n *\n * Usage:\n * ```ts\n * const myStore = createClassyStore({ users: reactiveMap<string, User>() });\n * myStore.users.set('id1', { name: 'Alice' }); // reactive\n * ```\n */\nexport class ReactiveMap<K, V> {\n static [PROXYABLE] = true;\n\n /** @internal Backing storage — proxied by store(). */\n _entries: [K, V][] = [];\n\n /** Deduplicates initial entries by key (last value wins, matching native `Map`). */\n constructor(initial?: Iterable<[K, V]>) {\n if (initial) {\n for (const [k, v] of initial) {\n const index = this._entries.findIndex(([ek]) => Object.is(ek, k));\n if (index !== -1) {\n this._entries[index] = [k, v];\n } else {\n this._entries.push([k, v]);\n }\n }\n }\n }\n\n /** Returns the number of entries. */\n get size(): number {\n return this._entries.length;\n }\n\n /** Returns the value for `key`, or `undefined`. O(n) linear scan. */\n get(key: K): V | undefined {\n const entry = this._entries.find(([k]) => Object.is(k, key));\n return entry ? entry[1] : undefined;\n }\n\n /** Returns `true` if `key` exists. O(n) linear scan. */\n has(key: K): boolean {\n return this._entries.some(([k]) => Object.is(k, key));\n }\n\n /** Sets `key` to `value`. Updates in-place if key exists, appends otherwise. */\n set(key: K, value: V): this {\n const index = this._entries.findIndex(([k]) => Object.is(k, key));\n if (index !== -1) {\n this._entries[index] = [key, value];\n } else {\n this._entries.push([key, value]);\n }\n return this;\n }\n\n /** Removes the entry for `key`. Returns `true` if found. */\n delete(key: K): boolean {\n const index = this._entries.findIndex(([k]) => Object.is(k, key));\n if (index === -1) return false;\n this._entries.splice(index, 1);\n return true;\n }\n\n /** Removes all entries. Uses splice to trigger proxy notification. */\n clear(): void {\n this._entries.splice(0, this._entries.length);\n }\n\n /** Returns an iterator over the keys. */\n keys(): IterableIterator<K> {\n return this._entries.map(([k]) => k)[Symbol.iterator]();\n }\n\n /** Returns an iterator over the values. */\n values(): IterableIterator<V> {\n return this._entries.map(([, v]) => v)[Symbol.iterator]();\n }\n\n /** Returns an iterator over [key, value] pairs. */\n entries(): IterableIterator<[K, V]> {\n return this._entries.map(([k, v]) => [k, v] as [K, V])[Symbol.iterator]();\n }\n\n /** Calls `callback` for each entry, matching the native `Map.forEach` signature. */\n forEach(callback: (value: V, key: K, map: ReactiveMap<K, V>) => void): void {\n for (const [k, v] of this._entries) {\n callback(v, k, this);\n }\n }\n\n /** Enables `for...of` iteration over [key, value] pairs. */\n [Symbol.iterator](): IterableIterator<[K, V]> {\n return this.entries();\n }\n}\n\n// ── ReactiveSet ───────────────────────────────────────────────────────────────\n\n/**\n * A Set-like class backed by a plain array so `createClassyStore()` can proxy mutations.\n *\n * Native `Set` uses internal slots that ES6 Proxy can't intercept, so mutations\n * like `.add()` would be invisible to the store. `ReactiveSet` solves this by\n * storing items in a plain array (`_items`) that the proxy can track.\n *\n * Usage:\n * ```ts\n * const myStore = createClassyStore({ tags: reactiveSet<string>(['urgent']) });\n * myStore.tags.add('bug'); // reactive\n * ```\n */\nexport class ReactiveSet<T> {\n static [PROXYABLE] = true;\n\n /** @internal Backing storage — proxied by store(). */\n _items: T[] = [];\n\n /** Deduplicates initial values using `Object.is` comparison. */\n constructor(initial?: Iterable<T>) {\n if (initial) {\n for (const v of initial) {\n if (!this._items.some((item) => Object.is(item, v))) {\n this._items.push(v);\n }\n }\n }\n }\n\n /** Returns the number of unique items. */\n get size(): number {\n return this._items.length;\n }\n\n /** Returns `true` if `value` exists. O(n) linear scan. */\n has(value: T): boolean {\n return this._items.some((item) => Object.is(item, value));\n }\n\n /** Adds `value` if not already present (no-op for duplicates). */\n add(value: T): this {\n if (!this.has(value)) {\n this._items.push(value);\n }\n return this;\n }\n\n /** Removes `value`. Returns `true` if found. */\n delete(value: T): boolean {\n const index = this._items.findIndex((item) => Object.is(item, value));\n if (index === -1) return false;\n this._items.splice(index, 1);\n return true;\n }\n\n /** Removes all items. Uses splice to trigger proxy notification. */\n clear(): void {\n this._items.splice(0, this._items.length);\n }\n\n /** Returns an iterator over the values (same as `values()`, matching Set API). */\n keys(): IterableIterator<T> {\n return this._items[Symbol.iterator]();\n }\n\n /** Returns an iterator over the values. */\n values(): IterableIterator<T> {\n return this._items[Symbol.iterator]();\n }\n\n /** Returns an iterator over [value, value] pairs, matching the native Set API. */\n entries(): IterableIterator<[T, T]> {\n return this._items.map((v) => [v, v] as [T, T])[Symbol.iterator]();\n }\n\n /** Calls `callback` for each item, matching the native `Set.forEach` signature. */\n forEach(callback: (value: T, key: T, set: ReactiveSet<T>) => void): void {\n for (const v of this._items) {\n callback(v, v, this);\n }\n }\n\n /** Enables `for...of` iteration over values. */\n [Symbol.iterator](): IterableIterator<T> {\n return this.values();\n }\n}\n\n// ── Factory functions ─────────────────────────────────────────────────────────\n\n/**\n * Creates a reactive Map-like collection backed by a plain array.\n * Wrap the parent object with `createClassyStore()` for full reactivity.\n *\n * @param initial - Optional iterable of `[key, value]` pairs.\n */\nexport function reactiveMap<K, V>(\n initial?: Iterable<[K, V]>,\n): ReactiveMap<K, V> {\n return new ReactiveMap(initial);\n}\n\n/**\n * Creates a reactive Set-like collection backed by a plain array.\n * Wrap the parent object with `createClassyStore()` for full reactivity.\n *\n * @param initial - Optional iterable of values.\n */\nexport function reactiveSet<T>(initial?: Iterable<T>): ReactiveSet<T> {\n return new ReactiveSet(initial);\n}\n","/**\n * Shallow-equal comparison for objects and arrays.\n * Useful as a custom `isEqual` for `useStore` selectors that return objects/arrays.\n *\n * - Primitives compared with `Object.is`.\n * - Arrays: length + element-wise `Object.is`.\n * - Objects: key count + value-wise `Object.is`.\n */\nexport function shallowEqual<T>(a: T, b: T): boolean {\n if (Object.is(a, b)) return true;\n if (\n typeof a !== 'object' ||\n a === null ||\n typeof b !== 'object' ||\n b === null\n ) {\n return false;\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) return false;\n }\n return true;\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (\n !Object.hasOwn(b, key) ||\n !Object.is(\n (a as Record<string, unknown>)[key],\n (b as Record<string, unknown>)[key],\n )\n ) {\n return false;\n }\n }\n return true;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_snapshot = require('../snapshot-BKVFJLuo.cjs');
|
|
3
|
+
let proxy_compare = require("proxy-compare");
|
|
4
|
+
let react = require("react");
|
|
5
|
+
|
|
6
|
+
//#region src/react/react.ts
|
|
7
|
+
function useStore(proxyStore, selector, isEqual) {
|
|
8
|
+
require_snapshot.getInternal(proxyStore);
|
|
9
|
+
const subscribe$1 = (0, react.useCallback)((onStoreChange) => require_snapshot.subscribe(proxyStore, onStoreChange), [proxyStore]);
|
|
10
|
+
const snapRef = (0, react.useRef)(void 0);
|
|
11
|
+
const resultRef = (0, react.useRef)(void 0);
|
|
12
|
+
const affected = (0, react.useRef)(/* @__PURE__ */ new WeakMap()).current;
|
|
13
|
+
const proxyCache = (0, react.useRef)(/* @__PURE__ */ new WeakMap()).current;
|
|
14
|
+
const prevSnapRef = (0, react.useRef)(void 0);
|
|
15
|
+
const wrappedRef = (0, react.useRef)(void 0);
|
|
16
|
+
const getSnapshot = () => selector ? getSelectorSnapshot(proxyStore, snapRef, resultRef, selector, isEqual) : getAutoTrackSnapshot(proxyStore, affected, proxyCache, prevSnapRef, wrappedRef);
|
|
17
|
+
return (0, react.useSyncExternalStore)(subscribe$1, getSnapshot, getSnapshot);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* `getSnapshot` implementation for selector mode.
|
|
21
|
+
*
|
|
22
|
+
* Fast-paths when the snapshot reference hasn't changed (O(1)). Otherwise
|
|
23
|
+
* runs the selector against the new snapshot and compares the result to the
|
|
24
|
+
* previous one via `Object.is` (or a custom `isEqual`). Returns the previous
|
|
25
|
+
* result reference when equal, preventing unnecessary React re-renders.
|
|
26
|
+
*
|
|
27
|
+
* Pure function -- no hooks, safe to call from `useSyncExternalStore`.
|
|
28
|
+
*/
|
|
29
|
+
function getSelectorSnapshot(proxyStore, snapRef, resultRef, selector, isEqual) {
|
|
30
|
+
const nextSnap = require_snapshot.snapshot(proxyStore);
|
|
31
|
+
if (snapRef.current === nextSnap && resultRef.current !== void 0) return resultRef.current;
|
|
32
|
+
const nextResult = selector(nextSnap);
|
|
33
|
+
snapRef.current = nextSnap;
|
|
34
|
+
if (resultRef.current !== void 0 && (isEqual ? isEqual(resultRef.current, nextResult) : Object.is(resultRef.current, nextResult))) return resultRef.current;
|
|
35
|
+
resultRef.current = nextResult;
|
|
36
|
+
return nextResult;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* `getSnapshot` implementation for auto-tracked (selectorless) mode.
|
|
40
|
+
*
|
|
41
|
+
* Uses `proxy-compare` to diff only the properties the component actually read.
|
|
42
|
+
* If the snapshot reference is the same, returns the cached tracking proxy.
|
|
43
|
+
* If the snapshot changed but no tracked property differs (`isChanged` returns
|
|
44
|
+
* false), also returns the cached proxy -- avoiding re-render. Only when a
|
|
45
|
+
* relevant property changed does it create a new `createProxy` wrapper.
|
|
46
|
+
*
|
|
47
|
+
* Pure function -- no hooks, safe to call from `useSyncExternalStore`.
|
|
48
|
+
*/
|
|
49
|
+
function getAutoTrackSnapshot(proxyStore, affected, proxyCache, prevSnapRef, wrappedRef) {
|
|
50
|
+
const nextSnap = require_snapshot.snapshot(proxyStore);
|
|
51
|
+
if (prevSnapRef.current === nextSnap) return wrappedRef.current;
|
|
52
|
+
if (prevSnapRef.current !== void 0 && !(0, proxy_compare.isChanged)(prevSnapRef.current, nextSnap, affected)) return wrappedRef.current;
|
|
53
|
+
prevSnapRef.current = nextSnap;
|
|
54
|
+
const wrapped = (0, proxy_compare.createProxy)(nextSnap, affected, proxyCache);
|
|
55
|
+
wrappedRef.current = wrapped;
|
|
56
|
+
return wrapped;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//#endregion
|
|
60
|
+
exports.useStore = useStore;
|
|
61
|
+
//# sourceMappingURL=react.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.cjs","names":["subscribe","coreSubscribe","snapshot"],"sources":["../../src/react/react.ts"],"sourcesContent":["import {createProxy, isChanged} from 'proxy-compare';\nimport {useCallback, useRef, useSyncExternalStore} from 'react';\nimport {subscribe as coreSubscribe, getInternal} from '../core/core';\nimport {snapshot} from '../snapshot/snapshot';\nimport type {Snapshot} from '../types';\n\n// ── Overloads ─────────────────────────────────────────────────────────────────\n\n/**\n * Subscribe to a store proxy with an explicit selector.\n *\n * Re-renders only when the selected value changes (compared via `Object.is`\n * by default, or a custom `isEqual`).\n *\n * @param proxyStore - A reactive proxy created by `store()`.\n * @param selector - Picks data from the immutable snapshot.\n * @param isEqual - Optional custom equality function (default: `Object.is`).\n */\nexport function useStore<T extends object, S>(\n proxyStore: T,\n selector: (snap: Snapshot<T>) => S,\n isEqual?: (a: S, b: S) => boolean,\n): S;\n\n/**\n * Subscribe to a store proxy **without** a selector (auto-tracked mode).\n *\n * Returns a `proxy-compare` tracking proxy over the immutable snapshot.\n * The component only re-renders when a property it actually read changes.\n *\n * @param proxyStore - A reactive proxy created by `store()`.\n */\nexport function useStore<T extends object>(proxyStore: T): Snapshot<T>;\n\n// ── Implementation ────────────────────────────────────────────────────────────\n\nexport function useStore<T extends object, S>(\n proxyStore: T,\n selector?: (snap: Snapshot<T>) => S,\n isEqual?: (a: S, b: S) => boolean,\n): Snapshot<T> | S {\n // Validate that the argument is actually a store proxy (throws if not).\n getInternal(proxyStore);\n\n // Stable subscribe function (internal identity never changes for a given store).\n const subscribe = useCallback(\n (onStoreChange: () => void) => coreSubscribe(proxyStore, onStoreChange),\n [proxyStore],\n );\n\n // ── Refs used by both modes (always allocated to satisfy Rules of Hooks) ──\n\n // Selector mode refs\n const snapRef = useRef<Snapshot<T> | undefined>(undefined);\n const resultRef = useRef<S | undefined>(undefined);\n\n // Auto-track mode refs\n const affected = useRef(new WeakMap<object, unknown>()).current;\n const proxyCache = useRef(new WeakMap<object, unknown>()).current;\n const prevSnapRef = useRef<Snapshot<T> | undefined>(undefined);\n const wrappedRef = useRef<Snapshot<T> | undefined>(undefined);\n\n // ── Single getSnapshot for useSyncExternalStore ───────────────────────────\n\n const getSnapshot = (): Snapshot<T> | S =>\n selector\n ? getSelectorSnapshot(proxyStore, snapRef, resultRef, selector, isEqual)\n : getAutoTrackSnapshot(\n proxyStore,\n affected,\n proxyCache,\n prevSnapRef,\n wrappedRef,\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n// ── Selector mode logic (pure function, no hooks) ─────────────────────────────\n\n/**\n * `getSnapshot` implementation for selector mode.\n *\n * Fast-paths when the snapshot reference hasn't changed (O(1)). Otherwise\n * runs the selector against the new snapshot and compares the result to the\n * previous one via `Object.is` (or a custom `isEqual`). Returns the previous\n * result reference when equal, preventing unnecessary React re-renders.\n *\n * Pure function -- no hooks, safe to call from `useSyncExternalStore`.\n */\nfunction getSelectorSnapshot<T extends object, S>(\n proxyStore: T,\n snapRef: React.RefObject<Snapshot<T> | undefined>,\n resultRef: React.RefObject<S | undefined>,\n selector: (snap: Snapshot<T>) => S,\n isEqual?: (a: S, b: S) => boolean,\n): S {\n const nextSnap = snapshot(proxyStore);\n\n // Fast path: same snapshot reference → same result.\n if (snapRef.current === nextSnap && resultRef.current !== undefined) {\n return resultRef.current;\n }\n\n const nextResult = selector(nextSnap);\n snapRef.current = nextSnap;\n\n // Check equality with previous result.\n if (\n resultRef.current !== undefined &&\n (isEqual\n ? isEqual(resultRef.current, nextResult)\n : Object.is(resultRef.current, nextResult))\n ) {\n return resultRef.current;\n }\n\n resultRef.current = nextResult;\n return nextResult;\n}\n\n// ── Auto-tracked (selectorless) mode logic (pure function, no hooks) ──────────\n\n/**\n * `getSnapshot` implementation for auto-tracked (selectorless) mode.\n *\n * Uses `proxy-compare` to diff only the properties the component actually read.\n * If the snapshot reference is the same, returns the cached tracking proxy.\n * If the snapshot changed but no tracked property differs (`isChanged` returns\n * false), also returns the cached proxy -- avoiding re-render. Only when a\n * relevant property changed does it create a new `createProxy` wrapper.\n *\n * Pure function -- no hooks, safe to call from `useSyncExternalStore`.\n */\nfunction getAutoTrackSnapshot<T extends object>(\n proxyStore: T,\n affected: WeakMap<object, unknown>,\n proxyCache: WeakMap<object, unknown>,\n prevSnapRef: React.RefObject<Snapshot<T> | undefined>,\n wrappedRef: React.RefObject<Snapshot<T> | undefined>,\n): Snapshot<T> {\n const nextSnap = snapshot(proxyStore);\n\n // If the raw snapshot is the same reference, nothing changed.\n if (prevSnapRef.current === nextSnap) {\n return wrappedRef.current as Snapshot<T>;\n }\n\n // Check if any property the component actually read has changed.\n if (\n prevSnapRef.current !== undefined &&\n !isChanged(prevSnapRef.current, nextSnap, affected)\n ) {\n // No property the component cares about changed → return same wrapped proxy.\n return wrappedRef.current as Snapshot<T>;\n }\n\n // Something relevant changed — create a new tracking proxy.\n prevSnapRef.current = nextSnap;\n const wrapped = createProxy(nextSnap, affected, proxyCache) as Snapshot<T>;\n wrappedRef.current = wrapped;\n return wrapped;\n}\n"],"mappings":";;;;;;AAoCA,SAAgB,SACd,YACA,UACA,SACiB;AAEjB,8BAAY,WAAW;CAGvB,MAAMA,sCACH,kBAA8BC,2BAAc,YAAY,cAAc,EACvE,CAAC,WAAW,CACb;CAKD,MAAM,4BAA0C,OAAU;CAC1D,MAAM,8BAAkC,OAAU;CAGlD,MAAM,6CAAkB,IAAI,SAA0B,CAAC,CAAC;CACxD,MAAM,+CAAoB,IAAI,SAA0B,CAAC,CAAC;CAC1D,MAAM,gCAA8C,OAAU;CAC9D,MAAM,+BAA6C,OAAU;CAI7D,MAAM,oBACJ,WACI,oBAAoB,YAAY,SAAS,WAAW,UAAU,QAAQ,GACtE,qBACE,YACA,UACA,YACA,aACA,WACD;AAEP,wCAA4BD,aAAW,aAAa,YAAY;;;;;;;;;;;;AAelE,SAAS,oBACP,YACA,SACA,WACA,UACA,SACG;CACH,MAAM,WAAWE,0BAAS,WAAW;AAGrC,KAAI,QAAQ,YAAY,YAAY,UAAU,YAAY,OACxD,QAAO,UAAU;CAGnB,MAAM,aAAa,SAAS,SAAS;AACrC,SAAQ,UAAU;AAGlB,KACE,UAAU,YAAY,WACrB,UACG,QAAQ,UAAU,SAAS,WAAW,GACtC,OAAO,GAAG,UAAU,SAAS,WAAW,EAE5C,QAAO,UAAU;AAGnB,WAAU,UAAU;AACpB,QAAO;;;;;;;;;;;;;AAgBT,SAAS,qBACP,YACA,UACA,YACA,aACA,YACa;CACb,MAAM,WAAWA,0BAAS,WAAW;AAGrC,KAAI,YAAY,YAAY,SAC1B,QAAO,WAAW;AAIpB,KACE,YAAY,YAAY,UACxB,8BAAW,YAAY,SAAS,UAAU,SAAS,CAGnD,QAAO,WAAW;AAIpB,aAAY,UAAU;CACtB,MAAM,yCAAsB,UAAU,UAAU,WAAW;AAC3D,YAAW,UAAU;AACrB,QAAO"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { t as Snapshot } from "../types-B6RZUB86.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/react/react.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Subscribe to a store proxy with an explicit selector.
|
|
6
|
+
*
|
|
7
|
+
* Re-renders only when the selected value changes (compared via `Object.is`
|
|
8
|
+
* by default, or a custom `isEqual`).
|
|
9
|
+
*
|
|
10
|
+
* @param proxyStore - A reactive proxy created by `store()`.
|
|
11
|
+
* @param selector - Picks data from the immutable snapshot.
|
|
12
|
+
* @param isEqual - Optional custom equality function (default: `Object.is`).
|
|
13
|
+
*/
|
|
14
|
+
declare function useStore<T extends object, S>(proxyStore: T, selector: (snap: Snapshot<T>) => S, isEqual?: (a: S, b: S) => boolean): S;
|
|
15
|
+
/**
|
|
16
|
+
* Subscribe to a store proxy **without** a selector (auto-tracked mode).
|
|
17
|
+
*
|
|
18
|
+
* Returns a `proxy-compare` tracking proxy over the immutable snapshot.
|
|
19
|
+
* The component only re-renders when a property it actually read changes.
|
|
20
|
+
*
|
|
21
|
+
* @param proxyStore - A reactive proxy created by `store()`.
|
|
22
|
+
*/
|
|
23
|
+
declare function useStore<T extends object>(proxyStore: T): Snapshot<T>;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { useStore };
|
|
26
|
+
//# sourceMappingURL=react.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.cts","names":[],"sources":["../../src/react/react.ts"],"mappings":";;;;;AAkBA;;;;;;;;iBAAgB,QAAA,qBAAA,CACd,UAAA,EAAY,CAAA,EACZ,QAAA,GAAW,IAAA,EAAM,QAAA,CAAS,CAAA,MAAO,CAAA,EACjC,OAAA,IAAW,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,eACnB,CAAA;;;;;;;;;iBAUa,QAAA,kBAAA,CAA2B,UAAA,EAAY,CAAA,GAAI,QAAA,CAAS,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { t as Snapshot } from "../types-vWYkF3tH.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/react/react.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Subscribe to a store proxy with an explicit selector.
|
|
6
|
+
*
|
|
7
|
+
* Re-renders only when the selected value changes (compared via `Object.is`
|
|
8
|
+
* by default, or a custom `isEqual`).
|
|
9
|
+
*
|
|
10
|
+
* @param proxyStore - A reactive proxy created by `store()`.
|
|
11
|
+
* @param selector - Picks data from the immutable snapshot.
|
|
12
|
+
* @param isEqual - Optional custom equality function (default: `Object.is`).
|
|
13
|
+
*/
|
|
14
|
+
declare function useStore<T extends object, S>(proxyStore: T, selector: (snap: Snapshot<T>) => S, isEqual?: (a: S, b: S) => boolean): S;
|
|
15
|
+
/**
|
|
16
|
+
* Subscribe to a store proxy **without** a selector (auto-tracked mode).
|
|
17
|
+
*
|
|
18
|
+
* Returns a `proxy-compare` tracking proxy over the immutable snapshot.
|
|
19
|
+
* The component only re-renders when a property it actually read changes.
|
|
20
|
+
*
|
|
21
|
+
* @param proxyStore - A reactive proxy created by `store()`.
|
|
22
|
+
*/
|
|
23
|
+
declare function useStore<T extends object>(proxyStore: T): Snapshot<T>;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { useStore };
|
|
26
|
+
//# sourceMappingURL=react.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.mts","names":[],"sources":["../../src/react/react.ts"],"mappings":";;;;;AAkBA;;;;;;;;iBAAgB,QAAA,qBAAA,CACd,UAAA,EAAY,CAAA,EACZ,QAAA,GAAW,IAAA,EAAM,QAAA,CAAS,CAAA,MAAO,CAAA,EACjC,OAAA,IAAW,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,eACnB,CAAA;;;;;;;;;iBAUa,QAAA,kBAAA,CAA2B,UAAA,EAAY,CAAA,GAAI,QAAA,CAAS,CAAA"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { a as subscribe, r as getInternal, t as snapshot } from "../snapshot-P0QPV1ER.mjs";
|
|
2
|
+
import { createProxy, isChanged } from "proxy-compare";
|
|
3
|
+
import { useCallback, useRef, useSyncExternalStore } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/react/react.ts
|
|
6
|
+
function useStore(proxyStore, selector, isEqual) {
|
|
7
|
+
getInternal(proxyStore);
|
|
8
|
+
const subscribe$1 = useCallback((onStoreChange) => subscribe(proxyStore, onStoreChange), [proxyStore]);
|
|
9
|
+
const snapRef = useRef(void 0);
|
|
10
|
+
const resultRef = useRef(void 0);
|
|
11
|
+
const affected = useRef(/* @__PURE__ */ new WeakMap()).current;
|
|
12
|
+
const proxyCache = useRef(/* @__PURE__ */ new WeakMap()).current;
|
|
13
|
+
const prevSnapRef = useRef(void 0);
|
|
14
|
+
const wrappedRef = useRef(void 0);
|
|
15
|
+
const getSnapshot = () => selector ? getSelectorSnapshot(proxyStore, snapRef, resultRef, selector, isEqual) : getAutoTrackSnapshot(proxyStore, affected, proxyCache, prevSnapRef, wrappedRef);
|
|
16
|
+
return useSyncExternalStore(subscribe$1, getSnapshot, getSnapshot);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* `getSnapshot` implementation for selector mode.
|
|
20
|
+
*
|
|
21
|
+
* Fast-paths when the snapshot reference hasn't changed (O(1)). Otherwise
|
|
22
|
+
* runs the selector against the new snapshot and compares the result to the
|
|
23
|
+
* previous one via `Object.is` (or a custom `isEqual`). Returns the previous
|
|
24
|
+
* result reference when equal, preventing unnecessary React re-renders.
|
|
25
|
+
*
|
|
26
|
+
* Pure function -- no hooks, safe to call from `useSyncExternalStore`.
|
|
27
|
+
*/
|
|
28
|
+
function getSelectorSnapshot(proxyStore, snapRef, resultRef, selector, isEqual) {
|
|
29
|
+
const nextSnap = snapshot(proxyStore);
|
|
30
|
+
if (snapRef.current === nextSnap && resultRef.current !== void 0) return resultRef.current;
|
|
31
|
+
const nextResult = selector(nextSnap);
|
|
32
|
+
snapRef.current = nextSnap;
|
|
33
|
+
if (resultRef.current !== void 0 && (isEqual ? isEqual(resultRef.current, nextResult) : Object.is(resultRef.current, nextResult))) return resultRef.current;
|
|
34
|
+
resultRef.current = nextResult;
|
|
35
|
+
return nextResult;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* `getSnapshot` implementation for auto-tracked (selectorless) mode.
|
|
39
|
+
*
|
|
40
|
+
* Uses `proxy-compare` to diff only the properties the component actually read.
|
|
41
|
+
* If the snapshot reference is the same, returns the cached tracking proxy.
|
|
42
|
+
* If the snapshot changed but no tracked property differs (`isChanged` returns
|
|
43
|
+
* false), also returns the cached proxy -- avoiding re-render. Only when a
|
|
44
|
+
* relevant property changed does it create a new `createProxy` wrapper.
|
|
45
|
+
*
|
|
46
|
+
* Pure function -- no hooks, safe to call from `useSyncExternalStore`.
|
|
47
|
+
*/
|
|
48
|
+
function getAutoTrackSnapshot(proxyStore, affected, proxyCache, prevSnapRef, wrappedRef) {
|
|
49
|
+
const nextSnap = snapshot(proxyStore);
|
|
50
|
+
if (prevSnapRef.current === nextSnap) return wrappedRef.current;
|
|
51
|
+
if (prevSnapRef.current !== void 0 && !isChanged(prevSnapRef.current, nextSnap, affected)) return wrappedRef.current;
|
|
52
|
+
prevSnapRef.current = nextSnap;
|
|
53
|
+
const wrapped = createProxy(nextSnap, affected, proxyCache);
|
|
54
|
+
wrappedRef.current = wrapped;
|
|
55
|
+
return wrapped;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
//#endregion
|
|
59
|
+
export { useStore };
|
|
60
|
+
//# sourceMappingURL=react.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.mjs","names":["subscribe","coreSubscribe"],"sources":["../../src/react/react.ts"],"sourcesContent":["import {createProxy, isChanged} from 'proxy-compare';\nimport {useCallback, useRef, useSyncExternalStore} from 'react';\nimport {subscribe as coreSubscribe, getInternal} from '../core/core';\nimport {snapshot} from '../snapshot/snapshot';\nimport type {Snapshot} from '../types';\n\n// ── Overloads ─────────────────────────────────────────────────────────────────\n\n/**\n * Subscribe to a store proxy with an explicit selector.\n *\n * Re-renders only when the selected value changes (compared via `Object.is`\n * by default, or a custom `isEqual`).\n *\n * @param proxyStore - A reactive proxy created by `store()`.\n * @param selector - Picks data from the immutable snapshot.\n * @param isEqual - Optional custom equality function (default: `Object.is`).\n */\nexport function useStore<T extends object, S>(\n proxyStore: T,\n selector: (snap: Snapshot<T>) => S,\n isEqual?: (a: S, b: S) => boolean,\n): S;\n\n/**\n * Subscribe to a store proxy **without** a selector (auto-tracked mode).\n *\n * Returns a `proxy-compare` tracking proxy over the immutable snapshot.\n * The component only re-renders when a property it actually read changes.\n *\n * @param proxyStore - A reactive proxy created by `store()`.\n */\nexport function useStore<T extends object>(proxyStore: T): Snapshot<T>;\n\n// ── Implementation ────────────────────────────────────────────────────────────\n\nexport function useStore<T extends object, S>(\n proxyStore: T,\n selector?: (snap: Snapshot<T>) => S,\n isEqual?: (a: S, b: S) => boolean,\n): Snapshot<T> | S {\n // Validate that the argument is actually a store proxy (throws if not).\n getInternal(proxyStore);\n\n // Stable subscribe function (internal identity never changes for a given store).\n const subscribe = useCallback(\n (onStoreChange: () => void) => coreSubscribe(proxyStore, onStoreChange),\n [proxyStore],\n );\n\n // ── Refs used by both modes (always allocated to satisfy Rules of Hooks) ──\n\n // Selector mode refs\n const snapRef = useRef<Snapshot<T> | undefined>(undefined);\n const resultRef = useRef<S | undefined>(undefined);\n\n // Auto-track mode refs\n const affected = useRef(new WeakMap<object, unknown>()).current;\n const proxyCache = useRef(new WeakMap<object, unknown>()).current;\n const prevSnapRef = useRef<Snapshot<T> | undefined>(undefined);\n const wrappedRef = useRef<Snapshot<T> | undefined>(undefined);\n\n // ── Single getSnapshot for useSyncExternalStore ───────────────────────────\n\n const getSnapshot = (): Snapshot<T> | S =>\n selector\n ? getSelectorSnapshot(proxyStore, snapRef, resultRef, selector, isEqual)\n : getAutoTrackSnapshot(\n proxyStore,\n affected,\n proxyCache,\n prevSnapRef,\n wrappedRef,\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n// ── Selector mode logic (pure function, no hooks) ─────────────────────────────\n\n/**\n * `getSnapshot` implementation for selector mode.\n *\n * Fast-paths when the snapshot reference hasn't changed (O(1)). Otherwise\n * runs the selector against the new snapshot and compares the result to the\n * previous one via `Object.is` (or a custom `isEqual`). Returns the previous\n * result reference when equal, preventing unnecessary React re-renders.\n *\n * Pure function -- no hooks, safe to call from `useSyncExternalStore`.\n */\nfunction getSelectorSnapshot<T extends object, S>(\n proxyStore: T,\n snapRef: React.RefObject<Snapshot<T> | undefined>,\n resultRef: React.RefObject<S | undefined>,\n selector: (snap: Snapshot<T>) => S,\n isEqual?: (a: S, b: S) => boolean,\n): S {\n const nextSnap = snapshot(proxyStore);\n\n // Fast path: same snapshot reference → same result.\n if (snapRef.current === nextSnap && resultRef.current !== undefined) {\n return resultRef.current;\n }\n\n const nextResult = selector(nextSnap);\n snapRef.current = nextSnap;\n\n // Check equality with previous result.\n if (\n resultRef.current !== undefined &&\n (isEqual\n ? isEqual(resultRef.current, nextResult)\n : Object.is(resultRef.current, nextResult))\n ) {\n return resultRef.current;\n }\n\n resultRef.current = nextResult;\n return nextResult;\n}\n\n// ── Auto-tracked (selectorless) mode logic (pure function, no hooks) ──────────\n\n/**\n * `getSnapshot` implementation for auto-tracked (selectorless) mode.\n *\n * Uses `proxy-compare` to diff only the properties the component actually read.\n * If the snapshot reference is the same, returns the cached tracking proxy.\n * If the snapshot changed but no tracked property differs (`isChanged` returns\n * false), also returns the cached proxy -- avoiding re-render. Only when a\n * relevant property changed does it create a new `createProxy` wrapper.\n *\n * Pure function -- no hooks, safe to call from `useSyncExternalStore`.\n */\nfunction getAutoTrackSnapshot<T extends object>(\n proxyStore: T,\n affected: WeakMap<object, unknown>,\n proxyCache: WeakMap<object, unknown>,\n prevSnapRef: React.RefObject<Snapshot<T> | undefined>,\n wrappedRef: React.RefObject<Snapshot<T> | undefined>,\n): Snapshot<T> {\n const nextSnap = snapshot(proxyStore);\n\n // If the raw snapshot is the same reference, nothing changed.\n if (prevSnapRef.current === nextSnap) {\n return wrappedRef.current as Snapshot<T>;\n }\n\n // Check if any property the component actually read has changed.\n if (\n prevSnapRef.current !== undefined &&\n !isChanged(prevSnapRef.current, nextSnap, affected)\n ) {\n // No property the component cares about changed → return same wrapped proxy.\n return wrappedRef.current as Snapshot<T>;\n }\n\n // Something relevant changed — create a new tracking proxy.\n prevSnapRef.current = nextSnap;\n const wrapped = createProxy(nextSnap, affected, proxyCache) as Snapshot<T>;\n wrappedRef.current = wrapped;\n return wrapped;\n}\n"],"mappings":";;;;;AAoCA,SAAgB,SACd,YACA,UACA,SACiB;AAEjB,aAAY,WAAW;CAGvB,MAAMA,cAAY,aACf,kBAA8BC,UAAc,YAAY,cAAc,EACvE,CAAC,WAAW,CACb;CAKD,MAAM,UAAU,OAAgC,OAAU;CAC1D,MAAM,YAAY,OAAsB,OAAU;CAGlD,MAAM,WAAW,uBAAO,IAAI,SAA0B,CAAC,CAAC;CACxD,MAAM,aAAa,uBAAO,IAAI,SAA0B,CAAC,CAAC;CAC1D,MAAM,cAAc,OAAgC,OAAU;CAC9D,MAAM,aAAa,OAAgC,OAAU;CAI7D,MAAM,oBACJ,WACI,oBAAoB,YAAY,SAAS,WAAW,UAAU,QAAQ,GACtE,qBACE,YACA,UACA,YACA,aACA,WACD;AAEP,QAAO,qBAAqBD,aAAW,aAAa,YAAY;;;;;;;;;;;;AAelE,SAAS,oBACP,YACA,SACA,WACA,UACA,SACG;CACH,MAAM,WAAW,SAAS,WAAW;AAGrC,KAAI,QAAQ,YAAY,YAAY,UAAU,YAAY,OACxD,QAAO,UAAU;CAGnB,MAAM,aAAa,SAAS,SAAS;AACrC,SAAQ,UAAU;AAGlB,KACE,UAAU,YAAY,WACrB,UACG,QAAQ,UAAU,SAAS,WAAW,GACtC,OAAO,GAAG,UAAU,SAAS,WAAW,EAE5C,QAAO,UAAU;AAGnB,WAAU,UAAU;AACpB,QAAO;;;;;;;;;;;;;AAgBT,SAAS,qBACP,YACA,UACA,YACA,aACA,YACa;CACb,MAAM,WAAW,SAAS,WAAW;AAGrC,KAAI,YAAY,YAAY,SAC1B,QAAO,WAAW;AAIpB,KACE,YAAY,YAAY,UACxB,CAAC,UAAU,YAAY,SAAS,UAAU,SAAS,CAGnD,QAAO,WAAW;AAIpB,aAAY,UAAU;CACtB,MAAM,UAAU,YAAY,UAAU,UAAU,WAAW;AAC3D,YAAW,UAAU;AACrB,QAAO"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
//#region src/utils.ts
|
|
2
|
+
//#region src/utils/internal/internal.ts
|
|
3
3
|
const objectProto = Object.getPrototypeOf({});
|
|
4
4
|
/**
|
|
5
5
|
* Symbol that class instances can use to opt-in to deep proxying.
|
|
@@ -47,31 +47,9 @@ function findGetterDescriptor(target, prop) {
|
|
|
47
47
|
proto = Object.getPrototypeOf(proto);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Shallow-equal comparison for objects and arrays.
|
|
52
|
-
* Useful as a custom `isEqual` for `useStore` selectors that return objects/arrays.
|
|
53
|
-
*
|
|
54
|
-
* - Primitives compared with `Object.is`.
|
|
55
|
-
* - Arrays: length + element-wise `Object.is`.
|
|
56
|
-
* - Objects: key count + value-wise `Object.is`.
|
|
57
|
-
*/
|
|
58
|
-
function shallowEqual(a, b) {
|
|
59
|
-
if (Object.is(a, b)) return true;
|
|
60
|
-
if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return false;
|
|
61
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
62
|
-
if (a.length !== b.length) return false;
|
|
63
|
-
for (let i = 0; i < a.length; i++) if (!Object.is(a[i], b[i])) return false;
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
const keysA = Object.keys(a);
|
|
67
|
-
const keysB = Object.keys(b);
|
|
68
|
-
if (keysA.length !== keysB.length) return false;
|
|
69
|
-
for (const key of keysA) if (!Object.hasOwn(b, key) || !Object.is(a[key], b[key])) return false;
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
50
|
|
|
73
51
|
//#endregion
|
|
74
|
-
//#region src/core.ts
|
|
52
|
+
//#region src/core/core.ts
|
|
75
53
|
/** Global version counter shared across all stores. */
|
|
76
54
|
let globalVersion = 0;
|
|
77
55
|
/** Maps every store proxy → its internal bookkeeping. */
|
|
@@ -148,7 +126,7 @@ function evaluateComputed(internal, prop, getterFn, receiver) {
|
|
|
148
126
|
}
|
|
149
127
|
/**
|
|
150
128
|
* Retrieve the internal bookkeeping for a store proxy.
|
|
151
|
-
* Throws if the object was not created with `
|
|
129
|
+
* Throws if the object was not created with `createClassyStore()`.
|
|
152
130
|
*/
|
|
153
131
|
function getInternal(proxy) {
|
|
154
132
|
const internal = internalsMap.get(proxy);
|
|
@@ -270,15 +248,20 @@ function createStoreProxy(target, parent) {
|
|
|
270
248
|
*
|
|
271
249
|
* @param instance - A class instance (or plain object) to make reactive.
|
|
272
250
|
* @returns The same object wrapped in a reactive Proxy.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* const myStore = createClassyStore(new MyClass());
|
|
255
|
+
* ```
|
|
273
256
|
*/
|
|
274
|
-
function
|
|
257
|
+
function createClassyStore(instance) {
|
|
275
258
|
return createStoreProxy(instance, null);
|
|
276
259
|
}
|
|
277
260
|
/**
|
|
278
261
|
* Subscribe to store changes. The callback fires once per batched mutation
|
|
279
262
|
* (coalesced via `queueMicrotask`), not once per individual property write.
|
|
280
263
|
*
|
|
281
|
-
* @param proxy - A reactive proxy created by `
|
|
264
|
+
* @param proxy - A reactive proxy created by `createClassyStore()`.
|
|
282
265
|
* @param callback - Invoked after each batched mutation.
|
|
283
266
|
* @returns An unsubscribe function. Call it to stop receiving notifications.
|
|
284
267
|
*/
|
|
@@ -301,12 +284,12 @@ function getVersion(proxy) {
|
|
|
301
284
|
}
|
|
302
285
|
|
|
303
286
|
//#endregion
|
|
304
|
-
//#region src/snapshot.ts
|
|
287
|
+
//#region src/snapshot/snapshot.ts
|
|
305
288
|
/**
|
|
306
289
|
* Version-stamped snapshot cache for tracked (proxied) sub-trees.
|
|
307
290
|
* Key: raw target object → [version, frozen snapshot].
|
|
308
291
|
*/
|
|
309
|
-
const
|
|
292
|
+
const snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
310
293
|
/**
|
|
311
294
|
* Cache for untracked nested objects (never accessed through the proxy).
|
|
312
295
|
* Key: the raw mutable object → frozen deep clone.
|
|
@@ -325,7 +308,7 @@ const snapshotGetterCache = /* @__PURE__ */ new WeakMap();
|
|
|
325
308
|
* boundaries: if `this.items` hasn't changed between snapshots (same
|
|
326
309
|
* reference via structural sharing), the getter returns the same result.
|
|
327
310
|
*/
|
|
328
|
-
const
|
|
311
|
+
const crossSnapshotMemo = /* @__PURE__ */ new WeakMap();
|
|
329
312
|
/**
|
|
330
313
|
* Run a getter against the snapshot with dependency tracking.
|
|
331
314
|
*
|
|
@@ -359,7 +342,7 @@ function computeWithTracking(snap, getterFn) {
|
|
|
359
342
|
* guarantees stable refs for unchanged sub-trees). For getter properties
|
|
360
343
|
* this invokes the getter (which is itself memoized), then compares.
|
|
361
344
|
*/
|
|
362
|
-
function
|
|
345
|
+
function areMemoizedDepsValid(currentSnap, deps) {
|
|
363
346
|
for (const dep of deps) {
|
|
364
347
|
const currentValue = Reflect.get(currentSnap, dep.prop, currentSnap);
|
|
365
348
|
if (!Object.is(currentValue, dep.value)) return false;
|
|
@@ -378,16 +361,16 @@ function areMemoedDepsValid(currentSnap, deps) {
|
|
|
378
361
|
function evaluateSnapshotGetter(currentSnap, target, key, getterFn) {
|
|
379
362
|
const perSnapCache = snapshotGetterCache.get(currentSnap);
|
|
380
363
|
if (perSnapCache?.has(key)) return perSnapCache.get(key);
|
|
381
|
-
let memoMap =
|
|
364
|
+
let memoMap = crossSnapshotMemo.get(target);
|
|
382
365
|
const prev = memoMap?.get(key);
|
|
383
366
|
let result;
|
|
384
|
-
if (prev &&
|
|
367
|
+
if (prev && areMemoizedDepsValid(currentSnap, prev.deps)) result = prev.result;
|
|
385
368
|
else {
|
|
386
369
|
const computation = computeWithTracking(currentSnap, getterFn);
|
|
387
370
|
result = computation.result;
|
|
388
371
|
if (!memoMap) {
|
|
389
372
|
memoMap = /* @__PURE__ */ new Map();
|
|
390
|
-
|
|
373
|
+
crossSnapshotMemo.set(target, memoMap);
|
|
391
374
|
}
|
|
392
375
|
memoMap.set(key, {
|
|
393
376
|
deps: computation.deps,
|
|
@@ -501,7 +484,7 @@ function installMemoizedGetters(snap, target) {
|
|
|
501
484
|
* are installed as lazy-memoizing accessors via `installMemoizedGetters`.
|
|
502
485
|
*/
|
|
503
486
|
function createSnapshotRecursive(target, internal) {
|
|
504
|
-
const cached =
|
|
487
|
+
const cached = snapshotCache.get(target);
|
|
505
488
|
if (cached && cached[0] === internal.version) return cached[1];
|
|
506
489
|
let snap;
|
|
507
490
|
if (Array.isArray(target)) {
|
|
@@ -518,7 +501,7 @@ function createSnapshotRecursive(target, internal) {
|
|
|
518
501
|
installMemoizedGetters(snap, target);
|
|
519
502
|
}
|
|
520
503
|
Object.freeze(snap);
|
|
521
|
-
|
|
504
|
+
snapshotCache.set(target, [internal.version, snap]);
|
|
522
505
|
return snap;
|
|
523
506
|
}
|
|
524
507
|
/**
|
|
@@ -534,7 +517,7 @@ function createSnapshotRecursive(target, internal) {
|
|
|
534
517
|
* per snapshot and their results are stable across snapshots when dependencies
|
|
535
518
|
* haven't changed (cross-snapshot memoization).
|
|
536
519
|
*
|
|
537
|
-
* @param proxyStore - A reactive proxy created by `
|
|
520
|
+
* @param proxyStore - A reactive proxy created by `createClassyStore()`.
|
|
538
521
|
* @returns A deeply frozen plain-JS object (Snapshot<T>).
|
|
539
522
|
*/
|
|
540
523
|
function snapshot(proxyStore) {
|
|
@@ -549,6 +532,12 @@ Object.defineProperty(exports, 'PROXYABLE', {
|
|
|
549
532
|
return PROXYABLE;
|
|
550
533
|
}
|
|
551
534
|
});
|
|
535
|
+
Object.defineProperty(exports, 'createClassyStore', {
|
|
536
|
+
enumerable: true,
|
|
537
|
+
get: function () {
|
|
538
|
+
return createClassyStore;
|
|
539
|
+
}
|
|
540
|
+
});
|
|
552
541
|
Object.defineProperty(exports, 'findGetterDescriptor', {
|
|
553
542
|
enumerable: true,
|
|
554
543
|
get: function () {
|
|
@@ -567,28 +556,16 @@ Object.defineProperty(exports, 'getVersion', {
|
|
|
567
556
|
return getVersion;
|
|
568
557
|
}
|
|
569
558
|
});
|
|
570
|
-
Object.defineProperty(exports, 'shallowEqual', {
|
|
571
|
-
enumerable: true,
|
|
572
|
-
get: function () {
|
|
573
|
-
return shallowEqual;
|
|
574
|
-
}
|
|
575
|
-
});
|
|
576
559
|
Object.defineProperty(exports, 'snapshot', {
|
|
577
560
|
enumerable: true,
|
|
578
561
|
get: function () {
|
|
579
562
|
return snapshot;
|
|
580
563
|
}
|
|
581
564
|
});
|
|
582
|
-
Object.defineProperty(exports, 'store', {
|
|
583
|
-
enumerable: true,
|
|
584
|
-
get: function () {
|
|
585
|
-
return store;
|
|
586
|
-
}
|
|
587
|
-
});
|
|
588
565
|
Object.defineProperty(exports, 'subscribe', {
|
|
589
566
|
enumerable: true,
|
|
590
567
|
get: function () {
|
|
591
568
|
return subscribe;
|
|
592
569
|
}
|
|
593
570
|
});
|
|
594
|
-
//# sourceMappingURL=snapshot-
|
|
571
|
+
//# sourceMappingURL=snapshot-BKVFJLuo.cjs.map
|