You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@tanstack/store

Package Overview
Dependencies
Maintainers
2
Versions
70
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tanstack/store - npm Package Compare versions

Comparing version
0.6.0
to
0.7.0
+119
dist/cjs/derived.cjs
"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;
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()
}
}
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
}
}
}
}
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)
}
}
/**
* @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":";;;;;;;;;;;;;;"}

@@ -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';

@@ -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';

@@ -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":";;;;"}
{
"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": {}
}

@@ -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'