diff --git a/packages/agera/.size-limit.json b/packages/agera/.size-limit.json index d0cedebe..bcb3ca68 100644 --- a/packages/agera/.size-limit.json +++ b/packages/agera/.size-limit.json @@ -3,24 +3,24 @@ "name": "All publics", "path": "dist/index.js", "import": "*", - "limit": "2.49 kB" + "limit": "2.46 kB" }, { "name": "Signal", "path": "dist/index.js", "import": "{ signal }", - "limit": "1.6 kB" + "limit": "1.59 kB" }, { "name": "Minimal set", "path": "dist/index.js", "import": "{ signal, computed, effect }", - "limit": "1.78 kB" + "limit": "1.75 kB" }, { "name": "Popular set", "path": "dist/index.js", "import": "{ signal, computed, effect, mountable, onMounted }", - "limit": "1.91 kB" + "limit": "1.87 kB" } ] diff --git a/packages/agera/history/v3.2.1-c00e639.diff b/packages/agera/history/v3.2.1-c00e639.diff new file mode 100644 index 00000000..7e0dfad9 --- /dev/null +++ b/packages/agera/history/v3.2.1-c00e639.diff @@ -0,0 +1,1038 @@ +--- alien-signals/flags.ts 2026-07-12 03:08:06.882028913 +0400 ++++ src/internals/flags.ts 2026-03-09 02:32:55.214662359 +0400 +@@ -1,9 +1,25 @@ +-export const enum ReactiveFlags { +- None = 0, +- Mutable = 1, +- Watching = 2, +- RecursedCheck = 4, +- Recursed = 8, +- Dirty = 16, +- Pending = 32 +-} ++// Reactive flags ++export const NoneFlag = 0 ++ ++export const MutableFlag = 1 << 0 ++ ++export const WatchingFlag = 1 << 1 ++ ++export const RecursedCheckFlag = 1 << 2 ++ ++export const RecursedFlag = 1 << 3 ++ ++export const DirtyFlag = 1 << 4 ++ ++export const PendingFlag = 1 << 5 ++ ++// Mode flags ++export const ScopeMode = 1 << 0 ++ ++export const LazyMode = 1 << 1 ++ ++export const WritableMode = 1 << 2 ++ ++export const MountableMode = 1 << 3 ++ ++export const ExternalModesBase = 4 +--- alien-signals/system.ts 2026-07-12 03:09:10.093748624 +0400 ++++ src/internals/system.ts 2026-07-12 02:54:50.145551641 +0400 +@@ -1,54 +1,89 @@ ++import { isFunction } from '../utils.js' + import type { + ReactiveNode, + SignalNode, + ComputedNode, + EffectNode, +- EffectScopeNode, + Link, +- Stack ++ Stack, ++ AnySignal, ++ WritableSignal, ++ ReadableSignal, ++ EffectCallback, ++ Destroy, ++ Compute, ++ NewValue, ++ Morph + } from './types.js' +-import { ReactiveFlags } from './flags.js' +- +-// Marks a parent (effect or scope) whose deps include at least one child +-// effect. Used to gate the dispose-children-first slow path in run() so +-// leaf effects (no children, no own cleanup) avoid the extra deps walk. +-// The bit is outside ReactiveFlags' range and never touched by system.ts. +-const HasChildEffect = 64 ++import { ++ NoneFlag, ++ MutableFlag, ++ WatchingFlag, ++ RecursedCheckFlag, ++ RecursedFlag, ++ DirtyFlag, ++ PendingFlag, ++ ScopeMode, ++ WritableMode, ++ MountableMode, ++ LazyMode ++} from './flags.js' ++import { ++ incrementEffectCount, ++ isSubscriber, ++ isActiveSubscriber, ++ decrementEffectCount, ++ notifyMounted, ++ isMountableUsed, ++ pushNoMount, ++ popNoMount, ++ activeNoMount ++} from './lifecycle.js' + + let cycle = 0 +-let runDepth = 0 + let batchDepth = 0 ++let flushDepth = 0 + let notifyIndex = 0 + let queuedLength = 0 + let activeSub: ReactiveNode | undefined + const queued: (EffectNode | undefined)[] = [] + +-function runCleanup(e: EffectNode): void { +- const cleanup = e.cleanup! +- +- e.cleanup = undefined +- +- const prevSub = activeSub +- +- activeSub = undefined ++/** ++ * Run a function without tracking dependencies. ++ * @param fn ++ * @returns The result of the function. ++ */ ++export function untracked(fn: () => T): T { ++ const prevSub = pushActiveSub(undefined) + + try { +- cleanup() ++ return fn() + } finally { +- activeSub = prevSub ++ popActiveSub(prevSub) + } + } + +-function update(node: SignalNode | ComputedNode | EffectScopeNode): boolean { +- if ('getter' in node) { +- return updateComputed(node) ++function destroyEffect(dep: ReactiveNode) { ++ const effect = dep as EffectNode ++ const { destroy } = effect ++ ++ if (destroy !== undefined) { ++ effect.destroy = undefined ++ untracked(destroy) + } ++} + +- if ('currentValue' in node) { +- return updateSignal(node) ++function update(node: ReactiveNode): boolean { ++ if ('compute' in node) { ++ return updateComputed(node as ComputedNode) + } + +- node.flags = ReactiveFlags.Mutable ++ if ('pendingValue' in node) { ++ return updateSignal(node as SignalNode) ++ } ++ ++ // Effect scope node ++ node.flags = MutableFlag + + return true + } +@@ -59,10 +94,10 @@ + + do { + queued[insertIndex++] = effect +- effect.flags &= ~ReactiveFlags.Watching ++ effect.flags &= ~WatchingFlag + effect = effect.subs?.sub as EffectNode + +- if (effect === undefined || !(effect.flags & ReactiveFlags.Watching)) { ++ if (effect === undefined || !(effect.flags & WatchingFlag)) { + break + } + } while (true) +@@ -77,18 +112,13 @@ + } + } + +-function unwatched(node: SignalNode | ComputedNode | EffectNode | EffectScopeNode) { +- if ('getter' in node) { +- if (node.depsTail !== undefined) { +- node.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty +- disposeAllDepsInReverse(node) +- } +- } else if ('currentValue' in node) { +- // Nothing to do for signals, they are always mutable and never dirty until pendingValue changes +- } else if ('fn' in node) { +- effectOper.call(node) +- } else { ++function unwatched(node: ReactiveNode) { ++ if (!(node.flags & MutableFlag)) { + effectScopeOper.call(node) ++ } else if (node.depsTail !== undefined) { ++ node.depsTail = undefined ++ node.flags = MutableFlag | DirtyFlag ++ purgeDeps(node) + } + } + +@@ -141,6 +171,17 @@ + } else { + dep.subs = newLink + } ++ ++ const isDepMountable = isMountableUsed && dep.modes & MountableMode ++ ++ // Mark computed signals as mountable if any of their dependencies are mountable ++ if (isDepMountable && 'compute' in sub) { ++ sub.modes |= MountableMode ++ } ++ ++ if (isDepMountable && isActiveSubscriber(sub) && sub.noMount !== dep) { ++ incrementEffectCount(dep) ++ } + } + + function unlink(link: Link, sub = link.sub): Link | undefined { +@@ -170,16 +211,27 @@ + dep.subsTail = prevSub + } + ++ let unwatch = false ++ + if (prevSub !== undefined) { + prevSub.nextSub = nextSub + } else if ((dep.subs = nextSub) === undefined) { ++ destroyEffect(dep) ++ unwatch = true ++ } ++ ++ if (isMountableUsed && dep.modes & MountableMode && isSubscriber(sub) && sub.noMount !== dep) { ++ decrementEffectCount(dep, unwatch) ++ } ++ ++ if (unwatch) { + unwatched(dep) + } + + return nextDep + } + +-function propagate(link: Link, innerWrite: boolean): void { ++function propagate(link: Link): void { + let next = link.nextSub + let stack: Stack | undefined + +@@ -187,28 +239,25 @@ + const sub = link.sub + let flags = sub.flags + +- if (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed | ReactiveFlags.Dirty | ReactiveFlags.Pending))) { +- sub.flags = flags | ReactiveFlags.Pending +- +- if (innerWrite) { +- sub.flags |= ReactiveFlags.Recursed +- } +- } else if (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed))) { +- flags = ReactiveFlags.None +- } else if (!(flags & ReactiveFlags.RecursedCheck)) { +- sub.flags = (flags & ~ReactiveFlags.Recursed) | ReactiveFlags.Pending +- } else if (!(flags & (ReactiveFlags.Dirty | ReactiveFlags.Pending)) && isValidLink(link, sub)) { +- sub.flags = flags | (ReactiveFlags.Recursed | ReactiveFlags.Pending) +- flags &= ReactiveFlags.Mutable ++ if (!(flags & (RecursedCheckFlag | RecursedFlag | DirtyFlag | PendingFlag))) { ++ sub.flags = flags | PendingFlag ++ } else if (!(flags & (RecursedCheckFlag | RecursedFlag))) { ++ // flags = NoneFlag ++ flags &= MutableFlag ++ } else if (!(flags & RecursedCheckFlag)) { ++ sub.flags = (flags & ~RecursedFlag) | PendingFlag ++ } else if (!(flags & (DirtyFlag | PendingFlag)) && isValidLink(link, sub)) { ++ sub.flags = flags | (RecursedFlag | PendingFlag) ++ flags &= MutableFlag + } else { +- flags = ReactiveFlags.None ++ flags = NoneFlag + } + +- if (flags & ReactiveFlags.Watching) { ++ if (flags & WatchingFlag) { + notify(sub as EffectNode) + } + +- if (flags & ReactiveFlags.Mutable) { ++ if (flags & MutableFlag) { + const subSubs = sub.subs + + if (subSubs !== undefined) { +@@ -254,9 +303,9 @@ + const dep = link.dep + const flags = dep.flags + +- if (sub.flags & ReactiveFlags.Dirty) { ++ if (sub.flags & DirtyFlag) { + dirty = true +- } else if ((flags & (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) === (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) { ++ } else if ((flags & (MutableFlag | DirtyFlag)) === (MutableFlag | DirtyFlag)) { + const subs = dep.subs! + + if (update(dep)) { +@@ -266,7 +315,7 @@ + + dirty = true + } +- } else if ((flags & (ReactiveFlags.Mutable | ReactiveFlags.Pending)) === (ReactiveFlags.Mutable | ReactiveFlags.Pending)) { ++ } else if ((flags & (MutableFlag | PendingFlag)) === (MutableFlag | PendingFlag)) { + stack = { + value: link, + prev: stack +@@ -305,7 +354,7 @@ + + dirty = false + } else { +- sub.flags &= ~ReactiveFlags.Pending ++ sub.flags &= ~PendingFlag + } + + sub = link.sub +@@ -318,7 +367,7 @@ + } + } + +- return dirty && !!sub.flags ++ return dirty && sub.flags !== NoneFlag + } while (true) + } + +@@ -327,10 +376,10 @@ + const sub = link.sub + const flags = sub.flags + +- if ((flags & (ReactiveFlags.Pending | ReactiveFlags.Dirty)) === ReactiveFlags.Pending) { +- sub.flags = flags | ReactiveFlags.Dirty ++ if ((flags & (PendingFlag | DirtyFlag)) === PendingFlag) { ++ sub.flags = flags | DirtyFlag + +- if ((flags & (ReactiveFlags.Watching | ReactiveFlags.RecursedCheck)) === ReactiveFlags.Watching) { ++ if ((flags & (WatchingFlag | RecursedCheckFlag)) === WatchingFlag) { + notify(sub as EffectNode) + } + } +@@ -351,149 +400,214 @@ + return false + } + +-export function getActiveSub(): ReactiveNode | undefined { +- return activeSub +-} +- +-export function setActiveSub(sub?: ReactiveNode) { ++export function pushActiveSub(sub?: ReactiveNode) { + const prevSub = activeSub + + activeSub = sub + return prevSub + } + +-export function getBatchDepth(): number { +- return batchDepth ++export function popActiveSub(prevSub?: ReactiveNode) { ++ activeSub = prevSub + } + +-export function startBatch() { ++export function batch(fn: () => T): T { + ++batchDepth +-} + +-export function endBatch() { +- if (!--batchDepth) { +- flush() ++ try { ++ return fn() ++ } finally { ++ if (!--batchDepth) { ++ flush() ++ } + } + } + +-export function isSignal(fn: () => void): boolean { +- return fn.name === `bound ${signalOper.name}` +-} +- +-export function isComputed(fn: () => void): boolean { +- return fn.name === `bound ${computedOper.name}` +-} +- +-export function isEffect(fn: () => void): boolean { +- return fn.name === `bound ${effectOper.name}` +-} ++let signalCallback: (($signal: AnySignal) => void) | undefined + +-export function isEffectScope(fn: () => void): boolean { +- return fn.name === `bound ${effectScopeOper.name}` +-} ++export function onSignal(callback: ($signal: AnySignal) => void) { ++ const prevCallback = signalCallback + +-export function signal(): { +- (): T | undefined +- (value: T | undefined): void +-} +-export function signal(initialValue: T): { +- (): T +- (value: T): void +-} +- +-export function signal(initialValue?: T): { +- (): T | undefined +- (value: T | undefined): void +-} { +- return signalOper.bind({ +- currentValue: initialValue, +- pendingValue: initialValue, ++ signalCallback = prevCallback ++ ? $signal => (prevCallback($signal), callback($signal)) ++ : callback ++} ++ ++export function createSignal( ++ constructor: (value?: unknown) => unknown, ++ node: ComputedNode | SignalNode, ++ ctx: ComputedNode | SignalNode | Morph = node ++) { ++ const $signal = constructor.bind(ctx) as AnySignal ++ ++ $signal.node = node ++ ++ signalCallback?.($signal) ++ ++ return $signal ++} ++ ++/** ++ * Create a signal with atomic value. ++ * @returns A signal. ++ */ ++export function signal(): WritableSignal ++/** ++ * Create a signal with initial atomic value ++ * @param value - Initial value of the signal. ++ * @returns A signal. ++ */ ++export function signal(value: T): WritableSignal ++ ++/* @__NO_SIDE_EFFECTS__ */ ++export function signal(value?: T): WritableSignal { ++ return createSignal(signalOper, { ++ value, ++ pendingValue: value, + subs: undefined, + subsTail: undefined, +- flags: ReactiveFlags.Mutable +- }) as () => T | undefined ++ flags: MutableFlag, ++ modes: WritableMode, ++ subsCount: 0 ++ }) as WritableSignal + } + +-export function computed(getter: (previousValue?: T) => T): () => T { +- return computedOper.bind({ ++/** ++ * Create a signal that reactivly computes its value from other signals. ++ * @param compute - The function to compute the value. ++ * @returns A signal. ++ */ ++/* @__NO_SIDE_EFFECTS__ */ ++export function computed(compute: Compute): ReadableSignal { ++ return createSignal(computedOper, { + value: undefined, + subs: undefined, + subsTail: undefined, + deps: undefined, + depsTail: undefined, +- flags: ReactiveFlags.None, +- getter: getter as (previousValue?: unknown) => unknown +- }) as () => T ++ flags: NoneFlag, ++ modes: NoneFlag, ++ compute, ++ subsCount: 0 ++ }) as ReadableSignal + } + +-export function effect(fn: () => void | (() => void)): () => void { ++/** ++ * Run effect function and re-run it on dependency change. ++ * @param fn - The effect function to run. ++ * @param noDefer - Ignore effect deferring. ++ * @returns A function to stop the effect. ++ */ ++export function effect(fn: EffectCallback, noDefer = false): Destroy { + const e: EffectNode = { + fn, +- cleanup: undefined, ++ destroy: undefined, + subs: undefined, + subsTail: undefined, + deps: undefined, + depsTail: undefined, +- flags: ReactiveFlags.Watching | ReactiveFlags.RecursedCheck ++ flags: WatchingFlag | RecursedCheckFlag, ++ modes: NoneFlag + } +- const prevSub = setActiveSub(e) + +- if (prevSub !== undefined) { +- link(e, prevSub, 0) +- prevSub.flags |= HasChildEffect ++ if (isMountableUsed && activeNoMount !== undefined) { ++ e.noMount = activeNoMount + } + +- try { +- ++runDepth +- e.cleanup = e.fn() +- } finally { +- --runDepth +- activeSub = prevSub +- e.flags &= ~ReactiveFlags.RecursedCheck ++ if (activeSub !== undefined) { ++ link(e, activeSub, 0) ++ ++ if (!noDefer && activeSub.modes & LazyMode) { ++ e.modes |= LazyMode ++ return effectOper.bind(e) ++ } + } + ++ runEffect(e, true) ++ + return effectOper.bind(e) + } + +-export function effectScope(fn: () => void): () => void { +- const e: EffectScopeNode = { ++/** ++ * Run effect scope function to group effects. ++ * @param fn - The effect scope function to run. ++ * @returns A function to stop child effects. ++ */ ++export function effectScope(fn: () => void): Destroy { ++ const e: ReactiveNode = { + deps: undefined, + depsTail: undefined, + subs: undefined, + subsTail: undefined, +- flags: ReactiveFlags.Mutable ++ flags: MutableFlag, ++ modes: ScopeMode + } +- const prevSub = setActiveSub(e) ++ const prevSub = pushActiveSub(e) + + if (prevSub !== undefined) { + link(e, prevSub, 0) +- prevSub.flags |= HasChildEffect ++ ++ if (prevSub.modes & LazyMode) { ++ e.modes |= LazyMode ++ } + } + + try { + fn() + } finally { +- activeSub = prevSub ++ popActiveSub(prevSub) + } + + return effectScopeOper.bind(e) + } + ++/** ++ * Defer scope creation to delay its effects execution. ++ * @param fn - The deferred scope function to run. ++ * @returns A function to start effects. ++ */ ++export function deferScope(fn: () => void): () => Destroy { ++ const e: ReactiveNode = { ++ deps: undefined, ++ depsTail: undefined, ++ subs: undefined, ++ subsTail: undefined, ++ flags: MutableFlag, ++ modes: ScopeMode | LazyMode ++ } ++ const prevSub = pushActiveSub(e) ++ ++ try { ++ fn() ++ } finally { ++ popActiveSub(prevSub) ++ } ++ ++ return deferScopeOper.bind(e) ++} ++ ++/** ++ * Manually trigger signals update propagation. ++ * @param fn - Function with signal reads to trigger. ++ */ + export function trigger(fn: () => void) { + const sub: ReactiveNode = { + deps: undefined, + depsTail: undefined, +- flags: ReactiveFlags.Watching | ReactiveFlags.RecursedCheck ++ subs: undefined, ++ subsTail: undefined, ++ flags: WatchingFlag | RecursedCheckFlag, ++ modes: NoneFlag + } +- const prevSub = setActiveSub(sub) ++ const prevSub = pushActiveSub(sub) + + ++batchDepth + + try { + fn() + } finally { +- activeSub = prevSub +- sub.flags = ReactiveFlags.None ++ popActiveSub(prevSub) ++ sub.flags = NoneFlag + + let link = sub.deps + +@@ -505,7 +619,7 @@ + const subs = dep.subs + + if (subs !== undefined) { +- propagate(subs, !!runDepth) ++ propagate(subs) + shallowPropagate(subs) + } + } +@@ -517,98 +631,80 @@ + } + + function updateComputed(c: ComputedNode): boolean { +- if (c.flags & HasChildEffect) { +- let link = c.depsTail +- +- while (link !== undefined) { +- const prev = link.prevDep +- const dep = link.dep +- +- if (!('getter' in dep) && !('currentValue' in dep)) { +- unlink(link, c) +- } +- +- link = prev +- } +- } +- ++ ++cycle + c.depsTail = undefined +- c.flags = ReactiveFlags.Mutable | ReactiveFlags.RecursedCheck ++ c.flags = MutableFlag | RecursedCheckFlag + +- const prevSub = setActiveSub(c) ++ const prevSub = pushActiveSub(c) + + try { +- ++cycle +- + const oldValue = c.value + +- return oldValue !== (c.value = c.getter(oldValue)) ++ return oldValue !== (c.value = c.compute(oldValue)) + } finally { +- activeSub = prevSub +- c.flags &= ~ReactiveFlags.RecursedCheck ++ popActiveSub(prevSub) ++ c.flags &= ~RecursedCheckFlag + purgeDeps(c) ++ notifyMounted(activeSub) + } + } + + function updateSignal(s: SignalNode): boolean { +- s.flags = ReactiveFlags.Mutable +- return s.currentValue !== (s.currentValue = s.pendingValue) ++ s.flags = MutableFlag ++ return s.value !== (s.value = s.pendingValue) ++} ++ ++function runEffect(e: EffectNode, warmup?: true): void { ++ const prevSub = pushActiveSub(e) ++ const prevNoMount = isMountableUsed ? pushNoMount(e.noMount) : undefined ++ ++ try { ++ e.destroy = e.fn(warmup) || undefined ++ } finally { ++ popNoMount(prevNoMount) ++ popActiveSub(prevSub) ++ e.flags &= ~RecursedCheckFlag ++ ++ if (warmup === undefined) { ++ purgeDeps(e) ++ } ++ ++ notifyMounted(activeSub) ++ } + } + + function run(e: EffectNode): void { + const flags = e.flags + + if ( +- flags & ReactiveFlags.Dirty ++ flags & DirtyFlag + || ( +- flags & ReactiveFlags.Pending ++ flags & PendingFlag + && checkDirty(e.deps!, e) + ) + ) { +- if (flags & HasChildEffect) { +- let link = e.depsTail +- +- while (link !== undefined) { +- const prev = link.prevDep +- const dep = link.dep +- +- if (!('getter' in dep) && !('currentValue' in dep)) { +- unlink(link, e) +- } +- +- link = prev +- } +- } ++ if (e.destroy !== undefined) { ++ destroyEffect(e) + +- if (e.cleanup) { +- runCleanup(e) +- +- if (!e.flags) { ++ // Destroy function disposed the effect, do not re-run it ++ if (e.flags === NoneFlag) { + return + } + } + ++ ++cycle + e.depsTail = undefined +- e.flags = ReactiveFlags.Watching | ReactiveFlags.RecursedCheck ++ e.flags = WatchingFlag | RecursedCheckFlag + +- const prevSub = setActiveSub(e) +- +- try { +- ++cycle +- ++runDepth +- e.cleanup = e.fn() +- } finally { +- --runDepth +- activeSub = prevSub +- e.flags &= ~ReactiveFlags.RecursedCheck +- purgeDeps(e) +- } ++ runEffect(e) + } else if (e.deps !== undefined) { +- e.flags = ReactiveFlags.Watching | (flags & HasChildEffect) ++ e.flags = WatchingFlag + } + } + + function flush(): void { ++ ++flushDepth ++ + try { + while (notifyIndex < queuedLength) { + const effect = queued[notifyIndex]! +@@ -621,11 +717,12 @@ + const effect = queued[notifyIndex]! + + queued[notifyIndex++] = undefined +- effect.flags |= ReactiveFlags.Watching | ReactiveFlags.Recursed ++ effect.flags |= WatchingFlag | RecursedFlag + } + + notifyIndex = 0 + queuedLength = 0 ++ --flushDepth + } + } + +@@ -633,12 +730,12 @@ + const flags = this.flags + + if ( +- flags & ReactiveFlags.Dirty ++ flags & DirtyFlag + || ( +- flags & ReactiveFlags.Pending ++ flags & PendingFlag + && ( + checkDirty(this.deps!, this) +- || (this.flags = flags & ~ReactiveFlags.Pending, false) ++ || (this.flags = flags & ~PendingFlag, false) + ) + ) + ) { +@@ -650,15 +747,15 @@ + } + } + } else if (!flags) { +- this.flags = ReactiveFlags.Mutable | ReactiveFlags.RecursedCheck ++ this.flags = MutableFlag | RecursedCheckFlag + +- const prevSub = setActiveSub(this) ++ const prevSub = pushActiveSub(this) + + try { +- this.value = this.getter() ++ this.value = this.compute() + } finally { +- activeSub = prevSub +- this.flags &= ~ReactiveFlags.RecursedCheck ++ popActiveSub(prevSub) ++ this.flags &= ~RecursedCheckFlag + } + } + +@@ -671,23 +768,33 @@ + return this.value! + } + +-function signalOper(this: SignalNode, ...value: [T]): T | void { ++export function nextValue(prevValue: T, nextValue: NewValue): T { ++ return isFunction(nextValue) ? nextValue(prevValue) : nextValue ++} ++ ++export function signalNextValue($signal: WritableSignal, newValue: NewValue): T { ++ return nextValue($signal.node.pendingValue, newValue) ++} ++ ++function signalOper(this: SignalNode, ...value: [NewValue]): T | void { + if (value.length) { +- if (this.pendingValue !== (this.pendingValue = value[0])) { +- this.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty ++ const prevValue = this.pendingValue ++ ++ if (prevValue !== (this.pendingValue = nextValue(prevValue, value[0]))) { ++ this.flags = MutableFlag | DirtyFlag + + const subs = this.subs + + if (subs !== undefined) { +- propagate(subs, !!runDepth) ++ propagate(subs) + +- if (!batchDepth) { ++ if (!batchDepth && !flushDepth) { + flush() + } + } + } + } else { +- if (this.flags & ReactiveFlags.Dirty) { ++ if (this.flags & DirtyFlag) { + if (updateSignal(this)) { + const subs = this.subs + +@@ -703,21 +810,19 @@ + link(this, sub, cycle) + } + +- return this.currentValue ++ return this.value + } + } + + function effectOper(this: EffectNode): void { ++ destroyEffect(this) + effectScopeOper.call(this) +- +- if (this.cleanup) { +- runCleanup(this) +- } + } + +-function effectScopeOper(this: EffectScopeNode): void { +- this.flags = ReactiveFlags.None +- disposeAllDepsInReverse(this) ++function effectScopeOper(this: ReactiveNode): void { ++ this.depsTail = undefined ++ this.flags = NoneFlag ++ purgeDeps(this) + + const sub = this.subs + +@@ -726,15 +831,37 @@ + } + } + +-function disposeAllDepsInReverse(sub: ReactiveNode): void { +- let link = sub.depsTail ++function runDeferredEffects(link: Link): void { ++ do { ++ const dep = link.dep ++ const nextDep = link.nextDep + +- while (link !== undefined) { +- const prev = link.prevDep ++ if (dep.modes & LazyMode) { ++ dep.modes &= ~LazyMode ++ ++ if (dep.modes & ScopeMode) { ++ if (dep.deps !== undefined) { ++ runDeferredEffects(dep.deps) ++ } ++ } else { ++ runEffect(dep as EffectNode, true) ++ } ++ } ++ ++ link = nextDep! ++ } while (link !== undefined) ++} + +- unlink(link, sub) +- link = prev ++function deferScopeOper(this: ReactiveNode): Destroy { ++ this.modes &= ~LazyMode ++ ++ notifyMounted(activeSub) ++ ++ if (this.deps !== undefined) { ++ runDeferredEffects(this.deps) + } ++ ++ return effectScopeOper.bind(this) + } + + function purgeDeps(sub: ReactiveNode) { +--- alien-signals/types.ts 2026-07-12 03:08:10.442013129 +0400 ++++ src/internals/types.ts 2026-07-11 19:44:23.606753834 +0400 +@@ -1,11 +1,73 @@ +-import type { ReactiveFlags } from './flags.js' ++export type AnyFn = (...args: any) => any ++ ++export type DefineVirtualFlags = { ++ [K in F as `~${K}`]?: V ++} ++ ++export type EnableVirtualFlags = Omit & DefineVirtualFlags ++ ++export type Destroy = () => void ++ ++export type MaybeDestroy = Destroy | void ++ ++export type EffectCallback = (warmup?: true) => MaybeDestroy ++ ++export type ObserverCallback = (value: T) => void ++ ++export type Compute = (prevValue?: T) => T ++ ++export type Accessor = () => T ++ ++export type NewValue = T | ((prevValue: T) => T) ++ ++export interface ReadableNode extends ReactiveNode, DefineVirtualFlags<'writable' | 'mountable'> { ++ subsCount: number ++ mounted?: WritableSignal ++} ++ ++export interface WritableNode extends EnableVirtualFlags {} ++ ++export interface ReadableSignal extends Accessor { ++ node: ReadableNode ++} ++ ++export interface Morph { ++ source: WritableSignal ++ get(): T ++ set(value: NewValue): void ++} ++ ++export interface WritableSignal extends ReadableSignal { ++ node: SignalNode ++ (value: NewValue): void ++} ++ ++export type AnyAccessor = Accessor ++ ++export type AnyReadableSignal = ReadableSignal ++ ++export type AnyWritableSignal = WritableSignal ++ ++export type AnySignal = AnyReadableSignal | AnyWritableSignal ++ ++export type AnyAccessorOrSignal = AnyAccessor | AnySignal ++ ++export type AccessorValue = T extends Accessor ? U : never ++ ++export type MaybeAccessorValue = T extends Accessor ? U : T ++ ++export type Mountable = S & { ++ node: EnableVirtualFlags ++} + + export interface ReactiveNode { + deps?: Link + depsTail?: Link + subs?: Link + subsTail?: Link +- flags: ReactiveFlags ++ flags: number ++ modes: number ++ noMount?: ReactiveNode + } + + export interface Link { +@@ -23,19 +85,17 @@ + prev: Stack | undefined + } + +-export interface EffectScopeNode extends ReactiveNode {} +- + export interface EffectNode extends ReactiveNode { +- fn(): (() => void) | void +- cleanup: (() => void) | void ++ fn: EffectCallback ++ destroy: MaybeDestroy + } + +-export interface ComputedNode extends ReactiveNode { ++export interface ComputedNode extends ReadableNode { + value: T | undefined +- getter(previousValue?: T): T ++ compute: Compute + } + +-export interface SignalNode extends ReactiveNode { +- currentValue: T ++export interface SignalNode extends WritableNode { ++ value: T + pendingValue: T + } diff --git a/packages/agera/history/v3.2.1-c00e639.md b/packages/agera/history/v3.2.1-c00e639.md new file mode 100644 index 00000000..7f2b8a0f --- /dev/null +++ b/packages/agera/history/v3.2.1-c00e639.md @@ -0,0 +1,59 @@ +# Sync with alien-signals v3.2.1 (`c00e639`) + +- Date: 2026-07-12 +- Previous sync point: `836130c` (v3.1.2), see `v3.1.2-836130c.diff` +- New sync point: `c00e639` (v3.2.1 + 2 commits) +- Snapshot: `v3.2.1-c00e639.diff` — unified diff between upstream sources at `c00e639` + (flattened into agera's file layout `flags.ts` / `types.ts` / `system.ts` and reformatted + to agera code style, so only real differences show up) and `src/internals` at the sync commit. + +## Ported + +| Upstream | Description | agera commit | +| --- | --- | --- | +| `fb17aed` | refactor: hoist trigger sub flag reset out of propagate loop | `refactor(agera): hoist trigger sub flag reset out of propagate loop` | +| `ad52894` + `b83cf94` + `fbabdbd` | fix: make checkDirty resilient to graph mutations during update (#109, #110, #115) | `fix(agera): make checkDirty resilient to graph mutations during update` | +| `c28a596` | fix: make effectScope participate in propagation (#105, #111) | `fix(agera): make effectScope participate in propagation` | +| `cf75b33` + `4b15362` | feat: support effect cleanup functions (#113) — agera already had destroy functions; ported the missing semantics: dispose-from-destroy prevents re-run, destroy re-entrance safety. Running destroy outside tracking context was already covered by `untracked` | `fix(agera): prevent effect re-run and recursion when destroy function disposes it` | +| `9daa5c3` + `c00e639` | fix: writing a signal inside trigger after reading it crashes flush (#122) | `fix(agera): prevent flush crash when writing a signal inside trigger after reading it` | + +Notes: + +- `ad52894`'s `run()` guard was ported directly in its final `e.deps !== undefined` form from + `fbabdbd`, skipping the broken intermediate `e.flags` version that caused upstream issue #115. + +## Skipped deliberately + +### `7e53655` + `d4d0449` — runDepth / innerWrite propagation (#112) + +- The #99 bug class (inner write blocking future propagation through computed chain) does not + reproduce in agera: the `flushDepth` mechanism (nanoviews commit + `fix(agera): fix subscribers notification when computed updates another signal`) defers + nested flushes instead, which covers it. Regression tests added + (`test(agera): cover consecutive inner resets through computed chain`). +- `flushDepth` also fixes lost notifications when a computed updates another signal — upstream + still fails that scenario at HEAD (verified against `c00e639`). +- Porting runDepth/innerWrite on top of `flushDepth` was prototyped: zero observable effect on + the entire test suite, so it would only add dead complexity and bytes. +- Accepted divergence: with a two-level computed chain plus a sibling effect, an inner reset + re-runs the resetting effect more times than upstream (7 vs 4 runs over three writes). State + converges; the extra runs are the cost of the deferred-flush strategy. +- Shared limitation (both agera and upstream HEAD): a reset performed during the effect's + initial run leaves propagation stuck for subsequent writes. + +### `713a1c6` — nested-effect cleanup order and LIFO disposal (#116) + +- Upstream redefined cleanup ordering: children cleanups first (LIFO, depth-first reverse), + own cleanup last; child effects are also disposed before a re-run / computed re-eval. +- agera keeps its established contract: own destroy first, children in creation order (FIFO). + This contract is encoded in agera tests (clinchy effects, effectScope destroy order, + deferScope) and nanoviews rendering relies on it. +- Accepted divergence (hazard to revisit): on parent re-run or computed re-eval, the previous + inner effect's destroy runs after the new inner effect has been set up (overlap window). If + a destroy releases a keyed resource, it can tear down what the new run just set up. + +## Not applicable + +- `eb08702`, `a68f870`, `0775f82` — reactive-framework-test-suite integration (upstream test + infrastructure; could be adopted separately for conformance testing). +- Docs, README, version bump and chore commits. diff --git a/packages/agera/src/effect.spec.ts b/packages/agera/src/effect.spec.ts index b7316c49..a9b83b2d 100644 --- a/packages/agera/src/effect.spec.ts +++ b/packages/agera/src/effect.spec.ts @@ -11,6 +11,7 @@ import { batch, untracked, signal, + trigger, mountable, onMounted, deferScope, @@ -336,6 +337,53 @@ describe('agera', () => { expect(yCalled).toBe(2) }) + it('should handle consecutive inner resets through computed chain', () => { + const s = signal(0) + const c = computed(() => s()) + let runs = 0 + + effect(() => { + runs++ + + if (c() > 0) { + s(0) + } + }) + + expect(runs).toBe(1) + s(1) + expect(s()).toBe(0) + expect(runs).toBe(2) + s(2) + expect(s()).toBe(0) + expect(runs).toBe(3) + s(3) + expect(s()).toBe(0) + expect(runs).toBe(4) + }) + + it('should handle consecutive inner resets inside trigger', () => { + const s = signal(0) + const c = computed(() => s()) + let runs = 0 + + effect(() => { + runs++ + + if (c() > 0) { + trigger(() => { + s(0) + }) + } + }) + + expect(runs).toBe(1) + s(1) + expect(s()).toBe(0) + s(2) + expect(s()).toBe(0) + }) + it('should handle effect recursion for the first execution', () => { const src1 = signal(0) const src2 = signal(0) @@ -454,6 +502,60 @@ describe('agera', () => { ]) }) + it('should not recurse infinitely when destroy calls own stop', () => { + const s = signal(0) + let runs = 0 + // oxlint-disable-next-line eslint/prefer-const + let stop!: () => void + + stop = effect(() => { + runs++ + s() + + return () => { + stop() + } + }) + + expect(runs).toBe(1) + + s(1) + + expect(runs).toBe(1) + + s(2) + + expect(runs).toBe(1) + }) + + it('should not re-run effect disposed by own destroy function', () => { + const s = signal(0) + let runs = 0 + // oxlint-disable-next-line eslint/prefer-const + let stop!: () => void + + stop = effect(() => { + runs++ + s() + + return () => { + if (s() > 0) { + stop() + } + } + }) + + expect(runs).toBe(1) + + s(1) + + expect(runs).toBe(1) + + s(2) + + expect(runs).toBe(1) + }) + it('should handle clinchy effects', () => { const $yepnope = signal(true) const logs: string[] = [] @@ -732,6 +834,217 @@ describe('agera', () => { stop1() stop2() }) + + it('should not crash when effect disposes itself during computed update', () => { + const s = signal(false) + // oxlint-disable-next-line eslint/prefer-const + let dispose!: () => void + const a = computed(() => { + if (s()) { + dispose() + } + + return 0 + }) + const b = computed(() => a()) + + dispose = effect(() => { + b() + }) + + s(true) + }) + + it('should not crash when effect scope is disposed during computed update', () => { + const s = signal(false) + // oxlint-disable-next-line eslint/prefer-const + let disposeScope!: () => void + const a = computed(() => { + if (s()) { + disposeScope() + } + + return 0 + }) + const b = computed(() => a()) + + disposeScope = effectScope(() => { + effect(() => { + b() + }) + }) + + s(true) + }) + + it('should not re-run effect disposed during computed update', () => { + const s = signal(0) + // oxlint-disable-next-line eslint/prefer-const + let dispose1!: () => void + let e1runs = 0 + const a = computed(() => { + if (s() === 1) { + dispose1() + } + + return s() + }) + + dispose1 = effect(() => { + a() + e1runs++ + }) + // second subscriber keeps `a` alive + effect(() => { + a() + }) + + expect(e1runs).toBe(1) + s(1) + expect(e1runs).toBe(1) + }) + + it('should not re-notify disposed effect on later updates', () => { + const s = signal(0) + // oxlint-disable-next-line eslint/prefer-const + let dispose1!: () => void + let e1runs = 0 + const a = computed(() => { + if (s() === 1) { + dispose1() + } + + return s() + }) + + dispose1 = effect(() => { + a() + e1runs++ + }) + effect(() => { + a() + }) + + expect(e1runs).toBe(1) + s(1) + expect(e1runs).toBe(1) + s(2) + s(3) + s(4) + expect(e1runs).toBe(1) + }) + + it('should propagate to remaining subscribers after sibling effect disposal', () => { + const s = signal(0) + // oxlint-disable-next-line eslint/prefer-const + let dispose1!: () => void + let e2Value = -1 + const a = computed(() => { + if (s() === 1) { + dispose1() + } + + return s() + }) + + dispose1 = effect(() => { + a() + }) + effect(() => { + e2Value = a() + }) + + expect(e2Value).toBe(0) + s(1) + expect(e2Value).toBe(1) + }) + + it('should propagate to all remaining subscribers after sibling effect disposal', () => { + const s = signal(0) + // oxlint-disable-next-line eslint/prefer-const + let dispose1!: () => void + let e2Value = -1 + let e3Value = -1 + const a = computed(() => { + if (s() === 1) { + dispose1() + } + + return s() + }) + + dispose1 = effect(() => { + a() + }) + effect(() => { + e2Value = a() + }) + effect(() => { + e3Value = a() + }) + + expect(e2Value).toBe(0) + expect(e3Value).toBe(0) + s(1) + expect(e2Value).toBe(1) + expect(e3Value).toBe(1) + }) + + it('should finish effect body after self-dispose', () => { + const s = signal(0) + // oxlint-disable-next-line eslint/prefer-const + let dispose!: () => void + const stages: string[] = [] + + dispose = effect(() => { + stages.push('start') + s() + + if (s() === 1) { + dispose() + stages.push('after-dispose') + } + + stages.push('end') + }) + + expect(stages).toEqual(['start', 'end']) + + s(1) + + expect(stages).toEqual([ + 'start', + 'end', + 'start', + 'after-dispose', + 'end' + ]) + }) + + it('should keep outer effect responding to its own dep after inner re-runs', () => { + const a = signal(0) + const b = signal(0) + let outerRuns = 0 + let innerRuns = 0 + + effect(() => { + a() + outerRuns++ + effect(() => { + b() + innerRuns++ + }) + }) + expect(outerRuns).toBe(1) + expect(innerRuns).toBe(1) + + b(1) + expect(outerRuns).toBe(1) + expect(innerRuns).toBeGreaterThanOrEqual(2) + + a(1) + expect(outerRuns).toBe(2) + }) }) describe('effectScope', () => { @@ -928,6 +1241,119 @@ describe('agera', () => { expect(logs).toEqual(['value destroy']) }) + it('should link signal and computed to the same node', () => { + const a = signal(0) + const b = computed(() => 0) + let triggers = 0 + + effect(() => { + triggers += 1 + effectScope(() => { + effectScope(() => { + a() + b() + }) + }) + }) + + expect(triggers).toBe(1) + a(a() + 1) + expect(triggers).toBe(2) + trigger(b) + expect(triggers).toBe(3) + }) + + it('should propagate both signal and computed changes to outer effect', () => { + const s = signal(0) + const c = computed(() => s() * 2) + let triggers = 0 + + effect(() => { + triggers += 1 + effectScope(() => { + s() + c() + }) + }) + + expect(triggers).toBe(1) + + s(1) + expect(triggers).toBe(2) + + trigger(c) + expect(triggers).toBe(3) + }) + + it('should respond to consecutive signal updates', () => { + const s = signal(0) + let triggers = 0 + + effect(() => { + triggers += 1 + effectScope(() => { + s() + }) + }) + + expect(triggers).toBe(1) + s(1) + expect(triggers).toBe(2) + s(2) + expect(triggers).toBe(3) + }) + + it('should cache computed in standalone scope', () => { + const s = signal(0) + let computeCount = 0 + const dispose = effectScope(() => { + const c = computed(() => { + computeCount++ + return s() + }) + + expect(c()).toBe(0) + expect(c()).toBe(0) + }) + + expect(computeCount).toBe(1) + + dispose() + }) + + it('should not desync mountable subs count on direct signal read in scope', () => { + const $a = mountable(signal(1)) + const log: boolean[] = [] + + onMounted($a, (mounted) => { + log.push(mounted) + }) + + const stopOuter = effect(() => { + $a() + }) + const stop = effectScope(() => { + $a() + + effect(() => { + $a() + }) + }) + + expect($a.node.subsCount).toBe(2) + expect(log).toEqual([true]) + + stop() + + expect($a.node.subsCount).toBe(1) + expect(log).toEqual([true]) + + stopOuter() + + expect($a.node.subsCount).toBe(0) + expect(log).toEqual([true, false]) + }) + it('should decrement effect count on stop', () => { const $a = mountable(signal(1)) const $b = mountable(signal(1)) diff --git a/packages/agera/src/internals/system.ts b/packages/agera/src/internals/system.ts index 6c66443a..0bc1b1af 100644 --- a/packages/agera/src/internals/system.ts +++ b/packages/agera/src/internals/system.ts @@ -65,19 +65,27 @@ export function untracked(fn: () => T): T { function destroyEffect(dep: ReactiveNode) { const effect = dep as EffectNode + const { destroy } = effect - if (effect.destroy !== undefined) { - untracked(effect.destroy) + if (destroy !== undefined) { effect.destroy = undefined + untracked(destroy) } } -function update(node: SignalNode | ComputedNode): boolean { - if (node.depsTail !== undefined) { +function update(node: ReactiveNode): boolean { + if ('compute' in node) { return updateComputed(node as ComputedNode) } - return updateSignal(node as SignalNode) + if ('pendingValue' in node) { + return updateSignal(node as SignalNode) + } + + // Effect scope node + node.flags = MutableFlag + + return true } function notify(effect: EffectNode) { @@ -177,11 +185,13 @@ function link(dep: ReactiveNode, sub: ReactiveNode, version: number): void { } function unlink(link: Link, sub = link.sub): Link | undefined { - const dep = link.dep - const prevDep = link.prevDep - const nextDep = link.nextDep - const nextSub = link.nextSub - const prevSub = link.prevSub + const { + dep, + prevDep, + nextDep, + nextSub, + prevSub + } = link if (nextDep !== undefined) { nextDep.prevDep = prevDep @@ -296,9 +306,9 @@ function checkDirty(link: Link, sub: ReactiveNode): boolean { if (sub.flags & DirtyFlag) { dirty = true } else if ((flags & (MutableFlag | DirtyFlag)) === (MutableFlag | DirtyFlag)) { - if (update(dep as SignalNode | ComputedNode)) { - const subs = dep.subs! + const subs = dep.subs! + if (update(dep)) { if (subs.nextSub !== undefined) { shallowPropagate(subs) } @@ -306,11 +316,9 @@ function checkDirty(link: Link, sub: ReactiveNode): boolean { dirty = true } } else if ((flags & (MutableFlag | PendingFlag)) === (MutableFlag | PendingFlag)) { - if (link.nextSub !== undefined || link.prevSub !== undefined) { - stack = { - value: link, - prev: stack - } + stack = { + value: link, + prev: stack } link = dep.deps! @@ -329,20 +337,15 @@ function checkDirty(link: Link, sub: ReactiveNode): boolean { } while (checkDepth--) { - const firstSub = sub.subs! - const hasMultipleSubs = firstSub.nextSub !== undefined - - if (hasMultipleSubs) { - link = stack!.value - stack = stack!.prev - } else { - link = firstSub - } + link = stack!.value + stack = stack!.prev if (dirty) { - if (update(sub as SignalNode | ComputedNode)) { - if (hasMultipleSubs) { - shallowPropagate(firstSub) + const subs = sub.subs! + + if (update(sub)) { + if (subs.nextSub !== undefined) { + shallowPropagate(subs) } sub = link.sub @@ -364,7 +367,7 @@ function checkDirty(link: Link, sub: ReactiveNode): boolean { } } - return dirty + return dirty && sub.flags !== NoneFlag } while (true) } @@ -536,7 +539,7 @@ export function effectScope(fn: () => void): Destroy { depsTail: undefined, subs: undefined, subsTail: undefined, - flags: NoneFlag, + flags: MutableFlag, modes: ScopeMode } const prevSub = pushActiveSub(e) @@ -569,7 +572,7 @@ export function deferScope(fn: () => void): () => Destroy { depsTail: undefined, subs: undefined, subsTail: undefined, - flags: NoneFlag, + flags: MutableFlag, modes: ScopeMode | LazyMode } const prevSub = pushActiveSub(e) @@ -593,15 +596,18 @@ export function trigger(fn: () => void) { depsTail: undefined, subs: undefined, subsTail: undefined, - flags: WatchingFlag, + flags: WatchingFlag | RecursedCheckFlag, modes: NoneFlag } const prevSub = pushActiveSub(sub) + ++batchDepth + try { fn() } finally { popActiveSub(prevSub) + sub.flags = NoneFlag let link = sub.deps @@ -613,13 +619,12 @@ export function trigger(fn: () => void) { const subs = dep.subs if (subs !== undefined) { - sub.flags = NoneFlag propagate(subs) shallowPropagate(subs) } } - if (!batchDepth) { + if (!--batchDepth) { flush() } } @@ -654,7 +659,6 @@ function runEffect(e: EffectNode, warmup?: true): void { const prevNoMount = isMountableUsed ? pushNoMount(e.noMount) : undefined try { - destroyEffect(e) e.destroy = e.fn(warmup) || undefined } finally { popNoMount(prevNoMount) @@ -679,12 +683,21 @@ function run(e: EffectNode): void { && checkDirty(e.deps!, e) ) ) { + if (e.destroy !== undefined) { + destroyEffect(e) + + // Destroy function disposed the effect, do not re-run it + if (e.flags === NoneFlag) { + return + } + } + ++cycle e.depsTail = undefined e.flags = WatchingFlag | RecursedCheckFlag runEffect(e) - } else { + } else if (e.deps !== undefined) { e.flags = WatchingFlag } } @@ -746,10 +759,8 @@ function computedOper(this: ComputedNode): T { } } - const sub = activeSub - - if (sub !== undefined) { - link(this, sub, cycle) + if (activeSub !== undefined) { + link(this, activeSub, cycle) } return this.value! @@ -791,15 +802,8 @@ function signalOper(this: SignalNode, ...value: [NewValue]): T | void { } } - let sub = activeSub - - while (sub !== undefined) { - if (sub.flags & (MutableFlag | WatchingFlag)) { - link(this, sub, cycle) - break - } - - sub = sub.subs?.sub + if (activeSub !== undefined) { + link(this, activeSub, cycle) } return this.value diff --git a/packages/agera/src/signal.spec.ts b/packages/agera/src/signal.spec.ts index 2f0d19ea..1cd570a6 100644 --- a/packages/agera/src/signal.spec.ts +++ b/packages/agera/src/signal.spec.ts @@ -539,6 +539,37 @@ describe('agera', () => { }) expect(triggers).toBe(2) }) + + it('should allow writing a signal after reading it', () => { + const src1 = signal(1) + + trigger(() => { + src1() + src1(src1() + 1) + }) + + expect(src1()).toBe(2) + }) + + it('should rerun effect once when writing a signal after reading it', () => { + const src1 = signal(1) + let triggers = 0 + + effect(() => { + triggers++ + src1() + }) + + expect(triggers).toBe(1) + + trigger(() => { + src1() + src1(src1() + 1) + }) + + expect(triggers).toBe(2) + expect(src1()).toBe(2) + }) }) }) }) diff --git a/packages/kida/.size-limit.json b/packages/kida/.size-limit.json index f96852cd..4ac877f0 100644 --- a/packages/kida/.size-limit.json +++ b/packages/kida/.size-limit.json @@ -3,18 +3,18 @@ "name": "All publics", "path": "dist/index.js", "import": "*", - "limit": "3.95 kB" + "limit": "3.93 kB" }, { "name": "Signal", "path": "dist/index.js", "import": "{ signal }", - "limit": "1.61 kB" + "limit": "1.59 kB" }, { "name": "Popular set", "path": "dist/index.js", "import": "{ signal, record, computed, effect, mountable, onMount }", - "limit": "2.24 kB" + "limit": "2.21 kB" } ] diff --git a/packages/store/.size-limit.json b/packages/store/.size-limit.json index 25dd5268..f17da55e 100644 --- a/packages/store/.size-limit.json +++ b/packages/store/.size-limit.json @@ -3,18 +3,18 @@ "name": "All publics", "path": "dist/index.js", "import": "*", - "limit": "5.355 kB" + "limit": "5.34 kB" }, { "name": "Signal", "path": "dist/index.js", "import": "{ signal }", - "limit": "1.61 kB" + "limit": "1.59 kB" }, { "name": "Popular set", "path": "dist/index.js", "import": "{ signal, record, computed, effect, mountable, onMount }", - "limit": "2.25 kB" + "limit": "2.21 kB" } ]