@pyreon/reactivity 0.24.5 → 0.24.6

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 (44) hide show
  1. package/package.json +1 -4
  2. package/src/batch.ts +0 -196
  3. package/src/cell.ts +0 -72
  4. package/src/computed.ts +0 -313
  5. package/src/createSelector.ts +0 -109
  6. package/src/debug.ts +0 -134
  7. package/src/effect.ts +0 -467
  8. package/src/env.d.ts +0 -6
  9. package/src/index.ts +0 -60
  10. package/src/lpih.ts +0 -227
  11. package/src/manifest.ts +0 -660
  12. package/src/reactive-devtools.ts +0 -494
  13. package/src/reactive-trace.ts +0 -142
  14. package/src/reconcile.ts +0 -118
  15. package/src/resource.ts +0 -84
  16. package/src/scope.ts +0 -123
  17. package/src/signal.ts +0 -261
  18. package/src/store.ts +0 -250
  19. package/src/tests/batch.test.ts +0 -751
  20. package/src/tests/bind.test.ts +0 -84
  21. package/src/tests/branches.test.ts +0 -343
  22. package/src/tests/cell.test.ts +0 -159
  23. package/src/tests/computed.test.ts +0 -436
  24. package/src/tests/coverage-hardening.test.ts +0 -471
  25. package/src/tests/createSelector.test.ts +0 -291
  26. package/src/tests/debug.test.ts +0 -196
  27. package/src/tests/effect.test.ts +0 -464
  28. package/src/tests/fanout-repro.test.ts +0 -179
  29. package/src/tests/lpih-source-location.test.ts +0 -277
  30. package/src/tests/lpih.test.ts +0 -351
  31. package/src/tests/manifest-snapshot.test.ts +0 -96
  32. package/src/tests/reactive-devtools-treeshake.test.ts +0 -48
  33. package/src/tests/reactive-devtools.test.ts +0 -296
  34. package/src/tests/reactive-trace.test.ts +0 -102
  35. package/src/tests/reconcile-security.test.ts +0 -45
  36. package/src/tests/resource.test.ts +0 -326
  37. package/src/tests/scope.test.ts +0 -231
  38. package/src/tests/signal.test.ts +0 -368
  39. package/src/tests/store.test.ts +0 -286
  40. package/src/tests/tracking.test.ts +0 -158
  41. package/src/tests/vue-parity.test.ts +0 -191
  42. package/src/tests/watch.test.ts +0 -246
  43. package/src/tracking.ts +0 -139
  44. package/src/watch.ts +0 -68
package/src/tracking.ts DELETED
@@ -1,139 +0,0 @@
1
- // Global subscriber tracking context
2
-
3
- import { enqueuePendingNotification, isBatching } from './batch'
4
-
5
- let activeEffect: (() => void) | null = null
6
-
7
- // Tracks which subscriber sets each effect is registered in, so we can
8
- // clean them up before a re-run (dynamic dependency tracking).
9
- const effectDeps = new WeakMap<() => void, Set<Set<() => void>>>()
10
-
11
- // Fast deps collector for renderEffect — avoids WeakMap overhead entirely.
12
- // When set, trackSubscriber pushes subscriber sets here instead of effectDeps.
13
- let _depsCollector: Set<() => void>[] | null = null
14
-
15
- // Skip deps collection mode — for re-evaluating computeds/effects with static deps.
16
- // When true, trackSubscriber only does Set.add (no-op if already subscribed) and skips
17
- // the _depsCollector.push / WeakMap work entirely.
18
- let _skipDepsCollection = false
19
-
20
- export function setDepsCollector(collector: Set<() => void>[] | null): void {
21
- _depsCollector = collector
22
- }
23
-
24
- export function setSkipDepsCollection(skip: boolean): void {
25
- _skipDepsCollection = skip
26
- }
27
-
28
- /**
29
- * Subscriber host — any reactive source that can have downstream subscribers.
30
- * Signals, computeds, and createSelector buckets all implement this interface.
31
- * The Set is created lazily — only allocated when an effect actually tracks this source.
32
- */
33
- export interface SubscriberHost {
34
- /** @internal subscriber set — null until first tracked by an effect */
35
- _s: Set<() => void> | null
36
- }
37
-
38
- /**
39
- * Register the active effect as a subscriber of the given reactive source.
40
- * The subscriber Set is created lazily on the host — sources read only outside
41
- * effects never allocate a Set.
42
- */
43
- export function trackSubscriber(host: SubscriberHost) {
44
- if (activeEffect) {
45
- if (!host._s) host._s = new Set()
46
- host._s.add(activeEffect)
47
- // Skip collection mode: we're already subscribed (Set.add is no-op),
48
- // just need activeEffect set for nested computed reads to work.
49
- if (_skipDepsCollection) return
50
- if (_depsCollector) {
51
- // Fast path: renderEffect stores deps inline, no WeakMap
52
- _depsCollector.push(host._s)
53
- } else {
54
- // Record this dep so we can remove it on cleanup
55
- let deps = effectDeps.get(activeEffect)
56
- if (!deps) {
57
- deps = new Set()
58
- effectDeps.set(activeEffect, deps)
59
- }
60
- deps.add(host._s)
61
- }
62
- }
63
- }
64
-
65
- /**
66
- * Remove an effect from every subscriber set it was registered in,
67
- * then clear its dep record. Call this before each re-run and on dispose.
68
- */
69
- export function cleanupEffect(fn: () => void): void {
70
- const deps = effectDeps.get(fn)
71
- if (deps) {
72
- for (const sub of deps) sub.delete(fn)
73
- deps.clear()
74
- }
75
- }
76
-
77
- export function notifySubscribers(subscribers: Set<() => void>) {
78
- if (subscribers.size === 0) return
79
- // Single-subscriber fast path: avoid any iteration overhead.
80
- if (subscribers.size === 1) {
81
- const sub = subscribers.values().next().value as () => void
82
- if (isBatching()) enqueuePendingNotification(sub)
83
- else sub()
84
- return
85
- }
86
- if (isBatching()) {
87
- // Effects are queued not run inline — no re-entrancy risk, iterate the live Set directly.
88
- for (const sub of subscribers) enqueuePendingNotification(sub)
89
- } else {
90
- // Effects run inline and may call cleanupEffect (removes) + trackSubscriber (re-adds).
91
- // Instead of snapshotting with [...subscribers] (allocates an array), we iterate the
92
- // live Set but cap iterations at the original size to prevent infinite loops from
93
- // re-inserted entries. This is safe because:
94
- // - cleanupEffect removes the effect from the Set (no double-fire)
95
- // - trackSubscriber may re-add it (but we stop after originalSize iterations)
96
- // - Any effects re-added during this pass are already up-to-date (just ran)
97
- const originalSize = subscribers.size
98
- let i = 0
99
- for (const sub of subscribers) {
100
- if (i >= originalSize) break
101
- sub()
102
- i++
103
- }
104
- }
105
- }
106
-
107
- export function withTracking<T>(fn: () => void, compute: () => T): T {
108
- const prev = activeEffect
109
- activeEffect = fn
110
- try {
111
- return compute()
112
- } finally {
113
- activeEffect = prev
114
- }
115
- }
116
-
117
- // Stack for inlined tracking in renderEffect — avoids withTracking function call overhead.
118
- let _prevEffect: (() => void) | null = null
119
-
120
- export function _setActiveEffect(fn: () => void): void {
121
- _prevEffect = activeEffect
122
- activeEffect = fn
123
- }
124
-
125
- export function _restoreActiveEffect(): void {
126
- activeEffect = _prevEffect
127
- _prevEffect = null
128
- }
129
-
130
- /** Read signals without subscribing. Alias: `untrack`. */
131
- export function runUntracked<T>(fn: () => T): T {
132
- const prev = activeEffect
133
- activeEffect = null
134
- try {
135
- return fn()
136
- } finally {
137
- activeEffect = prev
138
- }
139
- }
package/src/watch.ts DELETED
@@ -1,68 +0,0 @@
1
- import { effect } from './effect'
2
-
3
- export interface WatchOptions {
4
- /** If true, call the callback immediately with the current value on setup. Default: false. */
5
- immediate?: boolean
6
- }
7
-
8
- /**
9
- * Watch a reactive source and run a callback whenever it changes.
10
- *
11
- * Returns a stop function that disposes the watcher.
12
- *
13
- * The callback receives (newValue, oldValue). On the first call (when
14
- * `immediate` is true) oldValue is `undefined`.
15
- *
16
- * The callback may return a cleanup function that is called before each
17
- * re-run and on stop — useful for cancelling async work.
18
- *
19
- * @example
20
- * const stop = watch(
21
- * () => userId(),
22
- * async (id, prev) => {
23
- * const data = await fetch(`/api/user/${id}`)
24
- * setUser(await data.json())
25
- * },
26
- * )
27
- * // Later: stop()
28
- */
29
- export function watch<T>(
30
- source: () => T,
31
- callback: (newVal: T, oldVal: T | undefined) => void | (() => void),
32
- opts: WatchOptions = {},
33
- ): () => void {
34
- let oldVal: T | undefined
35
- let isFirst = true
36
- let cleanupFn: (() => void) | undefined
37
-
38
- const e = effect(() => {
39
- const newVal = source()
40
-
41
- if (isFirst) {
42
- isFirst = false
43
- oldVal = newVal
44
- if (opts.immediate) {
45
- const result = callback(newVal, undefined)
46
- if (typeof result === 'function') cleanupFn = result
47
- }
48
- return
49
- }
50
-
51
- if (cleanupFn) {
52
- cleanupFn()
53
- cleanupFn = undefined
54
- }
55
-
56
- const result = callback(newVal, oldVal)
57
- if (typeof result === 'function') cleanupFn = result
58
- oldVal = newVal
59
- })
60
-
61
- return () => {
62
- e.dispose()
63
- if (cleanupFn) {
64
- cleanupFn()
65
- cleanupFn = undefined
66
- }
67
- }
68
- }