@tanstack/store 0.7.5 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,11 +8,13 @@ class Derived {
8
8
  this._subscriptions = [];
9
9
  this.lastSeenDepValues = [];
10
10
  this.getDepVals = () => {
11
- const prevDepVals = [];
12
- const currDepVals = [];
13
- for (const dep of this.options.deps) {
14
- prevDepVals.push(dep.prevState);
15
- currDepVals.push(dep.state);
11
+ const l = this.options.deps.length;
12
+ const prevDepVals = new Array(l);
13
+ const currDepVals = new Array(l);
14
+ for (let i = 0; i < l; i++) {
15
+ const dep = this.options.deps[i];
16
+ prevDepVals[i] = dep.prevState;
17
+ currDepVals[i] = dep.state;
16
18
  }
17
19
  this.lastSeenDepValues = currDepVals;
18
20
  return {
@@ -24,12 +26,8 @@ class Derived {
24
26
  this.recompute = () => {
25
27
  var _a, _b;
26
28
  this.prevState = this.state;
27
- const { prevDepVals, currDepVals, prevVal } = this.getDepVals();
28
- this.state = this.options.fn({
29
- prevDepVals,
30
- currDepVals,
31
- prevVal
32
- });
29
+ const depVals = this.getDepVals();
30
+ this.state = this.options.fn(depVals);
33
31
  (_b = (_a = this.options).onUpdate) == null ? void 0 : _b.call(_a);
34
32
  };
35
33
  this.checkIfRecalculationNeededDeeply = () => {
@@ -1 +1 @@
1
- {"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 []\n ? []\n : TArr extends readonly [infer Head, ...infer Tail]\n ? Head extends Derived<any> | Store<any>\n ? Tail extends ReadonlyArray<Derived<any> | Store<any>>\n ? [\n UnwrapDerivedOrStore<Head>,\n ...UnwrapReadonlyDerivedOrStoreArray<Tail>,\n ]\n : [UnwrapDerivedOrStore<Head>]\n : []\n : TArr extends ReadonlyArray<Derived<any> | Store<any>>\n ? Array<UnwrapDerivedOrStore<TArr[number]>>\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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"}
1
+ {"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 []\n ? []\n : TArr extends readonly [infer Head, ...infer Tail]\n ? Head extends Derived<any> | Store<any>\n ? Tail extends ReadonlyArray<Derived<any> | Store<any>>\n ? [\n UnwrapDerivedOrStore<Head>,\n ...UnwrapReadonlyDerivedOrStoreArray<Tail>,\n ]\n : [UnwrapDerivedOrStore<Head>]\n : []\n : TArr extends ReadonlyArray<Derived<any> | Store<any>>\n ? Array<UnwrapDerivedOrStore<TArr[number]>>\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 l = this.options.deps.length\n const prevDepVals = new Array<unknown>(l)\n const currDepVals = new Array<unknown>(l)\n for (let i = 0; i < l; i++) {\n const dep = this.options.deps[i]!\n prevDepVals[i] = dep.prevState\n currDepVals[i] = 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 depVals = this.getDepVals()\n this.state = this.options.fn(depVals as never)\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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"}
@@ -1 +1 @@
1
- {"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,iBAAiB;AAEvC,SAAK,WAAW,IAAIA,gBAAQ;AAAA,MAC1B,GAAG;AAAA,MACH,IAAI,MAAM;AAAA,MAAA;AAAA,MACV,WAAW;AACT,WAAA;AAAA,MAAG;AAAA,IACL,CACD;AAED,QAAI,OAAO;AACT,SAAA;AAAA,IAAG;AAAA,EACL;AAAA,EAGF,QAAQ;AACN,WAAO,KAAK,SAAS,MAAA;AAAA,EAAM;AAE/B;;"}
1
+ {"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,iBAAiB;AAEvC,SAAK,WAAW,IAAIA,gBAAQ;AAAA,MAC1B,GAAG;AAAA,MACH,IAAI,MAAM;AAAA,MAAC;AAAA,MACX,WAAW;AACT,WAAA;AAAA,MACF;AAAA,IAAA,CACD;AAED,QAAI,OAAO;AACT,SAAA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QAAQ;AACN,WAAO,KAAK,SAAS,MAAA;AAAA,EACvB;AACF;;"}
@@ -33,20 +33,22 @@ function __flush_internals(relatedVals) {
33
33
  }
34
34
  }
35
35
  function __notifyListeners(store) {
36
- store.listeners.forEach(
37
- (listener) => listener({
38
- prevVal: store.prevState,
39
- currentVal: store.state
40
- })
41
- );
36
+ const value = {
37
+ prevVal: store.prevState,
38
+ currentVal: store.state
39
+ };
40
+ for (const listener of store.listeners) {
41
+ listener(value);
42
+ }
42
43
  }
43
44
  function __notifyDerivedListeners(derived2) {
44
- derived2.listeners.forEach(
45
- (listener) => listener({
46
- prevVal: derived2.prevState,
47
- currentVal: derived2.state
48
- })
49
- );
45
+ const value = {
46
+ prevVal: derived2.prevState,
47
+ currentVal: derived2.state
48
+ };
49
+ for (const listener of derived2.listeners) {
50
+ listener(value);
51
+ }
50
52
  }
51
53
  function __flush(store) {
52
54
  if (__batchDepth > 0 && !__initialBatchValues.has(store)) {
@@ -92,7 +94,7 @@ function batch(fn) {
92
94
  } finally {
93
95
  __batchDepth--;
94
96
  if (__batchDepth === 0) {
95
- const pendingUpdateToFlush = Array.from(__pendingUpdates)[0];
97
+ const pendingUpdateToFlush = __pendingUpdates.values().next().value;
96
98
  if (pendingUpdateToFlush) {
97
99
  __flush(pendingUpdateToFlush);
98
100
  }
@@ -1 +1 @@
1
- {"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":";;;AAkBO,MAAM,uCAAuB,QAAA;AAI7B,MAAM,uCAAuB,QAAA;AAK7B,MAAM,gCAAgC;AAAA,EAC3C,SAAS,CAAA;AACX;AAEA,IAAI,eAAe;AACnB,IAAI,eAAe;AACnB,MAAM,uCAAuB,IAAA;AAE7B,MAAM,2CAA2B,IAAA;AAEjC,SAAS,kBAAkB,aAAoC;AAE7D,QAAM,SAAS,MAAM,KAAK,WAAW,EAAE,KAAK,CAAC,GAAG,MAAM;AAEpD,QAAI,aAAaA,QAAAA,WAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAG,QAAO;AAE/D,QAAI,aAAaA,QAAAA,WAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAG,QAAO;AAC/D,WAAO;AAAA,EAAA,CACR;AAED,aAAWC,YAAW,QAAQ;AAC5B,QAAI,8BAA8B,QAAQ,SAASA,QAAO,GAAG;AAC3D;AAAA,IAAA;AAGF,kCAA8B,QAAQ,KAAKA,QAAO;AAClD,IAAAA,SAAQ,UAAA;AAER,UAAM,SAAS,iBAAiB,IAAIA,QAAO;AAC3C,QAAI,QAAQ;AACV,iBAAW,SAAS,QAAQ;AAC1B,cAAM,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,IAAA,CACnB;AAAA,EAAA;AAEL;AAEA,SAAS,yBAAyBA,UAA2B;AAC3D,EAAAA,SAAQ,UAAU;AAAA,IAAQ,CAAC,aACzB,SAAS;AAAA,MACP,SAASA,SAAQ;AAAA,MACjB,YAAYA,SAAQ;AAAA,IAAA,CACrB;AAAA,EAAA;AAEL;AAKO,SAAS,QAAQ,OAAuB;AAE7C,MAAI,eAAe,KAAK,CAAC,qBAAqB,IAAI,KAAK,GAAG;AACxD,yBAAqB,IAAI,OAAO,MAAM,SAAS;AAAA,EAAA;AAGjD,mBAAiB,IAAI,KAAK;AAE1B,MAAI,eAAe,EAAG;AACtB,MAAI,aAAc;AAElB,MAAI;AACF,mBAAe;AAEf,WAAO,iBAAiB,OAAO,GAAG;AAChC,YAAM,SAAS,MAAM,KAAK,gBAAgB;AAC1C,uBAAiB,MAAA;AAGjB,iBAAWC,UAAS,QAAQ;AAE1B,cAAM,YAAY,qBAAqB,IAAIA,MAAK,KAAKA,OAAM;AAC3DA,eAAM,YAAY;AAClB,0BAAkBA,MAAK;AAAA,MAAA;AAIzB,iBAAWA,UAAS,QAAQ;AAC1B,cAAM,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAElB,sCAA8B,QAAQ,KAAKA,MAAK;AAChD,0BAAkB,WAAW;AAAA,MAAA;AAI/B,iBAAWA,UAAS,QAAQ;AAC1B,cAAM,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAElB,mBAAWD,YAAW,aAAa;AACjC,mCAAyBA,QAAO;AAAA,QAAA;AAAA,MAClC;AAAA,IACF;AAAA,EACF,UACF;AACE,mBAAe;AACf,kCAA8B,UAAU,CAAA;AACxC,yBAAqB,MAAA;AAAA,EAAM;AAE/B;AAEO,SAAS,MAAM,IAAgB;AACpC;AACA,MAAI;AACF,OAAA;AAAA,EAAG,UACL;AACE;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;;;;;;"}
1
+ {"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 const value = {\n prevVal: store.prevState as never,\n currentVal: store.state as never,\n }\n for (const listener of store.listeners) {\n listener(value)\n }\n}\n\nfunction __notifyDerivedListeners(derived: Derived<unknown>) {\n const value = {\n prevVal: derived.prevState as never,\n currentVal: derived.state as never,\n }\n for (const listener of derived.listeners) {\n listener(value)\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 = __pendingUpdates.values().next().value\n if (pendingUpdateToFlush) {\n __flush(pendingUpdateToFlush) // Trigger flush of all pending updates\n }\n }\n }\n}\n"],"names":["Derived","derived","store"],"mappings":";;;AAkBO,MAAM,uCAAuB,QAAA;AAI7B,MAAM,uCAAuB,QAAA;AAK7B,MAAM,gCAAgC;AAAA,EAC3C,SAAS,CAAA;AACX;AAEA,IAAI,eAAe;AACnB,IAAI,eAAe;AACnB,MAAM,uCAAuB,IAAA;AAE7B,MAAM,2CAA2B,IAAA;AAEjC,SAAS,kBAAkB,aAAoC;AAE7D,QAAM,SAAS,MAAM,KAAK,WAAW,EAAE,KAAK,CAAC,GAAG,MAAM;AAEpD,QAAI,aAAaA,QAAAA,WAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAG,QAAO;AAE/D,QAAI,aAAaA,QAAAA,WAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAG,QAAO;AAC/D,WAAO;AAAA,EACT,CAAC;AAED,aAAWC,YAAW,QAAQ;AAC5B,QAAI,8BAA8B,QAAQ,SAASA,QAAO,GAAG;AAC3D;AAAA,IACF;AAEA,kCAA8B,QAAQ,KAAKA,QAAO;AAClD,IAAAA,SAAQ,UAAA;AAER,UAAM,SAAS,iBAAiB,IAAIA,QAAO;AAC3C,QAAI,QAAQ;AACV,iBAAW,SAAS,QAAQ;AAC1B,cAAM,2BAA2B,iBAAiB,IAAI,KAAK;AAC3D,YAAI,CAAC,yBAA0B;AAC/B,0BAAkB,wBAAwB;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,OAAuB;AAChD,QAAM,QAAQ;AAAA,IACZ,SAAS,MAAM;AAAA,IACf,YAAY,MAAM;AAAA,EAAA;AAEpB,aAAW,YAAY,MAAM,WAAW;AACtC,aAAS,KAAK;AAAA,EAChB;AACF;AAEA,SAAS,yBAAyBA,UAA2B;AAC3D,QAAM,QAAQ;AAAA,IACZ,SAASA,SAAQ;AAAA,IACjB,YAAYA,SAAQ;AAAA,EAAA;AAEtB,aAAW,YAAYA,SAAQ,WAAW;AACxC,aAAS,KAAK;AAAA,EAChB;AACF;AAKO,SAAS,QAAQ,OAAuB;AAE7C,MAAI,eAAe,KAAK,CAAC,qBAAqB,IAAI,KAAK,GAAG;AACxD,yBAAqB,IAAI,OAAO,MAAM,SAAS;AAAA,EACjD;AAEA,mBAAiB,IAAI,KAAK;AAE1B,MAAI,eAAe,EAAG;AACtB,MAAI,aAAc;AAElB,MAAI;AACF,mBAAe;AAEf,WAAO,iBAAiB,OAAO,GAAG;AAChC,YAAM,SAAS,MAAM,KAAK,gBAAgB;AAC1C,uBAAiB,MAAA;AAGjB,iBAAWC,UAAS,QAAQ;AAE1B,cAAM,YAAY,qBAAqB,IAAIA,MAAK,KAAKA,OAAM;AAC3DA,eAAM,YAAY;AAClB,0BAAkBA,MAAK;AAAA,MACzB;AAGA,iBAAWA,UAAS,QAAQ;AAC1B,cAAM,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAElB,sCAA8B,QAAQ,KAAKA,MAAK;AAChD,0BAAkB,WAAW;AAAA,MAC/B;AAGA,iBAAWA,UAAS,QAAQ;AAC1B,cAAM,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAElB,mBAAWD,YAAW,aAAa;AACjC,mCAAyBA,QAAO;AAAA,QAClC;AAAA,MACF;AAAA,IACF;AAAA,EACF,UAAA;AACE,mBAAe;AACf,kCAA8B,UAAU,CAAA;AACxC,yBAAqB,MAAA;AAAA,EACvB;AACF;AAEO,SAAS,MAAM,IAAgB;AACpC;AACA,MAAI;AACF,OAAA;AAAA,EACF,UAAA;AACE;AACA,QAAI,iBAAiB,GAAG;AACtB,YAAM,uBAAuB,iBAAiB,OAAA,EAAS,OAAO;AAC9D,UAAI,sBAAsB;AACxB,gBAAQ,oBAAoB;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AACF;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"store.cjs","sources":["../../src/store.ts"],"sourcesContent":["import { __flush } from './scheduler'\nimport { isUpdaterFunction } from './types'\nimport type { AnyUpdater, Listener, Updater } 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 /**\n * Update the store state safely with improved type checking\n */\n setState(updater: (prevState: TState) => TState): void\n setState(updater: TState): void\n setState(updater: TUpdater): void\n setState(updater: Updater<TState> | TUpdater): void {\n this.prevState = this.state\n\n if (this.options?.updateFn) {\n this.state = this.options.updateFn(this.prevState)(updater as TUpdater)\n } else {\n if (isUpdaterFunction(updater)) {\n this.state = updater(this.prevState)\n } else {\n this.state = updater as TState\n }\n }\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":["isUpdaterFunction","__flush"],"mappings":";;;;AA2BO,MAAM,MAGX;AAAA,EAMA,YAAY,cAAsB,SAA0C;AAL5E,SAAA,gCAAgB,IAAA;AAWhB,SAAA,YAAY,CAAC,aAA+B;;AAC1C,WAAK,UAAU,IAAI,QAAQ;AAC3B,YAAM,SAAQ,gBAAK,YAAL,mBAAc,gBAAd,4BAA4B,UAAU;AACpD,aAAO,MAAM;AACX,aAAK,UAAU,OAAO,QAAQ;AAC9B;AAAA,MAAQ;AAAA,IACV;AAXA,SAAK,YAAY;AACjB,SAAK,QAAQ;AACb,SAAK,UAAU;AAAA,EAAA;AAAA,EAkBjB,SAAS,SAA2C;;AAClD,SAAK,YAAY,KAAK;AAEtB,SAAI,UAAK,YAAL,mBAAc,UAAU;AAC1B,WAAK,QAAQ,KAAK,QAAQ,SAAS,KAAK,SAAS,EAAE,OAAmB;AAAA,IAAA,OACjE;AACL,UAAIA,MAAAA,kBAAkB,OAAO,GAAG;AAC9B,aAAK,QAAQ,QAAQ,KAAK,SAAS;AAAA,MAAA,OAC9B;AACL,aAAK,QAAQ;AAAA,MAAA;AAAA,IACf;AAIF,qBAAK,YAAL,mBAAc,aAAd;AAGAC,cAAAA,QAAQ,IAAa;AAAA,EAAA;AAEzB;;"}
1
+ {"version":3,"file":"store.cjs","sources":["../../src/store.ts"],"sourcesContent":["import { __flush } from './scheduler'\nimport { isUpdaterFunction } from './types'\nimport type { AnyUpdater, Listener, Updater } 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 /**\n * Update the store state safely with improved type checking\n */\n setState(updater: (prevState: TState) => TState): void\n setState(updater: TState): void\n setState(updater: TUpdater): void\n setState(updater: Updater<TState> | TUpdater): void {\n this.prevState = this.state\n\n if (this.options?.updateFn) {\n this.state = this.options.updateFn(this.prevState)(updater as TUpdater)\n } else {\n if (isUpdaterFunction(updater)) {\n this.state = updater(this.prevState)\n } else {\n this.state = updater as TState\n }\n }\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":["isUpdaterFunction","__flush"],"mappings":";;;;AA2BO,MAAM,MAGX;AAAA,EAMA,YAAY,cAAsB,SAA0C;AAL5E,SAAA,gCAAgB,IAAA;AAWhB,SAAA,YAAY,CAAC,aAA+B;;AAC1C,WAAK,UAAU,IAAI,QAAQ;AAC3B,YAAM,SAAQ,gBAAK,YAAL,mBAAc,gBAAd,4BAA4B,UAAU;AACpD,aAAO,MAAM;AACX,aAAK,UAAU,OAAO,QAAQ;AAC9B;AAAA,MACF;AAAA,IACF;AAZE,SAAK,YAAY;AACjB,SAAK,QAAQ;AACb,SAAK,UAAU;AAAA,EACjB;AAAA,EAiBA,SAAS,SAA2C;;AAClD,SAAK,YAAY,KAAK;AAEtB,SAAI,UAAK,YAAL,mBAAc,UAAU;AAC1B,WAAK,QAAQ,KAAK,QAAQ,SAAS,KAAK,SAAS,EAAE,OAAmB;AAAA,IACxE,OAAO;AACL,UAAIA,MAAAA,kBAAkB,OAAO,GAAG;AAC9B,aAAK,QAAQ,QAAQ,KAAK,SAAS;AAAA,MACrC,OAAO;AACL,aAAK,QAAQ;AAAA,MACf;AAAA,IACF;AAGA,qBAAK,YAAL,mBAAc,aAAd;AAGAC,cAAAA,QAAQ,IAAa;AAAA,EACvB;AACF;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.cjs","sources":["../../src/types.ts"],"sourcesContent":["/**\n * @private\n */\nexport type AnyUpdater = (prev: any) => any\n\n/**\n * Type-safe updater that can be either a function or direct value\n */\nexport type Updater<T> = ((prev: T) => T) | T\n\n/**\n * @private\n */\nexport interface ListenerValue<T> {\n prevVal: T\n currentVal: T\n}\n\n/**\n * @private\n */\nexport type Listener<T> = (value: ListenerValue<T>) => void\n\n/**\n * Type guard to check if updater is a function\n */\nexport function isUpdaterFunction<T>(\n updater: Updater<T>,\n): updater is (prev: T) => T {\n return typeof updater === 'function'\n}\n"],"names":[],"mappings":";;AA0BO,SAAS,kBACd,SAC2B;AAC3B,SAAO,OAAO,YAAY;AAC5B;;"}
1
+ {"version":3,"file":"types.cjs","sources":["../../src/types.ts"],"sourcesContent":["/**\n * @private\n */\nexport type AnyUpdater = (prev: any) => any\n\n/**\n * Type-safe updater that can be either a function or direct value\n */\nexport type Updater<T> = ((prev: T) => T) | T\n\n/**\n * @private\n */\nexport interface ListenerValue<T> {\n readonly prevVal: T\n readonly currentVal: T\n}\n\n/**\n * @private\n */\nexport type Listener<T> = (value: ListenerValue<T>) => void\n\n/**\n * Type guard to check if updater is a function\n */\nexport function isUpdaterFunction<T>(\n updater: Updater<T>,\n): updater is (prev: T) => T {\n return typeof updater === 'function'\n}\n"],"names":[],"mappings":";;AA0BO,SAAS,kBACd,SAC2B;AAC3B,SAAO,OAAO,YAAY;AAC5B;;"}
@@ -10,8 +10,8 @@ export type Updater<T> = ((prev: T) => T) | T;
10
10
  * @private
11
11
  */
12
12
  export interface ListenerValue<T> {
13
- prevVal: T;
14
- currentVal: T;
13
+ readonly prevVal: T;
14
+ readonly currentVal: T;
15
15
  }
16
16
  /**
17
17
  * @private
@@ -6,11 +6,13 @@ class Derived {
6
6
  this._subscriptions = [];
7
7
  this.lastSeenDepValues = [];
8
8
  this.getDepVals = () => {
9
- const prevDepVals = [];
10
- const currDepVals = [];
11
- for (const dep of this.options.deps) {
12
- prevDepVals.push(dep.prevState);
13
- currDepVals.push(dep.state);
9
+ const l = this.options.deps.length;
10
+ const prevDepVals = new Array(l);
11
+ const currDepVals = new Array(l);
12
+ for (let i = 0; i < l; i++) {
13
+ const dep = this.options.deps[i];
14
+ prevDepVals[i] = dep.prevState;
15
+ currDepVals[i] = dep.state;
14
16
  }
15
17
  this.lastSeenDepValues = currDepVals;
16
18
  return {
@@ -22,12 +24,8 @@ class Derived {
22
24
  this.recompute = () => {
23
25
  var _a, _b;
24
26
  this.prevState = this.state;
25
- const { prevDepVals, currDepVals, prevVal } = this.getDepVals();
26
- this.state = this.options.fn({
27
- prevDepVals,
28
- currDepVals,
29
- prevVal
30
- });
27
+ const depVals = this.getDepVals();
28
+ this.state = this.options.fn(depVals);
31
29
  (_b = (_a = this.options).onUpdate) == null ? void 0 : _b.call(_a);
32
30
  };
33
31
  this.checkIfRecalculationNeededDeeply = () => {
@@ -1 +1 @@
1
- {"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 []\n ? []\n : TArr extends readonly [infer Head, ...infer Tail]\n ? Head extends Derived<any> | Store<any>\n ? Tail extends ReadonlyArray<Derived<any> | Store<any>>\n ? [\n UnwrapDerivedOrStore<Head>,\n ...UnwrapReadonlyDerivedOrStoreArray<Tail>,\n ]\n : [UnwrapDerivedOrStore<Head>]\n : []\n : TArr extends ReadonlyArray<Derived<any> | Store<any>>\n ? Array<UnwrapDerivedOrStore<TArr[number]>>\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":"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"}
1
+ {"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 []\n ? []\n : TArr extends readonly [infer Head, ...infer Tail]\n ? Head extends Derived<any> | Store<any>\n ? Tail extends ReadonlyArray<Derived<any> | Store<any>>\n ? [\n UnwrapDerivedOrStore<Head>,\n ...UnwrapReadonlyDerivedOrStoreArray<Tail>,\n ]\n : [UnwrapDerivedOrStore<Head>]\n : []\n : TArr extends ReadonlyArray<Derived<any> | Store<any>>\n ? Array<UnwrapDerivedOrStore<TArr[number]>>\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 l = this.options.deps.length\n const prevDepVals = new Array<unknown>(l)\n const currDepVals = new Array<unknown>(l)\n for (let i = 0; i < l; i++) {\n const dep = this.options.deps[i]!\n prevDepVals[i] = dep.prevState\n currDepVals[i] = 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 depVals = this.getDepVals()\n this.state = this.options.fn(depVals as never)\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":"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"}
@@ -1 +1 @@
1
- {"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,iBAAiB;AAEvC,SAAK,WAAW,IAAI,QAAQ;AAAA,MAC1B,GAAG;AAAA,MACH,IAAI,MAAM;AAAA,MAAA;AAAA,MACV,WAAW;AACT,WAAA;AAAA,MAAG;AAAA,IACL,CACD;AAED,QAAI,OAAO;AACT,SAAA;AAAA,IAAG;AAAA,EACL;AAAA,EAGF,QAAQ;AACN,WAAO,KAAK,SAAS,MAAA;AAAA,EAAM;AAE/B;"}
1
+ {"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,iBAAiB;AAEvC,SAAK,WAAW,IAAI,QAAQ;AAAA,MAC1B,GAAG;AAAA,MACH,IAAI,MAAM;AAAA,MAAC;AAAA,MACX,WAAW;AACT,WAAA;AAAA,MACF;AAAA,IAAA,CACD;AAED,QAAI,OAAO;AACT,SAAA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QAAQ;AACN,WAAO,KAAK,SAAS,MAAA;AAAA,EACvB;AACF;"}
@@ -31,20 +31,22 @@ function __flush_internals(relatedVals) {
31
31
  }
32
32
  }
33
33
  function __notifyListeners(store) {
34
- store.listeners.forEach(
35
- (listener) => listener({
36
- prevVal: store.prevState,
37
- currentVal: store.state
38
- })
39
- );
34
+ const value = {
35
+ prevVal: store.prevState,
36
+ currentVal: store.state
37
+ };
38
+ for (const listener of store.listeners) {
39
+ listener(value);
40
+ }
40
41
  }
41
42
  function __notifyDerivedListeners(derived) {
42
- derived.listeners.forEach(
43
- (listener) => listener({
44
- prevVal: derived.prevState,
45
- currentVal: derived.state
46
- })
47
- );
43
+ const value = {
44
+ prevVal: derived.prevState,
45
+ currentVal: derived.state
46
+ };
47
+ for (const listener of derived.listeners) {
48
+ listener(value);
49
+ }
48
50
  }
49
51
  function __flush(store) {
50
52
  if (__batchDepth > 0 && !__initialBatchValues.has(store)) {
@@ -90,7 +92,7 @@ function batch(fn) {
90
92
  } finally {
91
93
  __batchDepth--;
92
94
  if (__batchDepth === 0) {
93
- const pendingUpdateToFlush = Array.from(__pendingUpdates)[0];
95
+ const pendingUpdateToFlush = __pendingUpdates.values().next().value;
94
96
  if (pendingUpdateToFlush) {
95
97
  __flush(pendingUpdateToFlush);
96
98
  }
@@ -1 +1 @@
1
- {"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":";AAkBO,MAAM,uCAAuB,QAAA;AAI7B,MAAM,uCAAuB,QAAA;AAK7B,MAAM,gCAAgC;AAAA,EAC3C,SAAS,CAAA;AACX;AAEA,IAAI,eAAe;AACnB,IAAI,eAAe;AACnB,MAAM,uCAAuB,IAAA;AAE7B,MAAM,2CAA2B,IAAA;AAEjC,SAAS,kBAAkB,aAAoC;AAE7D,QAAM,SAAS,MAAM,KAAK,WAAW,EAAE,KAAK,CAAC,GAAG,MAAM;AAEpD,QAAI,aAAa,WAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAG,QAAO;AAE/D,QAAI,aAAa,WAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAG,QAAO;AAC/D,WAAO;AAAA,EAAA,CACR;AAED,aAAW,WAAW,QAAQ;AAC5B,QAAI,8BAA8B,QAAQ,SAAS,OAAO,GAAG;AAC3D;AAAA,IAAA;AAGF,kCAA8B,QAAQ,KAAK,OAAO;AAClD,YAAQ,UAAA;AAER,UAAM,SAAS,iBAAiB,IAAI,OAAO;AAC3C,QAAI,QAAQ;AACV,iBAAW,SAAS,QAAQ;AAC1B,cAAM,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,IAAA,CACnB;AAAA,EAAA;AAEL;AAEA,SAAS,yBAAyB,SAA2B;AAC3D,UAAQ,UAAU;AAAA,IAAQ,CAAC,aACzB,SAAS;AAAA,MACP,SAAS,QAAQ;AAAA,MACjB,YAAY,QAAQ;AAAA,IAAA,CACrB;AAAA,EAAA;AAEL;AAKO,SAAS,QAAQ,OAAuB;AAE7C,MAAI,eAAe,KAAK,CAAC,qBAAqB,IAAI,KAAK,GAAG;AACxD,yBAAqB,IAAI,OAAO,MAAM,SAAS;AAAA,EAAA;AAGjD,mBAAiB,IAAI,KAAK;AAE1B,MAAI,eAAe,EAAG;AACtB,MAAI,aAAc;AAElB,MAAI;AACF,mBAAe;AAEf,WAAO,iBAAiB,OAAO,GAAG;AAChC,YAAM,SAAS,MAAM,KAAK,gBAAgB;AAC1C,uBAAiB,MAAA;AAGjB,iBAAWA,UAAS,QAAQ;AAE1B,cAAM,YAAY,qBAAqB,IAAIA,MAAK,KAAKA,OAAM;AAC3DA,eAAM,YAAY;AAClB,0BAAkBA,MAAK;AAAA,MAAA;AAIzB,iBAAWA,UAAS,QAAQ;AAC1B,cAAM,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAElB,sCAA8B,QAAQ,KAAKA,MAAK;AAChD,0BAAkB,WAAW;AAAA,MAAA;AAI/B,iBAAWA,UAAS,QAAQ;AAC1B,cAAM,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAElB,mBAAW,WAAW,aAAa;AACjC,mCAAyB,OAAO;AAAA,QAAA;AAAA,MAClC;AAAA,IACF;AAAA,EACF,UACF;AACE,mBAAe;AACf,kCAA8B,UAAU,CAAA;AACxC,yBAAqB,MAAA;AAAA,EAAM;AAE/B;AAEO,SAAS,MAAM,IAAgB;AACpC;AACA,MAAI;AACF,OAAA;AAAA,EAAG,UACL;AACE;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;"}
1
+ {"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 const value = {\n prevVal: store.prevState as never,\n currentVal: store.state as never,\n }\n for (const listener of store.listeners) {\n listener(value)\n }\n}\n\nfunction __notifyDerivedListeners(derived: Derived<unknown>) {\n const value = {\n prevVal: derived.prevState as never,\n currentVal: derived.state as never,\n }\n for (const listener of derived.listeners) {\n listener(value)\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 = __pendingUpdates.values().next().value\n if (pendingUpdateToFlush) {\n __flush(pendingUpdateToFlush) // Trigger flush of all pending updates\n }\n }\n }\n}\n"],"names":["store"],"mappings":";AAkBO,MAAM,uCAAuB,QAAA;AAI7B,MAAM,uCAAuB,QAAA;AAK7B,MAAM,gCAAgC;AAAA,EAC3C,SAAS,CAAA;AACX;AAEA,IAAI,eAAe;AACnB,IAAI,eAAe;AACnB,MAAM,uCAAuB,IAAA;AAE7B,MAAM,2CAA2B,IAAA;AAEjC,SAAS,kBAAkB,aAAoC;AAE7D,QAAM,SAAS,MAAM,KAAK,WAAW,EAAE,KAAK,CAAC,GAAG,MAAM;AAEpD,QAAI,aAAa,WAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAG,QAAO;AAE/D,QAAI,aAAa,WAAW,EAAE,QAAQ,KAAK,SAAS,CAAC,EAAG,QAAO;AAC/D,WAAO;AAAA,EACT,CAAC;AAED,aAAW,WAAW,QAAQ;AAC5B,QAAI,8BAA8B,QAAQ,SAAS,OAAO,GAAG;AAC3D;AAAA,IACF;AAEA,kCAA8B,QAAQ,KAAK,OAAO;AAClD,YAAQ,UAAA;AAER,UAAM,SAAS,iBAAiB,IAAI,OAAO;AAC3C,QAAI,QAAQ;AACV,iBAAW,SAAS,QAAQ;AAC1B,cAAM,2BAA2B,iBAAiB,IAAI,KAAK;AAC3D,YAAI,CAAC,yBAA0B;AAC/B,0BAAkB,wBAAwB;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,OAAuB;AAChD,QAAM,QAAQ;AAAA,IACZ,SAAS,MAAM;AAAA,IACf,YAAY,MAAM;AAAA,EAAA;AAEpB,aAAW,YAAY,MAAM,WAAW;AACtC,aAAS,KAAK;AAAA,EAChB;AACF;AAEA,SAAS,yBAAyB,SAA2B;AAC3D,QAAM,QAAQ;AAAA,IACZ,SAAS,QAAQ;AAAA,IACjB,YAAY,QAAQ;AAAA,EAAA;AAEtB,aAAW,YAAY,QAAQ,WAAW;AACxC,aAAS,KAAK;AAAA,EAChB;AACF;AAKO,SAAS,QAAQ,OAAuB;AAE7C,MAAI,eAAe,KAAK,CAAC,qBAAqB,IAAI,KAAK,GAAG;AACxD,yBAAqB,IAAI,OAAO,MAAM,SAAS;AAAA,EACjD;AAEA,mBAAiB,IAAI,KAAK;AAE1B,MAAI,eAAe,EAAG;AACtB,MAAI,aAAc;AAElB,MAAI;AACF,mBAAe;AAEf,WAAO,iBAAiB,OAAO,GAAG;AAChC,YAAM,SAAS,MAAM,KAAK,gBAAgB;AAC1C,uBAAiB,MAAA;AAGjB,iBAAWA,UAAS,QAAQ;AAE1B,cAAM,YAAY,qBAAqB,IAAIA,MAAK,KAAKA,OAAM;AAC3DA,eAAM,YAAY;AAClB,0BAAkBA,MAAK;AAAA,MACzB;AAGA,iBAAWA,UAAS,QAAQ;AAC1B,cAAM,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAElB,sCAA8B,QAAQ,KAAKA,MAAK;AAChD,0BAAkB,WAAW;AAAA,MAC/B;AAGA,iBAAWA,UAAS,QAAQ;AAC1B,cAAM,cAAc,iBAAiB,IAAIA,MAAK;AAC9C,YAAI,CAAC,YAAa;AAElB,mBAAW,WAAW,aAAa;AACjC,mCAAyB,OAAO;AAAA,QAClC;AAAA,MACF;AAAA,IACF;AAAA,EACF,UAAA;AACE,mBAAe;AACf,kCAA8B,UAAU,CAAA;AACxC,yBAAqB,MAAA;AAAA,EACvB;AACF;AAEO,SAAS,MAAM,IAAgB;AACpC;AACA,MAAI;AACF,OAAA;AAAA,EACF,UAAA;AACE;AACA,QAAI,iBAAiB,GAAG;AACtB,YAAM,uBAAuB,iBAAiB,OAAA,EAAS,OAAO;AAC9D,UAAI,sBAAsB;AACxB,gBAAQ,oBAAoB;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AACF;"}
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sources":["../../src/store.ts"],"sourcesContent":["import { __flush } from './scheduler'\nimport { isUpdaterFunction } from './types'\nimport type { AnyUpdater, Listener, Updater } 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 /**\n * Update the store state safely with improved type checking\n */\n setState(updater: (prevState: TState) => TState): void\n setState(updater: TState): void\n setState(updater: TUpdater): void\n setState(updater: Updater<TState> | TUpdater): void {\n this.prevState = this.state\n\n if (this.options?.updateFn) {\n this.state = this.options.updateFn(this.prevState)(updater as TUpdater)\n } else {\n if (isUpdaterFunction(updater)) {\n this.state = updater(this.prevState)\n } else {\n this.state = updater as TState\n }\n }\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":";;AA2BO,MAAM,MAGX;AAAA,EAMA,YAAY,cAAsB,SAA0C;AAL5E,SAAA,gCAAgB,IAAA;AAWhB,SAAA,YAAY,CAAC,aAA+B;;AAC1C,WAAK,UAAU,IAAI,QAAQ;AAC3B,YAAM,SAAQ,gBAAK,YAAL,mBAAc,gBAAd,4BAA4B,UAAU;AACpD,aAAO,MAAM;AACX,aAAK,UAAU,OAAO,QAAQ;AAC9B;AAAA,MAAQ;AAAA,IACV;AAXA,SAAK,YAAY;AACjB,SAAK,QAAQ;AACb,SAAK,UAAU;AAAA,EAAA;AAAA,EAkBjB,SAAS,SAA2C;;AAClD,SAAK,YAAY,KAAK;AAEtB,SAAI,UAAK,YAAL,mBAAc,UAAU;AAC1B,WAAK,QAAQ,KAAK,QAAQ,SAAS,KAAK,SAAS,EAAE,OAAmB;AAAA,IAAA,OACjE;AACL,UAAI,kBAAkB,OAAO,GAAG;AAC9B,aAAK,QAAQ,QAAQ,KAAK,SAAS;AAAA,MAAA,OAC9B;AACL,aAAK,QAAQ;AAAA,MAAA;AAAA,IACf;AAIF,qBAAK,YAAL,mBAAc,aAAd;AAGA,YAAQ,IAAa;AAAA,EAAA;AAEzB;"}
1
+ {"version":3,"file":"store.js","sources":["../../src/store.ts"],"sourcesContent":["import { __flush } from './scheduler'\nimport { isUpdaterFunction } from './types'\nimport type { AnyUpdater, Listener, Updater } 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 /**\n * Update the store state safely with improved type checking\n */\n setState(updater: (prevState: TState) => TState): void\n setState(updater: TState): void\n setState(updater: TUpdater): void\n setState(updater: Updater<TState> | TUpdater): void {\n this.prevState = this.state\n\n if (this.options?.updateFn) {\n this.state = this.options.updateFn(this.prevState)(updater as TUpdater)\n } else {\n if (isUpdaterFunction(updater)) {\n this.state = updater(this.prevState)\n } else {\n this.state = updater as TState\n }\n }\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":";;AA2BO,MAAM,MAGX;AAAA,EAMA,YAAY,cAAsB,SAA0C;AAL5E,SAAA,gCAAgB,IAAA;AAWhB,SAAA,YAAY,CAAC,aAA+B;;AAC1C,WAAK,UAAU,IAAI,QAAQ;AAC3B,YAAM,SAAQ,gBAAK,YAAL,mBAAc,gBAAd,4BAA4B,UAAU;AACpD,aAAO,MAAM;AACX,aAAK,UAAU,OAAO,QAAQ;AAC9B;AAAA,MACF;AAAA,IACF;AAZE,SAAK,YAAY;AACjB,SAAK,QAAQ;AACb,SAAK,UAAU;AAAA,EACjB;AAAA,EAiBA,SAAS,SAA2C;;AAClD,SAAK,YAAY,KAAK;AAEtB,SAAI,UAAK,YAAL,mBAAc,UAAU;AAC1B,WAAK,QAAQ,KAAK,QAAQ,SAAS,KAAK,SAAS,EAAE,OAAmB;AAAA,IACxE,OAAO;AACL,UAAI,kBAAkB,OAAO,GAAG;AAC9B,aAAK,QAAQ,QAAQ,KAAK,SAAS;AAAA,MACrC,OAAO;AACL,aAAK,QAAQ;AAAA,MACf;AAAA,IACF;AAGA,qBAAK,YAAL,mBAAc,aAAd;AAGA,YAAQ,IAAa;AAAA,EACvB;AACF;"}
@@ -10,8 +10,8 @@ export type Updater<T> = ((prev: T) => T) | T;
10
10
  * @private
11
11
  */
12
12
  export interface ListenerValue<T> {
13
- prevVal: T;
14
- currentVal: T;
13
+ readonly prevVal: T;
14
+ readonly currentVal: T;
15
15
  }
16
16
  /**
17
17
  * @private
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sources":["../../src/types.ts"],"sourcesContent":["/**\n * @private\n */\nexport type AnyUpdater = (prev: any) => any\n\n/**\n * Type-safe updater that can be either a function or direct value\n */\nexport type Updater<T> = ((prev: T) => T) | T\n\n/**\n * @private\n */\nexport interface ListenerValue<T> {\n prevVal: T\n currentVal: T\n}\n\n/**\n * @private\n */\nexport type Listener<T> = (value: ListenerValue<T>) => void\n\n/**\n * Type guard to check if updater is a function\n */\nexport function isUpdaterFunction<T>(\n updater: Updater<T>,\n): updater is (prev: T) => T {\n return typeof updater === 'function'\n}\n"],"names":[],"mappings":"AA0BO,SAAS,kBACd,SAC2B;AAC3B,SAAO,OAAO,YAAY;AAC5B;"}
1
+ {"version":3,"file":"types.js","sources":["../../src/types.ts"],"sourcesContent":["/**\n * @private\n */\nexport type AnyUpdater = (prev: any) => any\n\n/**\n * Type-safe updater that can be either a function or direct value\n */\nexport type Updater<T> = ((prev: T) => T) | T\n\n/**\n * @private\n */\nexport interface ListenerValue<T> {\n readonly prevVal: T\n readonly currentVal: T\n}\n\n/**\n * @private\n */\nexport type Listener<T> = (value: ListenerValue<T>) => void\n\n/**\n * Type guard to check if updater is a function\n */\nexport function isUpdaterFunction<T>(\n updater: Updater<T>,\n): updater is (prev: T) => T {\n return typeof updater === 'function'\n}\n"],"names":[],"mappings":"AA0BO,SAAS,kBACd,SAC2B;AAC3B,SAAO,OAAO,YAAY;AAC5B;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/store",
3
- "version": "0.7.5",
3
+ "version": "0.7.7",
4
4
  "description": "Framework agnostic type-safe store w/ reactive framework adapters",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -41,7 +41,7 @@
41
41
  "src"
42
42
  ],
43
43
  "devDependencies": {
44
- "@angular/core": "^19.2.14",
44
+ "@angular/core": "^19.2.15",
45
45
  "@preact/signals": "^1.3.2",
46
46
  "solid-js": "^1.9.9",
47
47
  "vue": "^3.5.21"
package/src/derived.ts CHANGED
@@ -77,11 +77,13 @@ export class Derived<
77
77
 
78
78
  lastSeenDepValues: Array<unknown> = []
79
79
  getDepVals = () => {
80
- const prevDepVals = [] as Array<unknown>
81
- const currDepVals = [] as Array<unknown>
82
- for (const dep of this.options.deps) {
83
- prevDepVals.push(dep.prevState)
84
- currDepVals.push(dep.state)
80
+ const l = this.options.deps.length
81
+ const prevDepVals = new Array<unknown>(l)
82
+ const currDepVals = new Array<unknown>(l)
83
+ for (let i = 0; i < l; i++) {
84
+ const dep = this.options.deps[i]!
85
+ prevDepVals[i] = dep.prevState
86
+ currDepVals[i] = dep.state
85
87
  }
86
88
  this.lastSeenDepValues = currDepVals
87
89
  return {
@@ -151,12 +153,8 @@ export class Derived<
151
153
 
152
154
  recompute = () => {
153
155
  this.prevState = this.state
154
- const { prevDepVals, currDepVals, prevVal } = this.getDepVals()
155
- this.state = this.options.fn({
156
- prevDepVals: prevDepVals as never,
157
- currDepVals: currDepVals as never,
158
- prevVal,
159
- })
156
+ const depVals = this.getDepVals()
157
+ this.state = this.options.fn(depVals as never)
160
158
 
161
159
  this.options.onUpdate?.()
162
160
  }
package/src/scheduler.ts CHANGED
@@ -65,21 +65,23 @@ function __flush_internals(relatedVals: Set<Derived<unknown>>) {
65
65
  }
66
66
 
67
67
  function __notifyListeners(store: Store<unknown>) {
68
- store.listeners.forEach((listener) =>
69
- listener({
70
- prevVal: store.prevState as never,
71
- currentVal: store.state as never,
72
- }),
73
- )
68
+ const value = {
69
+ prevVal: store.prevState as never,
70
+ currentVal: store.state as never,
71
+ }
72
+ for (const listener of store.listeners) {
73
+ listener(value)
74
+ }
74
75
  }
75
76
 
76
77
  function __notifyDerivedListeners(derived: Derived<unknown>) {
77
- derived.listeners.forEach((listener) =>
78
- listener({
79
- prevVal: derived.prevState as never,
80
- currentVal: derived.state as never,
81
- }),
82
- )
78
+ const value = {
79
+ prevVal: derived.prevState as never,
80
+ currentVal: derived.state as never,
81
+ }
82
+ for (const listener of derived.listeners) {
83
+ listener(value)
84
+ }
83
85
  }
84
86
 
85
87
  /**
@@ -144,9 +146,7 @@ export function batch(fn: () => void) {
144
146
  } finally {
145
147
  __batchDepth--
146
148
  if (__batchDepth === 0) {
147
- const pendingUpdateToFlush = Array.from(__pendingUpdates)[0] as
148
- | Store<unknown>
149
- | undefined
149
+ const pendingUpdateToFlush = __pendingUpdates.values().next().value
150
150
  if (pendingUpdateToFlush) {
151
151
  __flush(pendingUpdateToFlush) // Trigger flush of all pending updates
152
152
  }
package/src/types.ts CHANGED
@@ -12,8 +12,8 @@ export type Updater<T> = ((prev: T) => T) | T
12
12
  * @private
13
13
  */
14
14
  export interface ListenerValue<T> {
15
- prevVal: T
16
- currentVal: T
15
+ readonly prevVal: T
16
+ readonly currentVal: T
17
17
  }
18
18
 
19
19
  /**