@fictjs/runtime 0.2.2 → 0.3.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.
- package/dist/advanced.cjs +10 -8
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +10 -16
- package/dist/advanced.d.ts +10 -16
- package/dist/advanced.js +5 -3
- package/dist/advanced.js.map +1 -1
- package/dist/{chunk-3U7EBKEU.cjs → chunk-ID3WBWNO.cjs} +559 -319
- package/dist/chunk-ID3WBWNO.cjs.map +1 -0
- package/dist/{chunk-3A4VW6AK.cjs → chunk-L4DIV3RC.cjs} +7 -7
- package/dist/{chunk-3A4VW6AK.cjs.map → chunk-L4DIV3RC.cjs.map} +1 -1
- package/dist/{chunk-URDFDRHR.cjs → chunk-M2TSXZ4C.cjs} +16 -16
- package/dist/{chunk-URDFDRHR.cjs.map → chunk-M2TSXZ4C.cjs.map} +1 -1
- package/dist/{chunk-YVS4WJ2W.js → chunk-SO6X7G5S.js} +558 -318
- package/dist/chunk-SO6X7G5S.js.map +1 -0
- package/dist/{chunk-LU2LD2WJ.js → chunk-TWELIZRY.js} +2 -2
- package/dist/{chunk-TEYUDPTA.js → chunk-XLIZJMMJ.js} +2 -2
- package/dist/{context-9gFXOdJl.d.cts → context-B25xyQrJ.d.cts} +36 -2
- package/dist/{context-4woHo7-L.d.ts → context-CGdP7_Jb.d.ts} +36 -2
- package/dist/{effect-ClARNUCc.d.cts → effect-D6kaLM2-.d.cts} +80 -1
- package/dist/{effect-ClARNUCc.d.ts → effect-D6kaLM2-.d.ts} +80 -1
- package/dist/index.cjs +40 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.dev.js +430 -246
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +39 -35
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +8 -6
- package/dist/internal.d.ts +8 -6
- package/dist/internal.js +7 -3
- package/dist/internal.js.map +1 -1
- package/dist/{props-DAyeRPwH.d.ts → props-BEgIVMRx.d.ts} +8 -15
- package/dist/{props-CBwuh35e.d.cts → props-BIfromL0.d.cts} +8 -15
- package/dist/scope-Cx_3CjIZ.d.cts +18 -0
- package/dist/scope-CzNkn587.d.ts +18 -0
- package/package.json +1 -1
- package/src/advanced.ts +1 -0
- package/src/binding.ts +30 -4
- package/src/constants.ts +5 -0
- package/src/cycle-guard.ts +164 -103
- package/src/devtools.ts +22 -2
- package/src/dom.ts +84 -10
- package/src/hooks.ts +60 -13
- package/src/index.ts +3 -1
- package/src/internal.ts +2 -2
- package/src/lifecycle.ts +13 -5
- package/src/memo.ts +3 -4
- package/src/props.ts +16 -0
- package/src/signal.ts +204 -36
- package/dist/chunk-3U7EBKEU.cjs.map +0 -1
- package/dist/chunk-YVS4WJ2W.js.map +0 -1
- package/dist/scope-DvgMquEy.d.ts +0 -55
- package/dist/scope-xmdo6lVU.d.cts +0 -55
- /package/dist/{chunk-LU2LD2WJ.js.map → chunk-TWELIZRY.js.map} +0 -0
- /package/dist/{chunk-TEYUDPTA.js.map → chunk-XLIZJMMJ.js.map} +0 -0
package/src/props.ts
CHANGED
|
@@ -203,6 +203,22 @@ export type PropGetter<T> = (() => T) & { __fictProp: true }
|
|
|
203
203
|
export interface PropOptions {
|
|
204
204
|
unwrap?: boolean
|
|
205
205
|
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Create a keyed prop getter that tracks both the key and the target access.
|
|
209
|
+
* Useful for dynamic property access like obj[key] where key is reactive.
|
|
210
|
+
*/
|
|
211
|
+
export function keyed<T, K extends string | number | symbol>(
|
|
212
|
+
target: T | PropGetter<T>,
|
|
213
|
+
key: K | (() => K),
|
|
214
|
+
options?: PropOptions,
|
|
215
|
+
): PropGetter<unknown> {
|
|
216
|
+
return prop(() => {
|
|
217
|
+
const resolvedTarget = isPropGetter(target) ? (target as () => T)() : target
|
|
218
|
+
const resolvedKey = typeof key === 'function' ? (key as () => K)() : key
|
|
219
|
+
return (resolvedTarget as Record<string | number | symbol, unknown>)[resolvedKey]
|
|
220
|
+
}, options)
|
|
221
|
+
}
|
|
206
222
|
/**
|
|
207
223
|
* Memoize a prop getter to cache expensive computations.
|
|
208
224
|
* Use when prop expressions involve heavy calculations or you need lazy, reactive props.
|
package/src/signal.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { beginFlushGuard, beforeEffectRunGuard, endFlushGuard } from './cycle-guard'
|
|
2
2
|
import { getDevtoolsHook } from './devtools'
|
|
3
|
+
import { __fictGetCurrentComponentId } from './hooks'
|
|
3
4
|
import {
|
|
4
5
|
getCurrentRoot,
|
|
5
6
|
handleError,
|
|
@@ -70,6 +71,30 @@ export interface BaseNode {
|
|
|
70
71
|
flags: number
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Options for creating a signal
|
|
76
|
+
*/
|
|
77
|
+
export interface SignalOptions<T> {
|
|
78
|
+
/** Custom equality check */
|
|
79
|
+
equals?: false | ((prev: T, next: T) => boolean)
|
|
80
|
+
/** Debug name */
|
|
81
|
+
name?: string
|
|
82
|
+
/** Source location */
|
|
83
|
+
devToolsSource?: string
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Options for creating a memo
|
|
88
|
+
*/
|
|
89
|
+
export interface MemoOptions<T> {
|
|
90
|
+
/** Custom equality check */
|
|
91
|
+
equals?: false | ((prev: T, next: T) => boolean)
|
|
92
|
+
/** Debug name */
|
|
93
|
+
name?: string
|
|
94
|
+
/** Source location */
|
|
95
|
+
devToolsSource?: string
|
|
96
|
+
}
|
|
97
|
+
|
|
73
98
|
/**
|
|
74
99
|
* Signal node - mutable reactive value
|
|
75
100
|
*/
|
|
@@ -82,6 +107,14 @@ export interface SignalNode<T = unknown> extends BaseNode {
|
|
|
82
107
|
deps?: undefined
|
|
83
108
|
depsTail?: undefined
|
|
84
109
|
getter?: undefined
|
|
110
|
+
/** DevTools ID */
|
|
111
|
+
__id?: number | undefined
|
|
112
|
+
/** Equality check */
|
|
113
|
+
equals?: false | ((prev: T, next: T) => boolean)
|
|
114
|
+
/** Debug name */
|
|
115
|
+
name?: string
|
|
116
|
+
/** Source location */
|
|
117
|
+
devToolsSource?: string
|
|
85
118
|
}
|
|
86
119
|
|
|
87
120
|
/**
|
|
@@ -96,6 +129,14 @@ export interface ComputedNode<T = unknown> extends BaseNode {
|
|
|
96
129
|
depsTail: Link | undefined
|
|
97
130
|
/** Getter function to compute the value */
|
|
98
131
|
getter: (oldValue: T | undefined) => T
|
|
132
|
+
/** DevTools ID */
|
|
133
|
+
__id?: number | undefined
|
|
134
|
+
/** Equality check */
|
|
135
|
+
equals?: false | ((prev: T, next: T) => boolean)
|
|
136
|
+
/** Debug name */
|
|
137
|
+
name?: string
|
|
138
|
+
/** Source location */
|
|
139
|
+
devToolsSource?: string
|
|
99
140
|
}
|
|
100
141
|
|
|
101
142
|
/**
|
|
@@ -464,6 +505,9 @@ function link(dep: ReactiveNode, sub: ReactiveNode, version: number): void {
|
|
|
464
505
|
else sub.deps = newLink
|
|
465
506
|
if (prevSub !== undefined) prevSub.nextSub = newLink
|
|
466
507
|
else dep.subs = newLink
|
|
508
|
+
|
|
509
|
+
// Track dependency for devtools
|
|
510
|
+
if (isDev) trackDependencyDevtools(dep, sub)
|
|
467
511
|
}
|
|
468
512
|
/**
|
|
469
513
|
* Remove a link between a dependency and a subscriber
|
|
@@ -488,6 +532,9 @@ function unlink(lnk: Link, sub: ReactiveNode = lnk.sub): Link | undefined {
|
|
|
488
532
|
if (prevSub !== undefined) prevSub.nextSub = nextSub
|
|
489
533
|
else if ((dep.subs = nextSub) === undefined) unwatched(dep)
|
|
490
534
|
|
|
535
|
+
// Notify devtools that dependency edge is removed
|
|
536
|
+
if (isDev) untrackDependencyDevtools(dep, sub)
|
|
537
|
+
|
|
491
538
|
return nextDep
|
|
492
539
|
}
|
|
493
540
|
/**
|
|
@@ -684,6 +731,16 @@ function update(node: ReactiveNode): boolean {
|
|
|
684
731
|
? updateComputed(node as ComputedNode)
|
|
685
732
|
: updateSignal(node as SignalNode)
|
|
686
733
|
}
|
|
734
|
+
|
|
735
|
+
function valuesDiffer<T>(
|
|
736
|
+
node: { equals?: false | ((prev: T, next: T) => boolean) },
|
|
737
|
+
prev: T,
|
|
738
|
+
next: T,
|
|
739
|
+
): boolean {
|
|
740
|
+
if (node.equals === false) return true
|
|
741
|
+
if (typeof node.equals === 'function') return !node.equals(prev, next)
|
|
742
|
+
return prev !== next
|
|
743
|
+
}
|
|
687
744
|
/**
|
|
688
745
|
* Notify an effect and add it to the queue
|
|
689
746
|
* @param effect - The effect to notify
|
|
@@ -740,7 +797,7 @@ function updateSignal(s: SignalNode): boolean {
|
|
|
740
797
|
s.flags = Mutable
|
|
741
798
|
const current = s.currentValue
|
|
742
799
|
const pending = s.pendingValue
|
|
743
|
-
if (current
|
|
800
|
+
if (valuesDiffer(s, current, pending)) {
|
|
744
801
|
s.currentValue = pending
|
|
745
802
|
return true
|
|
746
803
|
}
|
|
@@ -764,8 +821,9 @@ function updateComputed<T>(c: ComputedNode<T>): boolean {
|
|
|
764
821
|
activeSub = prevSub
|
|
765
822
|
c.flags &= ~Running
|
|
766
823
|
purgeDeps(c)
|
|
767
|
-
if (oldValue
|
|
824
|
+
if (valuesDiffer(c, oldValue, newValue)) {
|
|
768
825
|
c.value = newValue
|
|
826
|
+
if (isDev) updateComputedDevtools(c, newValue)
|
|
769
827
|
return true
|
|
770
828
|
}
|
|
771
829
|
return false
|
|
@@ -792,7 +850,7 @@ function runEffect(e: EffectNode): void {
|
|
|
792
850
|
}
|
|
793
851
|
}
|
|
794
852
|
++cycle
|
|
795
|
-
effectRunDevtools(e)
|
|
853
|
+
if (isDev) effectRunDevtools(e)
|
|
796
854
|
e.depsTail = undefined
|
|
797
855
|
e.flags = WatchingRunning
|
|
798
856
|
const prevSub = activeSub
|
|
@@ -837,7 +895,7 @@ function runEffect(e: EffectNode): void {
|
|
|
837
895
|
}
|
|
838
896
|
if (isDirty) {
|
|
839
897
|
++cycle
|
|
840
|
-
effectRunDevtools(e)
|
|
898
|
+
if (isDev) effectRunDevtools(e)
|
|
841
899
|
e.depsTail = undefined
|
|
842
900
|
e.flags = WatchingRunning
|
|
843
901
|
const prevSub = activeSub
|
|
@@ -896,10 +954,23 @@ function flush(): void {
|
|
|
896
954
|
while (highIndex < highPriorityQueue.length) {
|
|
897
955
|
const e = highPriorityQueue[highIndex]!
|
|
898
956
|
if (!beforeEffectRunGuard()) {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
957
|
+
// fix: When cycle guard fails, drop the current queues to avoid microtask spin.
|
|
958
|
+
// Dev mode will throw inside beforeEffectRunGuard; this branch is for prod warnings.
|
|
959
|
+
for (let i = 0; i < highPriorityQueue.length; i++) {
|
|
960
|
+
const queued = highPriorityQueue[i]
|
|
961
|
+
if (queued && queued.flags !== 0) {
|
|
962
|
+
queued.flags = Watching
|
|
963
|
+
}
|
|
902
964
|
}
|
|
965
|
+
for (let i = 0; i < lowPriorityQueue.length; i++) {
|
|
966
|
+
const queued = lowPriorityQueue[i]
|
|
967
|
+
if (queued && queued.flags !== 0) {
|
|
968
|
+
queued.flags = Watching
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
highPriorityQueue.length = 0
|
|
972
|
+
lowPriorityQueue.length = 0
|
|
973
|
+
flushScheduled = false
|
|
903
974
|
endFlushGuard()
|
|
904
975
|
return
|
|
905
976
|
}
|
|
@@ -923,10 +994,23 @@ function flush(): void {
|
|
|
923
994
|
}
|
|
924
995
|
const e = lowPriorityQueue[lowIndex]!
|
|
925
996
|
if (!beforeEffectRunGuard()) {
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
997
|
+
// fix: When cycle guard fails, drop the current queues to avoid microtask spin.
|
|
998
|
+
// Dev mode will throw inside beforeEffectRunGuard; this branch is for prod warnings.
|
|
999
|
+
for (let i = 0; i < highPriorityQueue.length; i++) {
|
|
1000
|
+
const queued = highPriorityQueue[i]
|
|
1001
|
+
if (queued && queued.flags !== 0) {
|
|
1002
|
+
queued.flags = Watching
|
|
1003
|
+
}
|
|
929
1004
|
}
|
|
1005
|
+
for (let i = 0; i < lowPriorityQueue.length; i++) {
|
|
1006
|
+
const queued = lowPriorityQueue[i]
|
|
1007
|
+
if (queued && queued.flags !== 0) {
|
|
1008
|
+
queued.flags = Watching
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
highPriorityQueue.length = 0
|
|
1012
|
+
lowPriorityQueue.length = 0
|
|
1013
|
+
flushScheduled = false
|
|
930
1014
|
endFlushGuard()
|
|
931
1015
|
return
|
|
932
1016
|
}
|
|
@@ -945,26 +1029,31 @@ function flush(): void {
|
|
|
945
1029
|
* @param initialValue - The initial value
|
|
946
1030
|
* @returns A signal accessor function
|
|
947
1031
|
*/
|
|
948
|
-
export function signal<T>(initialValue: T): SignalAccessor<T> {
|
|
949
|
-
const s = {
|
|
1032
|
+
export function signal<T>(initialValue: T, options?: SignalOptions<T>): SignalAccessor<T> {
|
|
1033
|
+
const s: SignalNode<T> = {
|
|
950
1034
|
currentValue: initialValue,
|
|
951
1035
|
pendingValue: initialValue,
|
|
952
1036
|
subs: undefined,
|
|
953
1037
|
subsTail: undefined,
|
|
954
1038
|
flags: Mutable,
|
|
955
1039
|
__id: undefined as number | undefined,
|
|
1040
|
+
...(options?.equals !== undefined ? { equals: options.equals } : {}),
|
|
1041
|
+
...(options?.name !== undefined ? { name: options.name } : {}),
|
|
1042
|
+
...(options?.devToolsSource !== undefined ? { devToolsSource: options.devToolsSource } : {}),
|
|
956
1043
|
}
|
|
957
|
-
registerSignalDevtools(
|
|
958
|
-
const accessor = signalOper.bind(s) as SignalAccessor<T> & Record<symbol, boolean>
|
|
1044
|
+
if (isDev) registerSignalDevtools(s)
|
|
1045
|
+
const accessor = signalOper.bind(s as any) as SignalAccessor<T> & Record<symbol, boolean>
|
|
959
1046
|
accessor[SIGNAL_MARKER] = true
|
|
960
1047
|
return accessor as SignalAccessor<T>
|
|
961
1048
|
}
|
|
962
1049
|
function signalOper<T>(this: SignalNode<T>, value?: T): T | void {
|
|
963
1050
|
if (arguments.length > 0) {
|
|
964
|
-
|
|
965
|
-
|
|
1051
|
+
const next = value as T
|
|
1052
|
+
const prev = this.pendingValue
|
|
1053
|
+
if (valuesDiffer(this, prev as T, next)) {
|
|
1054
|
+
this.pendingValue = next
|
|
966
1055
|
this.flags = MutableDirty
|
|
967
|
-
updateSignalDevtools(this,
|
|
1056
|
+
if (isDev) updateSignalDevtools(this, next)
|
|
968
1057
|
const subs = this.subs
|
|
969
1058
|
if (subs !== undefined) {
|
|
970
1059
|
propagate(subs)
|
|
@@ -977,7 +1066,7 @@ function signalOper<T>(this: SignalNode<T>, value?: T): T | void {
|
|
|
977
1066
|
const flags = this.flags
|
|
978
1067
|
// During cleanup, don't update signal - return currentValue as-is
|
|
979
1068
|
if (flags & Dirty && !inCleanup) {
|
|
980
|
-
if (updateSignal(this)) {
|
|
1069
|
+
if (updateSignal(this as any)) {
|
|
981
1070
|
const subs = this.subs
|
|
982
1071
|
if (subs !== undefined) shallowPropagate(subs)
|
|
983
1072
|
}
|
|
@@ -986,7 +1075,7 @@ function signalOper<T>(this: SignalNode<T>, value?: T): T | void {
|
|
|
986
1075
|
let sub = activeSub
|
|
987
1076
|
while (sub !== undefined) {
|
|
988
1077
|
if (sub.flags & 3) {
|
|
989
|
-
link(this, sub, cycle)
|
|
1078
|
+
link(this as any, sub, cycle)
|
|
990
1079
|
break
|
|
991
1080
|
}
|
|
992
1081
|
const subSubs = sub.subs
|
|
@@ -1001,9 +1090,13 @@ function signalOper<T>(this: SignalNode<T>, value?: T): T | void {
|
|
|
1001
1090
|
/**
|
|
1002
1091
|
* Create a computed reactive value
|
|
1003
1092
|
* @param getter - The getter function
|
|
1093
|
+
* @param options - Computed options
|
|
1004
1094
|
* @returns A computed accessor function
|
|
1005
1095
|
*/
|
|
1006
|
-
export function computed<T>(
|
|
1096
|
+
export function computed<T>(
|
|
1097
|
+
getter: (oldValue?: T) => T,
|
|
1098
|
+
options?: MemoOptions<T>,
|
|
1099
|
+
): ComputedAccessor<T> {
|
|
1007
1100
|
const c: ComputedNode<T> = {
|
|
1008
1101
|
value: undefined as unknown as T,
|
|
1009
1102
|
subs: undefined,
|
|
@@ -1012,13 +1105,24 @@ export function computed<T>(getter: (oldValue?: T) => T): ComputedAccessor<T> {
|
|
|
1012
1105
|
depsTail: undefined,
|
|
1013
1106
|
flags: 0,
|
|
1014
1107
|
getter,
|
|
1108
|
+
__id: undefined as number | undefined,
|
|
1109
|
+
...(options?.equals !== undefined ? { equals: options.equals } : {}),
|
|
1110
|
+
...(options?.name !== undefined ? { name: options.name } : {}),
|
|
1111
|
+
...(options?.devToolsSource !== undefined ? { devToolsSource: options.devToolsSource } : {}),
|
|
1015
1112
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1113
|
+
if (isDev) registerComputedDevtools(c)
|
|
1114
|
+
const bound = (computedOper as (this: ComputedNode<T>) => T).bind(
|
|
1115
|
+
c as any,
|
|
1116
|
+
) as ComputedAccessor<T> & Record<symbol, boolean>
|
|
1018
1117
|
bound[COMPUTED_MARKER] = true
|
|
1019
1118
|
return bound as ComputedAccessor<T>
|
|
1020
1119
|
}
|
|
1021
1120
|
function computedOper<T>(this: ComputedNode<T>): T {
|
|
1121
|
+
// fix: During cleanup, return cached value without triggering any updates.
|
|
1122
|
+
// This ensures cleanup functions see the previous state, not the new pending values.
|
|
1123
|
+
// Without this check, checkDirty() could commit pending signal values during cleanup.
|
|
1124
|
+
if (inCleanup) return this.value
|
|
1125
|
+
|
|
1022
1126
|
const flags = this.flags
|
|
1023
1127
|
|
|
1024
1128
|
if (flags & Dirty) {
|
|
@@ -1040,6 +1144,7 @@ function computedOper<T>(this: ComputedNode<T>): T {
|
|
|
1040
1144
|
const prevSub = setActiveSub(this)
|
|
1041
1145
|
try {
|
|
1042
1146
|
this.value = this.getter(undefined)
|
|
1147
|
+
if (isDev) updateComputedDevtools(this, this.value)
|
|
1043
1148
|
} finally {
|
|
1044
1149
|
setActiveSub(prevSub)
|
|
1045
1150
|
this.flags &= ~Running
|
|
@@ -1072,14 +1177,14 @@ export function effect(fn: () => void): EffectDisposer {
|
|
|
1072
1177
|
e.root = root
|
|
1073
1178
|
}
|
|
1074
1179
|
|
|
1075
|
-
registerEffectDevtools(e)
|
|
1180
|
+
if (isDev) registerEffectDevtools(e)
|
|
1076
1181
|
|
|
1077
1182
|
const prevSub = activeSub
|
|
1078
1183
|
if (prevSub !== undefined) link(e, prevSub, 0)
|
|
1079
1184
|
activeSub = e
|
|
1080
1185
|
|
|
1081
1186
|
try {
|
|
1082
|
-
effectRunDevtools(e)
|
|
1187
|
+
if (isDev) effectRunDevtools(e)
|
|
1083
1188
|
fn()
|
|
1084
1189
|
} finally {
|
|
1085
1190
|
activeSub = prevSub
|
|
@@ -1120,14 +1225,14 @@ export function effectWithCleanup(
|
|
|
1120
1225
|
e.root = resolvedRoot
|
|
1121
1226
|
}
|
|
1122
1227
|
|
|
1123
|
-
registerEffectDevtools(e)
|
|
1228
|
+
if (isDev) registerEffectDevtools(e)
|
|
1124
1229
|
|
|
1125
1230
|
const prevSub = activeSub
|
|
1126
1231
|
if (prevSub !== undefined) link(e, prevSub, 0)
|
|
1127
1232
|
activeSub = e
|
|
1128
1233
|
|
|
1129
1234
|
try {
|
|
1130
|
-
effectRunDevtools(e)
|
|
1235
|
+
if (isDev) effectRunDevtools(e)
|
|
1131
1236
|
fn()
|
|
1132
1237
|
} finally {
|
|
1133
1238
|
activeSub = prevSub
|
|
@@ -1269,6 +1374,21 @@ export function setActiveSub(sub: ReactiveNode | undefined): ReactiveNode | unde
|
|
|
1269
1374
|
export function getBatchDepth(): number {
|
|
1270
1375
|
return batchDepth
|
|
1271
1376
|
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Reset all global reactive state for test isolation.
|
|
1379
|
+
* ONLY use this in test setup/teardown - never in production code.
|
|
1380
|
+
* This clears effect queues, resets batch depth, and clears pending flushes.
|
|
1381
|
+
*/
|
|
1382
|
+
export function __resetReactiveState(): void {
|
|
1383
|
+
highPriorityQueue.length = 0
|
|
1384
|
+
lowPriorityQueue.length = 0
|
|
1385
|
+
batchDepth = 0
|
|
1386
|
+
activeSub = undefined
|
|
1387
|
+
flushScheduled = false
|
|
1388
|
+
isInTransition = false
|
|
1389
|
+
inCleanup = false
|
|
1390
|
+
cycle = 0
|
|
1391
|
+
}
|
|
1272
1392
|
/**
|
|
1273
1393
|
* Execute a function without tracking dependencies
|
|
1274
1394
|
* @param fn - The function to execute
|
|
@@ -1391,21 +1511,30 @@ interface DevtoolsIdentifiable {
|
|
|
1391
1511
|
__id?: number
|
|
1392
1512
|
}
|
|
1393
1513
|
|
|
1394
|
-
let registerSignalDevtools: (
|
|
1395
|
-
|
|
1396
|
-
let
|
|
1514
|
+
let registerSignalDevtools: <T>(node: SignalNode<T>) => number | undefined = () => undefined
|
|
1515
|
+
let updateSignalDevtools: <T>(node: SignalNode<T>, value: unknown) => void = () => {}
|
|
1516
|
+
let registerComputedDevtools: <T>(node: ComputedNode<T>) => number | undefined = () => undefined
|
|
1517
|
+
let updateComputedDevtools: <T>(node: ComputedNode<T>, value: unknown) => void = () => {}
|
|
1397
1518
|
let registerEffectDevtools: (node: EffectNode) => number | undefined = () => undefined
|
|
1398
1519
|
let effectRunDevtools: (node: EffectNode) => void = () => {}
|
|
1520
|
+
let trackDependencyDevtools: (dep: ReactiveNode, sub: ReactiveNode) => void = () => {}
|
|
1521
|
+
let untrackDependencyDevtools: (dep: ReactiveNode, sub: ReactiveNode) => void = () => {}
|
|
1399
1522
|
|
|
1400
1523
|
if (isDev) {
|
|
1401
|
-
|
|
1402
|
-
|
|
1524
|
+
// Unified ID counter for all reactive nodes (signal/computed/effect)
|
|
1525
|
+
// to prevent ID collisions when storing in single devtools maps
|
|
1526
|
+
let nextDevtoolsId = 0
|
|
1403
1527
|
|
|
1404
|
-
registerSignalDevtools =
|
|
1528
|
+
registerSignalDevtools = node => {
|
|
1405
1529
|
const hook = getDevtoolsHook()
|
|
1406
1530
|
if (!hook) return undefined
|
|
1407
|
-
const id = ++
|
|
1408
|
-
|
|
1531
|
+
const id = ++nextDevtoolsId
|
|
1532
|
+
const options: { name?: string; source?: string } = {}
|
|
1533
|
+
if (node.name !== undefined) options.name = node.name
|
|
1534
|
+
if (node.devToolsSource !== undefined) options.source = node.devToolsSource
|
|
1535
|
+
const ownerId = __fictGetCurrentComponentId()
|
|
1536
|
+
if (ownerId !== undefined) (options as any).ownerId = ownerId
|
|
1537
|
+
hook.registerSignal(id, node.currentValue, options)
|
|
1409
1538
|
;(node as SignalNode & DevtoolsIdentifiable).__id = id
|
|
1410
1539
|
return id
|
|
1411
1540
|
}
|
|
@@ -1417,11 +1546,34 @@ if (isDev) {
|
|
|
1417
1546
|
if (id) hook.updateSignal(id, value)
|
|
1418
1547
|
}
|
|
1419
1548
|
|
|
1549
|
+
registerComputedDevtools = node => {
|
|
1550
|
+
const hook = getDevtoolsHook()
|
|
1551
|
+
if (!hook) return undefined
|
|
1552
|
+
const id = ++nextDevtoolsId
|
|
1553
|
+
const options: { name?: string; source?: string } = {}
|
|
1554
|
+
if (node.name !== undefined) options.name = node.name
|
|
1555
|
+
if (node.devToolsSource !== undefined) options.source = node.devToolsSource
|
|
1556
|
+
const ownerId = __fictGetCurrentComponentId()
|
|
1557
|
+
if (ownerId !== undefined) (options as any).ownerId = ownerId
|
|
1558
|
+
;(options as any).hasValue = false
|
|
1559
|
+
hook.registerComputed(id, node.value, options)
|
|
1560
|
+
;(node as ComputedNode & DevtoolsIdentifiable).__id = id
|
|
1561
|
+
return id
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
updateComputedDevtools = (node, value) => {
|
|
1565
|
+
const hook = getDevtoolsHook()
|
|
1566
|
+
if (!hook) return
|
|
1567
|
+
const id = (node as ComputedNode & DevtoolsIdentifiable).__id
|
|
1568
|
+
if (id) hook.updateComputed(id, value)
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1420
1571
|
registerEffectDevtools = node => {
|
|
1421
1572
|
const hook = getDevtoolsHook()
|
|
1422
1573
|
if (!hook) return undefined
|
|
1423
|
-
const id = ++
|
|
1424
|
-
|
|
1574
|
+
const id = ++nextDevtoolsId
|
|
1575
|
+
const ownerId = __fictGetCurrentComponentId()
|
|
1576
|
+
hook.registerEffect(id, ownerId !== undefined ? { ownerId } : undefined)
|
|
1425
1577
|
;(node as EffectNode & DevtoolsIdentifiable).__id = id
|
|
1426
1578
|
return id
|
|
1427
1579
|
}
|
|
@@ -1432,6 +1584,22 @@ if (isDev) {
|
|
|
1432
1584
|
const id = (node as EffectNode & DevtoolsIdentifiable).__id
|
|
1433
1585
|
if (id) hook.effectRun(id)
|
|
1434
1586
|
}
|
|
1587
|
+
|
|
1588
|
+
trackDependencyDevtools = (dep, sub) => {
|
|
1589
|
+
const hook = getDevtoolsHook()
|
|
1590
|
+
if (!hook?.trackDependency) return
|
|
1591
|
+
const depId = (dep as ReactiveNode & DevtoolsIdentifiable).__id
|
|
1592
|
+
const subId = (sub as ReactiveNode & DevtoolsIdentifiable).__id
|
|
1593
|
+
if (depId && subId) hook.trackDependency(subId, depId)
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
untrackDependencyDevtools = (dep, sub) => {
|
|
1597
|
+
const hook = getDevtoolsHook()
|
|
1598
|
+
if (!hook?.untrackDependency) return
|
|
1599
|
+
const depId = (dep as ReactiveNode & DevtoolsIdentifiable).__id
|
|
1600
|
+
const subId = (sub as ReactiveNode & DevtoolsIdentifiable).__id
|
|
1601
|
+
if (depId && subId) hook.untrackDependency(subId, depId)
|
|
1602
|
+
}
|
|
1435
1603
|
}
|
|
1436
1604
|
|
|
1437
1605
|
// ============================================================================
|