@vielzeug/ripple 1.2.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/README.md +101 -0
- package/dist/_dev.cjs +2 -0
- package/dist/_dev.cjs.map +1 -0
- package/dist/_dev.d.ts +2 -0
- package/dist/_dev.d.ts.map +1 -0
- package/dist/_dev.js +9 -0
- package/dist/_dev.js.map +1 -0
- package/dist/_error-utils.cjs +2 -0
- package/dist/_error-utils.cjs.map +1 -0
- package/dist/_error-utils.d.ts +24 -0
- package/dist/_error-utils.d.ts.map +1 -0
- package/dist/_error-utils.js +20 -0
- package/dist/_error-utils.js.map +1 -0
- package/dist/_virtual/_rolldown/runtime.js +7 -0
- package/dist/async-computed.cjs +2 -0
- package/dist/async-computed.cjs.map +1 -0
- package/dist/async-computed.d.ts +36 -0
- package/dist/async-computed.d.ts.map +1 -0
- package/dist/async-computed.js +58 -0
- package/dist/async-computed.js.map +1 -0
- package/dist/computed.cjs +2 -0
- package/dist/computed.cjs.map +1 -0
- package/dist/computed.d.ts +26 -0
- package/dist/computed.d.ts.map +1 -0
- package/dist/computed.js +113 -0
- package/dist/computed.js.map +1 -0
- package/dist/devtools-hook.cjs +2 -0
- package/dist/devtools-hook.cjs.map +1 -0
- package/dist/devtools-hook.d.ts +4 -0
- package/dist/devtools-hook.d.ts.map +1 -0
- package/dist/devtools-hook.js +8 -0
- package/dist/devtools-hook.js.map +1 -0
- package/dist/devtools.d.ts +39 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/effect.cjs +2 -0
- package/dist/effect.cjs.map +1 -0
- package/dist/effect.d.ts +104 -0
- package/dist/effect.d.ts.map +1 -0
- package/dist/effect.js +173 -0
- package/dist/effect.js.map +1 -0
- package/dist/errors.cjs +2 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/reactive-base.cjs +2 -0
- package/dist/reactive-base.cjs.map +1 -0
- package/dist/reactive-base.d.ts +60 -0
- package/dist/reactive-base.d.ts.map +1 -0
- package/dist/reactive-base.js +56 -0
- package/dist/reactive-base.js.map +1 -0
- package/dist/ripple.cjs +2 -0
- package/dist/ripple.cjs.map +1 -0
- package/dist/ripple.iife.js +2 -0
- package/dist/ripple.iife.js.map +1 -0
- package/dist/ripple.js +2 -0
- package/dist/ripple.js.map +1 -0
- package/dist/scheduling.cjs +2 -0
- package/dist/scheduling.cjs.map +1 -0
- package/dist/scheduling.d.ts +5 -0
- package/dist/scheduling.d.ts.map +1 -0
- package/dist/scheduling.js +57 -0
- package/dist/scheduling.js.map +1 -0
- package/dist/signal.cjs +2 -0
- package/dist/signal.cjs.map +1 -0
- package/dist/signal.d.ts +24 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/signal.js +54 -0
- package/dist/signal.js.map +1 -0
- package/dist/src/devtools.cjs +2 -0
- package/dist/src/devtools.cjs.map +1 -0
- package/dist/src/devtools.js +36 -0
- package/dist/src/devtools.js.map +1 -0
- package/dist/src/ssr/index.cjs +2 -0
- package/dist/src/ssr/index.cjs.map +1 -0
- package/dist/src/ssr/index.js +38 -0
- package/dist/src/ssr/index.js.map +1 -0
- package/dist/ssr/index.d.ts +56 -0
- package/dist/ssr/index.d.ts.map +1 -0
- package/dist/store-history.cjs +2 -0
- package/dist/store-history.cjs.map +1 -0
- package/dist/store-history.d.ts +42 -0
- package/dist/store-history.d.ts.map +1 -0
- package/dist/store-history.js +60 -0
- package/dist/store-history.js.map +1 -0
- package/dist/store.cjs +2 -0
- package/dist/store.cjs.map +1 -0
- package/dist/store.d.ts +45 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +204 -0
- package/dist/store.js.map +1 -0
- package/dist/subscription.cjs +2 -0
- package/dist/subscription.cjs.map +1 -0
- package/dist/subscription.d.ts +22 -0
- package/dist/subscription.d.ts.map +1 -0
- package/dist/subscription.js +47 -0
- package/dist/subscription.js.map +1 -0
- package/dist/symbols.cjs +2 -0
- package/dist/symbols.cjs.map +1 -0
- package/dist/symbols.d.ts +6 -0
- package/dist/symbols.d.ts.map +1 -0
- package/dist/symbols.js +6 -0
- package/dist/symbols.js.map +1 -0
- package/dist/tracking.cjs +2 -0
- package/dist/tracking.cjs.map +1 -0
- package/dist/tracking.d.ts +82 -0
- package/dist/tracking.d.ts.map +1 -0
- package/dist/tracking.js +76 -0
- package/dist/tracking.js.map +1 -0
- package/dist/types.d.ts +315 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utilities.cjs +2 -0
- package/dist/utilities.cjs.map +1 -0
- package/dist/utilities.d.ts +55 -0
- package/dist/utilities.d.ts.map +1 -0
- package/dist/utilities.js +25 -0
- package/dist/utilities.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactive-base.js","names":[],"sources":["../src/reactive-base.ts"],"sourcesContent":["import type { Subscriber } from './types';\n\nimport { IS_COMPUTED, IS_SIGNAL } from './symbols';\n\n// ── Global FinalizationRegistry (F1) ──────────────────────────────────────────\n//\n// When a ComputedBase node becomes garbage-collectible, the registry fires and\n// removes the entry from its source's computedSubs_ map. This lets orphaned\n// computeds (created outside a scope/effect and never explicitly disposed) be\n// reclaimed by the GC without manual lifecycle management.\n\ntype ComputedWeakEntry = {\n readonly key: ComputedBase<unknown>;\n readonly map: Map<ComputedBase<unknown>, WeakRef<ComputedBase<unknown>>>;\n};\n\nconst computedSubRegistry = new FinalizationRegistry<ComputedWeakEntry>(({ key, map }) => {\n map.delete(key);\n});\n\n// ── ReactiveBase ──────────────────────────────────────────────────────────────\n\n/**\n * Abstract base shared by SignalImpl and ComputedImpl.\n *\n * Centralises:\n * - Subscriber-set management (computed subs via WeakRef, effect subs directly)\n * - Version counter (used for dep-version cache invalidation in computed)\n * - The [IS_SIGNAL] brand marker\n */\nexport abstract class ReactiveBase<T> {\n version = 0;\n readonly name: string | undefined;\n [IS_SIGNAL] = true;\n\n // A single Map<computed, WeakRef> replaces the previous Set<WeakRef> + WeakMap pair.\n // O(1) add, remove, and lookup without keeping two structures in sync.\n private readonly computedSubs_ = new Map<ComputedBase<unknown>, WeakRef<ComputedBase<unknown>>>();\n private readonly effectSubs_ = new Set<Subscriber>();\n\n constructor(name?: string) {\n this.name = name;\n }\n\n /**\n * Registers a computed as a downstream subscriber. Callers (trackSource in\n * tracking.ts) must ensure they only call this for genuinely NEW deps — i.e.\n * deps not already present in the previous dep list. This guarantees each\n * computed is registered exactly once per source, keeping the WeakRef set\n * clean without needing a dedup scan.\n */\n addComputedSub(c: ComputedBase<unknown>): void {\n const ref = new WeakRef(c);\n\n this.computedSubs_.set(c, ref);\n computedSubRegistry.register(c, { key: c, map: this.computedSubs_ }, ref);\n }\n\n removeComputedSub(c: ComputedBase<unknown>): void {\n const ref = this.computedSubs_.get(c);\n\n if (ref === undefined) return;\n\n this.computedSubs_.delete(c);\n computedSubRegistry.unregister(ref);\n }\n\n addEffectSub(subscriber: Subscriber): void {\n this.effectSubs_.add(subscriber);\n }\n\n removeEffectSub(subscriber: Subscriber): void {\n this.effectSubs_.delete(subscriber);\n }\n\n protected clearSubscribers(): void {\n for (const ref of this.computedSubs_.values()) {\n computedSubRegistry.unregister(ref);\n }\n\n this.computedSubs_.clear();\n this.effectSubs_.clear();\n }\n\n hasSubscribers(): boolean {\n if (this.effectSubs_.size > 0) return true;\n\n for (const ref of this.computedSubs_.values()) {\n if (ref.deref() !== undefined) return true;\n }\n\n return false;\n }\n\n /**\n * Yields live downstream computed subscribers.\n * Dead WeakRefs (GC'd computeds) are pruned eagerly from the map on each traversal,\n * keeping the subscriber set compact without requiring manual dispose calls.\n */\n *computedSubs(): Generator<ComputedBase<unknown>> {\n for (const [key, ref] of this.computedSubs_) {\n const node = ref.deref();\n\n if (node !== undefined) {\n yield node;\n } else {\n this.computedSubs_.delete(key);\n computedSubRegistry.unregister(ref);\n }\n }\n }\n\n effectSubs(): ReadonlySet<Subscriber> {\n return this.effectSubs_;\n }\n\n abstract get value(): T;\n abstract peek(): T;\n}\n\n// ── Refreshable ──────────────────────────────────────────────────────────────\n\n/**\n * Structural interface for lazy nodes that support dirty-flagging and\n * on-demand recompute. Used by tracking.ts to pull computed deps without\n * importing the concrete ComputedImpl class.\n */\nexport interface Refreshable {\n refreshIfDirty(): boolean;\n}\n\n// ── ComputedBase ──────────────────────────────────────────────────────────────\n\n/**\n * Extension of ReactiveBase for derived (lazy) nodes that can be marked dirty\n * and refreshed on demand. Exposes the two methods scheduling.ts needs without\n * importing the concrete ComputedImpl class (breaks the potential circular dep).\n */\nexport abstract class ComputedBase<T> extends ReactiveBase<T> implements Refreshable {\n [IS_COMPUTED] = true;\n lastPropEpoch_ = 0;\n\n abstract markDirty(): boolean;\n abstract refreshIfDirty(): boolean;\n}\n"],"mappings":";;AAgBA,IAAM,IAAsB,IAAI,sBAAyC,EAAE,QAAK,aAAU;CACxF,EAAI,OAAO,CAAG;AAChB,CAAC,GAYqB,IAAtB,MAAsC;CACpC,UAAU;CACV;CACA,CAAC,KAAa;CAId,gCAAiC,IAAI,IAA2D;CAChG,8BAA+B,IAAI,IAAgB;CAEnD,YAAY,GAAe;EACzB,KAAK,OAAO;CACd;CASA,eAAe,GAAgC;EAC7C,IAAM,IAAM,IAAI,QAAQ,CAAC;EAGzB,AADA,KAAK,cAAc,IAAI,GAAG,CAAG,GAC7B,EAAoB,SAAS,GAAG;GAAE,KAAK;GAAG,KAAK,KAAK;EAAc,GAAG,CAAG;CAC1E;CAEA,kBAAkB,GAAgC;EAChD,IAAM,IAAM,KAAK,cAAc,IAAI,CAAC;EAEhC,MAAQ,KAAA,MAEZ,KAAK,cAAc,OAAO,CAAC,GAC3B,EAAoB,WAAW,CAAG;CACpC;CAEA,aAAa,GAA8B;EACzC,KAAK,YAAY,IAAI,CAAU;CACjC;CAEA,gBAAgB,GAA8B;EAC5C,KAAK,YAAY,OAAO,CAAU;CACpC;CAEA,mBAAmC;EACjC,KAAK,IAAM,KAAO,KAAK,cAAc,OAAO,GAC1C,EAAoB,WAAW,CAAG;EAIpC,AADA,KAAK,cAAc,MAAM,GACzB,KAAK,YAAY,MAAM;CACzB;CAEA,iBAA0B;EACxB,IAAI,KAAK,YAAY,OAAO,GAAG,OAAO;EAEtC,KAAK,IAAM,KAAO,KAAK,cAAc,OAAO,GAC1C,IAAI,EAAI,MAAM,MAAM,KAAA,GAAW,OAAO;EAGxC,OAAO;CACT;CAOA,CAAC,eAAiD;EAChD,KAAK,IAAM,CAAC,GAAK,MAAQ,KAAK,eAAe;GAC3C,IAAM,IAAO,EAAI,MAAM;GAEvB,AAAI,MAAS,KAAA,KAGX,KAAK,cAAc,OAAO,CAAG,GAC7B,EAAoB,WAAW,CAAG,KAHlC,MAAM;EAKV;CACF;CAEA,aAAsC;EACpC,OAAO,KAAK;CACd;AAIF,GAoBsB,IAAtB,cAA8C,EAAuC;CACnF,CAAC,KAAe;CAChB,iBAAiB;AAInB"}
|
package/dist/ripple.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{},n=class extends e{},r=class extends e{},i=class extends e{},a=class extends e{},o=class extends e{},s=e=>e instanceof Error?e:Error(`Non-Error thrown: ${String(e)}`),c=e=>{let t=[];for(let n of e)try{n()}catch(e){t.push(s(e))}return t},l=(e,t)=>{let n=c(e);if(n.length===1)throw n[0];if(n.length>0)throw AggregateError(n,t)},u=(e,t,n)=>{throw t.length===0?e:AggregateError([s(e),...t],n,{cause:s(e)})},d=null,f=()=>d,p=Symbol(`ripple.is-signal`),m=Symbol(`ripple.is-computed`),h=Symbol(`ripple.is-store`),g=Symbol(`ripple.uninitialized`),_=new FinalizationRegistry(({key:e,map:t})=>{t.delete(e)}),v=class{version=0;name;[p]=!0;computedSubs_=new Map;effectSubs_=new Set;constructor(e){this.name=e}addComputedSub(e){let t=new WeakRef(e);this.computedSubs_.set(e,t),_.register(e,{key:e,map:this.computedSubs_},t)}removeComputedSub(e){let t=this.computedSubs_.get(e);t!==void 0&&(this.computedSubs_.delete(e),_.unregister(t))}addEffectSub(e){this.effectSubs_.add(e)}removeEffectSub(e){this.effectSubs_.delete(e)}clearSubscribers(){for(let e of this.computedSubs_.values())_.unregister(e);this.computedSubs_.clear(),this.effectSubs_.clear()}hasSubscribers(){if(this.effectSubs_.size>0)return!0;for(let e of this.computedSubs_.values())if(e.deref()!==void 0)return!0;return!1}*computedSubs(){for(let[e,t]of this.computedSubs_){let n=t.deref();n===void 0?(this.computedSubs_.delete(e),_.unregister(t)):yield n}}effectSubs(){return this.effectSubs_}},y=class extends v{[m]=!0;lastPropEpoch_=0},b=class{fn_;disposed_=!1;constructor(e){this.fn_=e}get disposed(){return this.disposed_}dispose(){if(this.disposed_)return;this.disposed_=!0;let e=this.fn_;this.fn_=null,e()}[Symbol.dispose](){this.dispose()}},x=class{awaitDone_;getCurrentRun_;syncStop_;asyncDisposePromise_=null;constructor(e,t,n){this.awaitDone_=t,this.getCurrentRun_=n,this.syncStop_=e}get disposed(){return this.syncStop_.disposed}dispose(){this.syncStop_.dispose()}run(){return this.getCurrentRun_()??Promise.resolve()}[Symbol.dispose](){this.dispose()}[Symbol.asyncDispose](){return this.asyncDisposePromise_===null?(this.dispose(),this.asyncDisposePromise_=this.awaitDone_(),this.asyncDisposePromise_):this.asyncDisposePromise_}},S=0,C=()=>++S,w=()=>S,T={scheduling:{activeDirty:`a`,batchDepth:0,dirtyWithEffectSubsA:new Set,dirtyWithEffectSubsB:new Set,pendingSubscribers:new Set},scopeCleanups:null,tracking:null},E=null,D=()=>E===null?T:E.get(),O=()=>D().scheduling,ee=()=>E!==null,k=()=>D().tracking,A=(e,t)=>{if(E!==null){let n=E.get();return E.run({...n,tracking:e},t)}let n=T;T={...T,tracking:e};try{return t()}finally{T=n}},j=(e,t)=>{if(E!==null){let n=E.get();return E.run({...n,scopeCleanups:e},t)}let n=T;T={...T,scopeCleanups:e};try{return t()}finally{T=n}},M=()=>D().scopeCleanups,N=e=>{let t=k();t?.kind===`effect`?t.cleanups.push(e):M()?.push(e)},P=e=>A(null,e),F=e=>{let t=k();if(t!==null){if(t.sourceObserver?.(e),t.kind===`computed`)t.depCollector.push({source:e,version:e.version});else if(t.kind===`effect`){let n=t.effect;e.addEffectSub(n),t.subscriptions.add(()=>e.removeEffectSub(n)),t.deps.set(e,e.version)}}},te=class extends y{value_;dirty_;computing_;disposed_;deps_;compute_;equals_;maxRevision_;constructor(e,t){let{equals:n,name:r}=t??{};super(r),this.value_=g,this.dirty_=!0,this.computing_=!1,this.disposed_=!1,this.deps_=[],this.maxRevision_=-1,this.compute_=e,this.equals_=n===void 0?Object.is:(e,t)=>n(e,t)}markDirty(){return this.disposed_||this.dirty_?!1:(this.dirty_=!0,!0)}refreshIfDirty(){if(!this.dirty_)return!1;if(w()<=this.maxRevision_)return this.dirty_=!1,!1;if(this.deps_.length>0){let e=!0;for(let t of this.deps_){let n=t.source;if(`refreshIfDirty`in n&&n.refreshIfDirty(),n.version!==t.version){e=!1;break}}if(e)return this.dirty_=!1,this.maxRevision_=w(),!1}return this.recompute()}runCompute(e){try{return A({computed:this,depCollector:e,kind:`computed`},this.compute_)}catch(e){throw s(e)}}recompute(){if(this.computing_)throw new t(`computed cycle detected${this.name?` "${this.name}"`:``}`);this.computing_=!0;try{let e=[];f()?.compute?.({name:this.name});let t=this.runCompute(e);return this.dirty_=!1,this.maxRevision_=w(),this.updateDeps(e),this.value_===g||!this.equals_(this.value_,t)?(this.value_=t,this.version++,!0):!1}finally{this.computing_=!1}}updateDeps(e){let t=this.deps_;if(t.length===e.length&&t.every((t,n)=>t.source===e[n].source)){for(let n=0;n<e.length;n++)t[n].version=e[n].version;return}let n=new Set(t.map(e=>e.source)),r=new Set(e.map(e=>e.source));for(let e of t)r.has(e.source)||e.source.removeComputedSub(this);for(let t of e)n.has(t.source)||t.source.addComputedSub(this);this.deps_=e}get value(){return this.disposed_?this.value_===g?void 0:this.value_:(this.refreshIfDirty(),F(this),this.value_)}peek(){return this.disposed_?this.value_===g?void 0:this.value_:(this.refreshIfDirty(),this.value_)}subscribe=e=>{if(this.disposed_){let e=new b(()=>{});return e.dispose(),e}return this.refreshIfDirty(),this.addEffectSub(e),new b(()=>{this.removeEffectSub(e)})};get disposed(){return this.disposed_}dispose(){if(!this.disposed_){this.disposed_=!0;for(let e of this.deps_)e.source.removeComputedSub(this);this.deps_=[],this.clearSubscribers(),f()?.dispose?.({kind:`computed`,name:this.name})}}[Symbol.dispose](){this.dispose()}},I=(e,t)=>{let n=new te(e,t);return N(()=>n.dispose()),n},ne=!globalThis.__RIPPLE_PROD__;function re(e){ne&&console.warn(`[@vielzeug/ripple] ${e}`)}var L=!1,R=0,z=[],B=e=>e.activeDirty===`a`?e.dirtyWithEffectSubsA:e.dirtyWithEffectSubsB,ie=(e,t)=>{let n=++R;for(let n of t.effectSubs())e.pendingSubscribers.add(n);for(let e of t.computedSubs())z.push(e);try{for(;z.length>0;){let t=z.pop();if(t.lastPropEpoch_!==n&&(t.lastPropEpoch_=n,t.markDirty())){t.effectSubs().size>0&&B(e).add(t);for(let e of t.computedSubs())z.push(e)}}}finally{z.length=0}},ae=e=>{for(;B(e).size>0;){let t=B(e);e.activeDirty=e.activeDirty===`a`?`b`:`a`,B(e).clear();for(let n of t)if(n.hasSubscribers()&&n.refreshIfDirty()){for(let t of n.effectSubs())e.pendingSubscribers.add(t);for(let t of n.computedSubs())t.markDirty()&&t.effectSubs().size>0&&B(e).add(t)}t.clear()}},V=e=>{let t=0;for(;e.pendingSubscribers.size>0||B(e).size>0;){if(++t>100)throw new i(`infinite flush loop (> 100 iterations)`);if(B(e).size>0&&ae(e),e.pendingSubscribers.size===0)continue;let n=[...e.pendingSubscribers];e.pendingSubscribers.clear(),l(n,`subscriber errors`)}},oe=globalThis.process?.versions!=null,se=e=>{if(!e.hasSubscribers())return;!L&&oe&&!ee()&&(L=!0,re(`Signal updated in a Node.js-like environment. The module-level flush queue is shared across concurrent requests — use per-request worker isolation or the @vielzeug/ripple/ssr sub-path for request-isolated scheduling.`));let t=O();ie(t,e),t.batchDepth===0&&V(t)},H=e=>{let t=O();t.batchDepth++;let n;try{n=e()}catch(e){throw t.batchDepth--,t.batchDepth===0&&(t.pendingSubscribers.clear(),t.dirtyWithEffectSubsA.clear(),t.dirtyWithEffectSubsB.clear()),e}return t.batchDepth--,t.batchDepth===0&&V(t),n},U=class extends v{value_;equals_;disposed_;constructor(e,t,n){super(n),this.value_=e,this.equals_=t??Object.is,this.disposed_=!1}get value(){return this.disposed_||F(this),this.value_}set value(e){if(this.disposed_||this.equals_(this.value_,e))return;let t=this.value_;this.value_=e,this.version=C(),f()?.write?.({name:this.name,newValue:e,oldValue:t}),se(this)}peek(){return this.value_}subscribe=e=>{if(this.disposed_){let e=new b(()=>{});return e.dispose(),e}return this.addEffectSub(e),new b(()=>{this.removeEffectSub(e)})};get disposed(){return this.disposed_}dispose(){this.disposed_||(this.disposed_=!0,this.clearSubscribers(),f()?.dispose?.({kind:`signal`,name:this.name}))}[Symbol.dispose](){this.dispose()}},W=(e,t)=>new U(e,t?.equals,t?.name),G=e=>{if(e===`__proto__`||e===`constructor`||e===`prototype`)throw new o(`Unsafe key "${e}" rejected to prevent prototype pollution.`)},ce=(e,t)=>{let n=e;for(let e of t){if(typeof n!=`object`||!n)return;n=n[e]}return n},K=(e,t,n)=>{if(t.length===0)return n;if(typeof e!=`object`||!e)throw new o(`Cannot write to a nested path through a null or non-object intermediate value.`);let[r,...i]=t,a=e;return{...a,[r]:K(a[r],i,n)}},q=class{[p]=!0;source_;write_;evict_;disposeSource_;name_;get name(){return this.name_??this.source_.name}constructor(e){this.source_=e.source,this.write_=e.write,this.evict_=e.evict,this.disposeSource_=e.disposeSource,this.name_=e.name}get value(){return this.source_.value}set value(e){this.write_(e)}peek(){return this.source_.peek()}subscribe(e){return this.source_.subscribe(e)}get disposed(){return this.source_.disposed}dispose(){this.evict_(),this.disposeSource_?.()}[Symbol.dispose](){this.dispose()}},le=class{[p]=!0;[h]=!0;name;current_;disposed_;initial_;lensCache_;propSignals_;readonlyProxy_;version_;constructor(e,t){this.name=t,this.current_=structuredClone(e),this.disposed_=!1,this.initial_=structuredClone(e),this.lensCache_=new Map,this.propSignals_=new Map,this.version_=new U(0,void 0,t?`${t}.version`:void 0),this.readonlyProxy_=new Proxy(this.current_,{deleteProperty(e,t){throw new o(`Direct deletion from store.value is not allowed. Use store.patch() or store.lens(). (key: "${String(t)}")`)},set(e,t){throw new o(`Direct mutation of store.value is not allowed. Use store.patch(), store.lens(), or store.replace(). (key: "${String(t)}")`)}})}propSignalFor_(e){let t=this.propSignals_.get(e);return t===void 0&&(t=new U(this.current_[e],void 0,this.name===void 0?e:`${this.name}.${e}`),this.propSignals_.set(e,t)),t}setPath_(e,t){let[n,...r]=e,i=this.current_[n],a=K(i,r,t);this.applyTopLevelChange_(n,a)}applyTopLevelChange_(e,t){G(e);let n=this.current_[e];Object.is(n,t)||(this.current_[e]=t,this.propSignalFor_(e).value=t,this.version_.value=this.version_.peek()+1)}deleteTopLevelKey_(e){Object.hasOwn(this.current_,e)&&(delete this.current_[e],this.propSignalFor_(e).value=void 0,this.version_.value=this.version_.peek()+1)}applyFullState_(e){let t=this.current_,n=new Set([...Object.keys(t),...Object.keys(e)]);for(let e of n)G(e);H(()=>{for(let t of n)Object.hasOwn(e,t)?this.applyTopLevelChange_(t,e[t]):this.deleteTopLevelKey_(t)})}get value(){return this.version_.value,this.readonlyProxy_}peek(){return Object.freeze(structuredClone(this.current_))}subscribe=e=>this.version_.subscribe(e);patch(e){if(typeof e!=`object`||!e||Array.isArray(e))throw new o(`store.patch() requires a plain object partial.`);let t=Object.keys(e);if(t.length!==0){for(let e of t)G(e);H(()=>{for(let n of t)this.applyTopLevelChange_(n,e[n])}),f()?.mutate?.({kind:`patch`,name:this.name})}}replace(e){let t=structuredClone(this.current_),n=e(t);n!==t&&(this.applyFullState_(n),f()?.mutate?.({kind:`replace`,name:this.name}))}reset(){this.applyFullState_(this.initial_),f()?.mutate?.({kind:`reset`,name:this.name})}lens(e){let t=this.lensCache_.get(e);if(t!==void 0)return t;let n=()=>{this.lensCache_.delete(e)},r=e.split(`.`);if(r.length>32)throw new o(`Lens path exceeds maximum depth of 32 segments: "${e}".`);for(let t of r){if(t===``)throw new o(`Empty path segment in lens path "${e}". Check for consecutive dots.`);if(t===`__proto__`||t===`constructor`||t===`prototype`)throw new o(`Unsafe path segment "${t}" in lens path "${e}".`)}let i;if(r.length===1)i=new q({evict:n,source:this.propSignalFor_(r[0]),write:t=>{H(()=>this.applyTopLevelChange_(r[0],t)),f()?.mutate?.({kind:`lens`,name:this.name,path:e})}});else{let t=I(()=>ce(this.propSignalFor_(r[0]).value,r.slice(1)));i=new q({disposeSource:()=>t.dispose(),evict:n,name:this.name?`${this.name}.${e}`:e,source:t,write:n=>{let i=t.peek();Object.is(i,n)||(H(()=>this.setPath_(r,n)),f()?.mutate?.({kind:`lens`,name:this.name,path:e}))}})}return this.lensCache_.set(e,i),i}get disposed(){return this.disposed_}dispose(){if(!this.disposed_){this.disposed_=!0;for(let e of[...this.lensCache_.values()])e.dispose();this.lensCache_.clear();for(let e of this.propSignals_.values())e.dispose();this.propSignals_.clear(),this.version_.dispose(),f()?.dispose?.({kind:`store`,name:this.name})}}[Symbol.dispose](){this.dispose()}},J=(e,t)=>{if(typeof e!=`object`||!e||Array.isArray(e))throw new o(`store() requires a plain object initial state.`);for(let t of Object.keys(e))G(t);return new le(e,t?.name)},ue=(e,t)=>{if(t===`sync`||t===void 0)return e;let n=!1;return()=>{n||(n=!0,queueMicrotask(()=>{n=!1,e()}))}},Y=(e,t)=>{let n=t?.scheduler??`sync`,r=t?.name,o=[],s=new Set,d=new Map,p=!1,h=!1,g=!1,_=()=>{if(d.clear(),o.length===0&&s.size===0)return;let e=[...o,...s];o.length=0,s.clear(),l(e,`effect teardown errors`)},v=()=>{if(!g){if(p){h=!0;return}p=!0;try{let t=0;do{if(++t>100)throw new i(`infinite effect loop (> 100 iterations)${r?` in "${r}"`:``}`);h=!1,_(),f()?.run?.({name:r});let n;try{n=A({cleanups:o,deps:d,effect:y,kind:`effect`,subscriptions:s},e)}catch(e){let t=[...c(s),...c(o)];o.length=0,s.clear(),u(e,t,`effect failure with cleanup errors`)}if(n!==void 0&&typeof n!=`function`)throw new a(`effect() returned ${typeof n} — expected a cleanup function or void.`);typeof n==`function`&&o.push(n)}while(h&&!g)}finally{p=!1}}},y=ue(v,n);v();let x=new b(()=>{g||(g=!0,_(),f()?.dispose?.({kind:`effect`,name:r}))});return M()?.push(()=>x.dispose()),{dispose:()=>x.dispose(),get disposed(){return x.disposed},getDependencies(){return[...d.keys()].map(e=>({kind:m in e?`computed`:`signal`,name:e.name}))},[Symbol.dispose](){x.dispose()}}},de=(e,t)=>{let n=null,r=null,i=null,a=t?.onError??(e=>{queueMicrotask(()=>{throw e})});return new x(Y(()=>{n?.abort(),r?.(),r=null,n=new AbortController;let{signal:t}=n,o=X(),s;return i=s=(async()=>{try{let n=await e(t,o);typeof n==`function`&&(t.aborted?n():r=n)}catch(e){t.aborted||a(e)}finally{o.dispose(),i===s&&(i=null)}})(),()=>{n?.abort(),r?.(),r=null}},{name:t?.name}),async()=>{let e=i;e&&await e},()=>i)},fe=e=>{let t=k();if(t?.kind===`effect`){t.cleanups.push(e);return}let n=M();if(n!==null){n.push(e);return}throw new a(`onCleanup() must be called from within an active effect or scope.`)},X=e=>{let t=[],r=new AbortController,i=!1,a=e=>{if(i)throw new n(`Cannot register cleanup on a disposed scope.`);t.push(e)},o=e=>{if(i)throw new n(`Cannot run inside a disposed scope.`);return j(t,e)},s=()=>{i||(i=!0,r.abort(),l([...t].reverse(),`scope cleanup errors`),t.length=0)},c={add:a,get disposalSignal(){return r.signal},dispose:s,get disposed(){return i},run:o,[Symbol.dispose]:s};return e&&o(e),c};function pe(e,t,n){let r=n?.equals??Object.is,i,o=(e,n)=>{i?.(),i=void 0;let r=t(e,n);if(r!==void 0&&typeof r!=`function`)throw new a(`watch() callback returned ${typeof r} — expected a cleanup function or void.`);typeof r==`function`&&(i=r)},s;if(typeof e==`function`){let t,a=!0,c=Y(()=>{let i=e();if(a){a=!1,t=i;return}if(!r(t,i)){let e=t;t=i,o(i,e),n?.once&&s.dispose()}},{name:n?.name});s=new b(()=>{i?.(),i=void 0,c.dispose()}),n?.immediate&&(o(t,void 0),n.once&&s.dispose())}else{let t=e.peek(),a=e.subscribe(()=>{let i=e.peek();r(t,i)||(o(i,t),t=i,n?.once&&s.dispose())});s=new b(()=>{i?.(),i=void 0,a.dispose()}),n?.immediate&&(o(t,void 0),n.once&&s.dispose())}return M()?.push(()=>s.dispose()),s}var me=e=>({get disposed(){return e.disposed},[p]:!0,get name(){return e.name},peek(){return e.peek()},subscribe(t){return e.subscribe(t)},get value(){return e.value}}),Z=e=>typeof e==`object`&&!!e&&!!e[p],he=e=>Z(e)&&!e[m]&&!e[h],ge=e=>typeof e==`object`&&!!e&&!!e[m],Q=e=>typeof e==`object`&&!!e&&!!e[h],_e=(e,t)=>{let n=t?.name,r=W({data:t?.initialValue,status:`loading`},n?{name:n}:void 0),i=W(0),a=null,o=!1,s=Y(()=>{i.value,a?.abort(),a=new AbortController;let{signal:t}=a,n=r.peek(),s=`data`in n?n.data:void 0;r.value={data:s,status:`loading`},(async()=>{try{let n=await e(t);!t.aborted&&!o&&(r.value={data:n,status:`ready`})}catch(e){!t.aborted&&!o&&(r.value={data:s,error:e,status:`error`})}})()},n?{name:n}:void 0),c=()=>{o||(i.value=i.peek()+1)},l=()=>{o||(o=!0,a?.abort(),s.dispose(),r.dispose(),i.dispose())};return N(l),{dispose:l,get disposed(){return o},[m]:!0,[p]:!0,get name(){return r.name},peek:()=>r.peek(),refresh:c,subscribe:e=>r.subscribe(e),[Symbol.dispose]:l,get value(){return r.value}}},$=(e,t)=>({label:t,state:Object.freeze(structuredClone({...e}))}),ve=(e,t)=>{let n=t?.maxHistory??50,r=!Q(e),i=r?J(e,{name:t?.name}):e,a=[$(i.peek())],o=W(0,{name:t?.name?`${t.name}.cursor`:void 0}),s=!1,c=e=>{s||(a.splice(o.peek()+1),a.push($(i.peek(),e)),a.length>n&&a.splice(0,a.length-n),o.value=a.length-1)},l=()=>{s||o.peek()<=0||(o.value=o.peek()-1,i.replace(()=>a[o.peek()].state))},u=()=>{s||o.peek()>=a.length-1||(o.value=o.peek()+1,i.replace(()=>a[o.peek()].state))},d=e=>{if(!(e<0||e>=a.length))return a[e]},f=()=>{s||(s=!0,o.dispose(),r&&i.dispose())};return{get canRedo(){return o.value<a.length-1},get canUndo(){return o.value>0},dispose:f,get disposed(){return s},historyAt:d,get historyLength(){return a.length},lens:e=>i.lens(e),get name(){return i.name},patch:e=>i.patch(e),peek:()=>i.peek(),push:()=>c(),pushNamed:e=>c(e),redo:u,replace:e=>i.replace(e),reset:()=>i.reset(),get store(){return i},subscribe:e=>i.subscribe(e),[Symbol.dispose]:f,undo:l,get value(){return i.value}}};exports.RippleComputedCycleError=t,exports.RippleDisposedScopeError=n,exports.RippleEnvironmentError=r,exports.RippleError=e,exports.RippleInfiniteLoopError=i,exports.RippleInvalidCleanupError=a,exports.RippleInvalidStoreError=o,exports.batch=H,exports.computed=I,exports.effect=Y,exports.effectAsync=de,exports.getDevToolsHook=f,exports.isComputed=ge,exports.isReactive=Z,exports.isSignal=he,exports.isStore=Q,exports.onCleanup=fe,exports.readonly=me,exports.resource=_e,exports.scope=X,exports.signal=W,exports.store=J,exports.storeWithHistory=ve,exports.untrack=P,exports.watch=pe;
|
|
2
|
+
//# sourceMappingURL=ripple.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ripple.cjs","names":[],"sources":["../src/errors.ts","../src/_error-utils.ts","../src/devtools-hook.ts","../src/symbols.ts","../src/reactive-base.ts","../src/subscription.ts","../src/tracking.ts","../src/computed.ts","../src/_dev.ts","../src/scheduling.ts","../src/signal.ts","../src/store.ts","../src/effect.ts","../src/utilities.ts","../src/async-computed.ts","../src/store-history.ts"],"sourcesContent":["// ── RippleError ─────────────────────────────────────────────────────────────\n\n/** Base class for all ripple errors. Use `instanceof RippleError` to catch any ripple-originated error. */\nexport class RippleError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is RippleError {\n return err instanceof RippleError;\n }\n}\n\n/** Thrown when a computed value reads itself, creating a circular dependency. */\nexport class RippleComputedCycleError extends RippleError {}\n\n/** Thrown when a method is called on a scope that has already been disposed. */\nexport class RippleDisposedScopeError extends RippleError {}\n\n/** Thrown when the current runtime environment does not support the requested feature (e.g. SSR APIs in the browser). */\nexport class RippleEnvironmentError extends RippleError {}\n\n/** Thrown when an effect or flush loop exceeds the maximum iteration count. */\nexport class RippleInfiniteLoopError extends RippleError {}\n\n/** Thrown when `onCleanup()` is called outside an active effect or scope. */\nexport class RippleInvalidCleanupError extends RippleError {}\n\n/** Thrown when a store is created or mutated with an invalid value or path. */\nexport class RippleInvalidStoreError extends RippleError {}\n","import type { CleanupFn } from './types';\n\n/**\n * Coerces any thrown value to an `Error`.\n * Non-Error throws (strings, numbers, etc.) are wrapped in a plain `Error`\n * with a descriptive message — they are NOT mis-branded as `RippleError`.\n */\nexport const ensureError = (error: unknown): Error =>\n error instanceof Error ? error : new Error(`Non-Error thrown: ${String(error)}`);\n\n/**\n * Runs each fn in `fns`, collecting any thrown errors. Does NOT re-throw.\n */\nexport const collectErrors = (fns: Iterable<CleanupFn>): Error[] => {\n const errors: Error[] = [];\n\n for (const fn of fns) {\n try {\n fn();\n } catch (e) {\n errors.push(ensureError(e));\n }\n }\n\n return errors;\n};\n\n/**\n * Runs all `fns`, collecting errors.\n * - Single error: re-throws the original error directly (preserves type + stack).\n * - Multiple errors: wraps in AggregateError with the given context label.\n */\nexport const runAll = (fns: Iterable<CleanupFn>, context: string): void => {\n const errors = collectErrors(fns);\n\n if (errors.length === 1) throw errors[0]!;\n\n if (errors.length > 0) {\n throw new AggregateError(errors, context);\n }\n};\n\n/**\n * Re-throws `primary`, augmented with any extra errors as an AggregateError.\n * If `extras` is empty, throws `primary` directly (preserves original type + stack).\n * Otherwise, `primary` is set as the `.cause` so callers can identify the root failure.\n */\nexport const rethrowWith = (primary: unknown, extras: Error[], context: string): never => {\n if (extras.length === 0) throw primary;\n\n throw new AggregateError([ensureError(primary), ...extras], context, {\n cause: ensureError(primary),\n });\n};\n","// ── DevTools hook — core stub ─────────────────────────────────────────────────\n//\n// This module is intentionally minimal: it only holds the module-level _hook\n// variable and exposes getDevToolsHook() for the hot paths in signal.ts,\n// computed.ts, effect.ts, and store.ts.\n//\n// installDevTools() lives in the @vielzeug/ripple/devtools sub-path and is\n// tree-shaken from production bundles when that sub-path is never imported.\n// setDevToolsHook() is the internal bridge called by installDevTools().\n\nimport type { RippleDevToolsHook } from './types';\n\nlet _hook: RippleDevToolsHook | null = null;\n\n/** Returns the currently installed DevTools hook, or `null` if none is installed. */\nexport const getDevToolsHook = (): RippleDevToolsHook | null => _hook;\n\n/** @internal Called only by `installDevTools` in `@vielzeug/ripple/devtools`. */\nexport const setDevToolsHook = (hook: RippleDevToolsHook | null): void => {\n _hook = hook;\n};\n","// Brand symbols used for runtime type guards (isSignal, isComputed, isStore).\n// Declared as unique symbols so TypeScript treats them as distinct types.\n\nexport const IS_SIGNAL: unique symbol = Symbol('ripple.is-signal');\nexport const IS_COMPUTED: unique symbol = Symbol('ripple.is-computed');\nexport const IS_STORE: unique symbol = Symbol('ripple.is-store');\n\n// Internal sentinels\n\n/** Marks an uninitialized computed value — distinct from any user-provided value. */\nexport const UNINITIALIZED: unique symbol = Symbol('ripple.uninitialized');\n","import type { Subscriber } from './types';\n\nimport { IS_COMPUTED, IS_SIGNAL } from './symbols';\n\n// ── Global FinalizationRegistry (F1) ──────────────────────────────────────────\n//\n// When a ComputedBase node becomes garbage-collectible, the registry fires and\n// removes the entry from its source's computedSubs_ map. This lets orphaned\n// computeds (created outside a scope/effect and never explicitly disposed) be\n// reclaimed by the GC without manual lifecycle management.\n\ntype ComputedWeakEntry = {\n readonly key: ComputedBase<unknown>;\n readonly map: Map<ComputedBase<unknown>, WeakRef<ComputedBase<unknown>>>;\n};\n\nconst computedSubRegistry = new FinalizationRegistry<ComputedWeakEntry>(({ key, map }) => {\n map.delete(key);\n});\n\n// ── ReactiveBase ──────────────────────────────────────────────────────────────\n\n/**\n * Abstract base shared by SignalImpl and ComputedImpl.\n *\n * Centralises:\n * - Subscriber-set management (computed subs via WeakRef, effect subs directly)\n * - Version counter (used for dep-version cache invalidation in computed)\n * - The [IS_SIGNAL] brand marker\n */\nexport abstract class ReactiveBase<T> {\n version = 0;\n readonly name: string | undefined;\n [IS_SIGNAL] = true;\n\n // A single Map<computed, WeakRef> replaces the previous Set<WeakRef> + WeakMap pair.\n // O(1) add, remove, and lookup without keeping two structures in sync.\n private readonly computedSubs_ = new Map<ComputedBase<unknown>, WeakRef<ComputedBase<unknown>>>();\n private readonly effectSubs_ = new Set<Subscriber>();\n\n constructor(name?: string) {\n this.name = name;\n }\n\n /**\n * Registers a computed as a downstream subscriber. Callers (trackSource in\n * tracking.ts) must ensure they only call this for genuinely NEW deps — i.e.\n * deps not already present in the previous dep list. This guarantees each\n * computed is registered exactly once per source, keeping the WeakRef set\n * clean without needing a dedup scan.\n */\n addComputedSub(c: ComputedBase<unknown>): void {\n const ref = new WeakRef(c);\n\n this.computedSubs_.set(c, ref);\n computedSubRegistry.register(c, { key: c, map: this.computedSubs_ }, ref);\n }\n\n removeComputedSub(c: ComputedBase<unknown>): void {\n const ref = this.computedSubs_.get(c);\n\n if (ref === undefined) return;\n\n this.computedSubs_.delete(c);\n computedSubRegistry.unregister(ref);\n }\n\n addEffectSub(subscriber: Subscriber): void {\n this.effectSubs_.add(subscriber);\n }\n\n removeEffectSub(subscriber: Subscriber): void {\n this.effectSubs_.delete(subscriber);\n }\n\n protected clearSubscribers(): void {\n for (const ref of this.computedSubs_.values()) {\n computedSubRegistry.unregister(ref);\n }\n\n this.computedSubs_.clear();\n this.effectSubs_.clear();\n }\n\n hasSubscribers(): boolean {\n if (this.effectSubs_.size > 0) return true;\n\n for (const ref of this.computedSubs_.values()) {\n if (ref.deref() !== undefined) return true;\n }\n\n return false;\n }\n\n /**\n * Yields live downstream computed subscribers.\n * Dead WeakRefs (GC'd computeds) are pruned eagerly from the map on each traversal,\n * keeping the subscriber set compact without requiring manual dispose calls.\n */\n *computedSubs(): Generator<ComputedBase<unknown>> {\n for (const [key, ref] of this.computedSubs_) {\n const node = ref.deref();\n\n if (node !== undefined) {\n yield node;\n } else {\n this.computedSubs_.delete(key);\n computedSubRegistry.unregister(ref);\n }\n }\n }\n\n effectSubs(): ReadonlySet<Subscriber> {\n return this.effectSubs_;\n }\n\n abstract get value(): T;\n abstract peek(): T;\n}\n\n// ── Refreshable ──────────────────────────────────────────────────────────────\n\n/**\n * Structural interface for lazy nodes that support dirty-flagging and\n * on-demand recompute. Used by tracking.ts to pull computed deps without\n * importing the concrete ComputedImpl class.\n */\nexport interface Refreshable {\n refreshIfDirty(): boolean;\n}\n\n// ── ComputedBase ──────────────────────────────────────────────────────────────\n\n/**\n * Extension of ReactiveBase for derived (lazy) nodes that can be marked dirty\n * and refreshed on demand. Exposes the two methods scheduling.ts needs without\n * importing the concrete ComputedImpl class (breaks the potential circular dep).\n */\nexport abstract class ComputedBase<T> extends ReactiveBase<T> implements Refreshable {\n [IS_COMPUTED] = true;\n lastPropEpoch_ = 0;\n\n abstract markDirty(): boolean;\n abstract refreshIfDirty(): boolean;\n}\n","import type { AsyncSubscription, CleanupFn, Subscription } from './types';\n\n// ── SubscriptionImpl ────────────────────────────────────────────────────────\n//\n// [Symbol.dispose] delegates to dispose() instead of duplicating the logic.\n// dispose() is idempotent — calling it twice is safe (fn_ set to null after first call).\n\nexport class SubscriptionImpl implements Subscription {\n private fn_: CleanupFn | null;\n private disposed_ = false;\n\n constructor(fn: CleanupFn) {\n this.fn_ = fn;\n }\n\n get disposed(): boolean {\n return this.disposed_;\n }\n\n dispose(): void {\n if (this.disposed_) return;\n\n this.disposed_ = true;\n\n const fn = this.fn_!;\n\n this.fn_ = null;\n fn();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n\n// ── AsyncSubscriptionImpl ─────────────────────────────────────────────────────\n//\n// Wraps a synchronous stop handle + an async drain function.\n// run() awaits the current in-flight run without stopping the effect.\n// [Symbol.asyncDispose] stops the effect synchronously and then awaits full teardown.\n\nexport class AsyncSubscriptionImpl implements AsyncSubscription {\n private awaitDone_: () => Promise<void>;\n private getCurrentRun_: () => Promise<void> | null;\n private syncStop_: Subscription;\n private asyncDisposePromise_: Promise<void> | null = null;\n\n constructor(syncStop: Subscription, awaitDone: () => Promise<void>, getCurrentRun: () => Promise<void> | null) {\n this.awaitDone_ = awaitDone;\n this.getCurrentRun_ = getCurrentRun;\n this.syncStop_ = syncStop;\n }\n\n get disposed(): boolean {\n return this.syncStop_.disposed;\n }\n\n dispose(): void {\n this.syncStop_.dispose();\n }\n\n run(): Promise<void> {\n return this.getCurrentRun_() ?? Promise.resolve();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n\n [Symbol.asyncDispose](): Promise<void> {\n if (this.asyncDisposePromise_ !== null) return this.asyncDisposePromise_;\n\n this.dispose();\n this.asyncDisposePromise_ = this.awaitDone_();\n\n return this.asyncDisposePromise_;\n }\n}\n","import type { ComputedBase, ReactiveBase } from './reactive-base';\nimport type { CleanupFn, Subscriber } from './types';\n\n// ── Global revision clock ─────────────────────────────────────────────────────\n//\n// Monotonically-increasing counter incremented on every signal write.\n// ComputedImpl records the clock value after each successful recompute and uses\n// it as an O(1) \"nothing has changed globally\" fast-path check in refreshIfDirty.\n\nlet globalRevision = 0;\n\nexport const tickRevision = (): number => ++globalRevision;\nexport const getRevision = (): number => globalRevision;\n\n// ── Internal types ────────────────────────────────────────────────────────────\n\nexport type DepEntry = {\n source: ReactiveBase<unknown>;\n version: number;\n};\n\n/** Called by trackSource to observe each reactive source accessed during a tracked run. */\nexport type SourceObserver = (source: ReactiveBase<unknown>) => void;\n\n// TrackingCtx carries both dep-collection state AND an optional sourceObserver.\n// Putting the observer inside the context (R12) scopes it to the specific effect/computed\n// run rather than making it a process-wide global — nested computed recomputes use their\n// own context (no observer), so the debugEffect pattern works without identity checks.\n// sourceObserver is shared across both branches and lives outside the discriminant union.\nexport type TrackingCtx = {\n sourceObserver?: SourceObserver;\n} & (\n | {\n computed: ComputedBase<unknown>;\n depCollector: DepEntry[];\n kind: 'computed';\n }\n | {\n cleanups: CleanupFn[];\n deps: Map<ReactiveBase<unknown>, number>;\n effect: Subscriber;\n kind: 'effect';\n subscriptions: Set<CleanupFn>;\n }\n);\n\n// ── Scheduling state ────────────────────────────────────────────────────────\n//\n// Colocated here (rather than in scheduling.ts) so it can be a field of\n// ExecutionContext without a runtime circular import between tracking.ts and\n// scheduling.ts — scheduling.ts only needs `getSchedulingState()` and the\n// `SchedulingState` type (type-only import, erased at compile time).\n//\n// Isolating this per-request (via the SSR hook) closes the documented gap where\n// concurrent SSR requests share one flush queue: `batchDepth`, `pendingSubscribers`,\n// and the dirty-computed sets are the only scheduling fields that can observably\n// leak across requests (they persist across `await` boundaries via `batch()`\n// spanning shared module-level signals). The propagation epoch/stack in\n// scheduling.ts stay module-level scratch memory — they're only touched within a\n// single synchronous call stack, which JS's run-to-completion model already isolates.\n\nexport type SchedulingState = {\n activeDirty: 'a' | 'b';\n batchDepth: number;\n readonly dirtyWithEffectSubsA: Set<ComputedBase<unknown>>;\n readonly dirtyWithEffectSubsB: Set<ComputedBase<unknown>>;\n readonly pendingSubscribers: Set<Subscriber>;\n};\n\nexport const createSchedulingState = (): SchedulingState => ({\n activeDirty: 'a',\n batchDepth: 0,\n dirtyWithEffectSubsA: new Set(),\n dirtyWithEffectSubsB: new Set(),\n pendingSubscribers: new Set(),\n});\n\n// ── ExecutionContext ──────────────────────────────────────────────────────────\n//\n// Unified context replaces two separate globals (_tracking and _scopeStack).\n// Both the dep-tracking context and the innermost scope's cleanup array live here.\n// The SSR hook overrides the entire context per-request — fixing the prior bug\n// where scope cleanups were always a process-wide singleton even with the hook.\n\nexport type ExecutionContext = {\n readonly scheduling: SchedulingState;\n readonly scopeCleanups: CleanupFn[] | null;\n readonly tracking: TrackingCtx | null;\n};\n\nexport type ContextHook = {\n get(): ExecutionContext;\n run<T>(ctx: ExecutionContext, fn: () => T): T;\n};\n\nlet _ctx: ExecutionContext = { scheduling: createSchedulingState(), scopeCleanups: null, tracking: null };\nlet _hook: ContextHook | null = null;\n\nconst getCtx = (): ExecutionContext => (_hook !== null ? _hook.get() : _ctx);\n\n/** Returns the scheduling-state bucket for the active execution context (module-level singleton outside SSR). */\nexport const getSchedulingState = (): SchedulingState => getCtx().scheduling;\n\n/** `true` once an SSR context hook is installed — scheduling state is then request-isolated. */\nexport const hasContextHook = (): boolean => _hook !== null;\n\n// ── Tracking ──────────────────────────────────────────────────────────────────\n\nexport const getTracking = (): TrackingCtx | null => getCtx().tracking;\n\nexport const withTracking = <T>(tracking: TrackingCtx | null, fn: () => T): T => {\n if (_hook !== null) {\n const current = _hook.get();\n\n return _hook.run({ ...current, tracking }, fn);\n }\n\n const prev = _ctx;\n\n _ctx = { ..._ctx, tracking };\n\n try {\n return fn();\n } finally {\n _ctx = prev;\n }\n};\n\n// ── Scope cleanup ─────────────────────────────────────────────────────────────\n\n/**\n * Pushes `cleanups` as the active scope cleanup array for the duration of `fn`.\n * Any `onCleanup()` calls inside `fn` will register into these cleanups.\n * Effects and computed values created inside `fn` are also auto-registered.\n */\nexport const withScopeCleanups = <T>(cleanups: CleanupFn[], fn: () => T): T => {\n if (_hook !== null) {\n const current = _hook.get();\n\n return _hook.run({ ...current, scopeCleanups: cleanups }, fn);\n }\n\n const prev = _ctx;\n\n _ctx = { ..._ctx, scopeCleanups: cleanups };\n\n try {\n return fn();\n } finally {\n _ctx = prev;\n }\n};\n\n/**\n * Returns the cleanup array of the innermost active scope, or `null` if not inside a scope.\n */\nexport const getScopeCleanups = (): CleanupFn[] | null => getCtx().scopeCleanups;\n\n// ── Auto-disposal ───────────────────────────────────────────────────────────────\n//\n// Shared by every primitive that ties its own lifetime to the context it was\n// created in (computed(), resource()): dispose when the enclosing effect re-runs\n// or disposes, or — if not inside an effect — when the enclosing scope disposes.\n// No-op if neither is active (the caller owns disposal directly).\n\n/**\n * Registers `dispose` to run when the enclosing effect re-runs/disposes, or —\n * if there is no enclosing effect — when the enclosing scope disposes.\n */\nexport const autoRegisterDisposal = (dispose: CleanupFn): void => {\n const ctx = getTracking();\n\n if (ctx?.kind === 'effect') {\n ctx.cleanups.push(dispose);\n } else {\n getScopeCleanups()?.push(dispose);\n }\n};\n\n// ── Context hook (for SSR) ────────────────────────────────────────────────────\n\n/**\n * @internal Used only by `/ripple/ssr`.\n * Installs a context hook that overrides both tracking and scope-cleanup access.\n * Returns the previous hook so callers can restore it.\n */\nexport const _installContextHook = (hook: ContextHook | null): ContextHook | null => {\n const prev = _hook;\n\n _hook = hook;\n\n return prev;\n};\n\n// ── Source observer ───────────────────────────────────────────────────────────\n//\n// Observer is now scoped to the active TrackingCtx instead of a process-wide global.\n// This means it only fires for direct deps of the current effect/computed — nested\n// computed recomputes use their own context (no observer) so no identity check needed.\n\n/**\n * Runs `fn` while calling `observer` for every reactive source directly accessed.\n * Used by `debugEffect` to detect which sources changed between runs.\n * No-op if called outside an active tracking context.\n */\nexport const withSourceObserver = <T>(observer: SourceObserver, fn: () => T): T => {\n const current = getTracking();\n\n if (current === null) return fn();\n\n return withTracking({ ...current, sourceObserver: observer }, fn);\n};\n\n// ── untrack ───────────────────────────────────────────────────────────────────\n\n/**\n * Runs `fn` without recording any reactive reads as dependencies of the enclosing\n * effect or computed. Useful when you need to read reactive state \"silently\".\n *\n * @example\n * ```ts\n * effect(() => {\n * const a = count.value; // tracked\n * const b = untrack(() => name.value); // NOT tracked — no re-run when name changes\n * });\n * ```\n */\nexport const untrack = <T>(fn: () => T): T => withTracking(null, fn);\n\n// ── trackSource ───────────────────────────────────────────────────────────────\n//\n// Records `source` as a dependency of the currently active tracking context.\n// For computed contexts: only records the dep entry — addComputedSub is called\n// later by updateDeps() in computed.ts (only for genuinely NEW deps, preventing\n// duplicate WeakRef entries).\n// For effect contexts: immediately subscribes the effect to the source and\n// records the dep for getDependencies().\n\nexport const trackSource = (source: ReactiveBase<unknown>): void => {\n const ctx = getTracking();\n\n if (ctx === null) return;\n\n ctx.sourceObserver?.(source);\n\n if (ctx.kind === 'computed') {\n ctx.depCollector.push({ source, version: source.version });\n } else if (ctx.kind === 'effect') {\n const owner = ctx.effect;\n\n source.addEffectSub(owner);\n ctx.subscriptions.add(() => source.removeEffectSub(owner));\n ctx.deps.set(source, source.version);\n }\n};\n","import type { Refreshable } from './reactive-base';\nimport type { DepEntry } from './tracking';\nimport type { Computed, ComputedOptions, Subscription } from './types';\n\nimport { ensureError } from './_error-utils';\nimport { getDevToolsHook } from './devtools-hook';\nimport { RippleComputedCycleError } from './errors';\nimport { ComputedBase } from './reactive-base';\nimport { SubscriptionImpl } from './subscription';\nimport { UNINITIALIZED } from './symbols';\nimport { autoRegisterDisposal, getRevision, trackSource, withTracking } from './tracking';\n\nexport class ComputedImpl<T> extends ComputedBase<T> implements Computed<T> {\n private value_: T | typeof UNINITIALIZED;\n private dirty_: boolean;\n private computing_: boolean;\n private disposed_: boolean;\n private deps_: DepEntry[];\n private compute_: () => T;\n private equals_: (a: unknown, b: unknown) => boolean;\n // Global revision at last successful compute — enables O(1) \"nothing changed\" fast-path.\n private maxRevision_: number;\n\n constructor(compute: () => T, options?: ComputedOptions<T>) {\n const { equals, name } = options ?? {};\n\n super(name);\n this.value_ = UNINITIALIZED;\n this.dirty_ = true;\n this.computing_ = false;\n this.disposed_ = false;\n this.deps_ = [];\n this.maxRevision_ = -1;\n this.compute_ = compute;\n this.equals_ = equals !== undefined ? (a, b) => equals(a as T, b as T) : Object.is;\n }\n\n markDirty(): boolean {\n if (this.disposed_ || this.dirty_) return false;\n\n this.dirty_ = true;\n\n return true;\n }\n\n refreshIfDirty(): boolean {\n if (!this.dirty_) return false;\n\n // O(1) global revision fast-path:\n // If the global revision clock has not advanced past our recorded maximum,\n // no signal anywhere in the system has been written since our last compute.\n // No dep can have changed — clear dirty and skip the per-dep scan entirely.\n if (getRevision() <= this.maxRevision_) {\n this.dirty_ = false;\n\n return false;\n }\n\n // Per-dep scan: if all dep versions still match, value cannot have changed.\n // For computed deps, we must refresh them first — their version only updates\n // after recompute, so a stale-version check would incorrectly short-circuit.\n if (this.deps_.length > 0) {\n let allSame = true;\n\n for (const d of this.deps_) {\n const src = d.source;\n\n // Flush dirty computed deps before comparing versions (lazy pull chain).\n if ('refreshIfDirty' in src) {\n (src as Refreshable).refreshIfDirty();\n }\n\n if (src.version !== d.version) {\n allSame = false;\n break;\n }\n }\n\n if (allSame) {\n this.dirty_ = false;\n this.maxRevision_ = getRevision();\n\n return false;\n }\n }\n\n return this.recompute();\n }\n\n private runCompute(depCollector: DepEntry[]): T {\n try {\n return withTracking({ computed: this, depCollector, kind: 'computed' }, this.compute_);\n } catch (error) {\n // Sources accessed before the throw remain in their computedSubs_ until the\n // next successful recompute or dispose(). The computed stays dirty and will retry.\n throw ensureError(error);\n }\n }\n\n private recompute(): boolean {\n if (this.computing_) {\n const label = this.name ? ` \"${this.name}\"` : '';\n\n throw new RippleComputedCycleError(`computed cycle detected${label}`);\n }\n\n this.computing_ = true;\n\n try {\n const newDeps: DepEntry[] = [];\n\n getDevToolsHook()?.compute?.({ name: this.name });\n\n const next = this.runCompute(newDeps);\n\n this.dirty_ = false;\n this.maxRevision_ = getRevision();\n\n // Diff deps: remove subscriptions for sources that are no longer dependencies.\n // Only subscribe to genuinely NEW deps — prevents duplicate WeakRef entries.\n this.updateDeps(newDeps);\n\n const isNew = this.value_ === UNINITIALIZED;\n\n if (isNew || !this.equals_(this.value_ as T, next)) {\n this.value_ = next;\n this.version++;\n\n return true;\n }\n\n return false;\n } finally {\n this.computing_ = false;\n }\n }\n\n private updateDeps(newDeps: DepEntry[]): void {\n const oldDeps = this.deps_;\n\n // Fast path: same sources in same order — just refresh stored versions\n if (oldDeps.length === newDeps.length && oldDeps.every((d, i) => d.source === newDeps[i].source)) {\n for (let i = 0; i < newDeps.length; i++) {\n oldDeps[i]!.version = newDeps[i]!.version;\n }\n\n return;\n }\n\n // Slow path: deps changed.\n // Build a set of OLD sources so we can detect which are genuinely NEW and\n // need a fresh addComputedSub call (WeakRef registration).\n const oldSourceSet = new Set(oldDeps.map((d) => d.source));\n const newSourceSet = new Set(newDeps.map((d) => d.source));\n\n // Unsubscribe from sources that are no longer dependencies\n for (const old of oldDeps) {\n if (!newSourceSet.has(old.source)) {\n old.source.removeComputedSub(this);\n }\n }\n\n // Subscribe to sources that are genuinely NEW (not in old deps)\n for (const dep of newDeps) {\n if (!oldSourceSet.has(dep.source)) {\n dep.source.addComputedSub(this);\n }\n }\n\n this.deps_ = newDeps;\n }\n\n get value(): T {\n // After disposal: return last known value without tracking (inert node, like signal).\n // If the computed was never evaluated (still UNINITIALIZED), return undefined.\n if (this.disposed_) {\n return this.value_ !== UNINITIALIZED ? (this.value_ as T) : (undefined as unknown as T);\n }\n\n this.refreshIfDirty();\n trackSource(this);\n\n return this.value_ as T;\n }\n\n // peek() after dispose: return last known value silently, consistent with signal.peek().\n // If the computed was never evaluated, return undefined (same as signal with undefined initial).\n peek(): T {\n if (this.disposed_) {\n return this.value_ !== UNINITIALIZED ? (this.value_ as T) : (undefined as unknown as T);\n }\n\n this.refreshIfDirty();\n\n return this.value_ as T;\n }\n\n readonly subscribe = (listener: () => void): Subscription => {\n if (this.disposed_) {\n const noop = new SubscriptionImpl(() => {});\n\n noop.dispose();\n\n return noop;\n }\n\n this.refreshIfDirty();\n this.addEffectSub(listener);\n\n return new SubscriptionImpl(() => {\n this.removeEffectSub(listener);\n });\n };\n\n get disposed(): boolean {\n return this.disposed_;\n }\n\n dispose(): void {\n if (this.disposed_) return;\n\n this.disposed_ = true;\n\n // Unsubscribe from all upstream sources\n for (const dep of this.deps_) {\n dep.source.removeComputedSub(this);\n }\n\n this.deps_ = [];\n this.clearSubscribers();\n getDevToolsHook()?.dispose?.({ kind: 'computed', name: this.name });\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n\n// ── computed() ───────────────────────────────────────────────────────────────\n\nexport const computed = <T>(compute: () => T, options?: ComputedOptions<T>): Computed<T> => {\n const comp = new ComputedImpl(compute, options);\n\n autoRegisterDisposal(() => comp.dispose());\n\n return comp;\n};\n","const isDev = !(globalThis as { __RIPPLE_PROD__?: boolean }).__RIPPLE_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/ripple] ${msg}`);\n}\n","import type { ComputedBase, ReactiveBase } from './reactive-base';\nimport type { Subscriber } from './types';\n\nimport { warn } from './_dev';\nimport { runAll } from './_error-utils';\nimport { RippleInfiniteLoopError } from './errors';\nimport { getSchedulingState, hasContextHook, type SchedulingState } from './tracking';\n\nexport const DEFAULT_MAX_ITERATIONS = 100;\n\n// batchDepth, pendingSubscribers, and the dirty-computed sets live in `SchedulingState`\n// (see tracking.ts) rather than as module-level variables — this lets the /ripple/ssr\n// sub-path isolate the flush queue per request (via the same AsyncLocalStorage-backed\n// context hook that already isolates tracking), closing the gap where concurrent SSR\n// requests previously shared one flush queue. Outside SSR (no hook installed), this is\n// a single module-level singleton with identical behavior and cost to before.\nlet ssrWarnFired = false;\n\n// ── F1: Simplified flush — no topological sort ─────────────────────────────\n//\n// Push phase (propagateDirty):\n// Mark all downstream computeds dirty in a BFS pass.\n// Directly collect effect subscribers of the changed signal.\n// For computeds with direct effect subscribers, defer to the pull phase.\n//\n// Pull phase (recomputeWithEffectSubs):\n// For each dirty computed that has direct effect subscribers, recompute it.\n// If the value changed, queue its effect subscribers.\n// Any further downstream dirty computeds with effect subs are handled in the\n// same loop (re-queued into dirtyWithEffectSubs if they become dirty during\n// a parent recompute).\n//\n// Effect phase: run all queued effect subscribers.\n//\n// This eliminates the O(n²) topo sort while preserving the equality-suppression\n// guarantee: effect subs of a computed are only notified when the computed's\n// value actually changes.\n//\n// Computeds without direct effect subs (pure lazy nodes) are still pulled\n// lazily when an effect reads them during re-run.\n\n// Module-level scratch state — safe to share even under SSR isolation. Both are only\n// touched within a single synchronous call stack (no `await` inside propagateDirty /\n// recomputeWithEffectSubs / flushEffects), which JS's run-to-completion model already\n// isolates; they never carry information across two separate top-level signal writes.\nlet _propEpoch = 0;\nconst _propagateStack: ComputedBase<unknown>[] = [];\n\n/** Returns (and possibly swaps) the currently-active dirty-with-effect-subs set. */\nconst activeDirtySet = (s: SchedulingState): Set<ComputedBase<unknown>> =>\n s.activeDirty === 'a' ? s.dirtyWithEffectSubsA : s.dirtyWithEffectSubsB;\n\nconst propagateDirty = (\n s: SchedulingState,\n node: {\n computedSubs(): Generator<ComputedBase<unknown>>;\n effectSubs(): ReadonlySet<Subscriber>;\n },\n): void => {\n const epoch = ++_propEpoch;\n\n // Queue direct effect subs of the changed node\n for (const sub of node.effectSubs()) {\n s.pendingSubscribers.add(sub);\n }\n\n for (const c of node.computedSubs()) {\n _propagateStack.push(c);\n }\n\n try {\n while (_propagateStack.length > 0) {\n const c = _propagateStack.pop()!;\n\n if (c.lastPropEpoch_ === epoch) continue;\n\n c.lastPropEpoch_ = epoch;\n\n if (!c.markDirty()) continue; // already dirty — already processed\n\n // If this computed has effect subs, defer recompute to the pull phase.\n // We cannot queue them immediately because the computed might not change.\n if (c.effectSubs().size > 0) {\n activeDirtySet(s).add(c);\n }\n\n for (const downstream of c.computedSubs()) {\n _propagateStack.push(downstream);\n }\n }\n } finally {\n _propagateStack.length = 0;\n }\n};\n\nconst recomputeWithEffectSubs = (s: SchedulingState): void => {\n // Iterate until stable — recomputing one computed may dirty others.\n // Set-swap pattern: swap active/idle sets each pass so new dirty nodes accumulate\n // in the idle set while we iterate the current batch — zero array allocations.\n while (activeDirtySet(s).size > 0) {\n const toProcess = activeDirtySet(s);\n\n // Swap: new dirty nodes from this iteration go into the other set\n s.activeDirty = s.activeDirty === 'a' ? 'b' : 'a';\n activeDirtySet(s).clear();\n\n for (const c of toProcess) {\n if (!c.hasSubscribers()) continue;\n\n const changed = c.refreshIfDirty();\n\n if (!changed) continue;\n\n // Value changed — queue effect subs\n for (const sub of c.effectSubs()) {\n s.pendingSubscribers.add(sub);\n }\n\n // Downstream computeds may now need recomputing too\n for (const downstream of c.computedSubs()) {\n if (downstream.markDirty() && downstream.effectSubs().size > 0) {\n activeDirtySet(s).add(downstream);\n }\n }\n }\n\n toProcess.clear();\n }\n};\n\nconst flushEffects = (s: SchedulingState): void => {\n let iterations = 0;\n\n while (s.pendingSubscribers.size > 0 || activeDirtySet(s).size > 0) {\n if (++iterations > DEFAULT_MAX_ITERATIONS) {\n throw new RippleInfiniteLoopError(`infinite flush loop (> ${DEFAULT_MAX_ITERATIONS} iterations)`);\n }\n\n if (activeDirtySet(s).size > 0) recomputeWithEffectSubs(s);\n\n if (s.pendingSubscribers.size === 0) continue;\n\n const subscribersToRun = [...s.pendingSubscribers];\n\n s.pendingSubscribers.clear();\n runAll(subscribersToRun, 'subscriber errors');\n }\n};\n\n// Fire the warning once per module load in environments that look like Node.js,\n// and only when no SSR context hook is installed (once installed, the flush queue\n// is genuinely request-isolated via SchedulingState — see tracking.ts).\n// window-check is unreliable (jsdom sets window in tests); check for process.versions instead.\n// Access entirely via globalThis to avoid requiring @types/node in downstream tsconfigs.\nconst isNodeLike = (globalThis as { process?: { versions?: unknown } }).process?.versions != null;\n\nexport const notifyNodeChange = (node: ReactiveBase<unknown>): void => {\n if (!node.hasSubscribers()) return;\n\n if (!ssrWarnFired && isNodeLike && !hasContextHook()) {\n ssrWarnFired = true;\n warn(\n 'Signal updated in a Node.js-like environment. The module-level flush queue is shared across concurrent requests ' +\n '— use per-request worker isolation or the @vielzeug/ripple/ssr sub-path for request-isolated scheduling.',\n );\n }\n\n const s = getSchedulingState();\n\n propagateDirty(s, node);\n\n if (s.batchDepth === 0) flushEffects(s);\n};\n\nexport const batch = <T>(fn: () => T): T => {\n const s = getSchedulingState();\n\n s.batchDepth++;\n\n let result: T;\n\n try {\n result = fn();\n } catch (e) {\n s.batchDepth--;\n\n // Outermost batch: clear the pending flush queue on body error.\n // Leaving stale subscribers in the queue would flush them on the next unrelated\n // signal write, surfacing errors in unexpected contexts.\n if (s.batchDepth === 0) {\n s.pendingSubscribers.clear();\n s.dirtyWithEffectSubsA.clear();\n s.dirtyWithEffectSubsB.clear();\n }\n\n throw e;\n }\n\n s.batchDepth--;\n\n if (s.batchDepth === 0) flushEffects(s);\n\n return result;\n};\n","import type { Signal, SignalOptions, Subscription } from './types';\n\nimport { getDevToolsHook } from './devtools-hook';\nimport { ReactiveBase } from './reactive-base';\nimport { notifyNodeChange } from './scheduling';\nimport { SubscriptionImpl } from './subscription';\nimport { tickRevision, trackSource } from './tracking';\n\nexport class SignalImpl<T> extends ReactiveBase<T> implements Signal<T> {\n private value_: T;\n private equals_: (a: T, b: T) => boolean;\n private disposed_: boolean;\n\n constructor(initial: T, equals?: (a: T, b: T) => boolean, name?: string) {\n super(name);\n this.value_ = initial;\n this.equals_ = equals ?? Object.is;\n this.disposed_ = false;\n }\n\n get value(): T {\n if (!this.disposed_) trackSource(this);\n\n return this.value_;\n }\n\n set value(next: T) {\n if (this.disposed_) return;\n\n if (this.equals_(this.value_, next)) return;\n\n const oldValue = this.value_;\n\n this.value_ = next;\n this.version = tickRevision();\n\n getDevToolsHook()?.write?.({ name: this.name, newValue: next, oldValue });\n notifyNodeChange(this);\n }\n\n peek(): T {\n return this.value_;\n }\n\n readonly subscribe = (listener: () => void): Subscription => {\n if (this.disposed_) {\n const noop = new SubscriptionImpl(() => {});\n\n noop.dispose();\n\n return noop;\n }\n\n this.addEffectSub(listener);\n\n return new SubscriptionImpl(() => {\n this.removeEffectSub(listener);\n });\n };\n\n /**\n * Marks the signal as permanently inert. After disposal:\n * - reads still return the last known value (but no longer track dependencies)\n * - writes are silently ignored\n * - new subscriptions are immediately unsubscribed\n * - all existing subscriber and computed-subscriber references are dropped\n */\n get disposed(): boolean {\n return this.disposed_;\n }\n\n dispose(): void {\n if (this.disposed_) return;\n\n this.disposed_ = true;\n this.clearSubscribers();\n getDevToolsHook()?.dispose?.({ kind: 'signal', name: this.name });\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n\nexport const signal = <T>(initial: T, options?: SignalOptions<T>): Signal<T> =>\n new SignalImpl(initial, options?.equals, options?.name);\n","import type { PathValue, Readable, Signal, Store, Subscription } from './types';\n\nimport { computed } from './computed';\nimport { getDevToolsHook } from './devtools-hook';\nimport { RippleInvalidStoreError } from './errors';\nimport { batch } from './scheduling';\nimport { SignalImpl } from './signal';\nimport { IS_SIGNAL, IS_STORE } from './symbols';\n\n// ── Key safety ────────────────────────────────────────────────────────────────\n\n/**\n * Rejects prototype-pollution-prone top-level keys. Called both as an upfront\n * validation pass (patch/replace — so a later unsafe key never leaves earlier,\n * safe keys partially applied) and defensively inside applyTopLevelChange_.\n */\nconst assertSafeKey = (key: string): void => {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n throw new RippleInvalidStoreError(`Unsafe key \"${key}\" rejected to prevent prototype pollution.`);\n }\n};\n\n// ── Nested path helpers ───────────────────────────────────────────────────────\n\nconst getNestedValue = (obj: unknown, parts: string[]): unknown => {\n let current = obj;\n\n for (const key of parts) {\n if (current == null || typeof current !== 'object') return undefined;\n\n current = (current as Record<string, unknown>)[key];\n }\n\n return current;\n};\n\nconst setNestedValue = <T>(obj: T, parts: string[], value: unknown): T => {\n if (parts.length === 0) return value as T;\n\n if (obj === null || typeof obj !== 'object') {\n throw new RippleInvalidStoreError('Cannot write to a nested path through a null or non-object intermediate value.');\n }\n\n const [key, ...rest] = parts;\n const currentObj = obj as Record<string, unknown>;\n\n return { ...currentObj, [key!]: setNestedValue(currentObj[key!], rest, value) } as T;\n};\n\n// ── LensSignal ───────────────────────────────────────────────────────────────\n//\n// A unified writable Signal that delegates reads to an inner Reactive<V>\n// and writes to an injected callback. Replaces the old TopLevelLens +\n// NestedLens pair — same interface, one class to update when Signal<T> changes.\n\ntype LensOptions<V> = {\n disposeSource?: () => void;\n evict: () => void;\n name?: string;\n source: Readable<V>;\n write: (v: V) => void;\n};\n\nclass LensSignal<V> implements Signal<V> {\n [IS_SIGNAL] = true as const;\n\n private source_: Readable<V>;\n private write_: (v: V) => void;\n private evict_: () => void;\n private disposeSource_: (() => void) | undefined;\n private name_: string | undefined;\n\n get name(): string | undefined {\n return this.name_ ?? this.source_.name;\n }\n\n constructor(opts: LensOptions<V>) {\n this.source_ = opts.source;\n this.write_ = opts.write;\n this.evict_ = opts.evict;\n this.disposeSource_ = opts.disposeSource;\n this.name_ = opts.name;\n }\n\n get value(): V {\n return this.source_.value;\n }\n\n set value(next: V) {\n this.write_(next);\n }\n\n peek(): V {\n return this.source_.peek();\n }\n\n subscribe(listener: () => void): Subscription {\n return this.source_.subscribe(listener);\n }\n\n get disposed(): boolean {\n return this.source_.disposed;\n }\n\n dispose(): void {\n this.evict_();\n this.disposeSource_?.();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n\n// ── StoreImpl ─────────────────────────────────────────────────────────────────\n//\n// Fine-grained reactive store.\n//\n// Internal layout:\n// - current_: mutable backing object — NEVER exposed directly, internal writes only\n// - readonlyProxy_: Proxy over current_ that throws on set/delete — returned by .peek()\n// - propSignals_: per-top-level-key SignalImpl, lazily created on first lens/read\n// - version_: whole-store monotonic counter (bumped on any change, used by subscribe())\n//\n// StoreImpl is a standalone class — does not extend ReactiveBase.\n// applyTopLevelChange_() has NO batch() wrapper — callers that need atomicity\n// (patch, replace, reset, and both lens write paths) wrap with batch() at their\n// own call sites; the method itself stays free of any scheduling concern.\n\nexport class StoreImpl<T extends object> implements Store<T> {\n [IS_SIGNAL] = true as const;\n [IS_STORE] = true as const;\n\n readonly name: string | undefined;\n\n private current_: T;\n private disposed_: boolean;\n private readonly initial_: T;\n private readonly lensCache_: Map<string, LensSignal<unknown>>;\n private readonly propSignals_: Map<string, SignalImpl<unknown>>;\n private readonly readonlyProxy_: Readonly<T>;\n private readonly version_: SignalImpl<number>;\n\n constructor(initial: T, name?: string) {\n this.name = name;\n this.current_ = structuredClone(initial);\n this.disposed_ = false;\n this.initial_ = structuredClone(initial);\n this.lensCache_ = new Map();\n this.propSignals_ = new Map();\n this.version_ = new SignalImpl(0, undefined, name ? `${name}.version` : undefined);\n\n // Shallow proxy: throws on any top-level mutation attempt.\n // All external reads go through this proxy; internal writes use this.current_ directly.\n // NOTE: nested objects are plain references — mutations on nested properties\n // bypass reactivity. Use store.lens('a.b') or store.replace() to update nested state.\n this.readonlyProxy_ = new Proxy(this.current_, {\n deleteProperty(_target, key): never {\n throw new RippleInvalidStoreError(\n `Direct deletion from store.value is not allowed. Use store.patch() or store.lens(). (key: \"${String(key)}\")`,\n );\n },\n set(_target, key): never {\n throw new RippleInvalidStoreError(\n `Direct mutation of store.value is not allowed. Use store.patch(), store.lens(), or store.replace(). (key: \"${String(key)}\")`,\n );\n },\n }) as Readonly<T>;\n }\n\n /** Returns (or lazily creates) the per-property signal for a top-level key. */\n propSignalFor_(key: string): SignalImpl<unknown> {\n let sig = this.propSignals_.get(key);\n\n if (sig === undefined) {\n sig = new SignalImpl(\n (this.current_ as Record<string, unknown>)[key],\n undefined,\n this.name !== undefined ? `${this.name}.${key}` : key,\n );\n this.propSignals_.set(key, sig);\n }\n\n return sig;\n }\n\n /** @internal Called by nested lens write to update the store through a path. */\n setPath_(parts: string[], value: unknown): void {\n const [rootKey, ...rest] = parts;\n const rootVal = (this.current_ as Record<string, unknown>)[rootKey!];\n const newRootVal = setNestedValue(rootVal, rest, value);\n\n this.applyTopLevelChange_(rootKey!, newRootVal);\n }\n\n /**\n * @internal Apply a change to a top-level key and propagate to prop signal + version.\n * R4: No batch() wrapper — callers that need atomicity must wrap themselves.\n */\n applyTopLevelChange_(key: string, newValue: unknown): void {\n assertSafeKey(key);\n\n const current = (this.current_ as Record<string, unknown>)[key];\n\n if (Object.is(current, newValue)) return;\n\n (this.current_ as Record<string, unknown>)[key] = newValue;\n this.propSignalFor_(key).value = newValue as never;\n this.version_.value = this.version_.peek() + 1;\n }\n\n /**\n * @internal Removes a top-level key entirely (as opposed to applyTopLevelChange_,\n * which can only ever assign a value — including `undefined` — never delete the key).\n * Companion used by applyFullState_ for keys absent from the target state.\n */\n deleteTopLevelKey_(key: string): void {\n if (!Object.hasOwn(this.current_ as object, key)) return;\n\n delete (this.current_ as Record<string, unknown>)[key];\n this.propSignalFor_(key).value = undefined as never;\n this.version_.value = this.version_.peek() + 1;\n }\n\n /**\n * @internal Applies `next` as the authoritative full top-level state: keys present in\n * `next` are set via applyTopLevelChange_, keys present in current_ but absent from\n * `next` are removed via deleteTopLevelKey_. Shared by replace() and reset() so both\n * can actually remove a key, not just null it out — see B1 in the ripple improvement plan.\n */\n applyFullState_(next: Record<string, unknown>): void {\n const current = this.current_ as Record<string, unknown>;\n const allKeys = new Set([...Object.keys(current), ...Object.keys(next)]);\n\n for (const key of allKeys) assertSafeKey(key);\n\n batch(() => {\n for (const key of allKeys) {\n if (Object.hasOwn(next, key)) {\n this.applyTopLevelChange_(key, next[key]);\n } else {\n this.deleteTopLevelKey_(key);\n }\n }\n });\n }\n\n /**\n * Returns the current state as a tracked read — registers the store as a dependency\n * inside `effect()` / `computed()`. Any mutation re-runs the subscriber.\n * For untracked one-off reads, use `peek()` instead.\n */\n get value(): Readonly<T> {\n void this.version_.value;\n\n return this.readonlyProxy_;\n }\n\n /**\n * Returns a frozen deep snapshot of the current state.\n * The returned object reflects state at call time — it does not update as the store mutates.\n * Nested objects are cloned (not aliased), so mutating nested properties on the snapshot\n * does not affect the live store state.\n * Use for serialization or one-off reads outside reactive contexts.\n * For reactive reads, use `store.lens(path)` instead.\n */\n peek(): Readonly<T> {\n return Object.freeze(structuredClone(this.current_)) as Readonly<T>;\n }\n\n readonly subscribe = (listener: () => void): Subscription => {\n return this.version_.subscribe(listener);\n };\n\n patch(partial: Partial<T>): void {\n if (typeof partial !== 'object' || partial === null || Array.isArray(partial)) {\n throw new RippleInvalidStoreError('store.patch() requires a plain object partial.');\n }\n\n const keys = Object.keys(partial) as Array<keyof T & string>;\n\n if (keys.length === 0) return;\n\n // Validate every key before mutating any — a key rejected mid-loop must not\n // leave earlier, safe keys partially applied (and un-notified — see batch()'s\n // error path, which clears the pending flush queue rather than replaying it).\n for (const key of keys) assertSafeKey(key);\n\n batch(() => {\n for (const key of keys) {\n this.applyTopLevelChange_(key, (partial as Record<string, unknown>)[key]);\n }\n });\n\n getDevToolsHook()?.mutate?.({ kind: 'patch', name: this.name });\n }\n\n replace(fn: (state: Readonly<T>) => T): void {\n const snapshot = structuredClone(this.current_) as Readonly<T>;\n const next = fn(snapshot);\n\n if (next === snapshot) return;\n\n this.applyFullState_(next as Record<string, unknown>);\n\n getDevToolsHook()?.mutate?.({ kind: 'replace', name: this.name });\n }\n\n reset(): void {\n this.applyFullState_(this.initial_ as Record<string, unknown>);\n\n getDevToolsHook()?.mutate?.({ kind: 'reset', name: this.name });\n }\n\n lens<P extends string>(path: P): Signal<PathValue<T, P>> {\n const cached = this.lensCache_.get(path);\n\n if (cached !== undefined) return cached as unknown as Signal<PathValue<T, P>>;\n\n const evict = (): void => {\n this.lensCache_.delete(path);\n };\n\n const parts = path.split('.');\n\n if (parts.length > 32) {\n throw new RippleInvalidStoreError(`Lens path exceeds maximum depth of 32 segments: \"${path}\".`);\n }\n\n for (const part of parts) {\n if (part === '') {\n throw new RippleInvalidStoreError(`Empty path segment in lens path \"${path}\". Check for consecutive dots.`);\n }\n\n if (part === '__proto__' || part === 'constructor' || part === 'prototype') {\n throw new RippleInvalidStoreError(`Unsafe path segment \"${part}\" in lens path \"${path}\".`);\n }\n }\n\n let lens: LensSignal<unknown>;\n\n if (parts.length === 1) {\n // Top-level lens: backed directly by the per-property signal.\n const propSig = this.propSignalFor_(parts[0]!) as SignalImpl<PathValue<T, P>>;\n\n lens = new LensSignal<PathValue<T, P>>({\n evict,\n // propSig lifecycle is owned by the store — do not dispose it\n source: propSig,\n write: (v) => {\n batch(() => this.applyTopLevelChange_(parts[0]!, v));\n getDevToolsHook()?.mutate?.({ kind: 'lens', name: this.name, path });\n },\n }) as unknown as LensSignal<unknown>;\n } else {\n // Nested lens: backed by a derived computed over the root property signal.\n // Write path uses batch() here so nested lens writes remain atomic.\n const readComputed = computed(\n () => getNestedValue(this.propSignalFor_(parts[0]!).value, parts.slice(1)) as PathValue<T, P>,\n );\n\n lens = new LensSignal<PathValue<T, P>>({\n disposeSource: () => readComputed.dispose(),\n evict,\n name: this.name ? `${this.name}.${path}` : path,\n source: readComputed,\n write: (v) => {\n const current = readComputed.peek();\n\n if (Object.is(current, v)) return;\n\n batch(() => this.setPath_(parts, v));\n getDevToolsHook()?.mutate?.({ kind: 'lens', name: this.name, path });\n },\n }) as unknown as LensSignal<unknown>;\n }\n\n this.lensCache_.set(path, lens);\n\n return lens as unknown as Signal<PathValue<T, P>>;\n }\n\n get disposed(): boolean {\n return this.disposed_;\n }\n\n dispose(): void {\n if (this.disposed_) return;\n\n this.disposed_ = true;\n\n for (const lens of [...this.lensCache_.values()]) {\n lens.dispose();\n }\n\n this.lensCache_.clear();\n\n for (const sig of this.propSignals_.values()) {\n sig.dispose();\n }\n\n this.propSignals_.clear();\n this.version_.dispose();\n getDevToolsHook()?.dispose?.({ kind: 'store', name: this.name });\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n\nexport const store = <T extends object>(initial: T, options?: { name?: string }): Store<T> => {\n if (typeof initial !== 'object' || initial === null || Array.isArray(initial)) {\n throw new RippleInvalidStoreError('store() requires a plain object initial state.');\n }\n\n // Same guard as patch()/replace() — reject an unsafe top-level key (e.g. a real\n // own \"__proto__\" property from JSON.parse) before it ever enters store state.\n for (const key of Object.keys(initial)) assertSafeKey(key);\n\n return new StoreImpl(initial, options?.name) as unknown as Store<T>;\n};\n","import type { ReactiveBase } from './reactive-base';\nimport type {\n AsyncEffectCallback,\n AsyncSubscription,\n CleanupFn,\n DepInfo,\n EffectAsyncOptions,\n EffectCallback,\n EffectHandle,\n EffectOptions,\n EqualityFn,\n Readable,\n Scope,\n Subscriber,\n Subscription,\n WatchOptions,\n} from './types';\n\nimport { collectErrors, rethrowWith, runAll } from './_error-utils';\nimport { getDevToolsHook } from './devtools-hook';\nimport { RippleDisposedScopeError, RippleInfiniteLoopError, RippleInvalidCleanupError } from './errors';\nimport { DEFAULT_MAX_ITERATIONS } from './scheduling';\nimport { AsyncSubscriptionImpl, SubscriptionImpl } from './subscription';\nimport { IS_COMPUTED } from './symbols';\nimport { getScopeCleanups, getTracking, withScopeCleanups, withTracking } from './tracking';\n\n/**\n * Wraps a core `run` function with a microtask-based scheduler that coalesces rapid re-runs.\n * The `subscriber` returned here is what gets registered in signal subscriber sets —\n * so notifications call the deferred wrapper, not `run` directly.\n *\n * The scheduler identity is stable for the lifetime of the effect.\n * Never replace `subscriber` with a new function after creation — doing so would break\n * the removeEffectSub calls that rely on reference identity.\n */\nconst withScheduler = (run: Subscriber, scheduler: EffectOptions['scheduler']): Subscriber => {\n if (scheduler === 'sync' || scheduler === undefined) return run;\n\n // 'microtask'\n let scheduled = false;\n\n return (): void => {\n if (scheduled) return;\n\n scheduled = true;\n queueMicrotask(() => {\n scheduled = false;\n run();\n });\n };\n};\n\n/**\n * Creates a reactive side-effect.\n * Runs immediately, re-runs whenever tracked dependencies change.\n * The callback may return a cleanup function that runs before the next re-run and on dispose.\n *\n * @example\n * ```ts\n * const stop = effect(() => {\n * document.title = count.value.toString();\n * return () => { document.title = ''; }; // optional cleanup\n * });\n *\n * stop.dispose(); // or: using stop = effect(...)\n * console.log(stop.getDependencies()); // reactive graph introspection\n * ```\n */\nexport const effect = (fn: EffectCallback, options?: EffectOptions): EffectHandle => {\n const scheduler = options?.scheduler ?? 'sync';\n const effectName = options?.name;\n\n const runCleanups: CleanupFn[] = [];\n const subscriptions = new Set<CleanupFn>();\n const currentDeps = new Map<ReactiveBase<unknown>, number>();\n let isRunning = false;\n let isDirty = false;\n let isDisposed = false;\n\n const teardown = (): void => {\n currentDeps.clear();\n\n if (runCleanups.length === 0 && subscriptions.size === 0) return;\n\n const toRun = [...runCleanups, ...subscriptions];\n\n runCleanups.length = 0;\n subscriptions.clear();\n\n runAll(toRun, 'effect teardown errors');\n };\n\n // The inner do-while handles self-cascade: when the effect body writes to a signal it reads,\n // the recursive flush sets isDirty via the isRunning guard.\n const run: Subscriber = (): void => {\n if (isDisposed) return;\n\n if (isRunning) {\n isDirty = true;\n\n return;\n }\n\n isRunning = true;\n\n try {\n let iterations = 0;\n\n do {\n if (++iterations > DEFAULT_MAX_ITERATIONS) {\n const label = effectName ? ` in \"${effectName}\"` : '';\n\n throw new RippleInfiniteLoopError(`infinite effect loop (> ${DEFAULT_MAX_ITERATIONS} iterations)${label}`);\n }\n\n isDirty = false;\n teardown();\n\n getDevToolsHook()?.run?.({ name: effectName });\n\n let returnedCleanup: CleanupFn | void = undefined;\n\n try {\n returnedCleanup = withTracking(\n { cleanups: runCleanups, deps: currentDeps, effect: subscriber, kind: 'effect', subscriptions },\n fn,\n );\n } catch (error) {\n const cleanupErrors = [...collectErrors(subscriptions), ...collectErrors(runCleanups)];\n\n runCleanups.length = 0;\n subscriptions.clear();\n rethrowWith(error, cleanupErrors, 'effect failure with cleanup errors');\n }\n\n if (returnedCleanup !== undefined && typeof returnedCleanup !== 'function') {\n throw new RippleInvalidCleanupError(\n `effect() returned ${typeof returnedCleanup} — expected a cleanup function or void.`,\n );\n }\n\n if (typeof returnedCleanup === 'function') runCleanups.push(returnedCleanup);\n } while (isDirty && !isDisposed);\n } finally {\n isRunning = false;\n }\n };\n\n // subscriber is what gets registered in signal subscriber Sets.\n // For 'sync' it is the same as run; for deferred schedulers it's the debouncing wrapper.\n const subscriber = withScheduler(run, scheduler);\n\n // Always run synchronously on creation to establish initial tracking.\n run();\n\n const sub = new SubscriptionImpl(() => {\n if (isDisposed) return;\n\n isDisposed = true;\n teardown();\n getDevToolsHook()?.dispose?.({ kind: 'effect', name: effectName });\n });\n\n // Auto-register disposal into the enclosing scope (if any).\n // Uses the separate scope stack — orthogonal to the effect/computed tracking context.\n getScopeCleanups()?.push(() => sub.dispose());\n\n const handle: EffectHandle = {\n dispose: () => sub.dispose(),\n get disposed() {\n return sub.disposed;\n },\n getDependencies(): ReadonlyArray<DepInfo> {\n return [...currentDeps.keys()].map((src) => ({\n kind: IS_COMPUTED in (src as object) ? 'computed' : 'signal',\n name: src.name,\n }));\n },\n [Symbol.dispose]() {\n sub.dispose();\n },\n };\n\n return handle;\n};\n\n/**\n * Like `effect()`, but the callback is async and receives an AbortSignal that fires when the\n * effect re-runs or is disposed. Read reactive dependencies synchronously before the first\n * `await` to register them as tracked.\n *\n * Returns an {@link AsyncSubscription} — call `await stop[Symbol.asyncDispose]()` to await\n * full teardown, or `stop.run()` to await just the current in-flight run.\n *\n * @example\n * ```ts\n * const userId = signal('u1');\n * const stop = effectAsync(async (signal) => {\n * const id = userId.value; // sync dep — tracked\n * const data = await fetchUser(id, { signal }); // aborted automatically if id changes\n * renderUser(data);\n * });\n *\n * await stop[Symbol.asyncDispose]();\n * ```\n */\nexport const effectAsync = (fn: AsyncEffectCallback, options?: EffectAsyncOptions): AsyncSubscription => {\n let controller: AbortController | null = null;\n let asyncCleanup: CleanupFn | null = null;\n let currentRunPromise: Promise<void> | null = null;\n\n const onError =\n options?.onError ??\n ((err: unknown) => {\n queueMicrotask(() => {\n throw err;\n });\n });\n\n const syncStop = effect(\n () => {\n controller?.abort();\n asyncCleanup?.();\n asyncCleanup = null;\n\n controller = new AbortController();\n\n const { signal } = controller;\n const runOwner = scope(); // per-run owner scope for async body resources\n\n let theRun!: Promise<void>;\n\n currentRunPromise = theRun = (async () => {\n try {\n const returned = await fn(signal, runOwner);\n\n if (typeof returned === 'function') {\n if (signal.aborted) {\n // Aborted while async fn was running — run cleanup immediately.\n returned();\n } else {\n asyncCleanup = returned;\n }\n }\n } catch (err) {\n if (!signal.aborted) onError(err);\n } finally {\n runOwner.dispose();\n\n if (currentRunPromise === theRun) currentRunPromise = null;\n }\n })();\n\n return () => {\n controller?.abort();\n asyncCleanup?.();\n asyncCleanup = null;\n };\n },\n { name: options?.name },\n );\n\n return new AsyncSubscriptionImpl(\n syncStop,\n async () => {\n const runningPromise = currentRunPromise;\n\n if (runningPromise) await runningPromise;\n },\n () => currentRunPromise,\n );\n};\n\nexport const onCleanup = (fn: CleanupFn): void => {\n const ctx = getTracking();\n\n if (ctx?.kind === 'effect') {\n ctx.cleanups.push(fn);\n\n return;\n }\n\n const scopeCleanups = getScopeCleanups();\n\n if (scopeCleanups !== null) {\n scopeCleanups.push(fn);\n\n return;\n }\n\n throw new RippleInvalidCleanupError('onCleanup() must be called from within an active effect or scope.');\n};\n\n/**\n * Creates a lifecycle scope that collects `onCleanup()` registrations and runs\n * them in reverse order when `dispose()` is called.\n *\n * The optional `setup` callback is run immediately inside the scope so that\n * `onCleanup()` calls in setup are captured without requiring a separate\n * `scope.run(setup)` call.\n *\n * Use `scope.add(fn)` to explicitly register a cleanup into this scope from\n * anywhere — including from inside an effect body where `onCleanup()` would otherwise\n * go to the effect's own cleanup queue.\n *\n * @example\n * ```ts\n * const s = scope(() => {\n * onCleanup(() => expensiveCleanup()); // captured by scope\n * });\n *\n * const stop = effect(() => {\n * void count.value;\n * s.add(() => doWork()); // explicit: goes to scope, not effect\n * onCleanup(() => cheapCleanup()); // goes to effect\n * });\n *\n * // later:\n * s.dispose(); // or: using s = scope(...)\n * ```\n */\nexport const scope = (setup?: () => void): Scope => {\n const cleanups: CleanupFn[] = [];\n const ac = new AbortController();\n let disposed = false;\n\n const scopeOnCleanup = (fn: CleanupFn): void => {\n if (disposed) throw new RippleDisposedScopeError('Cannot register cleanup on a disposed scope.');\n\n cleanups.push(fn);\n };\n\n const run = <T>(fn: () => T): T => {\n if (disposed) throw new RippleDisposedScopeError('Cannot run inside a disposed scope.');\n\n return withScopeCleanups(cleanups, fn);\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n ac.abort();\n runAll([...cleanups].reverse(), 'scope cleanup errors');\n cleanups.length = 0;\n };\n\n const api: Scope = {\n add: scopeOnCleanup,\n get disposalSignal() {\n return ac.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n run,\n [Symbol.dispose]: dispose,\n };\n\n if (setup) run(setup);\n\n return api;\n};\n\n// ── watch() ───────────────────────────────────────────────────────────────────\n\n/**\n * Observes a reactive source and calls `callback` whenever its value changes.\n * Does NOT invoke `callback` on creation (unless `immediate: true` is passed).\n *\n * `source` may be a single `Readable<T>` (signal, computed, or store lens), or a\n * function that reads multiple reactive sources — the function form tracks every\n * dependency read inside it, like `computed()`, without needing an intermediate\n * `computed()` node.\n *\n * When called without `immediate`, `prev` is always `T` — the previous value.\n * When `immediate: true` is passed, `prev` is `T | undefined` on the first call.\n *\n * @example\n * ```ts\n * const stop = watch(count, (next, prev) => {\n * console.log(`count changed: ${prev} → ${next}`);\n * });\n *\n * stop.dispose();\n * ```\n *\n * @example\n * ```ts\n * // Function form — tracks every signal read inside, re-runs when any changes.\n * const stopSum = watch(\n * () => a.value + b.value,\n * (sum, prevSum) => console.log(`sum: ${prevSum} → ${sum}`),\n * );\n *\n * stopSum.dispose();\n * ```\n */\nexport function watch<T>(\n source: Readable<T>,\n callback: (value: T, prev: T | undefined) => CleanupFn | void,\n options?: WatchOptions<T>,\n): Subscription;\nexport function watch<T>(\n source: () => T,\n callback: (value: T, prev: T | undefined) => CleanupFn | void,\n options?: WatchOptions<T>,\n): Subscription;\nexport function watch<T>(\n source: Readable<T> | (() => T),\n callback: (value: T, prev: T | undefined) => CleanupFn | void,\n options?: WatchOptions<T>,\n): Subscription {\n const equals: EqualityFn<T> = options?.equals ?? Object.is;\n let pendingCleanup: CleanupFn | undefined;\n const invokeCallback = (next: T, p: T | undefined): void => {\n pendingCleanup?.();\n pendingCleanup = undefined;\n\n const returned = callback(next, p);\n\n if (returned !== undefined && typeof returned !== 'function') {\n throw new RippleInvalidCleanupError(\n `watch() callback returned ${typeof returned} — expected a cleanup function or void.`,\n );\n }\n\n if (typeof returned === 'function') pendingCleanup = returned;\n };\n\n // Declared before the subscriber/effect closures so both they and the immediate\n // path can call watchSub.dispose() when once:true. Subscribers/effects always\n // (re-)run after watchSub is assigned, since it's assigned synchronously right after.\n\n let watchSub!: SubscriptionImpl;\n\n if (typeof source === 'function') {\n // Function form — track every dependency read inside `source` via a real effect,\n // mirroring computed()'s dependency collection. `first` suppresses the callback\n // on the initial (tracking-establishing) run — handled explicitly below (after\n // watchSub exists) instead of inside this synchronous first run, since effect()\n // runs its body immediately and watchSub isn't assigned until after it returns.\n let prev!: T;\n let first = true;\n\n const stopEffect = effect(\n () => {\n const next = source();\n\n if (first) {\n first = false;\n prev = next;\n\n return;\n }\n\n if (!equals(prev, next)) {\n const p = prev;\n\n prev = next;\n invokeCallback(next, p);\n\n if (options?.once) watchSub.dispose();\n }\n },\n { name: options?.name },\n );\n\n watchSub = new SubscriptionImpl(() => {\n pendingCleanup?.();\n pendingCleanup = undefined;\n stopEffect.dispose();\n });\n\n if (options?.immediate) {\n invokeCallback(prev, undefined);\n\n if (options.once) watchSub.dispose();\n }\n } else {\n let prev: T = source.peek();\n\n const innerSub = source.subscribe(() => {\n const next = source.peek();\n\n if (!equals(prev, next)) {\n invokeCallback(next, prev);\n prev = next;\n\n if (options?.once) watchSub.dispose();\n }\n });\n\n watchSub = new SubscriptionImpl(() => {\n pendingCleanup?.();\n pendingCleanup = undefined;\n innerSub.dispose();\n });\n\n if (options?.immediate) {\n invokeCallback(prev, undefined);\n\n if (options.once) watchSub.dispose();\n }\n }\n\n // Auto-register disposal into the enclosing scope (if any) — matches effect() behaviour.\n getScopeCleanups()?.push(() => watchSub.dispose());\n\n return watchSub;\n}\n","import type { Computed, Readable, Signal } from './types';\n\nimport { IS_COMPUTED, IS_SIGNAL, IS_STORE } from './symbols';\nimport { untrack } from './tracking';\n\nexport { untrack };\n\n// ── Readonly wrapper ──────────────────────────────────────────────────────────\n//\n// readonly() returns Readable<T> — no dispose() because the wrapper does not\n// own the source. Callers retain ownership and must dispose the source themselves.\n// The [IS_SIGNAL] brand is set so isReactive(readonly(s)) and isSignal(readonly(s))\n// both return true (it is reactive, not IS_COMPUTED, not IS_STORE).\n// [IS_COMPUTED] is intentionally NOT set — the wrapper is not a Computed<T>\n// and does not have dispose().\n\n/**\n * Wraps a reactive value to produce a structurally read-only view.\n * The `value` setter is hidden. Delegates reads directly to the source —\n * no extra reactive graph node, no extra subscription.\n *\n * The caller retains ownership of the source and is responsible for disposing it.\n *\n * @example\n * ```ts\n * const count = signal(0);\n * const readCount = readonly(count);\n * readCount.value; // fine — tracks dep on count\n * // readCount.value = 1; // TypeScript error — Readable has no setter\n * ```\n */\nexport const readonly = <T>(source: Readable<T>): Readable<T> => {\n const wrapper = {\n get disposed(): boolean {\n return source.disposed;\n },\n [IS_SIGNAL]: true as const,\n get name(): string | undefined {\n return source.name;\n },\n peek(): T {\n return source.peek();\n },\n subscribe(listener: () => void) {\n return source.subscribe(listener);\n },\n get value(): T {\n return source.value;\n },\n };\n\n return wrapper as unknown as Readable<T>;\n};\n\n// ── Type guards ───────────────────────────────────────────────────────────────\n\n/**\n * Returns `true` for any reactive value that implements `Readable<T>` — including\n * `signal()`, `computed()`, `store()`, and `readonly()` wrappers.\n *\n * This is the broad \"is this value reactive?\" check. Use `isSignal` for writable-only\n * signals, or `isComputed` to narrow to computed values.\n *\n * @example\n * ```ts\n * isReactive(signal(0)) // true\n * isReactive(computed(() => 1)) // true\n * isReactive(store({ x: 0 })) // true\n * isReactive(readonly(signal(0))) // true\n * isReactive({}) // false\n * ```\n */\nexport const isReactive = <T = unknown>(value: unknown): value is Readable<T> =>\n typeof value === 'object' && value !== null && !!(value as Record<typeof IS_SIGNAL, unknown>)[IS_SIGNAL];\n\n/**\n * Returns `true` only for writable `Signal<T>` values — excludes `computed()`, `store()`,\n * and `readonly()` wrappers.\n *\n * Use `isReactive` for the broad \"is this reactive?\" check.\n *\n * @example\n * ```ts\n * isSignal(signal(0)) // true\n * isSignal(computed(() => 1)) // false — computed excluded\n * isSignal(store({ x: 0 })) // false — store excluded\n * isSignal(readonly(signal(0))) // true — readonly has IS_SIGNAL, not IS_COMPUTED/IS_STORE\n * isSignal({}) // false\n * ```\n */\nexport const isSignal = <T = unknown>(value: unknown): value is Signal<T> =>\n isReactive(value) &&\n !(value as unknown as Record<typeof IS_COMPUTED, unknown>)[IS_COMPUTED] &&\n !(value as unknown as Record<typeof IS_STORE, unknown>)[IS_STORE];\n\nexport const isComputed = <T = unknown>(value: unknown): value is Computed<T> =>\n typeof value === 'object' && value !== null && !!(value as Record<typeof IS_COMPUTED, unknown>)[IS_COMPUTED];\n\nexport const isStore = <T extends object = object>(value: unknown): value is import('./types').Store<T> =>\n typeof value === 'object' && value !== null && !!(value as Record<typeof IS_STORE, unknown>)[IS_STORE];\n","import type { Resource, ResourceOptions, ResourceState } from './types';\n\nimport { effect } from './effect';\nimport { signal } from './signal';\nimport { IS_COMPUTED, IS_SIGNAL } from './symbols';\nimport { autoRegisterDisposal } from './tracking';\n\n/**\n * Creates a reactive async resource that emits a `ResourceState<T>` discriminated union.\n *\n * Dependencies are tracked synchronously in the factory (before the first `await`).\n * The factory receives an `AbortSignal` that fires when dependencies change or\n * the resource is disposed, cancelling any in-flight work.\n *\n * If `resource()` is created inside an active `effect()` or `scope.run()` context,\n * it is automatically registered for cleanup and disposed with that context —\n * matching `computed()`'s auto-disposal behavior.\n *\n * Call `refresh()` to force the factory to re-run immediately, even if no tracked\n * dependency changed — useful for manual \"retry\"/\"refetch\" actions.\n *\n * @example\n * ```ts\n * const userId = signal('u1');\n * const user = resource(async (signal) => {\n * const id = userId.value; // tracked dep — re-runs when userId changes\n * return fetchUser(id, { signal });\n * });\n *\n * effect(() => {\n * const s = user.value;\n * if (s.status === 'loading') return showSpinner();\n * if (s.status === 'error') return showError(s.error);\n * renderUser(s.data);\n * });\n *\n * user.refresh(); // force a re-fetch without changing userId\n * user.dispose();\n * ```\n */\nexport const resource = <T>(\n factory: (abortSignal: AbortSignal) => Promise<T>,\n options?: ResourceOptions<T>,\n): Resource<T> => {\n const name = options?.name;\n const state = signal<ResourceState<T>>(\n { data: options?.initialValue, status: 'loading' },\n name ? { name } : undefined,\n );\n // Internal-only trigger — never exposed. Read (but never written to a meaningful\n // value) inside the effect purely so it becomes a tracked dep; refresh() bumps it\n // to force a re-run without requiring a real dependency to change.\n const epoch = signal(0);\n\n let controller: AbortController | null = null;\n let disposed = false;\n\n const stop = effect(\n () => {\n void epoch.value;\n\n controller?.abort();\n controller = new AbortController();\n\n const { signal: abortSignal } = controller;\n\n const current = state.peek();\n const prevData = 'data' in current ? current.data : undefined;\n\n state.value = { data: prevData, status: 'loading' };\n\n void (async () => {\n try {\n const result = await factory(abortSignal);\n\n if (!abortSignal.aborted && !disposed) {\n state.value = { data: result, status: 'ready' };\n }\n } catch (err) {\n if (!abortSignal.aborted && !disposed) {\n state.value = { data: prevData, error: err, status: 'error' };\n }\n }\n })();\n },\n name ? { name } : undefined,\n );\n\n const refresh = (): void => {\n if (disposed) return;\n\n epoch.value = epoch.peek() + 1;\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n controller?.abort();\n stop.dispose();\n state.dispose();\n epoch.dispose();\n };\n\n autoRegisterDisposal(dispose);\n\n return {\n dispose,\n get disposed() {\n return disposed;\n },\n [IS_COMPUTED]: true as const,\n [IS_SIGNAL]: true as const,\n get name() {\n return state.name;\n },\n peek: () => state.peek(),\n refresh,\n subscribe: (listener: () => void) => state.subscribe(listener),\n [Symbol.dispose]: dispose,\n get value() {\n return state.value;\n },\n } as Resource<T>;\n};\n","import type { HistoryEntry, Store, StoreWithHistory } from './types';\n\nimport { signal } from './signal';\nimport { store } from './store';\nimport { isStore } from './utilities';\n\nconst snapshot = <T extends object>(state: Readonly<T>, label?: string): HistoryEntry<T> => ({\n label,\n state: Object.freeze(structuredClone({ ...state })) as Readonly<T>,\n});\n\n/**\n * Wraps a store (or creates one from an initial value) with explicit snapshot history\n * for undo/redo / time-travel.\n *\n * **Ownership:** if called with an initial value, the adapter creates and owns\n * the underlying store — `dispose()` will also dispose it. If called with an\n * existing `Store<T>`, the adapter does NOT own it — `dispose()` leaves the\n * store alive.\n *\n * History is **explicit** — call `push()` or `pushNamed()` after mutations you want\n * to be undoable. This gives you full control over snapshot granularity: you can\n * batch multiple mutations before pushing, or avoid pushing for programmatic\n * operations like server sync or reset-and-reload.\n *\n * This is a pure external adapter — it does not access any store internals.\n *\n * @example\n * ```ts\n * const h = storeWithHistory({ count: 0 }, { maxHistory: 10 });\n *\n * h.patch({ count: 1 });\n * h.push(); // save checkpoint\n * h.patch({ count: 2 });\n * h.pushNamed('increment');\n *\n * h.undo(); // back to { count: 1 }\n * h.redo(); // forward to { count: 2 }\n * h.historyAt(0); // { label: undefined, state: { count: 0 } }\n * h.canUndo; // true\n *\n * // Wrap an existing store:\n * const existingStore = store({ x: 0 });\n * const h2 = storeWithHistory(existingStore);\n * h2.dispose(); // existingStore is NOT disposed\n * ```\n */\nexport const storeWithHistory = <T extends object>(\n storeOrInitial: Store<T> | T,\n options?: { maxHistory?: number; name?: string },\n): StoreWithHistory<T> => {\n const maxHistory = options?.maxHistory ?? 50;\n const ownsStore = !isStore(storeOrInitial);\n const base: Store<T> = ownsStore ? store(storeOrInitial as T, { name: options?.name }) : (storeOrInitial as Store<T>);\n\n const snapshots: HistoryEntry<T>[] = [snapshot(base.peek())];\n\n // cursor is a reactive signal so canUndo/canRedo participate in the reactive graph.\n const cursor = signal(0, { name: options?.name ? `${options.name}.cursor` : undefined });\n\n let disposed = false;\n\n const pushSnapshot = (label?: string): void => {\n if (disposed) return;\n\n snapshots.splice(cursor.peek() + 1);\n snapshots.push(snapshot(base.peek(), label));\n\n if (snapshots.length > maxHistory) {\n snapshots.splice(0, snapshots.length - maxHistory);\n }\n\n cursor.value = snapshots.length - 1;\n };\n\n const undo = (): void => {\n if (disposed || cursor.peek() <= 0) return;\n\n cursor.value = cursor.peek() - 1;\n base.replace(() => snapshots[cursor.peek()]!.state as T);\n };\n\n const redo = (): void => {\n if (disposed || cursor.peek() >= snapshots.length - 1) return;\n\n cursor.value = cursor.peek() + 1;\n base.replace(() => snapshots[cursor.peek()]!.state as T);\n };\n\n const historyAt = (index: number): HistoryEntry<T> | undefined => {\n if (index < 0 || index >= snapshots.length) return undefined;\n\n return snapshots[index];\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n cursor.dispose();\n\n if (ownsStore) base.dispose();\n };\n\n // Explicit delegation: all Store<T> methods are forwarded directly to base.\n // History-specific properties are defined inline.\n const adapter: StoreWithHistory<T> = {\n get canRedo() {\n return cursor.value < snapshots.length - 1;\n },\n // History-specific\n get canUndo() {\n return cursor.value > 0;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n historyAt,\n get historyLength() {\n return snapshots.length;\n },\n lens: <P extends string>(path: P) => base.lens(path),\n // Store<T> delegation\n get name() {\n return base.name;\n },\n patch: (partial) => base.patch(partial),\n peek: () => base.peek(),\n push: () => pushSnapshot(),\n pushNamed: (label: string) => pushSnapshot(label),\n redo,\n replace: (fn) => base.replace(fn),\n reset: () => base.reset(),\n get store() {\n return base;\n },\n subscribe: (listener) => base.subscribe(listener),\n [Symbol.dispose]: dispose,\n undo,\n get value() {\n return base.value;\n },\n };\n\n return adapter;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var Ripple=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},n=class extends t{},r=class extends t{},i=class extends t{},a=class extends t{},o=class extends t{},s=class extends t{},c=e=>e instanceof Error?e:Error(`Non-Error thrown: ${String(e)}`),l=e=>{let t=[];for(let n of e)try{n()}catch(e){t.push(c(e))}return t},u=(e,t)=>{let n=l(e);if(n.length===1)throw n[0];if(n.length>0)throw AggregateError(n,t)},d=(e,t,n)=>{throw t.length===0?e:AggregateError([c(e),...t],n,{cause:c(e)})},f=null,p=()=>f,m=Symbol(`ripple.is-signal`),h=Symbol(`ripple.is-computed`),g=Symbol(`ripple.is-store`),_=Symbol(`ripple.uninitialized`),v=new FinalizationRegistry(({key:e,map:t})=>{t.delete(e)}),y=class{version=0;name;[m]=!0;computedSubs_=new Map;effectSubs_=new Set;constructor(e){this.name=e}addComputedSub(e){let t=new WeakRef(e);this.computedSubs_.set(e,t),v.register(e,{key:e,map:this.computedSubs_},t)}removeComputedSub(e){let t=this.computedSubs_.get(e);t!==void 0&&(this.computedSubs_.delete(e),v.unregister(t))}addEffectSub(e){this.effectSubs_.add(e)}removeEffectSub(e){this.effectSubs_.delete(e)}clearSubscribers(){for(let e of this.computedSubs_.values())v.unregister(e);this.computedSubs_.clear(),this.effectSubs_.clear()}hasSubscribers(){if(this.effectSubs_.size>0)return!0;for(let e of this.computedSubs_.values())if(e.deref()!==void 0)return!0;return!1}*computedSubs(){for(let[e,t]of this.computedSubs_){let n=t.deref();n===void 0?(this.computedSubs_.delete(e),v.unregister(t)):yield n}}effectSubs(){return this.effectSubs_}},b=class extends y{[h]=!0;lastPropEpoch_=0},x=class{fn_;disposed_=!1;constructor(e){this.fn_=e}get disposed(){return this.disposed_}dispose(){if(this.disposed_)return;this.disposed_=!0;let e=this.fn_;this.fn_=null,e()}[Symbol.dispose](){this.dispose()}},S=class{awaitDone_;getCurrentRun_;syncStop_;asyncDisposePromise_=null;constructor(e,t,n){this.awaitDone_=t,this.getCurrentRun_=n,this.syncStop_=e}get disposed(){return this.syncStop_.disposed}dispose(){this.syncStop_.dispose()}run(){return this.getCurrentRun_()??Promise.resolve()}[Symbol.dispose](){this.dispose()}[Symbol.asyncDispose](){return this.asyncDisposePromise_===null?(this.dispose(),this.asyncDisposePromise_=this.awaitDone_(),this.asyncDisposePromise_):this.asyncDisposePromise_}},C=0,ee=()=>++C,w=()=>C,T={scheduling:{activeDirty:`a`,batchDepth:0,dirtyWithEffectSubsA:new Set,dirtyWithEffectSubsB:new Set,pendingSubscribers:new Set},scopeCleanups:null,tracking:null},E=null,D=()=>E===null?T:E.get(),O=()=>D().scheduling,k=()=>E!==null,A=()=>D().tracking,j=(e,t)=>{if(E!==null){let n=E.get();return E.run({...n,tracking:e},t)}let n=T;T={...T,tracking:e};try{return t()}finally{T=n}},te=(e,t)=>{if(E!==null){let n=E.get();return E.run({...n,scopeCleanups:e},t)}let n=T;T={...T,scopeCleanups:e};try{return t()}finally{T=n}},M=()=>D().scopeCleanups,N=e=>{let t=A();t?.kind===`effect`?t.cleanups.push(e):M()?.push(e)},P=e=>j(null,e),F=e=>{let t=A();if(t!==null){if(t.sourceObserver?.(e),t.kind===`computed`)t.depCollector.push({source:e,version:e.version});else if(t.kind===`effect`){let n=t.effect;e.addEffectSub(n),t.subscriptions.add(()=>e.removeEffectSub(n)),t.deps.set(e,e.version)}}},ne=class extends b{value_;dirty_;computing_;disposed_;deps_;compute_;equals_;maxRevision_;constructor(e,t){let{equals:n,name:r}=t??{};super(r),this.value_=_,this.dirty_=!0,this.computing_=!1,this.disposed_=!1,this.deps_=[],this.maxRevision_=-1,this.compute_=e,this.equals_=n===void 0?Object.is:(e,t)=>n(e,t)}markDirty(){return this.disposed_||this.dirty_?!1:(this.dirty_=!0,!0)}refreshIfDirty(){if(!this.dirty_)return!1;if(w()<=this.maxRevision_)return this.dirty_=!1,!1;if(this.deps_.length>0){let e=!0;for(let t of this.deps_){let n=t.source;if(`refreshIfDirty`in n&&n.refreshIfDirty(),n.version!==t.version){e=!1;break}}if(e)return this.dirty_=!1,this.maxRevision_=w(),!1}return this.recompute()}runCompute(e){try{return j({computed:this,depCollector:e,kind:`computed`},this.compute_)}catch(e){throw c(e)}}recompute(){if(this.computing_)throw new n(`computed cycle detected${this.name?` "${this.name}"`:``}`);this.computing_=!0;try{let e=[];p()?.compute?.({name:this.name});let t=this.runCompute(e);return this.dirty_=!1,this.maxRevision_=w(),this.updateDeps(e),this.value_===_||!this.equals_(this.value_,t)?(this.value_=t,this.version++,!0):!1}finally{this.computing_=!1}}updateDeps(e){let t=this.deps_;if(t.length===e.length&&t.every((t,n)=>t.source===e[n].source)){for(let n=0;n<e.length;n++)t[n].version=e[n].version;return}let n=new Set(t.map(e=>e.source)),r=new Set(e.map(e=>e.source));for(let e of t)r.has(e.source)||e.source.removeComputedSub(this);for(let t of e)n.has(t.source)||t.source.addComputedSub(this);this.deps_=e}get value(){return this.disposed_?this.value_===_?void 0:this.value_:(this.refreshIfDirty(),F(this),this.value_)}peek(){return this.disposed_?this.value_===_?void 0:this.value_:(this.refreshIfDirty(),this.value_)}subscribe=e=>{if(this.disposed_){let e=new x(()=>{});return e.dispose(),e}return this.refreshIfDirty(),this.addEffectSub(e),new x(()=>{this.removeEffectSub(e)})};get disposed(){return this.disposed_}dispose(){if(!this.disposed_){this.disposed_=!0;for(let e of this.deps_)e.source.removeComputedSub(this);this.deps_=[],this.clearSubscribers(),p()?.dispose?.({kind:`computed`,name:this.name})}}[Symbol.dispose](){this.dispose()}},I=(e,t)=>{let n=new ne(e,t);return N(()=>n.dispose()),n},re=!globalThis.__RIPPLE_PROD__;function ie(e){re&&console.warn(`[@vielzeug/ripple] ${e}`)}var L=!1,ae=0,R=[],z=e=>e.activeDirty===`a`?e.dirtyWithEffectSubsA:e.dirtyWithEffectSubsB,oe=(e,t)=>{let n=++ae;for(let n of t.effectSubs())e.pendingSubscribers.add(n);for(let e of t.computedSubs())R.push(e);try{for(;R.length>0;){let t=R.pop();if(t.lastPropEpoch_!==n&&(t.lastPropEpoch_=n,t.markDirty())){t.effectSubs().size>0&&z(e).add(t);for(let e of t.computedSubs())R.push(e)}}}finally{R.length=0}},se=e=>{for(;z(e).size>0;){let t=z(e);e.activeDirty=e.activeDirty===`a`?`b`:`a`,z(e).clear();for(let n of t)if(n.hasSubscribers()&&n.refreshIfDirty()){for(let t of n.effectSubs())e.pendingSubscribers.add(t);for(let t of n.computedSubs())t.markDirty()&&t.effectSubs().size>0&&z(e).add(t)}t.clear()}},B=e=>{let t=0;for(;e.pendingSubscribers.size>0||z(e).size>0;){if(++t>100)throw new a(`infinite flush loop (> 100 iterations)`);if(z(e).size>0&&se(e),e.pendingSubscribers.size===0)continue;let n=[...e.pendingSubscribers];e.pendingSubscribers.clear(),u(n,`subscriber errors`)}},ce=globalThis.process?.versions!=null,V=e=>{if(!e.hasSubscribers())return;!L&&ce&&!k()&&(L=!0,ie(`Signal updated in a Node.js-like environment. The module-level flush queue is shared across concurrent requests — use per-request worker isolation or the @vielzeug/ripple/ssr sub-path for request-isolated scheduling.`));let t=O();oe(t,e),t.batchDepth===0&&B(t)},H=e=>{let t=O();t.batchDepth++;let n;try{n=e()}catch(e){throw t.batchDepth--,t.batchDepth===0&&(t.pendingSubscribers.clear(),t.dirtyWithEffectSubsA.clear(),t.dirtyWithEffectSubsB.clear()),e}return t.batchDepth--,t.batchDepth===0&&B(t),n},U=class extends y{value_;equals_;disposed_;constructor(e,t,n){super(n),this.value_=e,this.equals_=t??Object.is,this.disposed_=!1}get value(){return this.disposed_||F(this),this.value_}set value(e){if(this.disposed_||this.equals_(this.value_,e))return;let t=this.value_;this.value_=e,this.version=ee(),p()?.write?.({name:this.name,newValue:e,oldValue:t}),V(this)}peek(){return this.value_}subscribe=e=>{if(this.disposed_){let e=new x(()=>{});return e.dispose(),e}return this.addEffectSub(e),new x(()=>{this.removeEffectSub(e)})};get disposed(){return this.disposed_}dispose(){this.disposed_||(this.disposed_=!0,this.clearSubscribers(),p()?.dispose?.({kind:`signal`,name:this.name}))}[Symbol.dispose](){this.dispose()}},W=(e,t)=>new U(e,t?.equals,t?.name),G=e=>{if(e===`__proto__`||e===`constructor`||e===`prototype`)throw new s(`Unsafe key "${e}" rejected to prevent prototype pollution.`)},le=(e,t)=>{let n=e;for(let e of t){if(typeof n!=`object`||!n)return;n=n[e]}return n},K=(e,t,n)=>{if(t.length===0)return n;if(typeof e!=`object`||!e)throw new s(`Cannot write to a nested path through a null or non-object intermediate value.`);let[r,...i]=t,a=e;return{...a,[r]:K(a[r],i,n)}},q=class{[m]=!0;source_;write_;evict_;disposeSource_;name_;get name(){return this.name_??this.source_.name}constructor(e){this.source_=e.source,this.write_=e.write,this.evict_=e.evict,this.disposeSource_=e.disposeSource,this.name_=e.name}get value(){return this.source_.value}set value(e){this.write_(e)}peek(){return this.source_.peek()}subscribe(e){return this.source_.subscribe(e)}get disposed(){return this.source_.disposed}dispose(){this.evict_(),this.disposeSource_?.()}[Symbol.dispose](){this.dispose()}},ue=class{[m]=!0;[g]=!0;name;current_;disposed_;initial_;lensCache_;propSignals_;readonlyProxy_;version_;constructor(e,t){this.name=t,this.current_=structuredClone(e),this.disposed_=!1,this.initial_=structuredClone(e),this.lensCache_=new Map,this.propSignals_=new Map,this.version_=new U(0,void 0,t?`${t}.version`:void 0),this.readonlyProxy_=new Proxy(this.current_,{deleteProperty(e,t){throw new s(`Direct deletion from store.value is not allowed. Use store.patch() or store.lens(). (key: "${String(t)}")`)},set(e,t){throw new s(`Direct mutation of store.value is not allowed. Use store.patch(), store.lens(), or store.replace(). (key: "${String(t)}")`)}})}propSignalFor_(e){let t=this.propSignals_.get(e);return t===void 0&&(t=new U(this.current_[e],void 0,this.name===void 0?e:`${this.name}.${e}`),this.propSignals_.set(e,t)),t}setPath_(e,t){let[n,...r]=e,i=this.current_[n],a=K(i,r,t);this.applyTopLevelChange_(n,a)}applyTopLevelChange_(e,t){G(e);let n=this.current_[e];Object.is(n,t)||(this.current_[e]=t,this.propSignalFor_(e).value=t,this.version_.value=this.version_.peek()+1)}deleteTopLevelKey_(e){Object.hasOwn(this.current_,e)&&(delete this.current_[e],this.propSignalFor_(e).value=void 0,this.version_.value=this.version_.peek()+1)}applyFullState_(e){let t=this.current_,n=new Set([...Object.keys(t),...Object.keys(e)]);for(let e of n)G(e);H(()=>{for(let t of n)Object.hasOwn(e,t)?this.applyTopLevelChange_(t,e[t]):this.deleteTopLevelKey_(t)})}get value(){return this.version_.value,this.readonlyProxy_}peek(){return Object.freeze(structuredClone(this.current_))}subscribe=e=>this.version_.subscribe(e);patch(e){if(typeof e!=`object`||!e||Array.isArray(e))throw new s(`store.patch() requires a plain object partial.`);let t=Object.keys(e);if(t.length!==0){for(let e of t)G(e);H(()=>{for(let n of t)this.applyTopLevelChange_(n,e[n])}),p()?.mutate?.({kind:`patch`,name:this.name})}}replace(e){let t=structuredClone(this.current_),n=e(t);n!==t&&(this.applyFullState_(n),p()?.mutate?.({kind:`replace`,name:this.name}))}reset(){this.applyFullState_(this.initial_),p()?.mutate?.({kind:`reset`,name:this.name})}lens(e){let t=this.lensCache_.get(e);if(t!==void 0)return t;let n=()=>{this.lensCache_.delete(e)},r=e.split(`.`);if(r.length>32)throw new s(`Lens path exceeds maximum depth of 32 segments: "${e}".`);for(let t of r){if(t===``)throw new s(`Empty path segment in lens path "${e}". Check for consecutive dots.`);if(t===`__proto__`||t===`constructor`||t===`prototype`)throw new s(`Unsafe path segment "${t}" in lens path "${e}".`)}let i;if(r.length===1)i=new q({evict:n,source:this.propSignalFor_(r[0]),write:t=>{H(()=>this.applyTopLevelChange_(r[0],t)),p()?.mutate?.({kind:`lens`,name:this.name,path:e})}});else{let t=I(()=>le(this.propSignalFor_(r[0]).value,r.slice(1)));i=new q({disposeSource:()=>t.dispose(),evict:n,name:this.name?`${this.name}.${e}`:e,source:t,write:n=>{let i=t.peek();Object.is(i,n)||(H(()=>this.setPath_(r,n)),p()?.mutate?.({kind:`lens`,name:this.name,path:e}))}})}return this.lensCache_.set(e,i),i}get disposed(){return this.disposed_}dispose(){if(!this.disposed_){this.disposed_=!0;for(let e of[...this.lensCache_.values()])e.dispose();this.lensCache_.clear();for(let e of this.propSignals_.values())e.dispose();this.propSignals_.clear(),this.version_.dispose(),p()?.dispose?.({kind:`store`,name:this.name})}}[Symbol.dispose](){this.dispose()}},J=(e,t)=>{if(typeof e!=`object`||!e||Array.isArray(e))throw new s(`store() requires a plain object initial state.`);for(let t of Object.keys(e))G(t);return new ue(e,t?.name)},de=(e,t)=>{if(t===`sync`||t===void 0)return e;let n=!1;return()=>{n||(n=!0,queueMicrotask(()=>{n=!1,e()}))}},Y=(e,t)=>{let n=t?.scheduler??`sync`,r=t?.name,i=[],s=new Set,c=new Map,f=!1,m=!1,g=!1,_=()=>{if(c.clear(),i.length===0&&s.size===0)return;let e=[...i,...s];i.length=0,s.clear(),u(e,`effect teardown errors`)},v=()=>{if(!g){if(f){m=!0;return}f=!0;try{let t=0;do{if(++t>100)throw new a(`infinite effect loop (> 100 iterations)${r?` in "${r}"`:``}`);m=!1,_(),p()?.run?.({name:r});let n;try{n=j({cleanups:i,deps:c,effect:y,kind:`effect`,subscriptions:s},e)}catch(e){let t=[...l(s),...l(i)];i.length=0,s.clear(),d(e,t,`effect failure with cleanup errors`)}if(n!==void 0&&typeof n!=`function`)throw new o(`effect() returned ${typeof n} — expected a cleanup function or void.`);typeof n==`function`&&i.push(n)}while(m&&!g)}finally{f=!1}}},y=de(v,n);v();let b=new x(()=>{g||(g=!0,_(),p()?.dispose?.({kind:`effect`,name:r}))});return M()?.push(()=>b.dispose()),{dispose:()=>b.dispose(),get disposed(){return b.disposed},getDependencies(){return[...c.keys()].map(e=>({kind:h in e?`computed`:`signal`,name:e.name}))},[Symbol.dispose](){b.dispose()}}},fe=(e,t)=>{let n=null,r=null,i=null,a=t?.onError??(e=>{queueMicrotask(()=>{throw e})});return new S(Y(()=>{n?.abort(),r?.(),r=null,n=new AbortController;let{signal:t}=n,o=X(),s;return i=s=(async()=>{try{let n=await e(t,o);typeof n==`function`&&(t.aborted?n():r=n)}catch(e){t.aborted||a(e)}finally{o.dispose(),i===s&&(i=null)}})(),()=>{n?.abort(),r?.(),r=null}},{name:t?.name}),async()=>{let e=i;e&&await e},()=>i)},pe=e=>{let t=A();if(t?.kind===`effect`){t.cleanups.push(e);return}let n=M();if(n!==null){n.push(e);return}throw new o(`onCleanup() must be called from within an active effect or scope.`)},X=e=>{let t=[],n=new AbortController,i=!1,a=e=>{if(i)throw new r(`Cannot register cleanup on a disposed scope.`);t.push(e)},o=e=>{if(i)throw new r(`Cannot run inside a disposed scope.`);return te(t,e)},s=()=>{i||(i=!0,n.abort(),u([...t].reverse(),`scope cleanup errors`),t.length=0)},c={add:a,get disposalSignal(){return n.signal},dispose:s,get disposed(){return i},run:o,[Symbol.dispose]:s};return e&&o(e),c};function me(e,t,n){let r=n?.equals??Object.is,i,a=(e,n)=>{i?.(),i=void 0;let r=t(e,n);if(r!==void 0&&typeof r!=`function`)throw new o(`watch() callback returned ${typeof r} — expected a cleanup function or void.`);typeof r==`function`&&(i=r)},s;if(typeof e==`function`){let t,o=!0,c=Y(()=>{let i=e();if(o){o=!1,t=i;return}if(!r(t,i)){let e=t;t=i,a(i,e),n?.once&&s.dispose()}},{name:n?.name});s=new x(()=>{i?.(),i=void 0,c.dispose()}),n?.immediate&&(a(t,void 0),n.once&&s.dispose())}else{let t=e.peek(),o=e.subscribe(()=>{let i=e.peek();r(t,i)||(a(i,t),t=i,n?.once&&s.dispose())});s=new x(()=>{i?.(),i=void 0,o.dispose()}),n?.immediate&&(a(t,void 0),n.once&&s.dispose())}return M()?.push(()=>s.dispose()),s}var he=e=>({get disposed(){return e.disposed},[m]:!0,get name(){return e.name},peek(){return e.peek()},subscribe(t){return e.subscribe(t)},get value(){return e.value}}),Z=e=>typeof e==`object`&&!!e&&!!e[m],ge=e=>Z(e)&&!e[h]&&!e[g],_e=e=>typeof e==`object`&&!!e&&!!e[h],Q=e=>typeof e==`object`&&!!e&&!!e[g],ve=(e,t)=>{let n=t?.name,r=W({data:t?.initialValue,status:`loading`},n?{name:n}:void 0),i=W(0),a=null,o=!1,s=Y(()=>{i.value,a?.abort(),a=new AbortController;let{signal:t}=a,n=r.peek(),s=`data`in n?n.data:void 0;r.value={data:s,status:`loading`},(async()=>{try{let n=await e(t);!t.aborted&&!o&&(r.value={data:n,status:`ready`})}catch(e){!t.aborted&&!o&&(r.value={data:s,error:e,status:`error`})}})()},n?{name:n}:void 0),c=()=>{o||(i.value=i.peek()+1)},l=()=>{o||(o=!0,a?.abort(),s.dispose(),r.dispose(),i.dispose())};return N(l),{dispose:l,get disposed(){return o},[h]:!0,[m]:!0,get name(){return r.name},peek:()=>r.peek(),refresh:c,subscribe:e=>r.subscribe(e),[Symbol.dispose]:l,get value(){return r.value}}},$=(e,t)=>({label:t,state:Object.freeze(structuredClone({...e}))});return e.RippleComputedCycleError=n,e.RippleDisposedScopeError=r,e.RippleEnvironmentError=i,e.RippleError=t,e.RippleInfiniteLoopError=a,e.RippleInvalidCleanupError=o,e.RippleInvalidStoreError=s,e.batch=H,e.computed=I,e.effect=Y,e.effectAsync=fe,e.getDevToolsHook=p,e.isComputed=_e,e.isReactive=Z,e.isSignal=ge,e.isStore=Q,e.onCleanup=pe,e.readonly=he,e.resource=ve,e.scope=X,e.signal=W,e.store=J,e.storeWithHistory=(e,t)=>{let n=t?.maxHistory??50,r=!Q(e),i=r?J(e,{name:t?.name}):e,a=[$(i.peek())],o=W(0,{name:t?.name?`${t.name}.cursor`:void 0}),s=!1,c=e=>{s||(a.splice(o.peek()+1),a.push($(i.peek(),e)),a.length>n&&a.splice(0,a.length-n),o.value=a.length-1)},l=()=>{s||o.peek()<=0||(o.value=o.peek()-1,i.replace(()=>a[o.peek()].state))},u=()=>{s||o.peek()>=a.length-1||(o.value=o.peek()+1,i.replace(()=>a[o.peek()].state))},d=e=>{if(!(e<0||e>=a.length))return a[e]},f=()=>{s||(s=!0,o.dispose(),r&&i.dispose())};return{get canRedo(){return o.value<a.length-1},get canUndo(){return o.value>0},dispose:f,get disposed(){return s},historyAt:d,get historyLength(){return a.length},lens:e=>i.lens(e),get name(){return i.name},patch:e=>i.patch(e),peek:()=>i.peek(),push:()=>c(),pushNamed:e=>c(e),redo:u,replace:e=>i.replace(e),reset:()=>i.reset(),get store(){return i},subscribe:e=>i.subscribe(e),[Symbol.dispose]:f,undo:l,get value(){return i.value}}},e.untrack=P,e.watch=me,e})({});
|
|
2
|
+
//# sourceMappingURL=ripple.iife.js.map
|