@tanstack/store 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/cjs/alien.cjs +345 -0
  2. package/dist/cjs/alien.cjs.map +1 -0
  3. package/dist/cjs/alien.d.cts +57 -0
  4. package/dist/cjs/atom.cjs +222 -0
  5. package/dist/cjs/atom.cjs.map +1 -0
  6. package/dist/cjs/atom.d.cts +16 -0
  7. package/dist/cjs/batch.cjs +15 -0
  8. package/dist/cjs/batch.cjs.map +1 -0
  9. package/dist/cjs/batch.d.cts +1 -0
  10. package/dist/cjs/index.cjs +8 -12
  11. package/dist/cjs/index.cjs.map +1 -1
  12. package/dist/cjs/index.d.cts +3 -4
  13. package/dist/cjs/store.cjs +22 -29
  14. package/dist/cjs/store.cjs.map +1 -1
  15. package/dist/cjs/store.d.cts +11 -30
  16. package/dist/cjs/types.d.cts +47 -20
  17. package/dist/esm/alien.d.ts +57 -0
  18. package/dist/esm/alien.js +345 -0
  19. package/dist/esm/alien.js.map +1 -0
  20. package/dist/esm/atom.d.ts +16 -0
  21. package/dist/esm/atom.js +222 -0
  22. package/dist/esm/atom.js.map +1 -0
  23. package/dist/esm/batch.d.ts +1 -0
  24. package/dist/esm/batch.js +15 -0
  25. package/dist/esm/batch.js.map +1 -0
  26. package/dist/esm/index.d.ts +3 -4
  27. package/dist/esm/index.js +8 -12
  28. package/dist/esm/index.js.map +1 -1
  29. package/dist/esm/store.d.ts +11 -30
  30. package/dist/esm/store.js +23 -30
  31. package/dist/esm/store.js.map +1 -1
  32. package/dist/esm/types.d.ts +47 -20
  33. package/package.json +6 -7
  34. package/src/alien.ts +654 -0
  35. package/src/atom.ts +298 -0
  36. package/src/batch.ts +12 -0
  37. package/src/index.ts +3 -4
  38. package/src/store.ts +33 -72
  39. package/src/types.ts +60 -24
  40. package/dist/cjs/derived.cjs +0 -130
  41. package/dist/cjs/derived.cjs.map +0 -1
  42. package/dist/cjs/derived.d.cts +0 -50
  43. package/dist/cjs/effect.cjs +0 -24
  44. package/dist/cjs/effect.cjs.map +0 -1
  45. package/dist/cjs/effect.d.cts +0 -18
  46. package/dist/cjs/scheduler.cjs +0 -103
  47. package/dist/cjs/scheduler.cjs.map +0 -1
  48. package/dist/cjs/scheduler.d.cts +0 -27
  49. package/dist/cjs/types.cjs +0 -7
  50. package/dist/cjs/types.cjs.map +0 -1
  51. package/dist/esm/derived.d.ts +0 -50
  52. package/dist/esm/derived.js +0 -130
  53. package/dist/esm/derived.js.map +0 -1
  54. package/dist/esm/effect.d.ts +0 -18
  55. package/dist/esm/effect.js +0 -24
  56. package/dist/esm/effect.js.map +0 -1
  57. package/dist/esm/scheduler.d.ts +0 -27
  58. package/dist/esm/scheduler.js +0 -103
  59. package/dist/esm/scheduler.js.map +0 -1
  60. package/dist/esm/types.js +0 -7
  61. package/dist/esm/types.js.map +0 -1
  62. package/src/derived.ts +0 -219
  63. package/src/effect.ts +0 -42
  64. package/src/scheduler.ts +0 -146
package/dist/esm/types.js DELETED
@@ -1,7 +0,0 @@
1
- function isUpdaterFunction(updater) {
2
- return typeof updater === "function";
3
- }
4
- export {
5
- isUpdaterFunction
6
- };
7
- //# sourceMappingURL=types.js.map
@@ -1 +0,0 @@
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/src/derived.ts DELETED
@@ -1,219 +0,0 @@
1
- import { Store } from './store'
2
- import { __derivedToStore, __storeToDerived } from './scheduler'
3
- import type { Listener } from './types'
4
-
5
- export type UnwrapDerivedOrStore<T> =
6
- T extends Derived<infer InnerD>
7
- ? InnerD
8
- : T extends Store<infer InnerS>
9
- ? InnerS
10
- : never
11
-
12
- type UnwrapReadonlyDerivedOrStoreArray<
13
- TArr extends ReadonlyArray<Derived<any> | Store<any>>,
14
- > = TArr extends readonly []
15
- ? []
16
- : TArr extends readonly [infer Head, ...infer Tail]
17
- ? Head extends Derived<any> | Store<any>
18
- ? Tail extends ReadonlyArray<Derived<any> | Store<any>>
19
- ? [
20
- UnwrapDerivedOrStore<Head>,
21
- ...UnwrapReadonlyDerivedOrStoreArray<Tail>,
22
- ]
23
- : [UnwrapDerivedOrStore<Head>]
24
- : []
25
- : TArr extends ReadonlyArray<Derived<any> | Store<any>>
26
- ? Array<UnwrapDerivedOrStore<TArr[number]>>
27
- : []
28
-
29
- // Can't have currVal, as it's being evaluated from the current derived fn
30
- export interface DerivedFnProps<
31
- TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>,
32
- TUnwrappedArr extends
33
- UnwrapReadonlyDerivedOrStoreArray<TArr> = UnwrapReadonlyDerivedOrStoreArray<TArr>,
34
- > {
35
- // `undefined` if it's the first run
36
- /**
37
- * `undefined` if it's the first run
38
- * @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
39
- */
40
- prevVal: unknown | undefined
41
- prevDepVals: TUnwrappedArr | undefined
42
- currDepVals: TUnwrappedArr
43
- }
44
-
45
- export interface DerivedOptions<
46
- TState,
47
- TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>,
48
- > {
49
- onSubscribe?: (
50
- listener: Listener<TState>,
51
- derived: Derived<TState>,
52
- ) => () => void
53
- onUpdate?: () => void
54
- deps: TArr
55
- /**
56
- * Values of the `deps` from before and after the current invocation of `fn`
57
- */
58
- fn: (props: DerivedFnProps<TArr>) => TState
59
- }
60
-
61
- export class Derived<
62
- TState,
63
- const TArr extends ReadonlyArray<
64
- Derived<any> | Store<any>
65
- > = ReadonlyArray<any>,
66
- > {
67
- listeners = new Set<Listener<TState>>()
68
- state: TState
69
- prevState: TState | undefined
70
- options: DerivedOptions<TState, TArr>
71
-
72
- /**
73
- * Functions representing the subscriptions. Call a function to cleanup
74
- * @private
75
- */
76
- _subscriptions: Array<() => void> = []
77
-
78
- lastSeenDepValues: Array<unknown> = []
79
- getDepVals = () => {
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
87
- }
88
- this.lastSeenDepValues = currDepVals
89
- return {
90
- prevDepVals,
91
- currDepVals,
92
- prevVal: this.prevState ?? undefined,
93
- }
94
- }
95
-
96
- constructor(options: DerivedOptions<TState, TArr>) {
97
- this.options = options
98
- this.state = options.fn({
99
- prevDepVals: undefined,
100
- prevVal: undefined,
101
- currDepVals: this.getDepVals().currDepVals as never,
102
- })
103
- }
104
-
105
- registerOnGraph(
106
- deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps,
107
- ) {
108
- const toSort = new Set<Array<Derived<unknown>>>()
109
- for (const dep of deps) {
110
- if (dep instanceof Derived) {
111
- // First register the intermediate derived value if it's not already registered
112
- dep.registerOnGraph()
113
- // Then register this derived with the dep's underlying stores
114
- this.registerOnGraph(dep.options.deps)
115
- } else if (dep instanceof Store) {
116
- // Register the derived as related derived to the store
117
- let relatedLinkedDerivedVals = __storeToDerived.get(dep)
118
- if (!relatedLinkedDerivedVals) {
119
- relatedLinkedDerivedVals = [this as never]
120
- __storeToDerived.set(dep, relatedLinkedDerivedVals)
121
- } else if (!relatedLinkedDerivedVals.includes(this as never)) {
122
- relatedLinkedDerivedVals.push(this as never)
123
- toSort.add(relatedLinkedDerivedVals)
124
- }
125
-
126
- // Register the store as a related store to this derived
127
- let relatedStores = __derivedToStore.get(this as never)
128
- if (!relatedStores) {
129
- relatedStores = new Set()
130
- __derivedToStore.set(this as never, relatedStores)
131
- }
132
- relatedStores.add(dep)
133
- }
134
- }
135
- for (const arr of toSort) {
136
- // First sort deriveds by dependency order
137
- arr.sort((a, b) => {
138
- // If a depends on b, b should go first
139
- if (a instanceof Derived && a.options.deps.includes(b)) return 1
140
- // If b depends on a, a should go first
141
- if (b instanceof Derived && b.options.deps.includes(a)) return -1
142
- return 0
143
- })
144
- }
145
- }
146
-
147
- unregisterFromGraph(
148
- deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps,
149
- ) {
150
- for (const dep of deps) {
151
- if (dep instanceof Derived) {
152
- this.unregisterFromGraph(dep.options.deps)
153
- } else if (dep instanceof Store) {
154
- const relatedLinkedDerivedVals = __storeToDerived.get(dep)
155
- if (relatedLinkedDerivedVals) {
156
- relatedLinkedDerivedVals.splice(
157
- relatedLinkedDerivedVals.indexOf(this as never),
158
- 1,
159
- )
160
- }
161
-
162
- const relatedStores = __derivedToStore.get(this as never)
163
- if (relatedStores) {
164
- relatedStores.delete(dep)
165
- }
166
- }
167
- }
168
- }
169
-
170
- recompute = () => {
171
- this.prevState = this.state
172
- const depVals = this.getDepVals()
173
- this.state = this.options.fn(depVals as never)
174
-
175
- this.options.onUpdate?.()
176
- }
177
-
178
- checkIfRecalculationNeededDeeply = () => {
179
- for (const dep of this.options.deps) {
180
- if (dep instanceof Derived) {
181
- dep.checkIfRecalculationNeededDeeply()
182
- }
183
- }
184
- let shouldRecompute = false
185
- const lastSeenDepValues = this.lastSeenDepValues
186
- const { currDepVals } = this.getDepVals()
187
- for (let i = 0; i < currDepVals.length; i++) {
188
- if (currDepVals[i] !== lastSeenDepValues[i]) {
189
- shouldRecompute = true
190
- break
191
- }
192
- }
193
-
194
- if (shouldRecompute) {
195
- this.recompute()
196
- }
197
- }
198
-
199
- mount = () => {
200
- this.registerOnGraph()
201
- this.checkIfRecalculationNeededDeeply()
202
-
203
- return () => {
204
- this.unregisterFromGraph()
205
- for (const cleanup of this._subscriptions) {
206
- cleanup()
207
- }
208
- }
209
- }
210
-
211
- subscribe = (listener: Listener<TState>) => {
212
- this.listeners.add(listener)
213
- const unsub = this.options.onSubscribe?.(listener, this)
214
- return () => {
215
- this.listeners.delete(listener)
216
- unsub?.()
217
- }
218
- }
219
- }
package/src/effect.ts DELETED
@@ -1,42 +0,0 @@
1
- import { Derived } from './derived'
2
- import type { DerivedOptions } from './derived'
3
-
4
- interface EffectOptions
5
- extends Omit<
6
- DerivedOptions<unknown>,
7
- 'onUpdate' | 'onSubscribe' | 'lazy' | 'fn'
8
- > {
9
- /**
10
- * Should the effect trigger immediately?
11
- * @default false
12
- */
13
- eager?: boolean
14
- fn: () => void
15
- }
16
-
17
- export class Effect {
18
- /**
19
- * @private
20
- */
21
- _derived: Derived<void>
22
-
23
- constructor(opts: EffectOptions) {
24
- const { eager, fn, ...derivedProps } = opts
25
-
26
- this._derived = new Derived({
27
- ...derivedProps,
28
- fn: () => {},
29
- onUpdate() {
30
- fn()
31
- },
32
- })
33
-
34
- if (eager) {
35
- fn()
36
- }
37
- }
38
-
39
- mount() {
40
- return this._derived.mount()
41
- }
42
- }
package/src/scheduler.ts DELETED
@@ -1,146 +0,0 @@
1
- import type { Derived } from './derived'
2
- import type { Store } from './store'
3
-
4
- /**
5
- * This is here to solve the pyramid dependency problem where:
6
- * A
7
- * / \
8
- * B C
9
- * \ /
10
- * D
11
- *
12
- * Where we deeply traverse this tree, how do we avoid D being recomputed twice; once when B is updated, once when C is.
13
- *
14
- * To solve this, we create linkedDeps that allows us to sync avoid writes to the state until all of the deps have been
15
- * resolved.
16
- *
17
- * This is a record of stores, because derived stores are not able to write values to, but stores are
18
- */
19
- export const __storeToDerived = new WeakMap<
20
- Store<unknown>,
21
- Array<Derived<unknown>>
22
- >()
23
- export const __derivedToStore = new WeakMap<
24
- Derived<unknown>,
25
- Set<Store<unknown>>
26
- >()
27
-
28
- export const __depsThatHaveWrittenThisTick = {
29
- current: [] as Array<Derived<unknown> | Store<unknown>>,
30
- }
31
-
32
- let __isFlushing = false
33
- let __batchDepth = 0
34
- const __pendingUpdates = new Set<Store<unknown>>()
35
- // Add a map to store initial values before batch
36
- const __initialBatchValues = new Map<Store<unknown>, unknown>()
37
-
38
- function __flush_internals(relatedVals: ReadonlyArray<Derived<unknown>>) {
39
- for (const derived of relatedVals) {
40
- if (__depsThatHaveWrittenThisTick.current.includes(derived)) {
41
- continue
42
- }
43
-
44
- __depsThatHaveWrittenThisTick.current.push(derived)
45
- derived.recompute()
46
-
47
- const stores = __derivedToStore.get(derived)
48
- if (stores) {
49
- for (const store of stores) {
50
- const relatedLinkedDerivedVals = __storeToDerived.get(store)
51
- if (!relatedLinkedDerivedVals?.length) continue
52
- __flush_internals(relatedLinkedDerivedVals)
53
- }
54
- }
55
- }
56
- }
57
-
58
- function __notifyListeners(store: Store<unknown>) {
59
- const value = {
60
- prevVal: store.prevState as never,
61
- currentVal: store.state as never,
62
- }
63
- for (const listener of store.listeners) {
64
- listener(value)
65
- }
66
- }
67
-
68
- function __notifyDerivedListeners(derived: Derived<unknown>) {
69
- const value = {
70
- prevVal: derived.prevState as never,
71
- currentVal: derived.state as never,
72
- }
73
- for (const listener of derived.listeners) {
74
- listener(value)
75
- }
76
- }
77
-
78
- /**
79
- * @private only to be called from `Store` on write
80
- */
81
- export function __flush(store: Store<unknown>) {
82
- // If we're starting a batch, store the initial values
83
- if (__batchDepth > 0 && !__initialBatchValues.has(store)) {
84
- __initialBatchValues.set(store, store.prevState)
85
- }
86
-
87
- __pendingUpdates.add(store)
88
-
89
- if (__batchDepth > 0) return
90
- if (__isFlushing) return
91
-
92
- try {
93
- __isFlushing = true
94
-
95
- while (__pendingUpdates.size > 0) {
96
- const stores = Array.from(__pendingUpdates)
97
- __pendingUpdates.clear()
98
-
99
- // First notify listeners with updated values
100
- for (const store of stores) {
101
- // Use initial batch values for prevState if we have them
102
- const prevState = __initialBatchValues.get(store) ?? store.prevState
103
- store.prevState = prevState
104
- __notifyListeners(store)
105
- }
106
-
107
- // Then update all derived values
108
- for (const store of stores) {
109
- const derivedVals = __storeToDerived.get(store)
110
- if (!derivedVals) continue
111
-
112
- __depsThatHaveWrittenThisTick.current.push(store)
113
- __flush_internals(derivedVals)
114
- }
115
-
116
- // Notify derived listeners after recomputing
117
- for (const store of stores) {
118
- const derivedVals = __storeToDerived.get(store)
119
- if (!derivedVals) continue
120
-
121
- for (const derived of derivedVals) {
122
- __notifyDerivedListeners(derived)
123
- }
124
- }
125
- }
126
- } finally {
127
- __isFlushing = false
128
- __depsThatHaveWrittenThisTick.current = []
129
- __initialBatchValues.clear()
130
- }
131
- }
132
-
133
- export function batch(fn: () => void) {
134
- __batchDepth++
135
- try {
136
- fn()
137
- } finally {
138
- __batchDepth--
139
- if (__batchDepth === 0) {
140
- const pendingUpdateToFlush = __pendingUpdates.values().next().value
141
- if (pendingUpdateToFlush) {
142
- __flush(pendingUpdateToFlush) // Trigger flush of all pending updates
143
- }
144
- }
145
- }
146
- }