@tanstack/store
Advanced tools
| "use strict"; | ||
| Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); | ||
| const store = require("./store.cjs"); | ||
| const scheduler = require("./scheduler.cjs"); | ||
| class Derived { | ||
| constructor(options) { | ||
| this.listeners = /* @__PURE__ */ new Set(); | ||
| this._subscriptions = []; | ||
| this.lastSeenDepValues = []; | ||
| this.getDepVals = () => { | ||
| const prevDepVals = []; | ||
| const currDepVals = []; | ||
| for (const dep of this.options.deps) { | ||
| prevDepVals.push(dep.prevState); | ||
| currDepVals.push(dep.state); | ||
| } | ||
| this.lastSeenDepValues = currDepVals; | ||
| return { | ||
| prevDepVals, | ||
| currDepVals, | ||
| prevVal: this.prevState ?? void 0 | ||
| }; | ||
| }; | ||
| this.recompute = () => { | ||
| var _a, _b; | ||
| this.prevState = this.state; | ||
| const { prevDepVals, currDepVals, prevVal } = this.getDepVals(); | ||
| this.state = this.options.fn({ | ||
| prevDepVals, | ||
| currDepVals, | ||
| prevVal | ||
| }); | ||
| (_b = (_a = this.options).onUpdate) == null ? void 0 : _b.call(_a); | ||
| }; | ||
| this.checkIfRecalculationNeededDeeply = () => { | ||
| for (const dep of this.options.deps) { | ||
| if (dep instanceof Derived) { | ||
| dep.checkIfRecalculationNeededDeeply(); | ||
| } | ||
| } | ||
| let shouldRecompute = false; | ||
| const lastSeenDepValues = this.lastSeenDepValues; | ||
| const { currDepVals } = this.getDepVals(); | ||
| for (let i = 0; i < currDepVals.length; i++) { | ||
| if (currDepVals[i] !== lastSeenDepValues[i]) { | ||
| shouldRecompute = true; | ||
| break; | ||
| } | ||
| } | ||
| if (shouldRecompute) { | ||
| this.recompute(); | ||
| } | ||
| }; | ||
| this.mount = () => { | ||
| this.registerOnGraph(); | ||
| this.checkIfRecalculationNeededDeeply(); | ||
| return () => { | ||
| this.unregisterFromGraph(); | ||
| for (const cleanup of this._subscriptions) { | ||
| cleanup(); | ||
| } | ||
| }; | ||
| }; | ||
| this.subscribe = (listener) => { | ||
| var _a, _b; | ||
| this.listeners.add(listener); | ||
| const unsub = (_b = (_a = this.options).onSubscribe) == null ? void 0 : _b.call(_a, listener, this); | ||
| return () => { | ||
| this.listeners.delete(listener); | ||
| unsub == null ? void 0 : unsub(); | ||
| }; | ||
| }; | ||
| this.options = options; | ||
| this.state = options.fn({ | ||
| prevDepVals: void 0, | ||
| prevVal: void 0, | ||
| currDepVals: this.getDepVals().currDepVals | ||
| }); | ||
| } | ||
| registerOnGraph(deps = this.options.deps) { | ||
| for (const dep of deps) { | ||
| if (dep instanceof Derived) { | ||
| dep.registerOnGraph(); | ||
| this.registerOnGraph(dep.options.deps); | ||
| } else if (dep instanceof store.Store) { | ||
| let relatedLinkedDerivedVals = scheduler.__storeToDerived.get(dep); | ||
| if (!relatedLinkedDerivedVals) { | ||
| relatedLinkedDerivedVals = /* @__PURE__ */ new Set(); | ||
| scheduler.__storeToDerived.set(dep, relatedLinkedDerivedVals); | ||
| } | ||
| relatedLinkedDerivedVals.add(this); | ||
| let relatedStores = scheduler.__derivedToStore.get(this); | ||
| if (!relatedStores) { | ||
| relatedStores = /* @__PURE__ */ new Set(); | ||
| scheduler.__derivedToStore.set(this, relatedStores); | ||
| } | ||
| relatedStores.add(dep); | ||
| } | ||
| } | ||
| } | ||
| unregisterFromGraph(deps = this.options.deps) { | ||
| for (const dep of deps) { | ||
| if (dep instanceof Derived) { | ||
| this.unregisterFromGraph(dep.options.deps); | ||
| } else if (dep instanceof store.Store) { | ||
| const relatedLinkedDerivedVals = scheduler.__storeToDerived.get(dep); | ||
| if (relatedLinkedDerivedVals) { | ||
| relatedLinkedDerivedVals.delete(this); | ||
| } | ||
| const relatedStores = scheduler.__derivedToStore.get(this); | ||
| if (relatedStores) { | ||
| relatedStores.delete(dep); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| exports.Derived = Derived; | ||
| //# sourceMappingURL=derived.cjs.map |
| {"version":3,"file":"derived.cjs","sources":["../../src/derived.ts"],"sourcesContent":["import { Store } from './store'\nimport { __derivedToStore, __storeToDerived } from './scheduler'\nimport type { Listener } from './types'\n\nexport type UnwrapDerivedOrStore<T> =\n T extends Derived<infer InnerD>\n ? InnerD\n : T extends Store<infer InnerS>\n ? InnerS\n : never\n\ntype UnwrapReadonlyDerivedOrStoreArray<\n TArr extends ReadonlyArray<Derived<any> | Store<any>>,\n> = TArr extends readonly [infer Head, ...infer Tail]\n ? Head extends Derived<any> | Store<any>\n ? Tail extends ReadonlyArray<Derived<any> | Store<any>>\n ? [UnwrapDerivedOrStore<Head>, ...UnwrapReadonlyDerivedOrStoreArray<Tail>]\n : []\n : []\n : []\n\n// Can't have currVal, as it's being evaluated from the current derived fn\nexport interface DerivedFnProps<\n TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>,\n TUnwrappedArr extends\n UnwrapReadonlyDerivedOrStoreArray<TArr> = UnwrapReadonlyDerivedOrStoreArray<TArr>,\n> {\n // `undefined` if it's the first run\n /**\n * `undefined` if it's the first run\n * @privateRemarks this also cannot be typed as TState, as it breaks the inferencing of the function's return type when an argument is used - even with `NoInfer` usage\n */\n prevVal: unknown | undefined\n prevDepVals: TUnwrappedArr | undefined\n currDepVals: TUnwrappedArr\n}\n\nexport interface DerivedOptions<\n TState,\n TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>,\n> {\n onSubscribe?: (\n listener: Listener<TState>,\n derived: Derived<TState>,\n ) => () => void\n onUpdate?: () => void\n deps: TArr\n /**\n * Values of the `deps` from before and after the current invocation of `fn`\n */\n fn: (props: DerivedFnProps<TArr>) => TState\n}\n\nexport class Derived<\n TState,\n const TArr extends ReadonlyArray<\n Derived<any> | Store<any>\n > = ReadonlyArray<any>,\n> {\n listeners = new Set<Listener<TState>>()\n state: TState\n prevState: TState | undefined\n options: DerivedOptions<TState, TArr>\n\n /**\n * Functions representing the subscriptions. Call a function to cleanup\n * @private\n */\n _subscriptions: Array<() => void> = []\n\n lastSeenDepValues: Array<unknown> = []\n getDepVals = () => {\n const prevDepVals = [] as Array<unknown>\n const currDepVals = [] as Array<unknown>\n for (const dep of this.options.deps) {\n prevDepVals.push(dep.prevState)\n currDepVals.push(dep.state)\n }\n this.lastSeenDepValues = currDepVals\n return {\n prevDepVals,\n currDepVals,\n prevVal: this.prevState ?? undefined,\n }\n }\n\n constructor(options: DerivedOptions<TState, TArr>) {\n this.options = options\n this.state = options.fn({\n prevDepVals: undefined,\n prevVal: undefined,\n currDepVals: this.getDepVals().currDepVals as never,\n })\n }\n\n registerOnGraph(\n deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps,\n ) {\n for (const dep of deps) {\n if (dep instanceof Derived) {\n // First register the intermediate derived value if it's not already registered\n dep.registerOnGraph()\n // Then register this derived with the dep's underlying stores\n this.registerOnGraph(dep.options.deps)\n } else if (dep instanceof Store) {\n // Register the derived as related derived to the store\n let relatedLinkedDerivedVals = __storeToDerived.get(dep)\n if (!relatedLinkedDerivedVals) {\n relatedLinkedDerivedVals = new Set()\n __storeToDerived.set(dep, relatedLinkedDerivedVals)\n }\n relatedLinkedDerivedVals.add(this as never)\n\n // Register the store as a related store to this derived\n let relatedStores = __derivedToStore.get(this as never)\n if (!relatedStores) {\n relatedStores = new Set()\n __derivedToStore.set(this as never, relatedStores)\n }\n relatedStores.add(dep)\n }\n }\n }\n\n unregisterFromGraph(\n deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps,\n ) {\n for (const dep of deps) {\n if (dep instanceof Derived) {\n this.unregisterFromGraph(dep.options.deps)\n } else if (dep instanceof Store) {\n const relatedLinkedDerivedVals = __storeToDerived.get(dep)\n if (relatedLinkedDerivedVals) {\n relatedLinkedDerivedVals.delete(this as never)\n }\n\n const relatedStores = __derivedToStore.get(this as never)\n if (relatedStores) {\n relatedStores.delete(dep)\n }\n }\n }\n }\n\n recompute = () => {\n this.prevState = this.state\n const { prevDepVals, currDepVals, prevVal } = this.getDepVals()\n this.state = this.options.fn({\n prevDepVals: prevDepVals as never,\n currDepVals: currDepVals as never,\n prevVal,\n })\n\n this.options.onUpdate?.()\n }\n\n checkIfRecalculationNeededDeeply = () => {\n for (const dep of this.options.deps) {\n if (dep instanceof Derived) {\n dep.checkIfRecalculationNeededDeeply()\n }\n }\n let shouldRecompute = false\n const lastSeenDepValues = this.lastSeenDepValues\n const { currDepVals } = this.getDepVals()\n for (let i = 0; i < currDepVals.length; i++) {\n if (currDepVals[i] !== lastSeenDepValues[i]) {\n shouldRecompute = true\n break\n }\n }\n\n if (shouldRecompute) {\n this.recompute()\n }\n }\n\n mount = () => {\n this.registerOnGraph()\n this.checkIfRecalculationNeededDeeply()\n\n return () => {\n this.unregisterFromGraph()\n for (const cleanup of this._subscriptions) {\n cleanup()\n }\n }\n }\n\n subscribe = (listener: Listener<TState>) => {\n this.listeners.add(listener)\n const unsub = this.options.onSubscribe?.(listener, this)\n return () => {\n this.listeners.delete(listener)\n unsub?.()\n }\n }\n}\n"],"names":["Store","__storeToDerived","__derivedToStore"],"mappings":"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"} |
| import { Store } from './store.cjs'; | ||
| import { Listener } from './types.cjs'; | ||
| export type UnwrapDerivedOrStore<T> = T extends Derived<infer InnerD> ? InnerD : T extends Store<infer InnerS> ? InnerS : never; | ||
| type UnwrapReadonlyDerivedOrStoreArray<TArr extends ReadonlyArray<Derived<any> | Store<any>>> = TArr extends readonly [infer Head, ...infer Tail] ? Head extends Derived<any> | Store<any> ? Tail extends ReadonlyArray<Derived<any> | Store<any>> ? [UnwrapDerivedOrStore<Head>, ...UnwrapReadonlyDerivedOrStoreArray<Tail>] : [] : [] : []; | ||
| export interface DerivedFnProps<TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>, TUnwrappedArr extends UnwrapReadonlyDerivedOrStoreArray<TArr> = UnwrapReadonlyDerivedOrStoreArray<TArr>> { | ||
| /** | ||
| * `undefined` if it's the first run | ||
| * @privateRemarks this also cannot be typed as TState, as it breaks the inferencing of the function's return type when an argument is used - even with `NoInfer` usage | ||
| */ | ||
| prevVal: unknown | undefined; | ||
| prevDepVals: TUnwrappedArr | undefined; | ||
| currDepVals: TUnwrappedArr; | ||
| } | ||
| export interface DerivedOptions<TState, TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>> { | ||
| onSubscribe?: (listener: Listener<TState>, derived: Derived<TState>) => () => void; | ||
| onUpdate?: () => void; | ||
| deps: TArr; | ||
| /** | ||
| * Values of the `deps` from before and after the current invocation of `fn` | ||
| */ | ||
| fn: (props: DerivedFnProps<TArr>) => TState; | ||
| } | ||
| export declare class Derived<TState, const TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>> { | ||
| listeners: Set<Listener<TState>>; | ||
| state: TState; | ||
| prevState: TState | undefined; | ||
| options: DerivedOptions<TState, TArr>; | ||
| /** | ||
| * Functions representing the subscriptions. Call a function to cleanup | ||
| * @private | ||
| */ | ||
| _subscriptions: Array<() => void>; | ||
| lastSeenDepValues: Array<unknown>; | ||
| getDepVals: () => { | ||
| prevDepVals: unknown[]; | ||
| currDepVals: unknown[]; | ||
| prevVal: NonNullable<TState> | undefined; | ||
| }; | ||
| constructor(options: DerivedOptions<TState, TArr>); | ||
| registerOnGraph(deps?: ReadonlyArray<Derived<any> | Store<any>>): void; | ||
| unregisterFromGraph(deps?: ReadonlyArray<Derived<any> | Store<any>>): void; | ||
| recompute: () => void; | ||
| checkIfRecalculationNeededDeeply: () => void; | ||
| mount: () => () => void; | ||
| subscribe: (listener: Listener<TState>) => () => void; | ||
| } | ||
| export {}; |
| "use strict"; | ||
| Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); | ||
| const derived = require("./derived.cjs"); | ||
| class Effect { | ||
| constructor(opts) { | ||
| const { eager, fn, ...derivedProps } = opts; | ||
| this._derived = new derived.Derived({ | ||
| ...derivedProps, | ||
| fn: () => { | ||
| }, | ||
| onUpdate() { | ||
| fn(); | ||
| } | ||
| }); | ||
| if (eager) { | ||
| fn(); | ||
| } | ||
| } | ||
| mount() { | ||
| return this._derived.mount(); | ||
| } | ||
| } | ||
| exports.Effect = Effect; | ||
| //# sourceMappingURL=effect.cjs.map |
| {"version":3,"file":"effect.cjs","sources":["../../src/effect.ts"],"sourcesContent":["import { Derived } from './derived'\nimport type { DerivedOptions } from './derived'\n\ninterface EffectOptions\n extends Omit<\n DerivedOptions<unknown>,\n 'onUpdate' | 'onSubscribe' | 'lazy' | 'fn'\n > {\n /**\n * Should the effect trigger immediately?\n * @default false\n */\n eager?: boolean\n fn: () => void\n}\n\nexport class Effect {\n /**\n * @private\n */\n _derived: Derived<void>\n\n constructor(opts: EffectOptions) {\n const { eager, fn, ...derivedProps } = opts\n\n this._derived = new Derived({\n ...derivedProps,\n fn: () => {},\n onUpdate() {\n fn()\n },\n })\n\n if (eager) {\n fn()\n }\n }\n\n mount() {\n return this._derived.mount()\n }\n}\n"],"names":["Derived"],"mappings":";;;AAgBO,MAAM,OAAO;AAAA,EAMlB,YAAY,MAAqB;AAC/B,UAAM,EAAE,OAAO,IAAI,GAAG,aAAiB,IAAA;AAElC,SAAA,WAAW,IAAIA,gBAAQ;AAAA,MAC1B,GAAG;AAAA,MACH,IAAI,MAAM;AAAA,MAAC;AAAA,MACX,WAAW;AACN,WAAA;AAAA,MAAA;AAAA,IACL,CACD;AAED,QAAI,OAAO;AACN,SAAA;AAAA,IAAA;AAAA,EACL;AAAA,EAGF,QAAQ;AACC,WAAA,KAAK,SAAS,MAAM;AAAA,EAAA;AAE/B;;"} |
| import { Derived, DerivedOptions } from './derived.cjs'; | ||
| interface EffectOptions extends Omit<DerivedOptions<unknown>, 'onUpdate' | 'onSubscribe' | 'lazy' | 'fn'> { | ||
| /** | ||
| * Should the effect trigger immediately? | ||
| * @default false | ||
| */ | ||
| eager?: boolean; | ||
| fn: () => void; | ||
| } | ||
| export declare class Effect { | ||
| /** | ||
| * @private | ||
| */ | ||
| _derived: Derived<void>; | ||
| constructor(opts: EffectOptions); | ||
| mount(): () => void; | ||
| } | ||
| export {}; |
| "use strict"; | ||
| Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); | ||
| const derived = require("./derived.cjs"); | ||
| const __storeToDerived = /* @__PURE__ */ new WeakMap(); | ||
| const __derivedToStore = /* @__PURE__ */ new WeakMap(); | ||
| const __depsThatHaveWrittenThisTick = { | ||
| current: [] | ||
| }; | ||
| let __isFlushing = false; | ||
| let __batchDepth = 0; | ||
| const __pendingUpdates = /* @__PURE__ */ new Set(); | ||
| const __initialBatchValues = /* @__PURE__ */ new Map(); | ||
| function __flush_internals(relatedVals) { | ||
| const sorted = Array.from(relatedVals).sort((a, b) => { | ||
| if (a instanceof derived.Derived && a.options.deps.includes(b)) return 1; | ||
| if (b instanceof derived.Derived && b.options.deps.includes(a)) return -1; | ||
| return 0; | ||
| }); | ||
| for (const derived2 of sorted) { | ||
| if (__depsThatHaveWrittenThisTick.current.includes(derived2)) { | ||
| continue; | ||
| } | ||
| __depsThatHaveWrittenThisTick.current.push(derived2); | ||
| derived2.recompute(); | ||
| const stores = __derivedToStore.get(derived2); | ||
| if (stores) { | ||
| for (const store of stores) { | ||
| const relatedLinkedDerivedVals = __storeToDerived.get(store); | ||
| if (!relatedLinkedDerivedVals) continue; | ||
| __flush_internals(relatedLinkedDerivedVals); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| function __notifyListeners(store) { | ||
| store.listeners.forEach( | ||
| (listener) => listener({ | ||
| prevVal: store.prevState, | ||
| currentVal: store.state | ||
| }) | ||
| ); | ||
| } | ||
| function __notifyDerivedListeners(derived2) { | ||
| derived2.listeners.forEach( | ||
| (listener) => listener({ | ||
| prevVal: derived2.prevState, | ||
| currentVal: derived2.state | ||
| }) | ||
| ); | ||
| } | ||
| function __flush(store) { | ||
| if (__batchDepth > 0 && !__initialBatchValues.has(store)) { | ||
| __initialBatchValues.set(store, store.prevState); | ||
| } | ||
| __pendingUpdates.add(store); | ||
| if (__batchDepth > 0) return; | ||
| if (__isFlushing) return; | ||
| try { | ||
| __isFlushing = true; | ||
| while (__pendingUpdates.size > 0) { | ||
| const stores = Array.from(__pendingUpdates); | ||
| __pendingUpdates.clear(); | ||
| for (const store2 of stores) { | ||
| const prevState = __initialBatchValues.get(store2) ?? store2.prevState; | ||
| store2.prevState = prevState; | ||
| __notifyListeners(store2); | ||
| } | ||
| for (const store2 of stores) { | ||
| const derivedVals = __storeToDerived.get(store2); | ||
| if (!derivedVals) continue; | ||
| __depsThatHaveWrittenThisTick.current.push(store2); | ||
| __flush_internals(derivedVals); | ||
| } | ||
| for (const store2 of stores) { | ||
| const derivedVals = __storeToDerived.get(store2); | ||
| if (!derivedVals) continue; | ||
| for (const derived2 of derivedVals) { | ||
| __notifyDerivedListeners(derived2); | ||
| } | ||
| } | ||
| } | ||
| } finally { | ||
| __isFlushing = false; | ||
| __depsThatHaveWrittenThisTick.current = []; | ||
| __initialBatchValues.clear(); | ||
| } | ||
| } | ||
| function batch(fn) { | ||
| __batchDepth++; | ||
| try { | ||
| fn(); | ||
| } finally { | ||
| __batchDepth--; | ||
| if (__batchDepth === 0) { | ||
| const pendingUpdateToFlush = Array.from(__pendingUpdates)[0]; | ||
| if (pendingUpdateToFlush) { | ||
| __flush(pendingUpdateToFlush); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| exports.__depsThatHaveWrittenThisTick = __depsThatHaveWrittenThisTick; | ||
| exports.__derivedToStore = __derivedToStore; | ||
| exports.__flush = __flush; | ||
| exports.__storeToDerived = __storeToDerived; | ||
| exports.batch = batch; | ||
| //# sourceMappingURL=scheduler.cjs.map |
| {"version":3,"file":"scheduler.cjs","sources":["../../src/scheduler.ts"],"sourcesContent":["import { Derived } from './derived'\nimport type { Store } from './store'\n\n/**\n * This is here to solve the pyramid dependency problem where:\n * A\n * / \\\n * B C\n * \\ /\n * D\n *\n * Where we deeply traverse this tree, how do we avoid D being recomputed twice; once when B is updated, once when C is.\n *\n * To solve this, we create linkedDeps that allows us to sync avoid writes to the state until all of the deps have been\n * resolved.\n *\n * This is a record of stores, because derived stores are not able to write values to, but stores are\n */\nexport const __storeToDerived = new WeakMap<\n Store<unknown>,\n Set<Derived<unknown>>\n>()\nexport const __derivedToStore = new WeakMap<\n Derived<unknown>,\n Set<Store<unknown>>\n>()\n\nexport const __depsThatHaveWrittenThisTick = {\n current: [] as Array<Derived<unknown> | Store<unknown>>,\n}\n\nlet __isFlushing = false\nlet __batchDepth = 0\nconst __pendingUpdates = new Set<Store<unknown>>()\n// Add a map to store initial values before batch\nconst __initialBatchValues = new Map<Store<unknown>, unknown>()\n\nfunction __flush_internals(relatedVals: Set<Derived<unknown>>) {\n // First sort deriveds by dependency order\n const sorted = Array.from(relatedVals).sort((a, b) => {\n // If a depends on b, b should go first\n if (a instanceof Derived && a.options.deps.includes(b)) return 1\n // If b depends on a, a should go first\n if (b instanceof Derived && b.options.deps.includes(a)) return -1\n return 0\n })\n\n for (const derived of sorted) {\n if (__depsThatHaveWrittenThisTick.current.includes(derived)) {\n continue\n }\n\n __depsThatHaveWrittenThisTick.current.push(derived)\n derived.recompute()\n\n const stores = __derivedToStore.get(derived)\n if (stores) {\n for (const store of stores) {\n const relatedLinkedDerivedVals = __storeToDerived.get(store)\n if (!relatedLinkedDerivedVals) continue\n __flush_internals(relatedLinkedDerivedVals)\n }\n }\n }\n}\n\nfunction __notifyListeners(store: Store<unknown>) {\n store.listeners.forEach((listener) =>\n listener({\n prevVal: store.prevState as never,\n currentVal: store.state as never,\n }),\n )\n}\n\nfunction __notifyDerivedListeners(derived: Derived<unknown>) {\n derived.listeners.forEach((listener) =>\n listener({\n prevVal: derived.prevState as never,\n currentVal: derived.state as never,\n }),\n )\n}\n\n/**\n * @private only to be called from `Store` on write\n */\nexport function __flush(store: Store<unknown>) {\n // If we're starting a batch, store the initial values\n if (__batchDepth > 0 && !__initialBatchValues.has(store)) {\n __initialBatchValues.set(store, store.prevState)\n }\n\n __pendingUpdates.add(store)\n\n if (__batchDepth > 0) return\n if (__isFlushing) return\n\n try {\n __isFlushing = true\n\n while (__pendingUpdates.size > 0) {\n const stores = Array.from(__pendingUpdates)\n __pendingUpdates.clear()\n\n // First notify listeners with updated values\n for (const store of stores) {\n // Use initial batch values for prevState if we have them\n const prevState = __initialBatchValues.get(store) ?? store.prevState\n store.prevState = prevState\n __notifyListeners(store)\n }\n\n // Then update all derived values\n for (const store of stores) {\n const derivedVals = __storeToDerived.get(store)\n if (!derivedVals) continue\n\n __depsThatHaveWrittenThisTick.current.push(store)\n __flush_internals(derivedVals)\n }\n\n // Notify derived listeners after recomputing\n for (const store of stores) {\n const derivedVals = __storeToDerived.get(store)\n if (!derivedVals) continue\n\n for (const derived of derivedVals) {\n __notifyDerivedListeners(derived)\n }\n }\n }\n } finally {\n __isFlushing = false\n __depsThatHaveWrittenThisTick.current = []\n __initialBatchValues.clear()\n }\n}\n\nexport function batch(fn: () => void) {\n __batchDepth++\n try {\n fn()\n } finally {\n __batchDepth--\n if (__batchDepth === 0) {\n const pendingUpdateToFlush = Array.from(__pendingUpdates)[0] as\n | Store<unknown>\n | undefined\n if (pendingUpdateToFlush) {\n __flush(pendingUpdateToFlush) // Trigger flush of all pending updates\n }\n }\n }\n}\n"],"names":["Derived","derived","store"],"mappings":";;;AAkBa,MAAA,uCAAuB,QAGlC;AACW,MAAA,uCAAuB,QAGlC;AAEK,MAAM,gCAAgC;AAAA,EAC3C,SAAS,CAAA;AACX;AAEA,IAAI,eAAe;AACnB,IAAI,eAAe;AACnB,MAAM,uCAAuB,IAAoB;AAEjD,MAAM,2CAA2B,IAA6B;AAE9D,SAAS,kBAAkB,aAAoC;AAEvD,QAAA,SAAS,MAAM,KAAK,WAAW,EAAE,KAAK,CAAC,GAAG,MAAM;AAEhD,QAAA,aAAaA,mBAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAU,QAAA;AAE3D,QAAA,aAAaA,mBAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAU,QAAA;AACxD,WAAA;AAAA,EAAA,CACR;AAED,aAAWC,YAAW,QAAQ;AAC5B,QAAI,8BAA8B,QAAQ,SAASA,QAAO,GAAG;AAC3D;AAAA,IAAA;AAG4B,kCAAA,QAAQ,KAAKA,QAAO;AAClD,IAAAA,SAAQ,UAAU;AAEZ,UAAA,SAAS,iBAAiB,IAAIA,QAAO;AAC3C,QAAI,QAAQ;AACV,iBAAW,SAAS,QAAQ;AACpB,cAAA,2BAA2B,iBAAiB,IAAI,KAAK;AAC3D,YAAI,CAAC,yBAA0B;AAC/B,0BAAkB,wBAAwB;AAAA,MAAA;AAAA,IAC5C;AAAA,EACF;AAEJ;AAEA,SAAS,kBAAkB,OAAuB;AAChD,QAAM,UAAU;AAAA,IAAQ,CAAC,aACvB,SAAS;AAAA,MACP,SAAS,MAAM;AAAA,MACf,YAAY,MAAM;AAAA,IACnB,CAAA;AAAA,EACH;AACF;AAEA,SAAS,yBAAyBA,UAA2B;AAC3D,EAAAA,SAAQ,UAAU;AAAA,IAAQ,CAAC,aACzB,SAAS;AAAA,MACP,SAASA,SAAQ;AAAA,MACjB,YAAYA,SAAQ;AAAA,IACrB,CAAA;AAAA,EACH;AACF;AAKO,SAAS,QAAQ,OAAuB;AAE7C,MAAI,eAAe,KAAK,CAAC,qBAAqB,IAAI,KAAK,GAAG;AACnC,yBAAA,IAAI,OAAO,MAAM,SAAS;AAAA,EAAA;AAGjD,mBAAiB,IAAI,KAAK;AAE1B,MAAI,eAAe,EAAG;AACtB,MAAI,aAAc;AAEd,MAAA;AACa,mBAAA;AAER,WAAA,iBAAiB,OAAO,GAAG;AAC1B,YAAA,SAAS,MAAM,KAAK,gBAAgB;AAC1C,uBAAiB,MAAM;AAGvB,iBAAWC,UAAS,QAAQ;AAE1B,cAAM,YAAY,qBAAqB,IAAIA,MAAK,KAAKA,OAAM;AAC3DA,eAAM,YAAY;AAClB,0BAAkBA,MAAK;AAAA,MAAA;AAIzB,iBAAWA,UAAS,QAAQ;AACpB,cAAA,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAEY,sCAAA,QAAQ,KAAKA,MAAK;AAChD,0BAAkB,WAAW;AAAA,MAAA;AAI/B,iBAAWA,UAAS,QAAQ;AACpB,cAAA,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAElB,mBAAWD,YAAW,aAAa;AACjC,mCAAyBA,QAAO;AAAA,QAAA;AAAA,MAClC;AAAA,IACF;AAAA,EACF,UACA;AACe,mBAAA;AACf,kCAA8B,UAAU,CAAC;AACzC,yBAAqB,MAAM;AAAA,EAAA;AAE/B;AAEO,SAAS,MAAM,IAAgB;AACpC;AACI,MAAA;AACC,OAAA;AAAA,EAAA,UACH;AACA;AACA,QAAI,iBAAiB,GAAG;AACtB,YAAM,uBAAuB,MAAM,KAAK,gBAAgB,EAAE,CAAC;AAG3D,UAAI,sBAAsB;AACxB,gBAAQ,oBAAoB;AAAA,MAAA;AAAA,IAC9B;AAAA,EACF;AAEJ;;;;;;"} |
| import { Derived } from './derived.cjs'; | ||
| import { Store } from './store.cjs'; | ||
| /** | ||
| * This is here to solve the pyramid dependency problem where: | ||
| * A | ||
| * / \ | ||
| * B C | ||
| * \ / | ||
| * D | ||
| * | ||
| * Where we deeply traverse this tree, how do we avoid D being recomputed twice; once when B is updated, once when C is. | ||
| * | ||
| * To solve this, we create linkedDeps that allows us to sync avoid writes to the state until all of the deps have been | ||
| * resolved. | ||
| * | ||
| * This is a record of stores, because derived stores are not able to write values to, but stores are | ||
| */ | ||
| export declare const __storeToDerived: WeakMap<Store<unknown, (cb: unknown) => unknown>, Set<Derived<unknown, readonly any[]>>>; | ||
| export declare const __derivedToStore: WeakMap<Derived<unknown, readonly any[]>, Set<Store<unknown, (cb: unknown) => unknown>>>; | ||
| export declare const __depsThatHaveWrittenThisTick: { | ||
| current: Array<Derived<unknown> | Store<unknown>>; | ||
| }; | ||
| /** | ||
| * @private only to be called from `Store` on write | ||
| */ | ||
| export declare function __flush(store: Store<unknown>): void; | ||
| export declare function batch(fn: () => void): void; |
| "use strict"; | ||
| Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); | ||
| const scheduler = require("./scheduler.cjs"); | ||
| class Store { | ||
| constructor(initialState, options) { | ||
| this.listeners = /* @__PURE__ */ new Set(); | ||
| this.subscribe = (listener) => { | ||
| var _a, _b; | ||
| this.listeners.add(listener); | ||
| const unsub = (_b = (_a = this.options) == null ? void 0 : _a.onSubscribe) == null ? void 0 : _b.call(_a, listener, this); | ||
| return () => { | ||
| this.listeners.delete(listener); | ||
| unsub == null ? void 0 : unsub(); | ||
| }; | ||
| }; | ||
| this.setState = (updater) => { | ||
| var _a, _b, _c; | ||
| this.prevState = this.state; | ||
| this.state = ((_a = this.options) == null ? void 0 : _a.updateFn) ? this.options.updateFn(this.prevState)(updater) : updater(this.prevState); | ||
| (_c = (_b = this.options) == null ? void 0 : _b.onUpdate) == null ? void 0 : _c.call(_b); | ||
| scheduler.__flush(this); | ||
| }; | ||
| this.prevState = initialState; | ||
| this.state = initialState; | ||
| this.options = options; | ||
| } | ||
| } | ||
| exports.Store = Store; | ||
| //# sourceMappingURL=store.cjs.map |
| {"version":3,"file":"store.cjs","sources":["../../src/store.ts"],"sourcesContent":["import { __flush } from './scheduler'\nimport type { AnyUpdater, Listener } from './types'\n\nexport interface StoreOptions<\n TState,\n TUpdater extends AnyUpdater = (cb: TState) => TState,\n> {\n /**\n * Replace the default update function with a custom one.\n */\n updateFn?: (previous: TState) => (updater: TUpdater) => TState\n /**\n * Called when a listener subscribes to the store.\n *\n * @return a function to unsubscribe the listener\n */\n onSubscribe?: (\n listener: Listener<TState>,\n store: Store<TState, TUpdater>,\n ) => () => void\n /**\n * Called after the state has been updated, used to derive other state.\n */\n onUpdate?: () => void\n}\n\nexport class Store<\n TState,\n TUpdater extends AnyUpdater = (cb: TState) => TState,\n> {\n listeners = new Set<Listener<TState>>()\n state: TState\n prevState: TState\n options?: StoreOptions<TState, TUpdater>\n\n constructor(initialState: TState, options?: StoreOptions<TState, TUpdater>) {\n this.prevState = initialState\n this.state = initialState\n this.options = options\n }\n\n subscribe = (listener: Listener<TState>) => {\n this.listeners.add(listener)\n const unsub = this.options?.onSubscribe?.(listener, this)\n return () => {\n this.listeners.delete(listener)\n unsub?.()\n }\n }\n\n setState = (updater: TUpdater) => {\n this.prevState = this.state\n this.state = this.options?.updateFn\n ? this.options.updateFn(this.prevState)(updater)\n : (updater as any)(this.prevState)\n\n // Always run onUpdate, regardless of batching\n this.options?.onUpdate?.()\n\n // Attempt to flush\n __flush(this as never)\n }\n}\n"],"names":["__flush"],"mappings":";;;AA0BO,MAAM,MAGX;AAAA,EAMA,YAAY,cAAsB,SAA0C;AAL5E,SAAA,gCAAgB,IAAsB;AAWtC,SAAA,YAAY,CAAC,aAA+B;;AACrC,WAAA,UAAU,IAAI,QAAQ;AAC3B,YAAM,SAAQ,gBAAK,YAAL,mBAAc,gBAAd,4BAA4B,UAAU;AACpD,aAAO,MAAM;AACN,aAAA,UAAU,OAAO,QAAQ;AACtB;AAAA,MACV;AAAA,IACF;AAEA,SAAA,WAAW,CAAC,YAAsB;;AAChC,WAAK,YAAY,KAAK;AACtB,WAAK,UAAQ,UAAK,YAAL,mBAAc,YACvB,KAAK,QAAQ,SAAS,KAAK,SAAS,EAAE,OAAO,IAC5C,QAAgB,KAAK,SAAS;AAGnC,uBAAK,YAAL,mBAAc,aAAd;AAGAA,gBAAAA,QAAQ,IAAa;AAAA,IACvB;AAzBE,SAAK,YAAY;AACjB,SAAK,QAAQ;AACb,SAAK,UAAU;AAAA,EAAA;AAwBnB;;"} |
| import { AnyUpdater, Listener } from './types.cjs'; | ||
| export interface StoreOptions<TState, TUpdater extends AnyUpdater = (cb: TState) => TState> { | ||
| /** | ||
| * Replace the default update function with a custom one. | ||
| */ | ||
| updateFn?: (previous: TState) => (updater: TUpdater) => TState; | ||
| /** | ||
| * Called when a listener subscribes to the store. | ||
| * | ||
| * @return a function to unsubscribe the listener | ||
| */ | ||
| onSubscribe?: (listener: Listener<TState>, store: Store<TState, TUpdater>) => () => void; | ||
| /** | ||
| * Called after the state has been updated, used to derive other state. | ||
| */ | ||
| onUpdate?: () => void; | ||
| } | ||
| export declare class Store<TState, TUpdater extends AnyUpdater = (cb: TState) => TState> { | ||
| listeners: Set<Listener<TState>>; | ||
| state: TState; | ||
| prevState: TState; | ||
| options?: StoreOptions<TState, TUpdater>; | ||
| constructor(initialState: TState, options?: StoreOptions<TState, TUpdater>); | ||
| subscribe: (listener: Listener<TState>) => () => void; | ||
| setState: (updater: TUpdater) => void; | ||
| } |
| /** | ||
| * @private | ||
| */ | ||
| export type AnyUpdater = (prev: any) => any; | ||
| /** | ||
| * @private | ||
| */ | ||
| export interface ListenerValue<T> { | ||
| prevVal: T; | ||
| currentVal: T; | ||
| } | ||
| /** | ||
| * @private | ||
| */ | ||
| export type Listener<T> = (value: ListenerValue<T>) => void; |
| import { Store } from './store.js'; | ||
| import { Listener } from './types.js'; | ||
| export type UnwrapDerivedOrStore<T> = T extends Derived<infer InnerD> ? InnerD : T extends Store<infer InnerS> ? InnerS : never; | ||
| type UnwrapReadonlyDerivedOrStoreArray<TArr extends ReadonlyArray<Derived<any> | Store<any>>> = TArr extends readonly [infer Head, ...infer Tail] ? Head extends Derived<any> | Store<any> ? Tail extends ReadonlyArray<Derived<any> | Store<any>> ? [UnwrapDerivedOrStore<Head>, ...UnwrapReadonlyDerivedOrStoreArray<Tail>] : [] : [] : []; | ||
| export interface DerivedFnProps<TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>, TUnwrappedArr extends UnwrapReadonlyDerivedOrStoreArray<TArr> = UnwrapReadonlyDerivedOrStoreArray<TArr>> { | ||
| /** | ||
| * `undefined` if it's the first run | ||
| * @privateRemarks this also cannot be typed as TState, as it breaks the inferencing of the function's return type when an argument is used - even with `NoInfer` usage | ||
| */ | ||
| prevVal: unknown | undefined; | ||
| prevDepVals: TUnwrappedArr | undefined; | ||
| currDepVals: TUnwrappedArr; | ||
| } | ||
| export interface DerivedOptions<TState, TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>> { | ||
| onSubscribe?: (listener: Listener<TState>, derived: Derived<TState>) => () => void; | ||
| onUpdate?: () => void; | ||
| deps: TArr; | ||
| /** | ||
| * Values of the `deps` from before and after the current invocation of `fn` | ||
| */ | ||
| fn: (props: DerivedFnProps<TArr>) => TState; | ||
| } | ||
| export declare class Derived<TState, const TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>> { | ||
| listeners: Set<Listener<TState>>; | ||
| state: TState; | ||
| prevState: TState | undefined; | ||
| options: DerivedOptions<TState, TArr>; | ||
| /** | ||
| * Functions representing the subscriptions. Call a function to cleanup | ||
| * @private | ||
| */ | ||
| _subscriptions: Array<() => void>; | ||
| lastSeenDepValues: Array<unknown>; | ||
| getDepVals: () => { | ||
| prevDepVals: unknown[]; | ||
| currDepVals: unknown[]; | ||
| prevVal: NonNullable<TState> | undefined; | ||
| }; | ||
| constructor(options: DerivedOptions<TState, TArr>); | ||
| registerOnGraph(deps?: ReadonlyArray<Derived<any> | Store<any>>): void; | ||
| unregisterFromGraph(deps?: ReadonlyArray<Derived<any> | Store<any>>): void; | ||
| recompute: () => void; | ||
| checkIfRecalculationNeededDeeply: () => void; | ||
| mount: () => () => void; | ||
| subscribe: (listener: Listener<TState>) => () => void; | ||
| } | ||
| export {}; |
| import { Store } from "./store.js"; | ||
| import { __storeToDerived, __derivedToStore } from "./scheduler.js"; | ||
| class Derived { | ||
| constructor(options) { | ||
| this.listeners = /* @__PURE__ */ new Set(); | ||
| this._subscriptions = []; | ||
| this.lastSeenDepValues = []; | ||
| this.getDepVals = () => { | ||
| const prevDepVals = []; | ||
| const currDepVals = []; | ||
| for (const dep of this.options.deps) { | ||
| prevDepVals.push(dep.prevState); | ||
| currDepVals.push(dep.state); | ||
| } | ||
| this.lastSeenDepValues = currDepVals; | ||
| return { | ||
| prevDepVals, | ||
| currDepVals, | ||
| prevVal: this.prevState ?? void 0 | ||
| }; | ||
| }; | ||
| this.recompute = () => { | ||
| var _a, _b; | ||
| this.prevState = this.state; | ||
| const { prevDepVals, currDepVals, prevVal } = this.getDepVals(); | ||
| this.state = this.options.fn({ | ||
| prevDepVals, | ||
| currDepVals, | ||
| prevVal | ||
| }); | ||
| (_b = (_a = this.options).onUpdate) == null ? void 0 : _b.call(_a); | ||
| }; | ||
| this.checkIfRecalculationNeededDeeply = () => { | ||
| for (const dep of this.options.deps) { | ||
| if (dep instanceof Derived) { | ||
| dep.checkIfRecalculationNeededDeeply(); | ||
| } | ||
| } | ||
| let shouldRecompute = false; | ||
| const lastSeenDepValues = this.lastSeenDepValues; | ||
| const { currDepVals } = this.getDepVals(); | ||
| for (let i = 0; i < currDepVals.length; i++) { | ||
| if (currDepVals[i] !== lastSeenDepValues[i]) { | ||
| shouldRecompute = true; | ||
| break; | ||
| } | ||
| } | ||
| if (shouldRecompute) { | ||
| this.recompute(); | ||
| } | ||
| }; | ||
| this.mount = () => { | ||
| this.registerOnGraph(); | ||
| this.checkIfRecalculationNeededDeeply(); | ||
| return () => { | ||
| this.unregisterFromGraph(); | ||
| for (const cleanup of this._subscriptions) { | ||
| cleanup(); | ||
| } | ||
| }; | ||
| }; | ||
| this.subscribe = (listener) => { | ||
| var _a, _b; | ||
| this.listeners.add(listener); | ||
| const unsub = (_b = (_a = this.options).onSubscribe) == null ? void 0 : _b.call(_a, listener, this); | ||
| return () => { | ||
| this.listeners.delete(listener); | ||
| unsub == null ? void 0 : unsub(); | ||
| }; | ||
| }; | ||
| this.options = options; | ||
| this.state = options.fn({ | ||
| prevDepVals: void 0, | ||
| prevVal: void 0, | ||
| currDepVals: this.getDepVals().currDepVals | ||
| }); | ||
| } | ||
| registerOnGraph(deps = this.options.deps) { | ||
| for (const dep of deps) { | ||
| if (dep instanceof Derived) { | ||
| dep.registerOnGraph(); | ||
| this.registerOnGraph(dep.options.deps); | ||
| } else if (dep instanceof Store) { | ||
| let relatedLinkedDerivedVals = __storeToDerived.get(dep); | ||
| if (!relatedLinkedDerivedVals) { | ||
| relatedLinkedDerivedVals = /* @__PURE__ */ new Set(); | ||
| __storeToDerived.set(dep, relatedLinkedDerivedVals); | ||
| } | ||
| relatedLinkedDerivedVals.add(this); | ||
| let relatedStores = __derivedToStore.get(this); | ||
| if (!relatedStores) { | ||
| relatedStores = /* @__PURE__ */ new Set(); | ||
| __derivedToStore.set(this, relatedStores); | ||
| } | ||
| relatedStores.add(dep); | ||
| } | ||
| } | ||
| } | ||
| unregisterFromGraph(deps = this.options.deps) { | ||
| for (const dep of deps) { | ||
| if (dep instanceof Derived) { | ||
| this.unregisterFromGraph(dep.options.deps); | ||
| } else if (dep instanceof Store) { | ||
| const relatedLinkedDerivedVals = __storeToDerived.get(dep); | ||
| if (relatedLinkedDerivedVals) { | ||
| relatedLinkedDerivedVals.delete(this); | ||
| } | ||
| const relatedStores = __derivedToStore.get(this); | ||
| if (relatedStores) { | ||
| relatedStores.delete(dep); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| export { | ||
| Derived | ||
| }; | ||
| //# sourceMappingURL=derived.js.map |
| {"version":3,"file":"derived.js","sources":["../../src/derived.ts"],"sourcesContent":["import { Store } from './store'\nimport { __derivedToStore, __storeToDerived } from './scheduler'\nimport type { Listener } from './types'\n\nexport type UnwrapDerivedOrStore<T> =\n T extends Derived<infer InnerD>\n ? InnerD\n : T extends Store<infer InnerS>\n ? InnerS\n : never\n\ntype UnwrapReadonlyDerivedOrStoreArray<\n TArr extends ReadonlyArray<Derived<any> | Store<any>>,\n> = TArr extends readonly [infer Head, ...infer Tail]\n ? Head extends Derived<any> | Store<any>\n ? Tail extends ReadonlyArray<Derived<any> | Store<any>>\n ? [UnwrapDerivedOrStore<Head>, ...UnwrapReadonlyDerivedOrStoreArray<Tail>]\n : []\n : []\n : []\n\n// Can't have currVal, as it's being evaluated from the current derived fn\nexport interface DerivedFnProps<\n TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>,\n TUnwrappedArr extends\n UnwrapReadonlyDerivedOrStoreArray<TArr> = UnwrapReadonlyDerivedOrStoreArray<TArr>,\n> {\n // `undefined` if it's the first run\n /**\n * `undefined` if it's the first run\n * @privateRemarks this also cannot be typed as TState, as it breaks the inferencing of the function's return type when an argument is used - even with `NoInfer` usage\n */\n prevVal: unknown | undefined\n prevDepVals: TUnwrappedArr | undefined\n currDepVals: TUnwrappedArr\n}\n\nexport interface DerivedOptions<\n TState,\n TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>,\n> {\n onSubscribe?: (\n listener: Listener<TState>,\n derived: Derived<TState>,\n ) => () => void\n onUpdate?: () => void\n deps: TArr\n /**\n * Values of the `deps` from before and after the current invocation of `fn`\n */\n fn: (props: DerivedFnProps<TArr>) => TState\n}\n\nexport class Derived<\n TState,\n const TArr extends ReadonlyArray<\n Derived<any> | Store<any>\n > = ReadonlyArray<any>,\n> {\n listeners = new Set<Listener<TState>>()\n state: TState\n prevState: TState | undefined\n options: DerivedOptions<TState, TArr>\n\n /**\n * Functions representing the subscriptions. Call a function to cleanup\n * @private\n */\n _subscriptions: Array<() => void> = []\n\n lastSeenDepValues: Array<unknown> = []\n getDepVals = () => {\n const prevDepVals = [] as Array<unknown>\n const currDepVals = [] as Array<unknown>\n for (const dep of this.options.deps) {\n prevDepVals.push(dep.prevState)\n currDepVals.push(dep.state)\n }\n this.lastSeenDepValues = currDepVals\n return {\n prevDepVals,\n currDepVals,\n prevVal: this.prevState ?? undefined,\n }\n }\n\n constructor(options: DerivedOptions<TState, TArr>) {\n this.options = options\n this.state = options.fn({\n prevDepVals: undefined,\n prevVal: undefined,\n currDepVals: this.getDepVals().currDepVals as never,\n })\n }\n\n registerOnGraph(\n deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps,\n ) {\n for (const dep of deps) {\n if (dep instanceof Derived) {\n // First register the intermediate derived value if it's not already registered\n dep.registerOnGraph()\n // Then register this derived with the dep's underlying stores\n this.registerOnGraph(dep.options.deps)\n } else if (dep instanceof Store) {\n // Register the derived as related derived to the store\n let relatedLinkedDerivedVals = __storeToDerived.get(dep)\n if (!relatedLinkedDerivedVals) {\n relatedLinkedDerivedVals = new Set()\n __storeToDerived.set(dep, relatedLinkedDerivedVals)\n }\n relatedLinkedDerivedVals.add(this as never)\n\n // Register the store as a related store to this derived\n let relatedStores = __derivedToStore.get(this as never)\n if (!relatedStores) {\n relatedStores = new Set()\n __derivedToStore.set(this as never, relatedStores)\n }\n relatedStores.add(dep)\n }\n }\n }\n\n unregisterFromGraph(\n deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps,\n ) {\n for (const dep of deps) {\n if (dep instanceof Derived) {\n this.unregisterFromGraph(dep.options.deps)\n } else if (dep instanceof Store) {\n const relatedLinkedDerivedVals = __storeToDerived.get(dep)\n if (relatedLinkedDerivedVals) {\n relatedLinkedDerivedVals.delete(this as never)\n }\n\n const relatedStores = __derivedToStore.get(this as never)\n if (relatedStores) {\n relatedStores.delete(dep)\n }\n }\n }\n }\n\n recompute = () => {\n this.prevState = this.state\n const { prevDepVals, currDepVals, prevVal } = this.getDepVals()\n this.state = this.options.fn({\n prevDepVals: prevDepVals as never,\n currDepVals: currDepVals as never,\n prevVal,\n })\n\n this.options.onUpdate?.()\n }\n\n checkIfRecalculationNeededDeeply = () => {\n for (const dep of this.options.deps) {\n if (dep instanceof Derived) {\n dep.checkIfRecalculationNeededDeeply()\n }\n }\n let shouldRecompute = false\n const lastSeenDepValues = this.lastSeenDepValues\n const { currDepVals } = this.getDepVals()\n for (let i = 0; i < currDepVals.length; i++) {\n if (currDepVals[i] !== lastSeenDepValues[i]) {\n shouldRecompute = true\n break\n }\n }\n\n if (shouldRecompute) {\n this.recompute()\n }\n }\n\n mount = () => {\n this.registerOnGraph()\n this.checkIfRecalculationNeededDeeply()\n\n return () => {\n this.unregisterFromGraph()\n for (const cleanup of this._subscriptions) {\n cleanup()\n }\n }\n }\n\n subscribe = (listener: Listener<TState>) => {\n this.listeners.add(listener)\n const unsub = this.options.onSubscribe?.(listener, this)\n return () => {\n this.listeners.delete(listener)\n unsub?.()\n }\n }\n}\n"],"names":[],"mappings":";;AAqDO,MAAM,QAKX;AAAA,EA4BA,YAAY,SAAuC;AA3BnD,SAAA,gCAAgB,IAAsB;AAStC,SAAA,iBAAoC,CAAC;AAErC,SAAA,oBAAoC,CAAC;AACrC,SAAA,aAAa,MAAM;AACjB,YAAM,cAAc,CAAC;AACrB,YAAM,cAAc,CAAC;AACV,iBAAA,OAAO,KAAK,QAAQ,MAAM;AACvB,oBAAA,KAAK,IAAI,SAAS;AAClB,oBAAA,KAAK,IAAI,KAAK;AAAA,MAAA;AAE5B,WAAK,oBAAoB;AAClB,aAAA;AAAA,QACL;AAAA,QACA;AAAA,QACA,SAAS,KAAK,aAAa;AAAA,MAC7B;AAAA,IACF;AA4DA,SAAA,YAAY,MAAM;;AAChB,WAAK,YAAY,KAAK;AACtB,YAAM,EAAE,aAAa,aAAa,QAAQ,IAAI,KAAK,WAAW;AACzD,WAAA,QAAQ,KAAK,QAAQ,GAAG;AAAA,QAC3B;AAAA,QACA;AAAA,QACA;AAAA,MAAA,CACD;AAED,uBAAK,SAAQ,aAAb;AAAA,IACF;AAEA,SAAA,mCAAmC,MAAM;AAC5B,iBAAA,OAAO,KAAK,QAAQ,MAAM;AACnC,YAAI,eAAe,SAAS;AAC1B,cAAI,iCAAiC;AAAA,QAAA;AAAA,MACvC;AAEF,UAAI,kBAAkB;AACtB,YAAM,oBAAoB,KAAK;AAC/B,YAAM,EAAE,YAAA,IAAgB,KAAK,WAAW;AACxC,eAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,YAAI,YAAY,CAAC,MAAM,kBAAkB,CAAC,GAAG;AACzB,4BAAA;AAClB;AAAA,QAAA;AAAA,MACF;AAGF,UAAI,iBAAiB;AACnB,aAAK,UAAU;AAAA,MAAA;AAAA,IAEnB;AAEA,SAAA,QAAQ,MAAM;AACZ,WAAK,gBAAgB;AACrB,WAAK,iCAAiC;AAEtC,aAAO,MAAM;AACX,aAAK,oBAAoB;AACd,mBAAA,WAAW,KAAK,gBAAgB;AACjC,kBAAA;AAAA,QAAA;AAAA,MAEZ;AAAA,IACF;AAEA,SAAA,YAAY,CAAC,aAA+B;;AACrC,WAAA,UAAU,IAAI,QAAQ;AAC3B,YAAM,SAAQ,gBAAK,SAAQ,gBAAb,4BAA2B,UAAU;AACnD,aAAO,MAAM;AACN,aAAA,UAAU,OAAO,QAAQ;AACtB;AAAA,MACV;AAAA,IACF;AA7GE,SAAK,UAAU;AACV,SAAA,QAAQ,QAAQ,GAAG;AAAA,MACtB,aAAa;AAAA,MACb,SAAS;AAAA,MACT,aAAa,KAAK,aAAa;AAAA,IAAA,CAChC;AAAA,EAAA;AAAA,EAGH,gBACE,OAAiD,KAAK,QAAQ,MAC9D;AACA,eAAW,OAAO,MAAM;AACtB,UAAI,eAAe,SAAS;AAE1B,YAAI,gBAAgB;AAEf,aAAA,gBAAgB,IAAI,QAAQ,IAAI;AAAA,MAAA,WAC5B,eAAe,OAAO;AAE3B,YAAA,2BAA2B,iBAAiB,IAAI,GAAG;AACvD,YAAI,CAAC,0BAA0B;AAC7B,yDAA+B,IAAI;AAClB,2BAAA,IAAI,KAAK,wBAAwB;AAAA,QAAA;AAEpD,iCAAyB,IAAI,IAAa;AAGtC,YAAA,gBAAgB,iBAAiB,IAAI,IAAa;AACtD,YAAI,CAAC,eAAe;AAClB,8CAAoB,IAAI;AACP,2BAAA,IAAI,MAAe,aAAa;AAAA,QAAA;AAEnD,sBAAc,IAAI,GAAG;AAAA,MAAA;AAAA,IACvB;AAAA,EACF;AAAA,EAGF,oBACE,OAAiD,KAAK,QAAQ,MAC9D;AACA,eAAW,OAAO,MAAM;AACtB,UAAI,eAAe,SAAS;AACrB,aAAA,oBAAoB,IAAI,QAAQ,IAAI;AAAA,MAAA,WAChC,eAAe,OAAO;AACzB,cAAA,2BAA2B,iBAAiB,IAAI,GAAG;AACzD,YAAI,0BAA0B;AAC5B,mCAAyB,OAAO,IAAa;AAAA,QAAA;AAGzC,cAAA,gBAAgB,iBAAiB,IAAI,IAAa;AACxD,YAAI,eAAe;AACjB,wBAAc,OAAO,GAAG;AAAA,QAAA;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAwDJ;"} |
| import { Derived, DerivedOptions } from './derived.js'; | ||
| interface EffectOptions extends Omit<DerivedOptions<unknown>, 'onUpdate' | 'onSubscribe' | 'lazy' | 'fn'> { | ||
| /** | ||
| * Should the effect trigger immediately? | ||
| * @default false | ||
| */ | ||
| eager?: boolean; | ||
| fn: () => void; | ||
| } | ||
| export declare class Effect { | ||
| /** | ||
| * @private | ||
| */ | ||
| _derived: Derived<void>; | ||
| constructor(opts: EffectOptions); | ||
| mount(): () => void; | ||
| } | ||
| export {}; |
| import { Derived } from "./derived.js"; | ||
| class Effect { | ||
| constructor(opts) { | ||
| const { eager, fn, ...derivedProps } = opts; | ||
| this._derived = new Derived({ | ||
| ...derivedProps, | ||
| fn: () => { | ||
| }, | ||
| onUpdate() { | ||
| fn(); | ||
| } | ||
| }); | ||
| if (eager) { | ||
| fn(); | ||
| } | ||
| } | ||
| mount() { | ||
| return this._derived.mount(); | ||
| } | ||
| } | ||
| export { | ||
| Effect | ||
| }; | ||
| //# sourceMappingURL=effect.js.map |
| {"version":3,"file":"effect.js","sources":["../../src/effect.ts"],"sourcesContent":["import { Derived } from './derived'\nimport type { DerivedOptions } from './derived'\n\ninterface EffectOptions\n extends Omit<\n DerivedOptions<unknown>,\n 'onUpdate' | 'onSubscribe' | 'lazy' | 'fn'\n > {\n /**\n * Should the effect trigger immediately?\n * @default false\n */\n eager?: boolean\n fn: () => void\n}\n\nexport class Effect {\n /**\n * @private\n */\n _derived: Derived<void>\n\n constructor(opts: EffectOptions) {\n const { eager, fn, ...derivedProps } = opts\n\n this._derived = new Derived({\n ...derivedProps,\n fn: () => {},\n onUpdate() {\n fn()\n },\n })\n\n if (eager) {\n fn()\n }\n }\n\n mount() {\n return this._derived.mount()\n }\n}\n"],"names":[],"mappings":";AAgBO,MAAM,OAAO;AAAA,EAMlB,YAAY,MAAqB;AAC/B,UAAM,EAAE,OAAO,IAAI,GAAG,aAAiB,IAAA;AAElC,SAAA,WAAW,IAAI,QAAQ;AAAA,MAC1B,GAAG;AAAA,MACH,IAAI,MAAM;AAAA,MAAC;AAAA,MACX,WAAW;AACN,WAAA;AAAA,MAAA;AAAA,IACL,CACD;AAED,QAAI,OAAO;AACN,SAAA;AAAA,IAAA;AAAA,EACL;AAAA,EAGF,QAAQ;AACC,WAAA,KAAK,SAAS,MAAM;AAAA,EAAA;AAE/B;"} |
| import { Derived } from './derived.js'; | ||
| import { Store } from './store.js'; | ||
| /** | ||
| * This is here to solve the pyramid dependency problem where: | ||
| * A | ||
| * / \ | ||
| * B C | ||
| * \ / | ||
| * D | ||
| * | ||
| * Where we deeply traverse this tree, how do we avoid D being recomputed twice; once when B is updated, once when C is. | ||
| * | ||
| * To solve this, we create linkedDeps that allows us to sync avoid writes to the state until all of the deps have been | ||
| * resolved. | ||
| * | ||
| * This is a record of stores, because derived stores are not able to write values to, but stores are | ||
| */ | ||
| export declare const __storeToDerived: WeakMap<Store<unknown, (cb: unknown) => unknown>, Set<Derived<unknown, readonly any[]>>>; | ||
| export declare const __derivedToStore: WeakMap<Derived<unknown, readonly any[]>, Set<Store<unknown, (cb: unknown) => unknown>>>; | ||
| export declare const __depsThatHaveWrittenThisTick: { | ||
| current: Array<Derived<unknown> | Store<unknown>>; | ||
| }; | ||
| /** | ||
| * @private only to be called from `Store` on write | ||
| */ | ||
| export declare function __flush(store: Store<unknown>): void; | ||
| export declare function batch(fn: () => void): void; |
| import { Derived } from "./derived.js"; | ||
| const __storeToDerived = /* @__PURE__ */ new WeakMap(); | ||
| const __derivedToStore = /* @__PURE__ */ new WeakMap(); | ||
| const __depsThatHaveWrittenThisTick = { | ||
| current: [] | ||
| }; | ||
| let __isFlushing = false; | ||
| let __batchDepth = 0; | ||
| const __pendingUpdates = /* @__PURE__ */ new Set(); | ||
| const __initialBatchValues = /* @__PURE__ */ new Map(); | ||
| function __flush_internals(relatedVals) { | ||
| const sorted = Array.from(relatedVals).sort((a, b) => { | ||
| if (a instanceof Derived && a.options.deps.includes(b)) return 1; | ||
| if (b instanceof Derived && b.options.deps.includes(a)) return -1; | ||
| return 0; | ||
| }); | ||
| for (const derived of sorted) { | ||
| if (__depsThatHaveWrittenThisTick.current.includes(derived)) { | ||
| continue; | ||
| } | ||
| __depsThatHaveWrittenThisTick.current.push(derived); | ||
| derived.recompute(); | ||
| const stores = __derivedToStore.get(derived); | ||
| if (stores) { | ||
| for (const store of stores) { | ||
| const relatedLinkedDerivedVals = __storeToDerived.get(store); | ||
| if (!relatedLinkedDerivedVals) continue; | ||
| __flush_internals(relatedLinkedDerivedVals); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| function __notifyListeners(store) { | ||
| store.listeners.forEach( | ||
| (listener) => listener({ | ||
| prevVal: store.prevState, | ||
| currentVal: store.state | ||
| }) | ||
| ); | ||
| } | ||
| function __notifyDerivedListeners(derived) { | ||
| derived.listeners.forEach( | ||
| (listener) => listener({ | ||
| prevVal: derived.prevState, | ||
| currentVal: derived.state | ||
| }) | ||
| ); | ||
| } | ||
| function __flush(store) { | ||
| if (__batchDepth > 0 && !__initialBatchValues.has(store)) { | ||
| __initialBatchValues.set(store, store.prevState); | ||
| } | ||
| __pendingUpdates.add(store); | ||
| if (__batchDepth > 0) return; | ||
| if (__isFlushing) return; | ||
| try { | ||
| __isFlushing = true; | ||
| while (__pendingUpdates.size > 0) { | ||
| const stores = Array.from(__pendingUpdates); | ||
| __pendingUpdates.clear(); | ||
| for (const store2 of stores) { | ||
| const prevState = __initialBatchValues.get(store2) ?? store2.prevState; | ||
| store2.prevState = prevState; | ||
| __notifyListeners(store2); | ||
| } | ||
| for (const store2 of stores) { | ||
| const derivedVals = __storeToDerived.get(store2); | ||
| if (!derivedVals) continue; | ||
| __depsThatHaveWrittenThisTick.current.push(store2); | ||
| __flush_internals(derivedVals); | ||
| } | ||
| for (const store2 of stores) { | ||
| const derivedVals = __storeToDerived.get(store2); | ||
| if (!derivedVals) continue; | ||
| for (const derived of derivedVals) { | ||
| __notifyDerivedListeners(derived); | ||
| } | ||
| } | ||
| } | ||
| } finally { | ||
| __isFlushing = false; | ||
| __depsThatHaveWrittenThisTick.current = []; | ||
| __initialBatchValues.clear(); | ||
| } | ||
| } | ||
| function batch(fn) { | ||
| __batchDepth++; | ||
| try { | ||
| fn(); | ||
| } finally { | ||
| __batchDepth--; | ||
| if (__batchDepth === 0) { | ||
| const pendingUpdateToFlush = Array.from(__pendingUpdates)[0]; | ||
| if (pendingUpdateToFlush) { | ||
| __flush(pendingUpdateToFlush); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| export { | ||
| __depsThatHaveWrittenThisTick, | ||
| __derivedToStore, | ||
| __flush, | ||
| __storeToDerived, | ||
| batch | ||
| }; | ||
| //# sourceMappingURL=scheduler.js.map |
| {"version":3,"file":"scheduler.js","sources":["../../src/scheduler.ts"],"sourcesContent":["import { Derived } from './derived'\nimport type { Store } from './store'\n\n/**\n * This is here to solve the pyramid dependency problem where:\n * A\n * / \\\n * B C\n * \\ /\n * D\n *\n * Where we deeply traverse this tree, how do we avoid D being recomputed twice; once when B is updated, once when C is.\n *\n * To solve this, we create linkedDeps that allows us to sync avoid writes to the state until all of the deps have been\n * resolved.\n *\n * This is a record of stores, because derived stores are not able to write values to, but stores are\n */\nexport const __storeToDerived = new WeakMap<\n Store<unknown>,\n Set<Derived<unknown>>\n>()\nexport const __derivedToStore = new WeakMap<\n Derived<unknown>,\n Set<Store<unknown>>\n>()\n\nexport const __depsThatHaveWrittenThisTick = {\n current: [] as Array<Derived<unknown> | Store<unknown>>,\n}\n\nlet __isFlushing = false\nlet __batchDepth = 0\nconst __pendingUpdates = new Set<Store<unknown>>()\n// Add a map to store initial values before batch\nconst __initialBatchValues = new Map<Store<unknown>, unknown>()\n\nfunction __flush_internals(relatedVals: Set<Derived<unknown>>) {\n // First sort deriveds by dependency order\n const sorted = Array.from(relatedVals).sort((a, b) => {\n // If a depends on b, b should go first\n if (a instanceof Derived && a.options.deps.includes(b)) return 1\n // If b depends on a, a should go first\n if (b instanceof Derived && b.options.deps.includes(a)) return -1\n return 0\n })\n\n for (const derived of sorted) {\n if (__depsThatHaveWrittenThisTick.current.includes(derived)) {\n continue\n }\n\n __depsThatHaveWrittenThisTick.current.push(derived)\n derived.recompute()\n\n const stores = __derivedToStore.get(derived)\n if (stores) {\n for (const store of stores) {\n const relatedLinkedDerivedVals = __storeToDerived.get(store)\n if (!relatedLinkedDerivedVals) continue\n __flush_internals(relatedLinkedDerivedVals)\n }\n }\n }\n}\n\nfunction __notifyListeners(store: Store<unknown>) {\n store.listeners.forEach((listener) =>\n listener({\n prevVal: store.prevState as never,\n currentVal: store.state as never,\n }),\n )\n}\n\nfunction __notifyDerivedListeners(derived: Derived<unknown>) {\n derived.listeners.forEach((listener) =>\n listener({\n prevVal: derived.prevState as never,\n currentVal: derived.state as never,\n }),\n )\n}\n\n/**\n * @private only to be called from `Store` on write\n */\nexport function __flush(store: Store<unknown>) {\n // If we're starting a batch, store the initial values\n if (__batchDepth > 0 && !__initialBatchValues.has(store)) {\n __initialBatchValues.set(store, store.prevState)\n }\n\n __pendingUpdates.add(store)\n\n if (__batchDepth > 0) return\n if (__isFlushing) return\n\n try {\n __isFlushing = true\n\n while (__pendingUpdates.size > 0) {\n const stores = Array.from(__pendingUpdates)\n __pendingUpdates.clear()\n\n // First notify listeners with updated values\n for (const store of stores) {\n // Use initial batch values for prevState if we have them\n const prevState = __initialBatchValues.get(store) ?? store.prevState\n store.prevState = prevState\n __notifyListeners(store)\n }\n\n // Then update all derived values\n for (const store of stores) {\n const derivedVals = __storeToDerived.get(store)\n if (!derivedVals) continue\n\n __depsThatHaveWrittenThisTick.current.push(store)\n __flush_internals(derivedVals)\n }\n\n // Notify derived listeners after recomputing\n for (const store of stores) {\n const derivedVals = __storeToDerived.get(store)\n if (!derivedVals) continue\n\n for (const derived of derivedVals) {\n __notifyDerivedListeners(derived)\n }\n }\n }\n } finally {\n __isFlushing = false\n __depsThatHaveWrittenThisTick.current = []\n __initialBatchValues.clear()\n }\n}\n\nexport function batch(fn: () => void) {\n __batchDepth++\n try {\n fn()\n } finally {\n __batchDepth--\n if (__batchDepth === 0) {\n const pendingUpdateToFlush = Array.from(__pendingUpdates)[0] as\n | Store<unknown>\n | undefined\n if (pendingUpdateToFlush) {\n __flush(pendingUpdateToFlush) // Trigger flush of all pending updates\n }\n }\n }\n}\n"],"names":["store"],"mappings":";AAkBa,MAAA,uCAAuB,QAGlC;AACW,MAAA,uCAAuB,QAGlC;AAEK,MAAM,gCAAgC;AAAA,EAC3C,SAAS,CAAA;AACX;AAEA,IAAI,eAAe;AACnB,IAAI,eAAe;AACnB,MAAM,uCAAuB,IAAoB;AAEjD,MAAM,2CAA2B,IAA6B;AAE9D,SAAS,kBAAkB,aAAoC;AAEvD,QAAA,SAAS,MAAM,KAAK,WAAW,EAAE,KAAK,CAAC,GAAG,MAAM;AAEhD,QAAA,aAAa,WAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAU,QAAA;AAE3D,QAAA,aAAa,WAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAU,QAAA;AACxD,WAAA;AAAA,EAAA,CACR;AAED,aAAW,WAAW,QAAQ;AAC5B,QAAI,8BAA8B,QAAQ,SAAS,OAAO,GAAG;AAC3D;AAAA,IAAA;AAG4B,kCAAA,QAAQ,KAAK,OAAO;AAClD,YAAQ,UAAU;AAEZ,UAAA,SAAS,iBAAiB,IAAI,OAAO;AAC3C,QAAI,QAAQ;AACV,iBAAW,SAAS,QAAQ;AACpB,cAAA,2BAA2B,iBAAiB,IAAI,KAAK;AAC3D,YAAI,CAAC,yBAA0B;AAC/B,0BAAkB,wBAAwB;AAAA,MAAA;AAAA,IAC5C;AAAA,EACF;AAEJ;AAEA,SAAS,kBAAkB,OAAuB;AAChD,QAAM,UAAU;AAAA,IAAQ,CAAC,aACvB,SAAS;AAAA,MACP,SAAS,MAAM;AAAA,MACf,YAAY,MAAM;AAAA,IACnB,CAAA;AAAA,EACH;AACF;AAEA,SAAS,yBAAyB,SAA2B;AAC3D,UAAQ,UAAU;AAAA,IAAQ,CAAC,aACzB,SAAS;AAAA,MACP,SAAS,QAAQ;AAAA,MACjB,YAAY,QAAQ;AAAA,IACrB,CAAA;AAAA,EACH;AACF;AAKO,SAAS,QAAQ,OAAuB;AAE7C,MAAI,eAAe,KAAK,CAAC,qBAAqB,IAAI,KAAK,GAAG;AACnC,yBAAA,IAAI,OAAO,MAAM,SAAS;AAAA,EAAA;AAGjD,mBAAiB,IAAI,KAAK;AAE1B,MAAI,eAAe,EAAG;AACtB,MAAI,aAAc;AAEd,MAAA;AACa,mBAAA;AAER,WAAA,iBAAiB,OAAO,GAAG;AAC1B,YAAA,SAAS,MAAM,KAAK,gBAAgB;AAC1C,uBAAiB,MAAM;AAGvB,iBAAWA,UAAS,QAAQ;AAE1B,cAAM,YAAY,qBAAqB,IAAIA,MAAK,KAAKA,OAAM;AAC3DA,eAAM,YAAY;AAClB,0BAAkBA,MAAK;AAAA,MAAA;AAIzB,iBAAWA,UAAS,QAAQ;AACpB,cAAA,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAEY,sCAAA,QAAQ,KAAKA,MAAK;AAChD,0BAAkB,WAAW;AAAA,MAAA;AAI/B,iBAAWA,UAAS,QAAQ;AACpB,cAAA,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAElB,mBAAW,WAAW,aAAa;AACjC,mCAAyB,OAAO;AAAA,QAAA;AAAA,MAClC;AAAA,IACF;AAAA,EACF,UACA;AACe,mBAAA;AACf,kCAA8B,UAAU,CAAC;AACzC,yBAAqB,MAAM;AAAA,EAAA;AAE/B;AAEO,SAAS,MAAM,IAAgB;AACpC;AACI,MAAA;AACC,OAAA;AAAA,EAAA,UACH;AACA;AACA,QAAI,iBAAiB,GAAG;AACtB,YAAM,uBAAuB,MAAM,KAAK,gBAAgB,EAAE,CAAC;AAG3D,UAAI,sBAAsB;AACxB,gBAAQ,oBAAoB;AAAA,MAAA;AAAA,IAC9B;AAAA,EACF;AAEJ;"} |
| import { AnyUpdater, Listener } from './types.js'; | ||
| export interface StoreOptions<TState, TUpdater extends AnyUpdater = (cb: TState) => TState> { | ||
| /** | ||
| * Replace the default update function with a custom one. | ||
| */ | ||
| updateFn?: (previous: TState) => (updater: TUpdater) => TState; | ||
| /** | ||
| * Called when a listener subscribes to the store. | ||
| * | ||
| * @return a function to unsubscribe the listener | ||
| */ | ||
| onSubscribe?: (listener: Listener<TState>, store: Store<TState, TUpdater>) => () => void; | ||
| /** | ||
| * Called after the state has been updated, used to derive other state. | ||
| */ | ||
| onUpdate?: () => void; | ||
| } | ||
| export declare class Store<TState, TUpdater extends AnyUpdater = (cb: TState) => TState> { | ||
| listeners: Set<Listener<TState>>; | ||
| state: TState; | ||
| prevState: TState; | ||
| options?: StoreOptions<TState, TUpdater>; | ||
| constructor(initialState: TState, options?: StoreOptions<TState, TUpdater>); | ||
| subscribe: (listener: Listener<TState>) => () => void; | ||
| setState: (updater: TUpdater) => void; | ||
| } |
| import { __flush } from "./scheduler.js"; | ||
| class Store { | ||
| constructor(initialState, options) { | ||
| this.listeners = /* @__PURE__ */ new Set(); | ||
| this.subscribe = (listener) => { | ||
| var _a, _b; | ||
| this.listeners.add(listener); | ||
| const unsub = (_b = (_a = this.options) == null ? void 0 : _a.onSubscribe) == null ? void 0 : _b.call(_a, listener, this); | ||
| return () => { | ||
| this.listeners.delete(listener); | ||
| unsub == null ? void 0 : unsub(); | ||
| }; | ||
| }; | ||
| this.setState = (updater) => { | ||
| var _a, _b, _c; | ||
| this.prevState = this.state; | ||
| this.state = ((_a = this.options) == null ? void 0 : _a.updateFn) ? this.options.updateFn(this.prevState)(updater) : updater(this.prevState); | ||
| (_c = (_b = this.options) == null ? void 0 : _b.onUpdate) == null ? void 0 : _c.call(_b); | ||
| __flush(this); | ||
| }; | ||
| this.prevState = initialState; | ||
| this.state = initialState; | ||
| this.options = options; | ||
| } | ||
| } | ||
| export { | ||
| Store | ||
| }; | ||
| //# sourceMappingURL=store.js.map |
| {"version":3,"file":"store.js","sources":["../../src/store.ts"],"sourcesContent":["import { __flush } from './scheduler'\nimport type { AnyUpdater, Listener } from './types'\n\nexport interface StoreOptions<\n TState,\n TUpdater extends AnyUpdater = (cb: TState) => TState,\n> {\n /**\n * Replace the default update function with a custom one.\n */\n updateFn?: (previous: TState) => (updater: TUpdater) => TState\n /**\n * Called when a listener subscribes to the store.\n *\n * @return a function to unsubscribe the listener\n */\n onSubscribe?: (\n listener: Listener<TState>,\n store: Store<TState, TUpdater>,\n ) => () => void\n /**\n * Called after the state has been updated, used to derive other state.\n */\n onUpdate?: () => void\n}\n\nexport class Store<\n TState,\n TUpdater extends AnyUpdater = (cb: TState) => TState,\n> {\n listeners = new Set<Listener<TState>>()\n state: TState\n prevState: TState\n options?: StoreOptions<TState, TUpdater>\n\n constructor(initialState: TState, options?: StoreOptions<TState, TUpdater>) {\n this.prevState = initialState\n this.state = initialState\n this.options = options\n }\n\n subscribe = (listener: Listener<TState>) => {\n this.listeners.add(listener)\n const unsub = this.options?.onSubscribe?.(listener, this)\n return () => {\n this.listeners.delete(listener)\n unsub?.()\n }\n }\n\n setState = (updater: TUpdater) => {\n this.prevState = this.state\n this.state = this.options?.updateFn\n ? this.options.updateFn(this.prevState)(updater)\n : (updater as any)(this.prevState)\n\n // Always run onUpdate, regardless of batching\n this.options?.onUpdate?.()\n\n // Attempt to flush\n __flush(this as never)\n }\n}\n"],"names":[],"mappings":";AA0BO,MAAM,MAGX;AAAA,EAMA,YAAY,cAAsB,SAA0C;AAL5E,SAAA,gCAAgB,IAAsB;AAWtC,SAAA,YAAY,CAAC,aAA+B;;AACrC,WAAA,UAAU,IAAI,QAAQ;AAC3B,YAAM,SAAQ,gBAAK,YAAL,mBAAc,gBAAd,4BAA4B,UAAU;AACpD,aAAO,MAAM;AACN,aAAA,UAAU,OAAO,QAAQ;AACtB;AAAA,MACV;AAAA,IACF;AAEA,SAAA,WAAW,CAAC,YAAsB;;AAChC,WAAK,YAAY,KAAK;AACtB,WAAK,UAAQ,UAAK,YAAL,mBAAc,YACvB,KAAK,QAAQ,SAAS,KAAK,SAAS,EAAE,OAAO,IAC5C,QAAgB,KAAK,SAAS;AAGnC,uBAAK,YAAL,mBAAc,aAAd;AAGA,cAAQ,IAAa;AAAA,IACvB;AAzBE,SAAK,YAAY;AACjB,SAAK,QAAQ;AACb,SAAK,UAAU;AAAA,EAAA;AAwBnB;"} |
| /** | ||
| * @private | ||
| */ | ||
| export type AnyUpdater = (prev: any) => any; | ||
| /** | ||
| * @private | ||
| */ | ||
| export interface ListenerValue<T> { | ||
| prevVal: T; | ||
| currentVal: T; | ||
| } | ||
| /** | ||
| * @private | ||
| */ | ||
| export type Listener<T> = (value: ListenerValue<T>) => void; |
+198
| import { Store } from './store' | ||
| import { __derivedToStore, __storeToDerived } from './scheduler' | ||
| import type { Listener } from './types' | ||
| export type UnwrapDerivedOrStore<T> = | ||
| T extends Derived<infer InnerD> | ||
| ? InnerD | ||
| : T extends Store<infer InnerS> | ||
| ? InnerS | ||
| : never | ||
| type UnwrapReadonlyDerivedOrStoreArray< | ||
| TArr extends ReadonlyArray<Derived<any> | Store<any>>, | ||
| > = TArr extends readonly [infer Head, ...infer Tail] | ||
| ? Head extends Derived<any> | Store<any> | ||
| ? Tail extends ReadonlyArray<Derived<any> | Store<any>> | ||
| ? [UnwrapDerivedOrStore<Head>, ...UnwrapReadonlyDerivedOrStoreArray<Tail>] | ||
| : [] | ||
| : [] | ||
| : [] | ||
| // Can't have currVal, as it's being evaluated from the current derived fn | ||
| export interface DerivedFnProps< | ||
| TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>, | ||
| TUnwrappedArr extends | ||
| UnwrapReadonlyDerivedOrStoreArray<TArr> = UnwrapReadonlyDerivedOrStoreArray<TArr>, | ||
| > { | ||
| // `undefined` if it's the first run | ||
| /** | ||
| * `undefined` if it's the first run | ||
| * @privateRemarks this also cannot be typed as TState, as it breaks the inferencing of the function's return type when an argument is used - even with `NoInfer` usage | ||
| */ | ||
| prevVal: unknown | undefined | ||
| prevDepVals: TUnwrappedArr | undefined | ||
| currDepVals: TUnwrappedArr | ||
| } | ||
| export interface DerivedOptions< | ||
| TState, | ||
| TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>, | ||
| > { | ||
| onSubscribe?: ( | ||
| listener: Listener<TState>, | ||
| derived: Derived<TState>, | ||
| ) => () => void | ||
| onUpdate?: () => void | ||
| deps: TArr | ||
| /** | ||
| * Values of the `deps` from before and after the current invocation of `fn` | ||
| */ | ||
| fn: (props: DerivedFnProps<TArr>) => TState | ||
| } | ||
| export class Derived< | ||
| TState, | ||
| const TArr extends ReadonlyArray< | ||
| Derived<any> | Store<any> | ||
| > = ReadonlyArray<any>, | ||
| > { | ||
| listeners = new Set<Listener<TState>>() | ||
| state: TState | ||
| prevState: TState | undefined | ||
| options: DerivedOptions<TState, TArr> | ||
| /** | ||
| * Functions representing the subscriptions. Call a function to cleanup | ||
| * @private | ||
| */ | ||
| _subscriptions: Array<() => void> = [] | ||
| lastSeenDepValues: Array<unknown> = [] | ||
| getDepVals = () => { | ||
| const prevDepVals = [] as Array<unknown> | ||
| const currDepVals = [] as Array<unknown> | ||
| for (const dep of this.options.deps) { | ||
| prevDepVals.push(dep.prevState) | ||
| currDepVals.push(dep.state) | ||
| } | ||
| this.lastSeenDepValues = currDepVals | ||
| return { | ||
| prevDepVals, | ||
| currDepVals, | ||
| prevVal: this.prevState ?? undefined, | ||
| } | ||
| } | ||
| constructor(options: DerivedOptions<TState, TArr>) { | ||
| this.options = options | ||
| this.state = options.fn({ | ||
| prevDepVals: undefined, | ||
| prevVal: undefined, | ||
| currDepVals: this.getDepVals().currDepVals as never, | ||
| }) | ||
| } | ||
| registerOnGraph( | ||
| deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps, | ||
| ) { | ||
| for (const dep of deps) { | ||
| if (dep instanceof Derived) { | ||
| // First register the intermediate derived value if it's not already registered | ||
| dep.registerOnGraph() | ||
| // Then register this derived with the dep's underlying stores | ||
| this.registerOnGraph(dep.options.deps) | ||
| } else if (dep instanceof Store) { | ||
| // Register the derived as related derived to the store | ||
| let relatedLinkedDerivedVals = __storeToDerived.get(dep) | ||
| if (!relatedLinkedDerivedVals) { | ||
| relatedLinkedDerivedVals = new Set() | ||
| __storeToDerived.set(dep, relatedLinkedDerivedVals) | ||
| } | ||
| relatedLinkedDerivedVals.add(this as never) | ||
| // Register the store as a related store to this derived | ||
| let relatedStores = __derivedToStore.get(this as never) | ||
| if (!relatedStores) { | ||
| relatedStores = new Set() | ||
| __derivedToStore.set(this as never, relatedStores) | ||
| } | ||
| relatedStores.add(dep) | ||
| } | ||
| } | ||
| } | ||
| unregisterFromGraph( | ||
| deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps, | ||
| ) { | ||
| for (const dep of deps) { | ||
| if (dep instanceof Derived) { | ||
| this.unregisterFromGraph(dep.options.deps) | ||
| } else if (dep instanceof Store) { | ||
| const relatedLinkedDerivedVals = __storeToDerived.get(dep) | ||
| if (relatedLinkedDerivedVals) { | ||
| relatedLinkedDerivedVals.delete(this as never) | ||
| } | ||
| const relatedStores = __derivedToStore.get(this as never) | ||
| if (relatedStores) { | ||
| relatedStores.delete(dep) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| recompute = () => { | ||
| this.prevState = this.state | ||
| const { prevDepVals, currDepVals, prevVal } = this.getDepVals() | ||
| this.state = this.options.fn({ | ||
| prevDepVals: prevDepVals as never, | ||
| currDepVals: currDepVals as never, | ||
| prevVal, | ||
| }) | ||
| this.options.onUpdate?.() | ||
| } | ||
| checkIfRecalculationNeededDeeply = () => { | ||
| for (const dep of this.options.deps) { | ||
| if (dep instanceof Derived) { | ||
| dep.checkIfRecalculationNeededDeeply() | ||
| } | ||
| } | ||
| let shouldRecompute = false | ||
| const lastSeenDepValues = this.lastSeenDepValues | ||
| const { currDepVals } = this.getDepVals() | ||
| for (let i = 0; i < currDepVals.length; i++) { | ||
| if (currDepVals[i] !== lastSeenDepValues[i]) { | ||
| shouldRecompute = true | ||
| break | ||
| } | ||
| } | ||
| if (shouldRecompute) { | ||
| this.recompute() | ||
| } | ||
| } | ||
| mount = () => { | ||
| this.registerOnGraph() | ||
| this.checkIfRecalculationNeededDeeply() | ||
| return () => { | ||
| this.unregisterFromGraph() | ||
| for (const cleanup of this._subscriptions) { | ||
| cleanup() | ||
| } | ||
| } | ||
| } | ||
| subscribe = (listener: Listener<TState>) => { | ||
| this.listeners.add(listener) | ||
| const unsub = this.options.onSubscribe?.(listener, this) | ||
| return () => { | ||
| this.listeners.delete(listener) | ||
| unsub?.() | ||
| } | ||
| } | ||
| } |
| import { Derived } from './derived' | ||
| import type { DerivedOptions } from './derived' | ||
| interface EffectOptions | ||
| extends Omit< | ||
| DerivedOptions<unknown>, | ||
| 'onUpdate' | 'onSubscribe' | 'lazy' | 'fn' | ||
| > { | ||
| /** | ||
| * Should the effect trigger immediately? | ||
| * @default false | ||
| */ | ||
| eager?: boolean | ||
| fn: () => void | ||
| } | ||
| export class Effect { | ||
| /** | ||
| * @private | ||
| */ | ||
| _derived: Derived<void> | ||
| constructor(opts: EffectOptions) { | ||
| const { eager, fn, ...derivedProps } = opts | ||
| this._derived = new Derived({ | ||
| ...derivedProps, | ||
| fn: () => {}, | ||
| onUpdate() { | ||
| fn() | ||
| }, | ||
| }) | ||
| if (eager) { | ||
| fn() | ||
| } | ||
| } | ||
| mount() { | ||
| return this._derived.mount() | ||
| } | ||
| } |
+155
| import { Derived } from './derived' | ||
| import type { Store } from './store' | ||
| /** | ||
| * This is here to solve the pyramid dependency problem where: | ||
| * A | ||
| * / \ | ||
| * B C | ||
| * \ / | ||
| * D | ||
| * | ||
| * Where we deeply traverse this tree, how do we avoid D being recomputed twice; once when B is updated, once when C is. | ||
| * | ||
| * To solve this, we create linkedDeps that allows us to sync avoid writes to the state until all of the deps have been | ||
| * resolved. | ||
| * | ||
| * This is a record of stores, because derived stores are not able to write values to, but stores are | ||
| */ | ||
| export const __storeToDerived = new WeakMap< | ||
| Store<unknown>, | ||
| Set<Derived<unknown>> | ||
| >() | ||
| export const __derivedToStore = new WeakMap< | ||
| Derived<unknown>, | ||
| Set<Store<unknown>> | ||
| >() | ||
| export const __depsThatHaveWrittenThisTick = { | ||
| current: [] as Array<Derived<unknown> | Store<unknown>>, | ||
| } | ||
| let __isFlushing = false | ||
| let __batchDepth = 0 | ||
| const __pendingUpdates = new Set<Store<unknown>>() | ||
| // Add a map to store initial values before batch | ||
| const __initialBatchValues = new Map<Store<unknown>, unknown>() | ||
| function __flush_internals(relatedVals: Set<Derived<unknown>>) { | ||
| // First sort deriveds by dependency order | ||
| const sorted = Array.from(relatedVals).sort((a, b) => { | ||
| // If a depends on b, b should go first | ||
| if (a instanceof Derived && a.options.deps.includes(b)) return 1 | ||
| // If b depends on a, a should go first | ||
| if (b instanceof Derived && b.options.deps.includes(a)) return -1 | ||
| return 0 | ||
| }) | ||
| for (const derived of sorted) { | ||
| if (__depsThatHaveWrittenThisTick.current.includes(derived)) { | ||
| continue | ||
| } | ||
| __depsThatHaveWrittenThisTick.current.push(derived) | ||
| derived.recompute() | ||
| const stores = __derivedToStore.get(derived) | ||
| if (stores) { | ||
| for (const store of stores) { | ||
| const relatedLinkedDerivedVals = __storeToDerived.get(store) | ||
| if (!relatedLinkedDerivedVals) continue | ||
| __flush_internals(relatedLinkedDerivedVals) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| function __notifyListeners(store: Store<unknown>) { | ||
| store.listeners.forEach((listener) => | ||
| listener({ | ||
| prevVal: store.prevState as never, | ||
| currentVal: store.state as never, | ||
| }), | ||
| ) | ||
| } | ||
| function __notifyDerivedListeners(derived: Derived<unknown>) { | ||
| derived.listeners.forEach((listener) => | ||
| listener({ | ||
| prevVal: derived.prevState as never, | ||
| currentVal: derived.state as never, | ||
| }), | ||
| ) | ||
| } | ||
| /** | ||
| * @private only to be called from `Store` on write | ||
| */ | ||
| export function __flush(store: Store<unknown>) { | ||
| // If we're starting a batch, store the initial values | ||
| if (__batchDepth > 0 && !__initialBatchValues.has(store)) { | ||
| __initialBatchValues.set(store, store.prevState) | ||
| } | ||
| __pendingUpdates.add(store) | ||
| if (__batchDepth > 0) return | ||
| if (__isFlushing) return | ||
| try { | ||
| __isFlushing = true | ||
| while (__pendingUpdates.size > 0) { | ||
| const stores = Array.from(__pendingUpdates) | ||
| __pendingUpdates.clear() | ||
| // First notify listeners with updated values | ||
| for (const store of stores) { | ||
| // Use initial batch values for prevState if we have them | ||
| const prevState = __initialBatchValues.get(store) ?? store.prevState | ||
| store.prevState = prevState | ||
| __notifyListeners(store) | ||
| } | ||
| // Then update all derived values | ||
| for (const store of stores) { | ||
| const derivedVals = __storeToDerived.get(store) | ||
| if (!derivedVals) continue | ||
| __depsThatHaveWrittenThisTick.current.push(store) | ||
| __flush_internals(derivedVals) | ||
| } | ||
| // Notify derived listeners after recomputing | ||
| for (const store of stores) { | ||
| const derivedVals = __storeToDerived.get(store) | ||
| if (!derivedVals) continue | ||
| for (const derived of derivedVals) { | ||
| __notifyDerivedListeners(derived) | ||
| } | ||
| } | ||
| } | ||
| } finally { | ||
| __isFlushing = false | ||
| __depsThatHaveWrittenThisTick.current = [] | ||
| __initialBatchValues.clear() | ||
| } | ||
| } | ||
| export function batch(fn: () => void) { | ||
| __batchDepth++ | ||
| try { | ||
| fn() | ||
| } finally { | ||
| __batchDepth-- | ||
| if (__batchDepth === 0) { | ||
| const pendingUpdateToFlush = Array.from(__pendingUpdates)[0] as | ||
| | Store<unknown> | ||
| | undefined | ||
| if (pendingUpdateToFlush) { | ||
| __flush(pendingUpdateToFlush) // Trigger flush of all pending updates | ||
| } | ||
| } | ||
| } | ||
| } |
+63
| import { __flush } from './scheduler' | ||
| import type { AnyUpdater, Listener } from './types' | ||
| export interface StoreOptions< | ||
| TState, | ||
| TUpdater extends AnyUpdater = (cb: TState) => TState, | ||
| > { | ||
| /** | ||
| * Replace the default update function with a custom one. | ||
| */ | ||
| updateFn?: (previous: TState) => (updater: TUpdater) => TState | ||
| /** | ||
| * Called when a listener subscribes to the store. | ||
| * | ||
| * @return a function to unsubscribe the listener | ||
| */ | ||
| onSubscribe?: ( | ||
| listener: Listener<TState>, | ||
| store: Store<TState, TUpdater>, | ||
| ) => () => void | ||
| /** | ||
| * Called after the state has been updated, used to derive other state. | ||
| */ | ||
| onUpdate?: () => void | ||
| } | ||
| export class Store< | ||
| TState, | ||
| TUpdater extends AnyUpdater = (cb: TState) => TState, | ||
| > { | ||
| listeners = new Set<Listener<TState>>() | ||
| state: TState | ||
| prevState: TState | ||
| options?: StoreOptions<TState, TUpdater> | ||
| constructor(initialState: TState, options?: StoreOptions<TState, TUpdater>) { | ||
| this.prevState = initialState | ||
| this.state = initialState | ||
| this.options = options | ||
| } | ||
| subscribe = (listener: Listener<TState>) => { | ||
| this.listeners.add(listener) | ||
| const unsub = this.options?.onSubscribe?.(listener, this) | ||
| return () => { | ||
| this.listeners.delete(listener) | ||
| unsub?.() | ||
| } | ||
| } | ||
| setState = (updater: TUpdater) => { | ||
| this.prevState = this.state | ||
| this.state = this.options?.updateFn | ||
| ? this.options.updateFn(this.prevState)(updater) | ||
| : (updater as any)(this.prevState) | ||
| // Always run onUpdate, regardless of batching | ||
| this.options?.onUpdate?.() | ||
| // Attempt to flush | ||
| __flush(this as never) | ||
| } | ||
| } |
+17
| /** | ||
| * @private | ||
| */ | ||
| export type AnyUpdater = (prev: any) => any | ||
| /** | ||
| * @private | ||
| */ | ||
| export interface ListenerValue<T> { | ||
| prevVal: T | ||
| currentVal: T | ||
| } | ||
| /** | ||
| * @private | ||
| */ | ||
| export type Listener<T> = (value: ListenerValue<T>) => void |
+12
-41
| "use strict"; | ||
| Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); | ||
| class Store { | ||
| constructor(initialState, options) { | ||
| this.listeners = /* @__PURE__ */ new Set(); | ||
| this._batching = false; | ||
| this._flushing = 0; | ||
| this.subscribe = (listener) => { | ||
| var _a, _b; | ||
| this.listeners.add(listener); | ||
| const unsub = (_b = (_a = this.options) == null ? void 0 : _a.onSubscribe) == null ? void 0 : _b.call(_a, listener, this); | ||
| return () => { | ||
| this.listeners.delete(listener); | ||
| unsub == null ? void 0 : unsub(); | ||
| }; | ||
| }; | ||
| this.setState = (updater) => { | ||
| var _a, _b, _c; | ||
| const previous = this.state; | ||
| this.state = ((_a = this.options) == null ? void 0 : _a.updateFn) ? this.options.updateFn(previous)(updater) : updater(previous); | ||
| (_c = (_b = this.options) == null ? void 0 : _b.onUpdate) == null ? void 0 : _c.call(_b); | ||
| this._flush(); | ||
| }; | ||
| this._flush = () => { | ||
| if (this._batching) return; | ||
| const flushId = ++this._flushing; | ||
| this.listeners.forEach((listener) => { | ||
| if (this._flushing !== flushId) return; | ||
| listener(); | ||
| }); | ||
| }; | ||
| this.batch = (cb) => { | ||
| if (this._batching) return cb(); | ||
| this._batching = true; | ||
| cb(); | ||
| this._batching = false; | ||
| this._flush(); | ||
| }; | ||
| this.state = initialState; | ||
| this.options = options; | ||
| } | ||
| } | ||
| exports.Store = Store; | ||
| const derived = require("./derived.cjs"); | ||
| const effect = require("./effect.cjs"); | ||
| const store = require("./store.cjs"); | ||
| const scheduler = require("./scheduler.cjs"); | ||
| exports.Derived = derived.Derived; | ||
| exports.Effect = effect.Effect; | ||
| exports.Store = store.Store; | ||
| exports.__depsThatHaveWrittenThisTick = scheduler.__depsThatHaveWrittenThisTick; | ||
| exports.__derivedToStore = scheduler.__derivedToStore; | ||
| exports.__flush = scheduler.__flush; | ||
| exports.__storeToDerived = scheduler.__storeToDerived; | ||
| exports.batch = scheduler.batch; | ||
| //# sourceMappingURL=index.cjs.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["/**\n * @private\n */\nexport type AnyUpdater = (...args: Array<any>) => any\n\n/**\n * @private\n */\nexport type Listener = () => void\n\nexport interface StoreOptions<\n TState,\n TUpdater extends AnyUpdater = (cb: TState) => TState,\n> {\n /**\n * Replace the default update function with a custom one.\n */\n updateFn?: (previous: TState) => (updater: TUpdater) => TState\n /**\n * Called when a listener subscribes to the store.\n *\n * @return a function to unsubscribe the listener\n */\n onSubscribe?: (\n listener: Listener,\n store: Store<TState, TUpdater>,\n ) => () => void\n /**\n * Called after the state has been updated, used to derive other state.\n */\n onUpdate?: () => void\n}\n\nexport class Store<\n TState,\n TUpdater extends AnyUpdater = (cb: TState) => TState,\n> {\n listeners = new Set<Listener>()\n state: TState\n options?: StoreOptions<TState, TUpdater>\n /**\n * @private\n */\n _batching = false\n /**\n * @private\n */\n _flushing = 0\n\n constructor(initialState: TState, options?: StoreOptions<TState, TUpdater>) {\n this.state = initialState\n this.options = options\n }\n\n subscribe = (listener: Listener) => {\n this.listeners.add(listener)\n const unsub = this.options?.onSubscribe?.(listener, this)\n return () => {\n this.listeners.delete(listener)\n unsub?.()\n }\n }\n\n setState = (updater: TUpdater) => {\n const previous = this.state\n this.state = this.options?.updateFn\n ? this.options.updateFn(previous)(updater)\n : (updater as any)(previous)\n\n // Always run onUpdate, regardless of batching\n this.options?.onUpdate?.()\n\n // Attempt to flush\n this._flush()\n }\n\n /**\n * @private\n */\n _flush = () => {\n if (this._batching) return\n const flushId = ++this._flushing\n this.listeners.forEach((listener) => {\n if (this._flushing !== flushId) return\n listener()\n })\n }\n\n batch = (cb: () => void) => {\n if (this._batching) return cb()\n this._batching = true\n cb()\n this._batching = false\n this._flush()\n }\n}\n"],"names":[],"mappings":";;AAiCO,MAAM,MAGX;AAAA,EAaA,YAAY,cAAsB,SAA0C;AAZ5E,SAAA,gCAAgB,IAAc;AAMlB,SAAA,YAAA;AAIA,SAAA,YAAA;AAOZ,SAAA,YAAY,CAAC,aAAuB;;AAC7B,WAAA,UAAU,IAAI,QAAQ;AAC3B,YAAM,SAAQ,gBAAK,YAAL,mBAAc,gBAAd,4BAA4B,UAAU;AACpD,aAAO,MAAM;AACN,aAAA,UAAU,OAAO,QAAQ;AACtB;AAAA,MACV;AAAA,IACF;AAEA,SAAA,WAAW,CAAC,YAAsB;;AAChC,YAAM,WAAW,KAAK;AACtB,WAAK,UAAQ,UAAK,YAAL,mBAAc,YACvB,KAAK,QAAQ,SAAS,QAAQ,EAAE,OAAO,IACtC,QAAgB,QAAQ;AAG7B,uBAAK,YAAL,mBAAc,aAAd;AAGA,WAAK,OAAO;AAAA,IACd;AAKA,SAAA,SAAS,MAAM;AACb,UAAI,KAAK,UAAW;AACd,YAAA,UAAU,EAAE,KAAK;AAClB,WAAA,UAAU,QAAQ,CAAC,aAAa;AAC/B,YAAA,KAAK,cAAc,QAAS;AACvB,iBAAA;AAAA,MAAA,CACV;AAAA,IACH;AAEA,SAAA,QAAQ,CAAC,OAAmB;AACtB,UAAA,KAAK,UAAW,QAAO,GAAG;AAC9B,WAAK,YAAY;AACd,SAAA;AACH,WAAK,YAAY;AACjB,WAAK,OAAO;AAAA,IACd;AA5CE,SAAK,QAAQ;AACb,SAAK,UAAU;AAAA,EAAA;AA4CnB;;"} | ||
| {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"} |
+5
-45
@@ -1,45 +0,5 @@ | ||
| /** | ||
| * @private | ||
| */ | ||
| export type AnyUpdater = (...args: Array<any>) => any; | ||
| /** | ||
| * @private | ||
| */ | ||
| export type Listener = () => void; | ||
| export interface StoreOptions<TState, TUpdater extends AnyUpdater = (cb: TState) => TState> { | ||
| /** | ||
| * Replace the default update function with a custom one. | ||
| */ | ||
| updateFn?: (previous: TState) => (updater: TUpdater) => TState; | ||
| /** | ||
| * Called when a listener subscribes to the store. | ||
| * | ||
| * @return a function to unsubscribe the listener | ||
| */ | ||
| onSubscribe?: (listener: Listener, store: Store<TState, TUpdater>) => () => void; | ||
| /** | ||
| * Called after the state has been updated, used to derive other state. | ||
| */ | ||
| onUpdate?: () => void; | ||
| } | ||
| export declare class Store<TState, TUpdater extends AnyUpdater = (cb: TState) => TState> { | ||
| listeners: Set<Listener>; | ||
| state: TState; | ||
| options?: StoreOptions<TState, TUpdater>; | ||
| /** | ||
| * @private | ||
| */ | ||
| _batching: boolean; | ||
| /** | ||
| * @private | ||
| */ | ||
| _flushing: number; | ||
| constructor(initialState: TState, options?: StoreOptions<TState, TUpdater>); | ||
| subscribe: (listener: Listener) => () => void; | ||
| setState: (updater: TUpdater) => void; | ||
| /** | ||
| * @private | ||
| */ | ||
| _flush: () => void; | ||
| batch: (cb: () => void) => void; | ||
| } | ||
| export * from './derived.cjs'; | ||
| export * from './effect.cjs'; | ||
| export * from './store.cjs'; | ||
| export * from './types.cjs'; | ||
| export * from './scheduler.cjs'; |
+5
-45
@@ -1,45 +0,5 @@ | ||
| /** | ||
| * @private | ||
| */ | ||
| export type AnyUpdater = (...args: Array<any>) => any; | ||
| /** | ||
| * @private | ||
| */ | ||
| export type Listener = () => void; | ||
| export interface StoreOptions<TState, TUpdater extends AnyUpdater = (cb: TState) => TState> { | ||
| /** | ||
| * Replace the default update function with a custom one. | ||
| */ | ||
| updateFn?: (previous: TState) => (updater: TUpdater) => TState; | ||
| /** | ||
| * Called when a listener subscribes to the store. | ||
| * | ||
| * @return a function to unsubscribe the listener | ||
| */ | ||
| onSubscribe?: (listener: Listener, store: Store<TState, TUpdater>) => () => void; | ||
| /** | ||
| * Called after the state has been updated, used to derive other state. | ||
| */ | ||
| onUpdate?: () => void; | ||
| } | ||
| export declare class Store<TState, TUpdater extends AnyUpdater = (cb: TState) => TState> { | ||
| listeners: Set<Listener>; | ||
| state: TState; | ||
| options?: StoreOptions<TState, TUpdater>; | ||
| /** | ||
| * @private | ||
| */ | ||
| _batching: boolean; | ||
| /** | ||
| * @private | ||
| */ | ||
| _flushing: number; | ||
| constructor(initialState: TState, options?: StoreOptions<TState, TUpdater>); | ||
| subscribe: (listener: Listener) => () => void; | ||
| setState: (updater: TUpdater) => void; | ||
| /** | ||
| * @private | ||
| */ | ||
| _flush: () => void; | ||
| batch: (cb: () => void) => void; | ||
| } | ||
| export * from './derived.js'; | ||
| export * from './effect.js'; | ||
| export * from './store.js'; | ||
| export * from './types.js'; | ||
| export * from './scheduler.js'; |
+12
-41
@@ -1,44 +0,15 @@ | ||
| class Store { | ||
| constructor(initialState, options) { | ||
| this.listeners = /* @__PURE__ */ new Set(); | ||
| this._batching = false; | ||
| this._flushing = 0; | ||
| this.subscribe = (listener) => { | ||
| var _a, _b; | ||
| this.listeners.add(listener); | ||
| const unsub = (_b = (_a = this.options) == null ? void 0 : _a.onSubscribe) == null ? void 0 : _b.call(_a, listener, this); | ||
| return () => { | ||
| this.listeners.delete(listener); | ||
| unsub == null ? void 0 : unsub(); | ||
| }; | ||
| }; | ||
| this.setState = (updater) => { | ||
| var _a, _b, _c; | ||
| const previous = this.state; | ||
| this.state = ((_a = this.options) == null ? void 0 : _a.updateFn) ? this.options.updateFn(previous)(updater) : updater(previous); | ||
| (_c = (_b = this.options) == null ? void 0 : _b.onUpdate) == null ? void 0 : _c.call(_b); | ||
| this._flush(); | ||
| }; | ||
| this._flush = () => { | ||
| if (this._batching) return; | ||
| const flushId = ++this._flushing; | ||
| this.listeners.forEach((listener) => { | ||
| if (this._flushing !== flushId) return; | ||
| listener(); | ||
| }); | ||
| }; | ||
| this.batch = (cb) => { | ||
| if (this._batching) return cb(); | ||
| this._batching = true; | ||
| cb(); | ||
| this._batching = false; | ||
| this._flush(); | ||
| }; | ||
| this.state = initialState; | ||
| this.options = options; | ||
| } | ||
| } | ||
| import { Derived } from "./derived.js"; | ||
| import { Effect } from "./effect.js"; | ||
| import { Store } from "./store.js"; | ||
| import { __depsThatHaveWrittenThisTick, __derivedToStore, __flush, __storeToDerived, batch } from "./scheduler.js"; | ||
| export { | ||
| Store | ||
| Derived, | ||
| Effect, | ||
| Store, | ||
| __depsThatHaveWrittenThisTick, | ||
| __derivedToStore, | ||
| __flush, | ||
| __storeToDerived, | ||
| batch | ||
| }; | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["/**\n * @private\n */\nexport type AnyUpdater = (...args: Array<any>) => any\n\n/**\n * @private\n */\nexport type Listener = () => void\n\nexport interface StoreOptions<\n TState,\n TUpdater extends AnyUpdater = (cb: TState) => TState,\n> {\n /**\n * Replace the default update function with a custom one.\n */\n updateFn?: (previous: TState) => (updater: TUpdater) => TState\n /**\n * Called when a listener subscribes to the store.\n *\n * @return a function to unsubscribe the listener\n */\n onSubscribe?: (\n listener: Listener,\n store: Store<TState, TUpdater>,\n ) => () => void\n /**\n * Called after the state has been updated, used to derive other state.\n */\n onUpdate?: () => void\n}\n\nexport class Store<\n TState,\n TUpdater extends AnyUpdater = (cb: TState) => TState,\n> {\n listeners = new Set<Listener>()\n state: TState\n options?: StoreOptions<TState, TUpdater>\n /**\n * @private\n */\n _batching = false\n /**\n * @private\n */\n _flushing = 0\n\n constructor(initialState: TState, options?: StoreOptions<TState, TUpdater>) {\n this.state = initialState\n this.options = options\n }\n\n subscribe = (listener: Listener) => {\n this.listeners.add(listener)\n const unsub = this.options?.onSubscribe?.(listener, this)\n return () => {\n this.listeners.delete(listener)\n unsub?.()\n }\n }\n\n setState = (updater: TUpdater) => {\n const previous = this.state\n this.state = this.options?.updateFn\n ? this.options.updateFn(previous)(updater)\n : (updater as any)(previous)\n\n // Always run onUpdate, regardless of batching\n this.options?.onUpdate?.()\n\n // Attempt to flush\n this._flush()\n }\n\n /**\n * @private\n */\n _flush = () => {\n if (this._batching) return\n const flushId = ++this._flushing\n this.listeners.forEach((listener) => {\n if (this._flushing !== flushId) return\n listener()\n })\n }\n\n batch = (cb: () => void) => {\n if (this._batching) return cb()\n this._batching = true\n cb()\n this._batching = false\n this._flush()\n }\n}\n"],"names":[],"mappings":"AAiCO,MAAM,MAGX;AAAA,EAaA,YAAY,cAAsB,SAA0C;AAZ5E,SAAA,gCAAgB,IAAc;AAMlB,SAAA,YAAA;AAIA,SAAA,YAAA;AAOZ,SAAA,YAAY,CAAC,aAAuB;AArB/B;AAsBE,WAAA,UAAU,IAAI,QAAQ;AAC3B,YAAM,SAAQ,gBAAK,YAAL,mBAAc,gBAAd,4BAA4B,UAAU;AACpD,aAAO,MAAM;AACN,aAAA,UAAU,OAAO,QAAQ;AACtB;AAAA,MACV;AAAA,IACF;AAEA,SAAA,WAAW,CAAC,YAAsB;AA9B7B;AA+BH,YAAM,WAAW,KAAK;AACtB,WAAK,UAAQ,UAAK,YAAL,mBAAc,YACvB,KAAK,QAAQ,SAAS,QAAQ,EAAE,OAAO,IACtC,QAAgB,QAAQ;AAG7B,uBAAK,YAAL,mBAAc,aAAd;AAGA,WAAK,OAAO;AAAA,IACd;AAKA,SAAA,SAAS,MAAM;AACb,UAAI,KAAK,UAAW;AACd,YAAA,UAAU,EAAE,KAAK;AAClB,WAAA,UAAU,QAAQ,CAAC,aAAa;AAC/B,YAAA,KAAK,cAAc,QAAS;AACvB,iBAAA;AAAA,MAAA,CACV;AAAA,IACH;AAEA,SAAA,QAAQ,CAAC,OAAmB;AACtB,UAAA,KAAK,UAAW,QAAO,GAAG;AAC9B,WAAK,YAAY;AACd,SAAA;AACH,WAAK,YAAY;AACjB,WAAK,OAAO;AAAA,IACd;AA5CE,SAAK,QAAQ;AACb,SAAK,UAAU;AAAA,EAAA;AA4CnB;"} | ||
| {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"} |
+8
-2
| { | ||
| "name": "@tanstack/store", | ||
| "version": "0.6.0", | ||
| "version": "0.7.0", | ||
| "description": "Framework agnostic type-safe store w/ reactive framework adapters", | ||
@@ -9,3 +9,3 @@ "author": "Tanner Linsley", | ||
| "type": "git", | ||
| "url": "https://github.com/TanStack/store.git", | ||
| "url": "git+https://github.com/TanStack/store.git", | ||
| "directory": "packages/store" | ||
@@ -44,3 +44,9 @@ }, | ||
| ], | ||
| "devDependencies": { | ||
| "@angular/core": "^19.0.5", | ||
| "@preact/signals": "^1.3.0", | ||
| "solid-js": "^1.9.3", | ||
| "vue": "^3.5.13" | ||
| }, | ||
| "scripts": {} | ||
| } |
+5
-96
@@ -1,96 +0,5 @@ | ||
| /** | ||
| * @private | ||
| */ | ||
| export type AnyUpdater = (...args: Array<any>) => any | ||
| /** | ||
| * @private | ||
| */ | ||
| export type Listener = () => void | ||
| export interface StoreOptions< | ||
| TState, | ||
| TUpdater extends AnyUpdater = (cb: TState) => TState, | ||
| > { | ||
| /** | ||
| * Replace the default update function with a custom one. | ||
| */ | ||
| updateFn?: (previous: TState) => (updater: TUpdater) => TState | ||
| /** | ||
| * Called when a listener subscribes to the store. | ||
| * | ||
| * @return a function to unsubscribe the listener | ||
| */ | ||
| onSubscribe?: ( | ||
| listener: Listener, | ||
| store: Store<TState, TUpdater>, | ||
| ) => () => void | ||
| /** | ||
| * Called after the state has been updated, used to derive other state. | ||
| */ | ||
| onUpdate?: () => void | ||
| } | ||
| export class Store< | ||
| TState, | ||
| TUpdater extends AnyUpdater = (cb: TState) => TState, | ||
| > { | ||
| listeners = new Set<Listener>() | ||
| state: TState | ||
| options?: StoreOptions<TState, TUpdater> | ||
| /** | ||
| * @private | ||
| */ | ||
| _batching = false | ||
| /** | ||
| * @private | ||
| */ | ||
| _flushing = 0 | ||
| constructor(initialState: TState, options?: StoreOptions<TState, TUpdater>) { | ||
| this.state = initialState | ||
| this.options = options | ||
| } | ||
| subscribe = (listener: Listener) => { | ||
| this.listeners.add(listener) | ||
| const unsub = this.options?.onSubscribe?.(listener, this) | ||
| return () => { | ||
| this.listeners.delete(listener) | ||
| unsub?.() | ||
| } | ||
| } | ||
| setState = (updater: TUpdater) => { | ||
| const previous = this.state | ||
| this.state = this.options?.updateFn | ||
| ? this.options.updateFn(previous)(updater) | ||
| : (updater as any)(previous) | ||
| // Always run onUpdate, regardless of batching | ||
| this.options?.onUpdate?.() | ||
| // Attempt to flush | ||
| this._flush() | ||
| } | ||
| /** | ||
| * @private | ||
| */ | ||
| _flush = () => { | ||
| if (this._batching) return | ||
| const flushId = ++this._flushing | ||
| this.listeners.forEach((listener) => { | ||
| if (this._flushing !== flushId) return | ||
| listener() | ||
| }) | ||
| } | ||
| batch = (cb: () => void) => { | ||
| if (this._batching) return cb() | ||
| this._batching = true | ||
| cb() | ||
| this._batching = false | ||
| this._flush() | ||
| } | ||
| } | ||
| export * from './derived' | ||
| export * from './effect' | ||
| export * from './store' | ||
| export * from './types' | ||
| export * from './scheduler' |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
80777
419.03%40
344.44%1153
426.48%4
Infinity%1
Infinity%1
Infinity%