@ -4,6 +4,7 @@ import { get_descriptors, get_prototype_of, index_of } from '../shared/utils.js'
import {
import {
destroy _block _effect _children ,
destroy _block _effect _children ,
destroy _effect _children ,
destroy _effect _children ,
effect _tracking ,
execute _effect _teardown
execute _effect _teardown
} from './reactivity/effects.js' ;
} from './reactivity/effects.js' ;
import {
import {
@ -11,13 +12,12 @@ import {
MAYBE _DIRTY ,
MAYBE _DIRTY ,
CLEAN ,
CLEAN ,
DERIVED ,
DERIVED ,
UNOWNED ,
DESTROYED ,
DESTROYED ,
BRANCH _EFFECT ,
BRANCH _EFFECT ,
STATE _SYMBOL ,
STATE _SYMBOL ,
BLOCK _EFFECT ,
BLOCK _EFFECT ,
ROOT _EFFECT ,
ROOT _EFFECT ,
DIS CONNECTED,
CONNECTED,
REACTION _IS _UPDATING ,
REACTION _IS _UPDATING ,
STALE _REACTION ,
STALE _REACTION ,
ERROR _VALUE ,
ERROR _VALUE ,
@ -137,10 +137,6 @@ export function set_update_version(value) {
update _version = value ;
update _version = value ;
}
}
// If we are working with a get() chain that has no active container,
// to prevent memory leaks, we skip adding the reaction.
export let skip _reaction = false ;
export function increment _write _version ( ) {
export function increment _write _version ( ) {
return ++ write _version ;
return ++ write _version ;
}
}
@ -158,55 +154,18 @@ export function is_dirty(reaction) {
return true ;
return true ;
}
}
if ( flags & DERIVED ) {
reaction . f &= ~ WAS _MARKED ;
}
if ( ( flags & MAYBE _DIRTY ) !== 0 ) {
if ( ( flags & MAYBE _DIRTY ) !== 0 ) {
var dependencies = reaction . deps ;
var dependencies = reaction . deps ;
var is _unowned = ( flags & UNOWNED ) !== 0 ;
if ( flags & DERIVED ) {
reaction . f &= ~ WAS _MARKED ;
}
if ( dependencies !== null ) {
if ( dependencies !== null ) {
var i ;
var dependency ;
var is _disconnected = ( flags & DISCONNECTED ) !== 0 ;
var is _unowned _connected = is _unowned && active _effect !== null && ! skip _reaction ;
var length = dependencies . length ;
var length = dependencies . length ;
// If we are working with a disconnected or an unowned signal that is now connected (due to an active effect)
for ( var i = 0 ; i < length ; i ++ ) {
// then we need to re-connect the reaction to the dependency, unless the effect has already been destroyed
var dependency = dependencies [ i ] ;
// (which can happen if the derived is read by an async derived)
if (
( is _disconnected || is _unowned _connected ) &&
( active _effect === null || ( active _effect . f & DESTROYED ) === 0 )
) {
var derived = /** @type {Derived} */ ( reaction ) ;
var parent = derived . parent ;
for ( i = 0 ; i < length ; i ++ ) {
dependency = dependencies [ i ] ;
// We always re-add all reactions (even duplicates) if the derived was
// previously disconnected, however we don't if it was unowned as we
// de-duplicate dependencies in that case
if ( is _disconnected || ! dependency ? . reactions ? . includes ( derived ) ) {
( dependency . reactions ? ? = [ ] ) . push ( derived ) ;
}
}
if ( is _disconnected ) {
derived . f ^= DISCONNECTED ;
}
// If the unowned derived is now fully connected to the graph again (it's unowned and reconnected, has a parent
// and the parent is not unowned), then we can mark it as connected again, removing the need for the unowned
// flag
if ( is _unowned _connected && parent !== null && ( parent . f & UNOWNED ) === 0 ) {
derived . f ^= UNOWNED ;
}
}
for ( i = 0 ; i < length ; i ++ ) {
dependency = dependencies [ i ] ;
if ( is _dirty ( /** @type {Derived} */ ( dependency ) ) ) {
if ( is _dirty ( /** @type {Derived} */ ( dependency ) ) ) {
update _derived ( /** @type {Derived} */ ( dependency ) ) ;
update _derived ( /** @type {Derived} */ ( dependency ) ) ;
@ -218,9 +177,12 @@ export function is_dirty(reaction) {
}
}
}
}
// Unowned signals should never be marked as clean unless they
if (
// are used within an active_effect without skip_reaction
( flags & CONNECTED ) !== 0 &&
if ( ! is _unowned || ( active _effect !== null && ! skip _reaction ) ) {
// During time traveling we don't want to reset the status so that
// traversal of the graph in the other batches still happens
batch _values === null
) {
set _signal _status ( reaction , CLEAN ) ;
set _signal _status ( reaction , CLEAN ) ;
}
}
}
}
@ -263,7 +225,6 @@ export function update_reaction(reaction) {
var previous _skipped _deps = skipped _deps ;
var previous _skipped _deps = skipped _deps ;
var previous _untracked _writes = untracked _writes ;
var previous _untracked _writes = untracked _writes ;
var previous _reaction = active _reaction ;
var previous _reaction = active _reaction ;
var previous _skip _reaction = skip _reaction ;
var previous _sources = current _sources ;
var previous _sources = current _sources ;
var previous _component _context = component _context ;
var previous _component _context = component _context ;
var previous _untracking = untracking ;
var previous _untracking = untracking ;
@ -274,8 +235,6 @@ export function update_reaction(reaction) {
new _deps = /** @type {null | Value[]} */ ( null ) ;
new _deps = /** @type {null | Value[]} */ ( null ) ;
skipped _deps = 0 ;
skipped _deps = 0 ;
untracked _writes = null ;
untracked _writes = null ;
skip _reaction =
( flags & UNOWNED ) !== 0 && ( untracking || ! is _updating _effect || active _reaction === null ) ;
active _reaction = ( flags & ( BRANCH _EFFECT | ROOT _EFFECT ) ) === 0 ? reaction : null ;
active _reaction = ( flags & ( BRANCH _EFFECT | ROOT _EFFECT ) ) === 0 ? reaction : null ;
current _sources = null ;
current _sources = null ;
@ -311,12 +270,7 @@ export function update_reaction(reaction) {
reaction . deps = deps = new _deps ;
reaction . deps = deps = new _deps ;
}
}
if (
if ( is _updating _effect && effect _tracking ( ) && ( reaction . f & CONNECTED ) !== 0 ) {
! skip _reaction ||
// Deriveds that already have reactions can cleanup, so we still add them as reactions
( ( flags & DERIVED ) !== 0 &&
/** @type {import('#client').Derived} */ ( reaction ) . reactions !== null )
) {
for ( i = skipped _deps ; i < deps . length ; i ++ ) {
for ( i = skipped _deps ; i < deps . length ; i ++ ) {
( deps [ i ] . reactions ? ? = [ ] ) . push ( reaction ) ;
( deps [ i ] . reactions ? ? = [ ] ) . push ( reaction ) ;
}
}
@ -373,7 +327,6 @@ export function update_reaction(reaction) {
skipped _deps = previous _skipped _deps ;
skipped _deps = previous _skipped _deps ;
untracked _writes = previous _untracked _writes ;
untracked _writes = previous _untracked _writes ;
active _reaction = previous _reaction ;
active _reaction = previous _reaction ;
skip _reaction = previous _skip _reaction ;
current _sources = previous _sources ;
current _sources = previous _sources ;
set _component _context ( previous _component _context ) ;
set _component _context ( previous _component _context ) ;
untracking = previous _untracking ;
untracking = previous _untracking ;
@ -415,9 +368,10 @@ function remove_reaction(signal, dependency) {
) {
) {
set _signal _status ( dependency , MAYBE _DIRTY ) ;
set _signal _status ( dependency , MAYBE _DIRTY ) ;
// If we are working with a derived that is owned by an effect, then mark it as being
// If we are working with a derived that is owned by an effect, then mark it as being
// disconnected.
// disconnected and remove the mark flag, as it cannot be reliably removed otherwise
if ( ( dependency . f & ( UNOWNED | DISCONNECTED ) ) === 0 ) {
if ( ( dependency . f & CONNECTED ) !== 0 ) {
dependency . f ^= DISCONNECTED ;
dependency . f ^= CONNECTED ;
dependency . f &= ~ WAS _MARKED ;
}
}
// Disconnect any reactions owned by this reaction
// Disconnect any reactions owned by this reaction
destroy _derived _effects ( /** @type {Derived} **/ ( dependency ) ) ;
destroy _derived _effects ( /** @type {Derived} **/ ( dependency ) ) ;
@ -564,10 +518,7 @@ export function get(signal) {
skipped _deps ++ ;
skipped _deps ++ ;
} else if ( new _deps === null ) {
} else if ( new _deps === null ) {
new _deps = [ signal ] ;
new _deps = [ signal ] ;
} else if ( ! skip _reaction || ! new _deps . includes ( signal ) ) {
} else if ( ! new _deps . includes ( signal ) ) {
// Normally we can push duplicated dependencies to `new_deps`, but if we're inside
// an unowned derived because skip_reaction is true, then we need to ensure that
// we don't have duplicates
new _deps . push ( signal ) ;
new _deps . push ( signal ) ;
}
}
}
}
@ -585,20 +536,6 @@ export function get(signal) {
}
}
}
}
}
}
} else if (
is _derived &&
/** @type {Derived} */ ( signal ) . deps === null &&
/** @type {Derived} */ ( signal ) . effects === null
) {
var derived = /** @type {Derived} */ ( signal ) ;
var parent = derived . parent ;
if ( parent !== null && ( parent . f & UNOWNED ) === 0 ) {
// If the derived is owned by another derived then mark it as unowned
// as the derived value might have been referenced in a different context
// since and thus its parent might not be its true owner anymore
derived . f ^= UNOWNED ;
}
}
}
if ( DEV ) {
if ( DEV ) {
@ -657,7 +594,7 @@ export function get(signal) {
}
}
if ( is _derived ) {
if ( is _derived ) {
derived = /** @type {Derived} */ ( signal ) ;
var derived = /** @type {Derived} */ ( signal ) ;
var value = derived . v ;
var value = derived . v ;
@ -684,9 +621,11 @@ export function get(signal) {
if ( is _dirty ( derived ) ) {
if ( is _dirty ( derived ) ) {
update _derived ( derived ) ;
update _derived ( derived ) ;
}
}
}
if ( batch _values ? . has ( signal ) ) {
if ( is _updating _effect && effect _tracking ( ) && ( derived . f & CONNECTED ) === 0 ) {
reconnect ( derived ) ;
}
} else if ( batch _values ? . has ( signal ) ) {
return batch _values . get ( signal ) ;
return batch _values . get ( signal ) ;
}
}
@ -697,6 +636,25 @@ export function get(signal) {
return signal . v ;
return signal . v ;
}
}
/ * *
* ( Re ) connect a disconnected derived , so that it is notified
* of changes in ` mark_reactions `
* @ param { Derived } derived
* /
function reconnect ( derived ) {
if ( derived . deps === null ) return ;
derived . f ^= CONNECTED ;
for ( const dep of derived . deps ) {
( dep . reactions ? ? = [ ] ) . push ( derived ) ;
if ( ( dep . f & DERIVED ) !== 0 && ( dep . f & CONNECTED ) === 0 ) {
reconnect ( /** @type {Derived} */ ( dep ) ) ;
}
}
}
/** @param {Derived} derived */
/** @param {Derived} derived */
function depends _on _old _values ( derived ) {
function depends _on _old _values ( derived ) {
if ( derived . v === UNINITIALIZED ) return true ; // we don't know, so assume the worst
if ( derived . v === UNINITIALIZED ) return true ; // we don't know, so assume the worst