@wcstack/signals 4.0.0-rc.8 → 4.0.0-rc.9

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.ja.md CHANGED
@@ -243,7 +243,7 @@ bound.bindCommand("cmdName", trigger, mapArgs?); // command-token: `trigger` が
243
243
  bound.dispose(); // 全リスナ/effect を detach
244
244
  ```
245
245
 
246
- `descriptor` を省略すると `target.constructor.wcBindable` から読みます。アダプタは wc-bindable の4マッピングを担います: `signals[name]` は property の**状態ビュー**(等価ガードあり — 同値なら更新なし)、`on(name)` は同じイベントの**発生ビュー**(ストリーム — 同値でも*毎回*更新)で既定 latest fold。`bindInput` は signal を input に反映し、same-value ガード(`node[name] !== v`)で「書き込み→イベント再発火→書き込み」のループを断ちます。`bindCommand` は trigger が**変化**したとき command を起動(初期値では発火しない)、`mapArgs` で引数を整形。`set`/`bindInput` は未宣言 input を、`command`/`bindCommand` は未宣言(または非関数)command を拒否。`error` signal の初期値は `null` です。`dispose` 後はアダプタが**不活性(inert)**(signal/ストリーム停止・メソッドは例外)で、`dispose` は冪等。`bindInput`/`bindCommand` は binding ごとの disposer も返します。
246
+ `descriptor` を省略すると `target.constructor.wcBindable` から読みます。アダプタは wc-bindable の4マッピングを担います: `signals[name]` は property の**状態ビュー**(等価ガードあり — 同値なら更新なし)、`on(name)` は同じイベントの**発生ビュー**(ストリーム — 同値でも*毎回*更新)で既定 latest fold。`bindInput` は signal を input に反映し、same-value ガード(`node[name] !== v`)で「書き込み→イベント再発火→書き込み」のループを断ちます。`undefined` を書くのは signal が別の値を持った後だけです(wc-bindable の applier プロファイルの規則 A2。React 19 と同じ): 最初の評価や `undefined` の後の `undefined` は「まだ値が無い」なので書かず、要素自身の初期状態(既定値やマークアップの値)をそのまま残します。値の後の `undefined` は書くので、要素はその状態に戻ります。binding を外しても何も書きません。`bindCommand` は trigger が**変化**したとき command を起動(初期値では発火しない)、`mapArgs` で引数を整形。`set`/`bindInput` は未宣言 input を、`command`/`bindCommand` は未宣言(または非関数)command を拒否。`error` signal の初期値は `null` です。`dispose` 後はアダプタが**不活性(inert)**(signal/ストリーム停止・メソッドは例外)で、`dispose` は冪等。`bindInput`/`bindCommand` は binding ごとの disposer も返します。
247
247
 
248
248
  **property signal が受け取る値。** `event` のたびに `signals[name]` / `on(name)` は `getter(event)` を採ります。descriptor が `getter` を省略していれば wc-bindable の既定 `(e) => e.detail` が適用されます — `@wcstack/state` の双方向バインドや `@wc-bindable/core` と同じ規則なので、同じ要素はどのアダプタでも同じに束縛されます。**初期シード**だけはプロパティ(`node[name]`)を読みます: bind 時点には由来となるイベントが無いためです(SPEC § Initial Sync)。wcstack の I/O ノードは全てプロパティ値をそのまま `detail` に載せるので両者は一致します。`detail` がプロパティと食い違う(`detail: { value }` 形・`detail` 無し)サードパーティ要素には明示の `getter`(例: `getter: (e) => e.target.value`)が必要です。
249
249
 
package/README.md CHANGED
@@ -244,7 +244,7 @@ bound.bindCommand("cmdName", trigger, mapArgs?); // command-token: invoke when `
244
244
  bound.dispose(); // detach all listeners/effects
245
245
  ```
246
246
 
247
- If `descriptor` is omitted, it's read from `target.constructor.wcBindable`. The adapter covers the four wc-bindable mappings: `signals[name]` is the **state view** of a property (equality-guarded — same value, no update); `on(name)` is the **occurrence view** of the same event (a stream — updates on *every* emit, even an equal value), folded latest-by-default. `bindInput` mirrors a signal into an input with a same-value guard (`node[name] !== v`) so a write that re-dispatches an event cannot loop. `bindCommand` invokes a command when the trigger **changes** (the initial value does not fire), with `mapArgs` shaping the call. `set`/`bindInput` reject undeclared inputs; `command`/`bindCommand` reject undeclared (or non-function) commands. The `error` signal's initial value is `null`. After `dispose` the adapter is **inert** (signals/streams stop, methods throw); `dispose` is idempotent. `bindInput`/`bindCommand` also return a per-binding disposer.
247
+ If `descriptor` is omitted, it's read from `target.constructor.wcBindable`. The adapter covers the four wc-bindable mappings: `signals[name]` is the **state view** of a property (equality-guarded — same value, no update); `on(name)` is the **occurrence view** of the same event (a stream — updates on *every* emit, even an equal value), folded latest-by-default. `bindInput` mirrors a signal into an input with a same-value guard (`node[name] !== v`) so a write that re-dispatches an event cannot loop. It writes `undefined` only after the signal had another value — the wc-bindable applier profile's rule (A2), the one React 19 follows: an `undefined` on the first evaluation, or after another `undefined`, means "no value yet" and leaves the element's own initial state (its default, or the markup) alone; one after a value is written, so the element restores that state. Disposing the binding writes nothing. `bindCommand` invokes a command when the trigger **changes** (the initial value does not fire), with `mapArgs` shaping the call. `set`/`bindInput` reject undeclared inputs; `command`/`bindCommand` reject undeclared (or non-function) commands. The `error` signal's initial value is `null`. After `dispose` the adapter is **inert** (signals/streams stop, methods throw); `dispose` is idempotent. `bindInput`/`bindCommand` also return a per-binding disposer.
248
248
 
249
249
  **What a property signal receives.** On each `event`, `signals[name]` / `on(name)` take `getter(event)`; when the descriptor omits `getter`, the wc-bindable default `(e) => e.detail` applies — the same rule as `@wcstack/state`'s two-way bindings and `@wc-bindable/core`, so an element binds identically under every adapter. The **initial seed** is the one place that reads the property (`node[name]`) instead: there is no event to derive from at bind time (SPEC § Initial Sync). Every wcstack I/O node dispatches its property value as `detail`, so the two reads agree; a third-party element whose `detail` differs from the property (`detail: { value }`, or no `detail` at all) needs an explicit `getter` — e.g. `getter: (e) => e.target.value`.
250
250
 
@@ -0,0 +1,2 @@
1
+ function e(e){const t=globalThis.reportError;"function"==typeof t?t(e):console.error(e)}function t(){return!0===globalThis.__WCS_DEV__}const n=new Set;function o(e,o,s,...r){if(!t())return;const i=e+"\0"+o;n.has(i)||(n.add(i),console.warn(`[wcstack/signals][${e}] ${s}`,...r))}let s=null;function r(e){null!==s&&(s._sources.add(e),e._observers.add(s))}function i(e){for(const t of e._sources)t._observers.delete(e);e._sources.clear()}let a=null;function c(e){null!==a&&a._owned.push(e)}function d(e){const t=e._owned;for(let e=t.length-1;e>=0;e--)t[e]();t.length=0}let l=new Set,u=!1;const f=[];function h(){return f.pop()??new Set}function _(){u||(u=!0,queueMicrotask(w))}const p=1e3;function v(e){const t=e.filter(e=>!e._disposed),n=t.map((e,t)=>` #${t+1} ${e._devStack?e._devStack.split("\n").slice(1,3).join(" | ").trim():"(no stack)"}`);return`\n[wcstack/signals] dev diagnostics: ${t.length} effect(s) still re-running on the final pass (likely cycle participants):\n${n.join("\n")}`}function w(){u=!1;let n=0,o=null;for(;l.size>0;){if(++n>p)throw l.clear(),new Error("flushEffects: exceeded "+p+" iterations — likely a reactive cycle (an effect writes a signal it depends on with an ever-changing value)."+(o?v(o):""));const s=l;l=h(),t()&&(o=[...s]);for(const t of s)if(!t._disposed)try{y(t)}catch(t){e(t)}s.clear(),f.push(s)}}function b(){w()}function m(e,t){if(e._state<t){const n=0===e._state;e._state=t,n&&e._isEffect&&(l.add(e),_());for(const t of e._observers)m(t,1)}}function y(e){if(0!==e._state){if(1===e._state)for(const t of e._sources)if(t instanceof E&&(y(t),2===e._state))break;try{2===e._state&&e._update()}finally{e._state=0}}}class g{_observers=new Set;_value;_equals;constructor(e,t){this._value=e,this._equals=t}get(){return r(this),this._value}peek(){return this._value}set(e){if(!this._equals(this._value,e)){this._value=e;for(const e of this._observers)m(e,2)}}}class E{_observers=new Set;_sources=new Set;_state=2;_isEffect=!1;_fn;_value=void 0;_initialized=!1;_running=!1;_equals;constructor(e,t){this._fn=e,this._equals=t,c(()=>i(this))}get(){return r(this),y(this),this._value}peek(){return y(this),this._value}_update(){if(this._running)throw new Error("computed: circular dependency — the computed reads itself.");const e=this._value;i(this);const t=s;s=this,this._running=!0;try{this._value=this._fn()}finally{this._running=!1,s=t}if(this._initialized){if(!this._equals(e,this._value))for(const e of this._observers)e._state=2,e._isEffect&&!e._disposed&&(l.add(e),_())}else this._initialized=!0}}class S{_observers=new Set;_sources=new Set;_owned=[];_state=2;_isEffect=!0;_disposed=!1;_devStack=void 0;_fn;_cleanup=void 0;constructor(e){this._fn=e,t()&&(this._devStack=(new Error).stack),c(()=>this.dispose()),y(this)}_update(){this._runCleanup(),d(this),i(this);const e=s,t=a;s=this,a=this;try{this._cleanup=this._fn()}finally{s=e,a=t}}_runCleanup(){"function"==typeof this._cleanup&&(this._cleanup(),this._cleanup=void 0)}dispose(){this._disposed||(this._disposed=!0,this._runCleanup(),d(this),i(this),l.delete(this))}}function k(e,t=Object.is){return new g(e,t)}function $(e,t=Object.is){return new E(e,t)}function C(e){return t()&&null===a&&o("UNOWNED_EFFECT","","effect created with no owner — it will never be disposed and may leak. Wrap it in createRoot(dispose => …) (or a SignalsElement render()) and dispose on teardown."),new S(e)}function N(e){const t={_owned:[]};let n=!1;const o=()=>{n||(n=!0,d(t))},r=a,i=s;a=t,s=null;try{return e(o)}catch(e){throw o(),e}finally{a=r,s=i}}function L(){return null!==a}function q(e){null===a&&o("ORPHAN_CLEANUP","","onCleanup called with no owner — the cleanup will never run. Call it inside an effect, createRoot, or a SignalsElement render()."),c(e)}const R=Symbol("bindNode.disposed");class I extends Error{[R]=!0}function T(e){return"object"==typeof e&&null!==e&&!0===e[R]}function j(e,t){return e[t]}const A=e=>e.detail;function x(e,t){const n=e,o=t??n.constructor.wcBindable;if(!o)throw new Error("bindNode: no wc-bindable descriptor provided and none found on target.constructor.wcBindable");const s=new Map(o.properties.map(e=>[e.name,e])),r=new Set((o.inputs??[]).map(e=>e.name)),i=new Set((o.commands??[]).map(e=>e.name)),a={},c=[];let d=!1;const l=(e,t)=>{if(d)throw new I(`bindNode.${e}: "${t}" called after dispose (the adapter is inert).`)};for(const e of o.properties){const t=k(j(n,e.name));a[e.name]=t;const o=e.getter??A,s=e=>{t.set(o(e))};n.addEventListener(e.event,s),c.push(()=>n.removeEventListener(e.event,s)),t.set(j(n,e.name))}return{signals:a,on(e,t={}){l("on",e);const o=s.get(e);if(!o)throw new Error(`bindNode.on: "${e}" is not a declared property on this node.`);const r=t.fold??((e,t)=>t),i=k(t.initial,()=>!1),a=o.getter??A,d=e=>{const t=a(e);i.set(r(i.peek(),t))};return n.addEventListener(o.event,d),c.push(()=>n.removeEventListener(o.event,d)),i},set(e,t){if(l("set",e),!r.has(e))throw new Error(`bindNode.set: "${e}" is not a declared input on this node.`);n[e]=t},bindInput(e,t){if(l("bindInput",e),!r.has(e))throw new Error(`bindNode.bindInput: "${e}" is not a declared input on this node.`);let o;const s=C(()=>{const s=t.get(),r=o;o=s,void 0===s&&void 0===r||n[e]!==s&&(n[e]=s)});return c.push(()=>s.dispose()),()=>s.dispose()},command(e,...t){if(l("command",e),!i.has(e))throw new Error(`bindNode.command: "${e}" is not a declared command on this node.`);if("function"!=typeof n[e])throw new TypeError(`bindNode.command: "${e}" is declared but not a function on the node.`);return n[e](...t)},bindCommand(e,t,o){if(l("bindCommand",e),!i.has(e))throw new Error(`bindNode.bindCommand: "${e}" is not a declared command on this node.`);if("function"!=typeof n[e])throw new TypeError(`bindNode.bindCommand: "${e}" is declared but not a function on the node.`);let s=!1;const r=C(()=>{const r=t.get();if(!s)return void(s=!0);const i=o?o(r):[r];n[e](...i)});return c.push(()=>r.dispose()),()=>r.dispose()},dispose(){if(!d){d=!0;for(const e of c)e()}}}}function z(t,n,o={}){const s=o.abort??"abort";return(o,r)=>(r.addEventListener("abort",()=>{try{t.command(s)}catch(t){T(t)||e(t)}},{once:!0}),n(t,o))}function O(e,t={}){const n=k(t.initial),o=k(!1),s=k(null);let r=null;const i=C(()=>{const i=t.args?t.args():void 0;r?.abort();const a=new AbortController;let c;r=a,o.set(!0),s.set(null);try{c=e(i,a.signal)}catch(e){return s.set(e),void o.set(!1)}Promise.resolve(c).then(e=>{a.signal.aborted||(n.set(e),o.set(!1))},e=>{a.signal.aborted||(s.set(e),o.set(!1))})}),a=()=>{r?.abort(),i.dispose()};return q(a),{value:n,loading:o,error:s,dispose:a}}function D(e,t={}){const n=t.fold??((e,t)=>t),o=k(t.initial),s=k("idle"),r=k(null);let i=null;const a=C(()=>{const a=t.args?t.args():void 0;i?.abort();const c=new AbortController;i=c,o.set(t.initial),r.set(null),s.set("active"),async function(e,t,n,o,s,r,i){let a=null,c=null;const d=()=>{if(a&&a!==c){c=a;try{a.return?.()?.then?.(void 0,()=>{})}catch{}}};n.addEventListener("abort",d,{once:!0});try{const i=await e(t,n);if(a=function(e,t){if("function"==typeof e?.getReader)return async function*(e,t){const n=e.getReader(),o=()=>{n.cancel().catch(()=>{})};t.addEventListener("abort",o,{once:!0});try{for(;;){const{done:e,value:t}=await n.read();if(e)return;yield t}}finally{t.removeEventListener("abort",o),n.releaseLock()}}(e,t);if("function"==typeof e?.[Symbol.asyncIterator])return e;throw new TypeError("streamResource: source must return an AsyncIterable or a ReadableStream (got neither).")}(i,n)[Symbol.asyncIterator](),n.aborted)return void d();for(;;){const e=await a.next();if(e.done)break;if(n.aborted)return;s.set(o(s.peek(),e.value))}if(n.aborted)return;r.set("done")}catch(e){if(n.aborted)return;i.set(e),r.set("error")}finally{n.removeEventListener("abort",d)}}(e,a,c.signal,n,o,s,r)}),c=()=>{i?.abort(),a.dispose()};return q(c),{value:o,status:s,error:r,dispose:c}}export{I as D,N as a,x as b,$ as c,D as d,C as e,b as f,t as g,L as h,T as i,z as n,q as o,O as r,k as s,o as w};
2
+ //# sourceMappingURL=core-CUHdCgQ2.esm.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-CUHdCgQ2.esm.min.js","sources":["../src/reportError.ts","../src/dev.ts","../src/reactive.ts","../src/bindNode.ts","../src/resource.ts","../src/streamResource.ts"],"sourcesContent":["// Internal error-reporting policy (NOT part of the public API; not re-exported\n// from exports.ts). Single source of truth for \"surface an error without crashing\".\n//\n// Delegates to the platform `reportError` when present — it dispatches a window\n// \"error\" event / logs to the console without aborting the current task — and falls\n// back to `console.error` otherwise. It NEVER re-throws (re-throwing would abort the\n// caller's drain / abort handler) and NEVER swallows silently (that would hide bugs).\nexport function reportError(err: unknown): void {\n const r = (globalThis as { reportError?: (e: unknown) => void }).reportError;\n if (typeof r === \"function\") {\n r(err);\n } else {\n console.error(err);\n }\n}\n","// Opt-in development diagnostics (NOT part of the public API; not re-exported from\n// exports.ts). Surfaces \"silent failure\" modes — duplicate/unkeyed list rows,\n// un-owned effects that leak, no-op onCleanup, reactive cycles — that production\n// otherwise swallows.\n//\n// DEFAULT OFF. Nothing here runs unless the consumer flips `globalThis.__WCS_DEV__`\n// to `true` BEFORE the failing operation. `isDev()` reads the flag at CALL TIME (not\n// module-load time), so a page can toggle it on for a debugging session without a\n// rebuild. See README \"Development mode\".\n//\n// TREE-SHAKING: the package sets `\"sideEffects\": false`, and every export here is a\n// pure function with no module-top-level side effects (the dedupe cache is a lazily-\n// populated Set, not eager work). A production bundler that never references warnDev\n// can drop this module entirely; a bundler that does keep it still pays nothing at\n// runtime because `warnDev` short-circuits on the `isDev()` guard before touching the\n// dedupe Set or formatting a message.\n\n/** Stable warning codes, so docs/tests can reference a warning without matching prose. */\nexport type DevWarningCode =\n | \"DUPLICATE_KEY\"\n | \"NON_PRIMITIVE_KEY\"\n | \"NULLISH_KEY\"\n | \"UNOWNED_EFFECT\"\n | \"UNOWNED_INSERT\"\n | \"ORPHAN_CLEANUP\"\n | \"REACTIVE_CYCLE\";\n\n/**\n * True only when the consumer has opted in via `globalThis.__WCS_DEV__ === true`.\n * Read at call time so the flag can be toggled at runtime. Kept tiny and pure so a\n * production bundle that strips dev branches pays nothing.\n */\nexport function isDev(): boolean {\n return (globalThis as { __WCS_DEV__?: boolean }).__WCS_DEV__ === true;\n}\n\n// Dedupe set: a given (code + dedupe-key) warning is emitted at most once, so a\n// per-row or per-flush failure does not flood the console. Lazily populated; empty\n// and inert until the first dev warning actually fires.\nconst emitted = new Set<string>();\n\n/**\n * Emit a development warning ONCE per `(code, dedupeKey)`, only when dev mode is on.\n * In production (`isDev()` false) this is a no-op that returns before any work.\n *\n * @param code stable warning code (see DevWarningCode)\n * @param dedupeKey extra discriminator folded into the dedupe identity (e.g. the\n * duplicate key value). Same code + same dedupeKey warns once.\n * @param message human-readable explanation; `detail` is appended for context.\n */\nexport function warnDev(\n code: DevWarningCode,\n dedupeKey: string,\n message: string,\n ...detail: unknown[]\n): void {\n if (!isDev()) {\n return;\n }\n const id = code + \"\u0000\" + dedupeKey;\n if (emitted.has(id)) {\n return;\n }\n emitted.add(id);\n console.warn(`[wcstack/signals][${code}] ${message}`, ...detail);\n}\n\n/**\n * Clear the dedupe cache. Test-only seam so each test can re-exercise a warning that\n * is otherwise emitted once per process. Not meant for application use.\n */\nexport function __resetDevWarnings(): void {\n emitted.clear();\n}\n","// Minimal signal core (v1 design notes).\n//\n// A self-contained, zero-dependency reactive primitive whose public API is\n// shaped after the TC39 Signals proposal (State / Computed / effect). The intent\n// is NOT to ship signal-polyfill as a runtime dependency, but to keep an in-house\n// implementation that can later be swapped for the native one without changing\n// call sites. See docs/signals-state-design.md §2 (the \"case C\" decision).\n//\n// Reactivity model: three-color marking (CLEAN / CHECK / DIRTY), pull-validated\n// (after Reactively / Solid). docs §8 (c).\n// - A signal write marks its direct observers DIRTY and their transitive\n// observers CHECK. Effects scheduled on the CLEAN→stale transition run on a\n// coalesced microtask (docs §5-3).\n// - On read/run, `updateIfNecessary` validates a CHECK node by refreshing its\n// computed sources first; it recomputes only if a source actually changed.\n// - EQUALITY SHORT-CIRCUIT: when a computed recomputes to an equal value it does\n// NOT mark its observers DIRTY, so downstream effects/computeds skip work.\n// - dependencies are re-tracked on every run, so conditional deps are pruned.\n\nimport { reportError } from \"./reportError.js\";\nimport { isDev, warnDev } from \"./dev.js\";\n\nexport type Cleanup = () => void;\nexport type Equals<T> = (a: T, b: T) => boolean;\n\nexport interface ReadSignal<T> {\n /** Read the value AND register the current observer as a dependent. */\n get(): T;\n /** Read the value WITHOUT tracking (no dependency edge created). */\n peek(): T;\n}\n\nexport interface WriteSignal<T> extends ReadSignal<T> {\n set(value: T): void;\n}\n\nexport interface EffectHandle {\n dispose(): void;\n}\n\n// Node freshness. CLEAN: up to date. CHECK: a transitive source may have changed\n// — must validate before use. DIRTY: a direct source changed — must recompute.\n// Typed as `number` (not literals) on purpose: `_state` is mutated through\n// aliasing during `updateIfNecessary`, so literal narrowing would wrongly flag\n// the `=== DIRTY` re-check after a source update.\nconst CLEAN: number = 0;\nconst CHECK: number = 1;\nconst DIRTY: number = 2;\n\ninterface Observable {\n _observers: Set<Computation>;\n}\n\ninterface Computation extends Observable {\n _state: number;\n _sources: Set<Observable>;\n _isEffect: boolean;\n _update(): void;\n}\n\n// --- tracking context -------------------------------------------------------\n\nlet currentObserver: Computation | null = null;\n\nfunction track(source: Observable): void {\n if (currentObserver !== null) {\n currentObserver._sources.add(source);\n source._observers.add(currentObserver);\n }\n}\n\nfunction untrack(node: Computation): void {\n for (const source of node._sources) {\n source._observers.delete(node);\n }\n node._sources.clear();\n}\n\n// --- ownership (disposal tree) ----------------------------------------------\n//\n// Orthogonal to dependency tracking. An \"owner\" collects the disposers of the\n// computations (effects, nested cleanups, child roots) created during its run.\n// An effect IS an owner: it owns whatever was created while it ran, and disposes\n// those children before it re-runs or is disposed. This is what lets the DOM\n// layer rebuild a subtree without leaking the previous subtree's effects\n// (docs §8 (d)). Modeled after Solid's reactive ownership.\n\ntype Disposer = () => void;\n\ninterface Owner {\n _owned: Disposer[];\n}\n\nlet currentOwner: Owner | null = null;\n\nfunction registerDisposer(disposer: Disposer): void {\n if (currentOwner !== null) {\n currentOwner._owned.push(disposer);\n }\n}\n\nfunction disposeOwned(owner: Owner): void {\n // Dispose in reverse creation order (children before parents, last-in-first-out).\n const owned = owner._owned;\n for (let i = owned.length - 1; i >= 0; i--) {\n owned[i]();\n }\n owned.length = 0;\n}\n\n// --- effect scheduling (microtask coalesce) ---------------------------------\n\n// The queue NEW work accumulates into (markStale / a computed's DIRTY promotion add\n// here). Each drain pass takes the current queue to iterate and installs a fresh\n// empty one in its place, so effects queued WHILE draining land in the next pass.\nlet pendingEffects = new Set<EffectNode>();\nlet flushScheduled = false;\n\n// Double-buffer free list: rather than `[...pendingEffects]` (a Set→Array copy) per\n// pass, a drain SWAPS the live queue with a recycled empty Set and iterates that Set\n// directly. The swapped-out Set must be a fresh buffer that re-entrancy cannot\n// clobber — a nested flushSync swaps `pendingEffects` again, so the outer pass keeps\n// iterating its OWN buffer (the same isolation the old local-array copy gave). After\n// draining, the buffer is cleared and returned to the pool for reuse.\nconst bufferPool: Set<EffectNode>[] = [];\n\nfunction takeBuffer(): Set<EffectNode> {\n return bufferPool.pop() ?? new Set<EffectNode>();\n}\n\nfunction schedule(): void {\n if (flushScheduled) {\n return;\n }\n flushScheduled = true;\n queueMicrotask(flushEffects);\n}\n\n// Upper bound on drain iterations. A well-behaved graph settles in a handful of\n// passes; exceeding this means an effect keeps dirtying its own dependency with a\n// changing value (a reactive cycle), which would otherwise hang the page. We bail\n// loudly instead of spinning forever (docs §5-3).\nconst MAX_FLUSH_ITERATIONS = 1000;\n\n// Dev-only: build a \"what was still running\" appendix for the cycle bail. Uses the\n// creation stack captured per effect (dev only) so the message points at WHERE the\n// looping effects were defined. Production never calls this (guarded by the dev\n// `lastBatch`), so the stack-capture cost never lands on shipped code.\nfunction describeCycle(batch: EffectNode[]): string {\n const live = batch.filter((e) => !e._disposed);\n const lines = live.map((e, i) => {\n const where = e._devStack ? e._devStack.split(\"\\n\").slice(1, 3).join(\" | \").trim() : \"(no stack)\";\n return ` #${i + 1} ${where}`;\n });\n return (\n `\\n[wcstack/signals] dev diagnostics: ${live.length} effect(s) still re-running on the ` +\n `final pass (likely cycle participants):\\n${lines.join(\"\\n\")}`\n );\n}\n\nfunction flushEffects(): void {\n flushScheduled = false;\n // Drain in a loop: an effect may, while running, dirty a signal that queues\n // further effects. Coalescing collapses multiple writes in one tick into a\n // single run per effect (docs §5-3).\n let iterations = 0;\n // Dev-only: the effects run on the most recent pass, so a cycle bail can point at\n // the culprits. Lazily allocated only in dev (no cost in production).\n let lastBatch: EffectNode[] | null = null;\n while (pendingEffects.size > 0) {\n if (++iterations > MAX_FLUSH_ITERATIONS) {\n // Drop the runaway queue so a single bad effect cannot wedge every later\n // flush, then surface the bug.\n pendingEffects.clear();\n throw new Error(\n \"flushEffects: exceeded \" +\n MAX_FLUSH_ITERATIONS +\n \" iterations — likely a reactive cycle (an effect writes a signal it depends on with an ever-changing value).\" +\n (lastBatch ? describeCycle(lastBatch) : \"\"),\n );\n }\n // Swap the live queue out for a fresh empty buffer, then iterate the swapped-out\n // Set directly (no array copy). New work queued by an effect's body lands in the\n // new `pendingEffects` and is picked up on the next pass — same coalescing as\n // before. `batch` is a local reference, so a re-entrant flushSync (which swaps\n // `pendingEffects` again) cannot clobber this pass's iteration.\n const batch = pendingEffects;\n pendingEffects = takeBuffer();\n if (isDev()) {\n // Record the effects on this pass; on the FINAL (overflowing) pass these are\n // the ones still re-queuing themselves — the cycle participants.\n lastBatch = [...batch];\n }\n for (const node of batch) {\n if (!node._disposed) {\n // Isolate each effect: a throw from one effect's body must NOT abort the\n // batch and silently strand its already-dequeued siblings. We hand the\n // error to reportError (which does not re-throw, so the drain continues)\n // and move on. updateIfNecessary guarantees the node is settled to CLEAN\n // even on throw, so it is not left stuck DIRTY.\n try {\n updateIfNecessary(node);\n } catch (err) {\n reportError(err);\n }\n }\n }\n // Recycle this pass's buffer. Clear AFTER iterating so a node is never dropped\n // mid-walk; the Set is now free for a later takeBuffer().\n batch.clear();\n bufferPool.push(batch);\n }\n}\n\n// Effect/computed errors are surfaced via the shared `reportError` policy\n// (./reportError.ts), called synchronously from the drain loop. It does NOT\n// re-throw (that would abort the drain) and does NOT swallow (that would hide the\n// bug). Re-entrancy is safe: the only re-scheduling path (markStale) defers to a\n// microtask, so reporting here cannot recurse into the running flush.\n\n/**\n * Synchronously flush queued effects. Provided for tests and for callers that\n * need DOM updates applied before reading the DOM back. In normal use effects\n * settle on their own microtask.\n */\nexport function flushSync(): void {\n // Do NOT touch flushScheduled here: a microtask may already be queued. Just\n // drain. flushEffects resets the flag itself; if a queued microtask still fires\n // afterwards it finds an empty queue and is a no-op. Touching the flag from here\n // would let a second flushSync race the queued microtask into a double drain.\n flushEffects();\n}\n\n// --- marking & validation ---------------------------------------------------\n\nfunction markStale(node: Computation, level: number): void {\n if (node._state < level) {\n const wasClean = node._state === CLEAN;\n node._state = level;\n if (wasClean && node._isEffect) {\n pendingEffects.add(node as EffectNode);\n schedule();\n }\n // Observers can only become CHECK from us: they need to re-validate, but\n // whether they truly changed depends on our recomputed value.\n for (const observer of node._observers) {\n markStale(observer, CHECK);\n }\n }\n}\n\nfunction updateIfNecessary(node: Computation): void {\n if (node._state === CLEAN) {\n return;\n }\n if (node._state === CHECK) {\n // A transitive source might have changed. Refresh computed sources; one of\n // them may set us DIRTY (via the equality check in its _update).\n // NOTE: this scans ALL sources each validation (O(sources)); the standard\n // pull-based scheme (Reactively/Solid) does the same. Intentional for v1 —\n // a future optimization could track per-source dirtiness to scan less.\n for (const source of node._sources) {\n if (source instanceof ComputedNode) {\n updateIfNecessary(source);\n if (node._state === DIRTY) {\n break;\n }\n }\n }\n }\n // Settle to CLEAN in a finally: if _update() (a user fn / equals) throws, the\n // node must NOT be left stuck DIRTY, otherwise every later peek/get would\n // re-run the throwing fn forever. The error propagates to the caller (a flush\n // isolates it per-effect; a direct get/peek surfaces it to that caller).\n try {\n if (node._state === DIRTY) {\n node._update();\n }\n } finally {\n node._state = CLEAN;\n }\n}\n\n// --- signal -----------------------------------------------------------------\n\nclass SignalNode<T> implements Observable, WriteSignal<T> {\n _observers = new Set<Computation>();\n private _value: T;\n private _equals: Equals<T>;\n\n constructor(value: T, equals: Equals<T>) {\n this._value = value;\n this._equals = equals;\n }\n\n get(): T {\n track(this);\n return this._value;\n }\n\n peek(): T {\n return this._value;\n }\n\n set(next: T): void {\n if (this._equals(this._value, next)) {\n return;\n }\n this._value = next;\n // No copy needed: markStale only schedules effects (on a microtask) and marks\n // observers — it never runs user code synchronously, so this set is not\n // mutated during the walk. Iterating it directly avoids a per-write allocation.\n for (const observer of this._observers) {\n markStale(observer, DIRTY);\n }\n }\n}\n\n// --- computed ---------------------------------------------------------------\n\nclass ComputedNode<T> implements Computation, ReadSignal<T> {\n _observers = new Set<Computation>();\n _sources = new Set<Observable>();\n _state = DIRTY; // never computed yet\n _isEffect = false;\n private _fn: () => T;\n private _value: T | undefined = undefined;\n private _initialized = false;\n private _running = false;\n private _equals: Equals<T>;\n\n constructor(fn: () => T, equals: Equals<T>) {\n this._fn = fn;\n this._equals = equals;\n // Be owned by the enclosing scope (a parent effect or createRoot). When that\n // scope tears down, untrack this computed so it is removed from its sources'\n // observer sets — otherwise a computed created inside an effect that re-runs\n // would leak a stale ComputedNode into each source on every run (docs §8 (d)).\n // Lazy + no children: teardown is just untrack, no cleanup/owned to dispose.\n registerDisposer(() => untrack(this));\n }\n\n get(): T {\n // Order matters: track BEFORE updateIfNecessary. The current observer must be\n // registered against THIS computed regardless of whether validation recomputes —\n // and recomputation swaps currentObserver to this node, so tracking first records\n // the edge under the OUTER observer (the caller), not this computed's own run.\n track(this);\n updateIfNecessary(this);\n return this._value as T;\n }\n\n peek(): T {\n updateIfNecessary(this);\n return this._value as T;\n }\n\n _update(): void {\n // Cycle guard: if _fn (directly or transitively) reads this computed, get()\n // would recurse into _update and blow the stack with an opaque RangeError.\n // Detect the self-reference and throw a clear error instead. (The effect-level\n // MAX_FLUSH_ITERATIONS guard does not cover this synchronous self-recursion.)\n if (this._running) {\n throw new Error(\"computed: circular dependency — the computed reads itself.\");\n }\n const previous = this._value;\n untrack(this);\n const prevObserver = currentObserver;\n currentObserver = this;\n this._running = true;\n try {\n this._value = this._fn();\n } finally {\n this._running = false;\n currentObserver = prevObserver;\n }\n // Equality short-circuit: only propagate when the value actually changed.\n // Our observers are already CHECK (from the originating markStale); promote\n // them to DIRTY so they recompute, otherwise leave them to settle CLEAN. Skip\n // on the first computation: there is no prior value to compare, and a custom\n // `equals` must not be invoked with the undefined sentinel.\n if (!this._initialized) {\n this._initialized = true;\n } else if (!this._equals(previous as T, this._value as T)) {\n for (const observer of this._observers) {\n observer._state = DIRTY;\n // Normally this computed is refreshed from inside flushEffects while\n // validating an already-scheduled effect, so the observer is in the queue\n // already. But if it is refreshed out-of-band (e.g. a `peek()` outside a\n // flush) an effect observer could otherwise be promoted to DIRTY without\n // being queued, and never re-run. Re-schedule defensively to keep the\n // \"DIRTY effect always runs\" invariant regardless of refresh path.\n if (observer._isEffect && !(observer as EffectNode)._disposed) {\n pendingEffects.add(observer as EffectNode);\n schedule();\n }\n }\n }\n }\n}\n\n// --- effect -----------------------------------------------------------------\n\nclass EffectNode implements Computation, Owner {\n _observers = new Set<Computation>(); // effects are not observed; kept for the interface\n _sources = new Set<Observable>();\n _owned: Disposer[] = [];\n _state = DIRTY;\n _isEffect = true;\n _disposed = false;\n // Dev-only creation stack, used to point a reactive-cycle bail at where the looping\n // effect was defined. Captured only when dev mode is on at construction time, so\n // production effects pay nothing for it.\n _devStack: string | undefined = undefined;\n private _fn: () => Cleanup | void;\n private _cleanup: Cleanup | void = undefined;\n\n constructor(fn: () => Cleanup | void) {\n this._fn = fn;\n if (isDev()) {\n this._devStack = new Error().stack;\n }\n // Be owned by the enclosing owner (a parent effect or a createRoot), so a\n // parent teardown disposes this effect.\n registerDisposer(() => this.dispose());\n updateIfNecessary(this); // initial run (state starts DIRTY)\n }\n\n _update(): void {\n this._runCleanup();\n disposeOwned(this); // tear down children created in the previous run\n untrack(this);\n const prevObserver = currentObserver;\n const prevOwner = currentOwner;\n currentObserver = this;\n currentOwner = this; // children created during the run are owned by this effect\n try {\n this._cleanup = this._fn();\n } finally {\n currentObserver = prevObserver;\n currentOwner = prevOwner;\n }\n }\n\n private _runCleanup(): void {\n if (typeof this._cleanup === \"function\") {\n this._cleanup();\n this._cleanup = undefined;\n }\n }\n\n dispose(): void {\n if (this._disposed) {\n return;\n }\n this._disposed = true;\n this._runCleanup();\n disposeOwned(this);\n untrack(this);\n pendingEffects.delete(this);\n }\n}\n\n// --- public API -------------------------------------------------------------\n\nexport function signal<T>(initial: T, equals: Equals<T> = Object.is): WriteSignal<T> {\n return new SignalNode(initial, equals);\n}\n\nexport function computed<T>(fn: () => T, equals: Equals<T> = Object.is): ReadSignal<T> {\n return new ComputedNode(fn, equals);\n}\n\nexport function effect(fn: () => Cleanup | void): EffectHandle {\n if (isDev() && currentOwner === null) {\n // No enclosing owner: nothing will ever dispose this effect, so it (and the\n // subscriptions it builds) leak. Legitimate top-level effects should be mounted\n // under createRoot / SignalsElement and disposed on teardown. Dev-only warn.\n warnDev(\n \"UNOWNED_EFFECT\",\n \"\",\n \"effect created with no owner — it will never be disposed and may leak. \" +\n \"Wrap it in createRoot(dispose => …) (or a SignalsElement render()) and \" +\n \"dispose on teardown.\",\n );\n }\n return new EffectNode(fn);\n}\n\n/**\n * Run `fn` inside a fresh ownership scope and return its result. Every effect (or\n * nested cleanup) created during `fn` — directly or transitively — is owned by\n * this root; calling the `dispose` passed to `fn` tears them all down.\n *\n * The root is detached: it is NOT auto-disposed by an enclosing owner. The caller\n * holds `dispose` (e.g. a custom element disposes it in disconnectedCallback).\n *\n * If `fn` throws, any effects/cleanups it created BEFORE the throw are disposed\n * before the error propagates — a caller that never received `dispose` (because the\n * call threw) cannot leak the half-built scope.\n */\nexport function createRoot<T>(fn: (dispose: () => void) => T): T {\n const owner: Owner = { _owned: [] };\n let disposed = false;\n const dispose = (): void => {\n if (disposed) {\n return;\n }\n disposed = true;\n disposeOwned(owner);\n };\n const prevOwner = currentOwner;\n const prevObserver = currentObserver;\n currentOwner = owner;\n // Detach the dependency-tracking context too, not just ownership. A root is a\n // fresh reactive boundary: signals read SYNCHRONOUSLY while `fn` builds the scope\n // must not register the OUTER observer as their dependent (Solid's createRoot\n // clears both owner and listener). Without this, a For/Index `each` that reads\n // `index()`/`item()` synchronously in its body would track the outer reconcile\n // effect against the row's idx/item signal, so reordering re-dirties the reconcile\n // effect — a self-loop the equality/MAX_FLUSH guards only paper over.\n currentObserver = null;\n try {\n return fn(dispose);\n } catch (err) {\n dispose(); // tear down whatever fn built before it threw\n throw err;\n } finally {\n currentOwner = prevOwner;\n currentObserver = prevObserver;\n }\n}\n\n/**\n * True when there is a current ownership scope (an enclosing effect or createRoot).\n * Dev-diagnostics seam for the DOM layer to flag un-owned reactive insertions\n * without re-implementing owner tracking. NOT part of the public API.\n */\nexport function hasOwner(): boolean {\n return currentOwner !== null;\n}\n\n/**\n * Register a teardown callback with the current owner. Runs when the owning effect\n * re-runs or is disposed, or when the enclosing root is disposed. A no-op when\n * there is no current owner.\n */\nexport function onCleanup(fn: () => void): void {\n if (currentOwner === null) {\n // No owner → registerDisposer drops `fn` on the floor: the cleanup never runs.\n // Silent in production; flag it in dev so the missing teardown is visible.\n warnDev(\n \"ORPHAN_CLEANUP\",\n \"\",\n \"onCleanup called with no owner — the cleanup will never run. \" +\n \"Call it inside an effect, createRoot, or a SignalsElement render().\",\n );\n }\n registerDisposer(fn);\n}\n","// wc-bindable → signal adapter. The crux of the design.\n//\n// Any async-IO node in wcstack speaks the wc-bindable protocol:\n// - properties: outputs — the node dispatches `event` on change; the value is\n// read via `getter(event)`, defaulting to `e.detail` (wc-bindable\n// SPEC § Default Getter). The property `name` is read only for the\n// initial seed, where no event exists yet (SPEC § Initial Sync).\n// - inputs: settable surface — write `node[name] = value`.\n// - commands: invocable methods — call `node[name](...args)`.\n// The node has NO idea whether the observer behind the binding is a proxy (state)\n// or a signal. So a single adapter that maps these surfaces onto signals makes every\n// existing node plug into the signal core unchanged. See docs/signals-state-design.md\n// §3, and the four mappings of §3-1:\n//\n// element → signal | property (latest snapshot) → read signal [`signals`]\n// element → signal | event-token (per-emit stream) → folded signal [`on`]\n// signal → element | input (write-back) → effect → prop [`bindInput`]\n// signal → element | command-token (start/cancel) → emit on change [`bindCommand`]\n//\n// `signals` is the STATE view of a property (equality-guarded — same value = no\n// update). `on` is the OCCURRENCE view of the same event (a stream — every emit\n// updates, even with an equal value), folded latest-by-default.\n\nimport { signal, effect, WriteSignal, ReadSignal } from \"./reactive.js\";\nimport { reportError } from \"./reportError.js\";\nimport type { ResourceSource } from \"./resource.js\";\n\nexport interface WcBindableProperty<V = unknown> {\n readonly name: string;\n readonly event: string;\n readonly getter?: (event: Event) => V;\n}\n\n// Structurally a SUBSET of the real `IWcBindable` (which additionally carries\n// `protocol` / `version`, and richer input/command entries). A real descriptor is\n// therefore assignable to this type — see __tests__/bindNode.compat.test.ts.\nexport interface WcBindableDescriptor {\n readonly properties: readonly WcBindableProperty[];\n readonly inputs?: readonly { readonly name: string }[];\n readonly commands?: readonly { readonly name: string }[];\n}\n\n// --- typed node shape (D1) ---------------------------------------------------\n//\n// `bindNode` is the package's centerpiece, yet without a type argument every output\n// was `ReadSignal<unknown>` and every `set`/`command` took `unknown` — the feature\n// was effectively untyped in consumer code. `NodeShape` is an OPT-IN description of a\n// node's reactive surface that drives the return type: declare the property value\n// types, the input value types, and the command signatures ONCE, and `bound.signals`,\n// `on`, `set`, `bindInput`, `command`, and `bindCommand` are all typed from it.\n//\n// interface FetchShape extends NodeShape {\n// signals: { value: Person[]; loading: boolean };\n// inputs: { url: string };\n// commands: { fetch: (url: string) => Promise<Person[]>; abort: () => void };\n// }\n// const bound = bindNode<FetchShape>(el, desc);\n// bound.signals.value.get(); // ReadSignal<Person[]> — key & type checked\n// bound.set(\"url\", \"/api/people\"); // string enforced\n// bound.command(\"fetch\", \"/api\"); // arg/return inferred\n//\n// The runtime is UNCHANGED — these types are erased; the adapter still validates names\n// against the descriptor at runtime. The default shape below keeps every existing\n// untyped call site compiling exactly as before (all-`unknown`, string keys).\n// Open command map: a command name → any call signature. `(...args: any[])` (NOT\n// `never[]`) is the constraint a USER shape's commands must satisfy — and, crucially,\n// the OPEN fallback used when a shape omits `commands`. `Parameters<(...args: any[])\n// => unknown>` is `any[]`, so an untyped `command(\"run\", \"/api\")` accepts any args,\n// preserving the pre-generic behaviour (symmetric with `inputs` → `unknown`). A shape\n// that DECLARES a precise signature (e.g. `(url: string) => Promise<X>`) still gets\n// exact arg/return checking on the typed path — the loose constraint only widens the\n// DEFAULT, never a declared command. (`no-explicit-any` is disabled for this package.)\ntype OpenCommandMap = Record<string, (...args: any[]) => unknown>;\n\nexport interface NodeShape {\n /** propertyName → latest-snapshot value type (what `signals[name]` / `on(name)` carry). */\n signals?: Record<string, unknown>;\n /** inputName → settable value type (what `set` / `bindInput` accept). */\n inputs?: Record<string, unknown>;\n /** commandName → its call signature (what `command` / `bindCommand` invoke). */\n commands?: OpenCommandMap;\n}\n\n// Back-compat default: a fully-open shape so `bindNode(target, desc)` with no type\n// argument behaves as before (signals keyed by arbitrary string → unknown; any\n// input/command name; unknown values; any command args). Stronger typing is opt-in\n// via the type arg.\nexport interface DefaultNodeShape extends NodeShape {\n signals: Record<string, unknown>;\n inputs: Record<string, unknown>;\n commands: OpenCommandMap;\n}\n\n// Resolve the three sub-maps with sane fallbacks when a shape omits one of them.\ntype SignalsOf<S extends NodeShape> = S[\"signals\"] extends Record<string, unknown>\n ? S[\"signals\"]\n : Record<string, unknown>;\ntype InputsOf<S extends NodeShape> = S[\"inputs\"] extends Record<string, unknown>\n ? S[\"inputs\"]\n : Record<string, unknown>;\ntype CommandsOf<S extends NodeShape> = S[\"commands\"] extends OpenCommandMap ? S[\"commands\"] : OpenCommandMap;\n\n/** Options for `on` — how each event emission folds into the stream signal. */\nexport interface EventStreamOptions<T, C> {\n /** Combine the running value with each emission. Default: latest (replace). */\n fold?: (acc: T | undefined, chunk: C) => T;\n /** Seed value before the first emission. */\n initial?: T;\n}\n\nexport interface BoundNode<S extends NodeShape = DefaultNodeShape> {\n /**\n * Output properties as read-only signals (latest value), keyed by property name and\n * typed from the node shape: `bound.signals.value.get()` carries the declared value\n * type (no longer `unknown`). Keys are the declared property names.\n */\n readonly signals: { readonly [K in keyof SignalsOf<S>]: ReadSignal<SignalsOf<S>[K]> };\n /**\n * An event-token STREAM for a declared property's event: a read signal that folds\n * every emission (latest by default). Unlike `signals[prop]`, it updates on EVERY\n * emit — even when the derived value is equal — because it models occurrences, not\n * state. The chunk per emit is `getter(event)` if the property declares one, else\n * the property's current value.\n *\n * With no fold the value type defaults to the property's declared snapshot type; a\n * `fold` may reshape it (explicit `T`/`C` type args override).\n */\n on<K extends keyof SignalsOf<S>>(prop: K): ReadSignal<SignalsOf<S>[K] | undefined>;\n on<T, C = SignalsOf<S>[keyof SignalsOf<S>]>(\n prop: keyof SignalsOf<S>,\n options: EventStreamOptions<T, C>,\n ): ReadSignal<T | undefined>;\n /** Write a declared input on the node (imperative); value type checked against the shape. */\n set<K extends keyof InputsOf<S>>(name: K, value: InputsOf<S>[K]): void;\n /**\n * Reactively write a declared input from a signal: an effect mirrors `source` into\n * `node[name]`. A same-value guard (`node[name] !== v`) skips redundant writes, so\n * a property whose write re-dispatches an event cannot feed back into an infinite\n * loop. `undefined` follows the wc-bindable applier profile (A2): it is not written\n * while the source has had no other value (the first evaluation, or after another\n * `undefined`), so the element keeps its own initial state; after a value it is\n * written, so the element restores that state. Returns a disposer; the effect is\n * also torn down by `dispose()` (neither writes anything).\n */\n bindInput<K extends keyof InputsOf<S>>(name: K, source: ReadSignal<InputsOf<S>[K]>): () => void;\n /** Invoke a declared command on the node (imperative); args/return typed from the shape. */\n command<K extends keyof CommandsOf<S>>(name: K, ...args: Parameters<CommandsOf<S>[K]>): ReturnType<CommandsOf<S>[K]>;\n /**\n * Command-token: invoke a declared command whenever `trigger` CHANGES. The initial\n * value does not fire (subscribe-without-firing), so a command like `abort` is not\n * triggered on mount. `mapArgs` shapes the call arguments from the trigger value\n * (default: the value itself as a single argument). Returns a disposer; also torn\n * down by `dispose()`.\n */\n bindCommand<K extends keyof CommandsOf<S>, V = unknown>(\n name: K,\n trigger: ReadSignal<V>,\n mapArgs?: (value: V) => Parameters<CommandsOf<S>[K]>,\n ): () => void;\n /**\n * Detach all listeners/effects and make the adapter inert. After dispose the output\n * signals/streams stop updating and `set`/`command`/`bindInput`/`bindCommand`/`on`\n * throw (use-after-dispose). Idempotent.\n */\n dispose(): void;\n}\n\n// Internal target type. `Record<string, any>` is needed to index arbitrary declared\n// members (target[name]) inside the adapter, but it is DELIBERATELY NOT in the public\n// `bindNode` signature — exposing it there erased the caller's element type and let any\n// stray member access type-check. Kept private; the public entry takes `EventTarget`.\ntype NodeTarget = EventTarget & Record<string, any>;\n\n// Internal, fully-loose surface the adapter object is built against (string keys,\n// `unknown` values). The public `BoundNode<S>` is a stricter projection of this same\n// runtime object; `bindNode` casts the built `UntypedBoundNode` to `BoundNode<S>` at\n// return. Kept private so the loose `unknown`-typed methods never leak to callers.\ninterface UntypedBoundNode {\n readonly signals: Record<string, ReadSignal<unknown>>;\n on<T = unknown, C = unknown>(prop: string, options?: EventStreamOptions<T, C>): ReadSignal<T | undefined>;\n set(name: string, value: unknown): void;\n bindInput(name: string, source: ReadSignal<unknown>): () => void;\n command(name: string, ...args: unknown[]): unknown;\n bindCommand(name: string, trigger: ReadSignal<unknown>, mapArgs?: (value: unknown) => unknown[]): () => void;\n dispose(): void;\n}\n\n// The error thrown by `assertLive` when an adapter method is called after dispose.\n// Carries a brand symbol so callers that need to distinguish a post-dispose throw\n// (e.g. nodeSource's abort bridge) can test it structurally instead of matching the\n// message string — the message is free to change without silently breaking the\n// guard. The brand survives even if a consumer's bundler duplicates the class (e.g.\n// `instanceof` across realms), since it's a Symbol on the instance, not a class\n// identity check.\nconst DISPOSED_ERROR = Symbol(\"bindNode.disposed\");\n\n/**\n * Error thrown when a {@link BoundNode} method is invoked after the adapter has been\n * disposed. Every surface that mutates or subscribes — `on` / `set` / `bindInput` /\n * `command` / `bindCommand` — throws this once `dispose()` has run, so a\n * use-after-dispose is loud instead of silently writing/invoking on an inert adapter.\n *\n * Prefer {@link isDisposedError} over `instanceof` for the check: the brand survives a\n * bundler that duplicates the class across realms, where `instanceof` would not. Use it\n * to make teardown-order races robust — e.g. swallow a post-dispose throw while letting\n * any other error surface:\n *\n * ```ts\n * try {\n * bound.command(\"abort\");\n * } catch (err) {\n * if (!isDisposedError(err)) throw err; // expected during teardown; ignore\n * }\n * ```\n */\nexport class DisposedError extends Error {\n readonly [DISPOSED_ERROR] = true;\n}\n\n/**\n * True if `err` is a {@link DisposedError} — i.e. a use-after-dispose error thrown by a\n * {@link BoundNode} method after `dispose()`. Brand-based (checks a Symbol on the\n * instance), so it stays correct even if a consumer's bundler duplicates the\n * `DisposedError` class across module realms, where `err instanceof DisposedError`\n * could be false.\n */\nexport function isDisposedError(err: unknown): boolean {\n return typeof err === \"object\" && err !== null && (err as Record<symbol, unknown>)[DISPOSED_ERROR] === true;\n}\n\nfunction readProperty(target: NodeTarget, name: string): unknown {\n return target[name];\n}\n\n// wc-bindable SPEC § Default Getter: when a property omits `getter`, an adapter MUST\n// read `e.detail`. This adapter used to read the property instead (#238), which\n// diverged from `@wcstack/state` and `@wc-bindable/core` — the same element could\n// bind correctly in one adapter and not the other. Only the initial seed reads the\n// property (SPEC § Initial Sync: there is no event to derive from at bind time).\nconst DEFAULT_GETTER = (e: Event): unknown => (e as CustomEvent).detail;\n\n/**\n * Adapt a wc-bindable node into signals. The public `target` parameter is a plain\n * `EventTarget` (the internal `Record<string, any>` indexing is cast away here, not\n * leaked to callers — see {@link NodeTarget}). Pass an optional {@link NodeShape} type\n * argument to type the result; omit it for the back-compat all-`unknown` shape.\n *\n * @example typed\n * interface FetchShape extends NodeShape {\n * signals: { value: Person[]; loading: boolean };\n * inputs: { url: string };\n * commands: { fetch: (url: string) => Promise<Person[]>; abort: () => void };\n * }\n * const bound = bindNode<FetchShape>(fetchEl, FetchCore.wcBindable);\n * bound.signals.value.get(); // ReadSignal<Person[]>\n * bound.set(\"url\", \"/api/people\"); // string enforced\n */\nexport function bindNode<S extends NodeShape = DefaultNodeShape>(\n target: EventTarget,\n descriptor?: WcBindableDescriptor,\n): BoundNode<S> {\n // The Record<string, any> indexing surface is needed internally to read/write/invoke\n // declared members by name; keep it private to this body.\n const node = target as NodeTarget;\n const desc = descriptor ?? (node.constructor as { wcBindable?: WcBindableDescriptor }).wcBindable;\n if (!desc) {\n throw new Error(\"bindNode: no wc-bindable descriptor provided and none found on target.constructor.wcBindable\");\n }\n\n // Name → declared-entry lookups so set/command/on reject names the node never\n // declared, instead of silently writing/invoking/listening on an arbitrary member.\n const propByName = new Map(desc.properties.map((p) => [p.name, p]));\n const declaredInputs = new Set((desc.inputs ?? []).map((i) => i.name));\n const declaredCommands = new Set((desc.commands ?? []).map((c) => c.name));\n\n const signals: Record<string, WriteSignal<unknown>> = {};\n const removers: Array<() => void> = [];\n let disposed = false;\n\n const assertLive = (op: string, name: string): void => {\n if (disposed) {\n throw new DisposedError(`bindNode.${op}: \"${name}\" called after dispose (the adapter is inert).`);\n }\n };\n\n for (const prop of desc.properties) {\n // Seed with the node's current value so the signal is valid before the first\n // event fires (e.g. FetchCore.value === null at rest).\n const cell = signal<unknown>(readProperty(node, prop.name));\n signals[prop.name] = cell;\n\n const read = prop.getter ?? DEFAULT_GETTER;\n const handler = (event: Event): void => {\n cell.set(read(event));\n };\n node.addEventListener(prop.event, handler);\n removers.push(() => node.removeEventListener(prop.event, handler));\n // Re-seed AFTER subscribing: the initial read above is a snapshot taken before\n // the listener was attached, so a value change in that gap would be missed.\n // Reading the property once more now closes the race (the equality guard makes\n // it a no-op when nothing changed). Note this is the property snapshot, not a\n // getter(event) — there is no event to derive from at bind time, and the SPEC's\n // default getter (`e.detail`) applies to events only.\n cell.set(readProperty(node, prop.name));\n }\n\n // Implemented against a string-keyed, all-`unknown` surface (`UntypedBoundNode`); the\n // generic typing in `BoundNode<S>` is a compile-time projection over the SAME\n // runtime, so the object is built once and cast to `BoundNode<S>` at return. Runtime\n // name validation against the descriptor is unchanged.\n const bound: UntypedBoundNode = {\n signals,\n\n on<T = unknown, C = unknown>(prop: string, options: EventStreamOptions<T, C> = {}): ReadSignal<T | undefined> {\n assertLive(\"on\", prop);\n const propDesc = propByName.get(prop);\n if (!propDesc) {\n throw new Error(`bindNode.on: \"${prop}\" is not a declared property on this node.`);\n }\n const fold = options.fold ?? ((_acc: T | undefined, chunk: C) => chunk as unknown as T);\n // equals: () => false — a stream notifies on EVERY emit, even an equal value.\n const cell = signal<T | undefined>(options.initial, () => false);\n const read = propDesc.getter ?? DEFAULT_GETTER;\n const handler = (event: Event): void => {\n const chunk = read(event) as C;\n cell.set(fold(cell.peek(), chunk));\n };\n node.addEventListener(propDesc.event, handler);\n removers.push(() => node.removeEventListener(propDesc.event, handler));\n return cell;\n },\n\n set(name: string, value: unknown): void {\n assertLive(\"set\", name);\n if (!declaredInputs.has(name)) {\n throw new Error(`bindNode.set: \"${name}\" is not a declared input on this node.`);\n }\n node[name] = value;\n },\n\n bindInput(name: string, source: ReadSignal<unknown>): () => void {\n assertLive(\"bindInput\", name);\n if (!declaredInputs.has(name)) {\n throw new Error(`bindNode.bindInput: \"${name}\" is not a declared input on this node.`);\n }\n // The previous evaluation, written or not. Its initial `undefined` stands for \"no\n // previous evaluation\", which A2 treats the same as a previous `undefined`.\n let previous: unknown = undefined;\n const handle = effect(() => {\n const v = source.get();\n const before = previous;\n previous = v;\n // A2: an `undefined` that follows no value means \"no value yet\" — writing it\n // would overwrite the element's own initial state (its default, or the markup)\n if (v === undefined && before === undefined) return;\n if (node[name] !== v) {\n node[name] = v; // same-value guard above breaks write→event→write loops\n }\n });\n removers.push(() => handle.dispose());\n return () => handle.dispose();\n },\n\n command(name: string, ...args: unknown[]): unknown {\n assertLive(\"command\", name);\n if (!declaredCommands.has(name)) {\n throw new Error(`bindNode.command: \"${name}\" is not a declared command on this node.`);\n }\n if (typeof node[name] !== \"function\") {\n throw new TypeError(`bindNode.command: \"${name}\" is declared but not a function on the node.`);\n }\n return node[name](...args);\n },\n\n bindCommand(name: string, trigger: ReadSignal<unknown>, mapArgs?: (value: unknown) => unknown[]): () => void {\n assertLive(\"bindCommand\", name);\n if (!declaredCommands.has(name)) {\n throw new Error(`bindNode.bindCommand: \"${name}\" is not a declared command on this node.`);\n }\n // Fail fast at bind time (not on first change), so a wrong name surfaces when\n // the subscription is set up rather than silently later inside a flush.\n if (typeof node[name] !== \"function\") {\n throw new TypeError(`bindNode.bindCommand: \"${name}\" is declared but not a function on the node.`);\n }\n let primed = false;\n const handle = effect(() => {\n const v = trigger.get();\n if (!primed) {\n primed = true; // subscribe on mount without firing (emit on CHANGE only)\n return;\n }\n const args = mapArgs ? mapArgs(v) : [v];\n node[name](...args);\n });\n removers.push(() => handle.dispose());\n return () => handle.dispose();\n },\n\n dispose(): void {\n if (disposed) {\n return; // idempotent\n }\n disposed = true;\n for (const remove of removers) {\n remove();\n }\n },\n };\n\n return bound as unknown as BoundNode<S>;\n}\n\n/**\n * Build a `resource` source from a wc-bindable node, bridging the resource's\n * AbortSignal to the node's cancel command (default `\"abort\"`) before delegating to\n * `run`. This generalizes the earlier hand-wired `sig → core.abort()` bridge (docs §8\n * (e), §5-2): wrap the result in `resource({ args })` and any node that declares an\n * abort command gets switchMap-style cancel/restart for free — an `args` change\n * aborts the in-flight call (firing the node's abort command, which cancels its real\n * AbortController) and starts the next.\n *\n * The node's own value/loading/error stay available via `bound.signals`; `resource`\n * here is used for the cancel/restart lifecycle, not to re-derive that triad.\n *\n * @example\n * const bound = bindNode(fetchEl);\n * const r = resource(\n * nodeSource(bound, (b, id) => b.command(\"fetch\", `/api/${id}`)),\n * { args: () => id.get() },\n * );\n */\nexport function nodeSource<T, A = void, S extends NodeShape = DefaultNodeShape>(\n bound: BoundNode<S>,\n run: (node: BoundNode<S>, args: A) => Promise<T> | T,\n options: { abort?: string } = {},\n): ResourceSource<T, A> {\n const abortName = options.abort ?? \"abort\";\n return (args, signal) => {\n // Honor the resource's cancel by invoking the node's abort command. `once`:\n // each resource run gets a fresh AbortSignal, so the listener fires at most once.\n //\n // GUARD: the abort listener runs SYNCHRONOUSLY inside AbortController.abort().\n // If `bound` was already disposed (e.g. the adapter and the resource share an\n // owner and the adapter's disposer ran first), `command` throws assertLive —\n // and a throw out of an abort listener surfaces as an unhandled exception during\n // the synchronous abort() call. Swallow a post-dispose throw so teardown order is\n // robust; report any OTHER error via the platform reporter without breaking abort.\n signal.addEventListener(\n \"abort\",\n () => {\n try {\n // `abortName` is a runtime string (validated by the adapter); the cast lets\n // it reach `command` regardless of the typed command-name union of `S`.\n (bound.command as (name: string, ...args: unknown[]) => unknown)(abortName);\n } catch (err) {\n // Brand-based check (not a message regex): a post-dispose throw is\n // expected here and swallowed; any OTHER error is reported.\n if (!isDisposedError(err)) {\n reportError(err);\n }\n }\n },\n { once: true },\n );\n return run(bound, args);\n };\n}\n","// Async resource (v1 design notes).\n//\n// Adapts an async producer into a reactive `{ value, loading, error }` triad —\n// the same shape FetchCore exposes (value/loading/error). When the resource's\n// `args` (a reactive getter) change, the in-flight request is aborted and a fresh\n// one starts: dependency-driven cancel/restart, i.e. RxJS switchMap.\n//\n// This is the signals-side counterpart of the state-side `$streams` adapter\n// (docs/state-stream-type-design.md §4-1). The hard part — making sure a stale\n// response from a superseded request never lands on the new state — is handled\n// here by checking `signal.aborted` before committing any result.\n\nimport { signal, effect, onCleanup, ReadSignal } from \"./reactive.js\";\n\nexport interface ResourceState<T> {\n value: ReadSignal<T | undefined>;\n // Progress is a BOOLEAN here, intentionally different from streamResource's\n // `status` enum. A resource is single-shot (one request → resolve/reject), so\n // its progress is binary: in-flight or not. A stream is continuous (idle →\n // active → done/error), so it needs the enum. The \"shared contract\" with the\n // state-side `$streams` adapter covers the cancel/restart SEMANTICS, not this\n // progress representation. (docs §8 (a)).\n loading: ReadSignal<boolean>;\n /** Last error, or `null` when there is none. Initial value is `null`; a (re)start resets it to `null`. */\n error: ReadSignal<unknown>;\n /** Abort any in-flight request and stop reacting to `args` changes. */\n dispose(): void;\n}\n\nexport interface ResourceOptions<T, A> {\n /** Reactive inputs. Reading signals here wires restart-on-change. */\n args?: () => A;\n /** Seed value before the first resolution. */\n initial?: T;\n}\n\nexport type ResourceSource<T, A> = (args: A, signal: AbortSignal) => Promise<T> | T;\n\nexport function resource<T, A = void>(\n source: ResourceSource<T, A>,\n options: ResourceOptions<T, A> = {},\n): ResourceState<T> {\n const value = signal<T | undefined>(options.initial);\n const loading = signal<boolean>(false);\n const error = signal<unknown>(null);\n\n let controller: AbortController | null = null;\n\n const runner = effect(() => {\n // Reading args() inside the effect is what subscribes us to its signals, so a\n // change re-runs this body — that IS the restart trigger.\n //\n // NOTE (effect-internal writes): this effect writes loading/error/value, which\n // are signals OTHER effects may observe. A write that actually CHANGES the value\n // marks those observers stale and queues them on the same flush; the drain loop\n // then runs them in the same tick. (A same-value write — e.g. error.set(null)\n // when error is already null — is a no-op via the equality guard and notifies\n // nothing.) We never read these signals inside THIS effect, so we don't dirty\n // our own dependency — no cycle. (docs §8 (a)).\n const a = (options.args ? options.args() : undefined) as A;\n\n // Abort the previous request before starting the next (switchMap).\n controller?.abort();\n const ac = new AbortController();\n controller = ac;\n\n loading.set(true);\n error.set(null);\n\n // Call the source SYNCHRONOUSLY (so it receives ac.signal immediately and can\n // wire its abort listener before any teardown), but guard it with try/catch so\n // a synchronous throw is normalized into the same error/loading state as a\n // rejected promise. Without the guard a sync throw would escape the effect body\n // (and, on the initial run, the resource() call itself), leaving loading stuck\n // true and error unset.\n let produced: Promise<T> | T;\n try {\n produced = source(a, ac.signal);\n } catch (err) {\n error.set(err);\n loading.set(false);\n return;\n }\n\n // Settle via two-arm `.then` rather than `await` + try/catch (streamResource's\n // shape): a resource is SINGLE-SHOT — one resolve/reject lands once — so a flat\n // resolved/rejected pair maps cleanly. A stream is a CONTINUOUS loop whose chunks\n // and termination must share one error path, which an `await` loop in try/catch\n // expresses; that structure would be needless here.\n Promise.resolve(produced).then(\n (resolved) => {\n // Drop the result if this request was superseded/disposed: committing it\n // would let a stale response overwrite the newer request's state.\n if (ac.signal.aborted) {\n return;\n }\n value.set(resolved);\n loading.set(false);\n },\n (err) => {\n if (ac.signal.aborted) {\n return;\n }\n error.set(err);\n loading.set(false);\n },\n );\n });\n\n const dispose = (): void => {\n controller?.abort();\n runner.dispose();\n };\n\n // Auto-dispose with the enclosing owner (createRoot / parent effect), so a\n // resource created inside a component is aborted on unmount. No-op when there\n // is no owner — the caller then disposes manually.\n onCleanup(dispose);\n\n return { value, loading, error, dispose };\n}\n","// Stream resource (docs §8 (a)). The signals-side counterpart of the state-side\n// `$streams` adapter (docs/state-stream-type-design.md).\n//\n// Adapts a continuous async flow (async iterable / ReadableStream / async\n// generator) into a single reactive value by FOLDING each chunk:\n// - latest (default): replace — value becomes the last chunk.\n// - reduce: accumulate — value = fold(acc, chunk), needs `initial`.\n// When the resource's `args` change, the in-flight stream is aborted and a fresh\n// one starts (switchMap), with `value` reset to `initial`.\n//\n// Deliberate non-goal: backpressure. The fold result IS the buffer; demand does\n// not flow back to the producer. Unbounded accumulation of an infinite stream is\n// a footgun — bound the fold (latest / count / last-N / window) for live streams.\n// This mirrors the state `$streams` norm and is what lets the impedance mismatch\n// be resolved honestly (state-stream §0, §4-3).\n//\n// Shared contract with state `$streams` (settled in v1):\n// - source(args, signal) receives an AbortSignal; honoring it drives restart.\n// - restart RESETS value to `initial`; error KEEPS the last value.\n// - status companion: \"idle\" | \"active\" | \"done\" | \"error\".\n// - async iterable is the lingua franca; anything getReader-bearing\n// (ReadableStream) is read via getReader() — see rescue levels below.\n//\n// CONTRACT (cooperative cancellation — STRONG REQUIREMENT): the `source` MUST honor\n// the `AbortSignal` it is given. Honoring it is what drives switchMap restart/dispose;\n// a source that ignores it cannot be reliably cancelled.\n//\n// Rescue levels on abort:\n// - ReadableStream: FULLY rescued. A parked read() is force-unwound via\n// reader.cancel(), which both releases the underlying source and settles the\n// pending read() so the loop unwinds. Anything getReader-bearing is routed\n// through this path even when it is ALSO natively async-iterable (Node 18+ /\n// Chrome 124+ / Firefox): the spec serializes asyncIterator.return() behind\n// the pending next() ([[ongoingPromise]]), so the AsyncIterable rescue below\n// cannot force-unwind a parked read on a native ReadableStream.\n// - AsyncIterable / async generator: PARTIALLY rescued. On abort we call\n// iterator.return() to trigger the generator's finally/cleanup. But a parked\n// `await` (the producer stalling before its next yield while IGNORING `signal`)\n// cannot be force-unwound from outside — return() only takes effect when the\n// generator next resumes. So a source that parks forever and never observes\n// `signal` still leaks its consume() task. Honor `signal` to bound this.\n// The `if (signal.aborted) return` check only runs after a chunk arrives, not while\n// parked — it drops stale chunks but is not, by itself, a cancellation mechanism.\n\nimport { signal, effect, onCleanup, ReadSignal, WriteSignal } from \"./reactive.js\";\n\nexport type StreamStatus = \"idle\" | \"active\" | \"done\" | \"error\";\n\nexport interface StreamResourceState<T> {\n value: ReadSignal<T | undefined>;\n // Progress is an ENUM here, intentionally different from resource's `loading`\n // boolean: a continuous stream moves idle → active → done/error, which a boolean\n // cannot express. The cancel/restart contract is shared with `resource` and the\n // state-side `$streams` adapter; the progress representation is not.\n status: ReadSignal<StreamStatus>;\n /** Last error, or `null` when there is none. Initial value is `null`; a (re)start resets it to `null`. */\n error: ReadSignal<unknown>;\n /** Abort the in-flight stream and stop reacting to `args`. */\n dispose(): void;\n}\n\nexport interface StreamResourceOptions<T, C, A> {\n /** Reactive inputs. Reading signals here wires restart-on-change. */\n args?: () => A;\n /** Combine the running value with each chunk. Default: latest (replace). */\n fold?: (acc: T | undefined, chunk: C) => T;\n /** Seed value, and the value `value` is reset to on each (re)start. */\n initial?: T;\n}\n\nexport type StreamProducer<C> = AsyncIterable<C> | ReadableStream<C>;\n\n/**\n * Produce a stream for the current `args`. The `signal` aborts on restart/dispose —\n * the source **MUST honor it** (this is a hard contract, not a suggestion): a\n * `ReadableStream` is fully cancelled via `reader.cancel()`, and an async generator\n * should observe `signal` (e.g. reject/break on `signal.aborted`) so a parked `await`\n * can unwind. On abort the adapter also calls the iterator's `return()` to run a\n * generator's `finally`, but a generator that parks forever while ignoring `signal`\n * cannot be force-unwound and will leak. See the module header's CONTRACT.\n */\nexport type StreamSource<C, A> = (\n args: A,\n signal: AbortSignal,\n) => StreamProducer<C> | Promise<StreamProducer<C>>;\n\nexport function streamResource<T, C = T, A = void>(\n source: StreamSource<C, A>,\n options: StreamResourceOptions<T, C, A> = {},\n): StreamResourceState<T> {\n const fold = options.fold ?? ((_acc: T | undefined, chunk: C) => chunk as unknown as T);\n const value = signal<T | undefined>(options.initial);\n const status = signal<StreamStatus>(\"idle\");\n const error = signal<unknown>(null);\n\n let controller: AbortController | null = null;\n\n const runner = effect(() => {\n // NOTE (effect-internal writes): like `resource`, this effect writes\n // value/status/error — signals other effects may observe. Those observers are\n // queued on the same flush and run in the same tick. This effect never reads\n // those signals, so it does not dirty its own dependency (no cycle). The async\n // `consume` writes them later from microtasks/timers, outside this flush.\n const a = (options.args ? options.args() : undefined) as A;\n\n controller?.abort();\n const ac = new AbortController();\n controller = ac;\n\n // Reset for the new run: a restart starts the fold from `initial`.\n value.set(options.initial);\n error.set(null);\n status.set(\"active\");\n\n void consume(source, a, ac.signal, fold, value, status, error);\n });\n\n const dispose = (): void => {\n controller?.abort();\n runner.dispose();\n };\n onCleanup(dispose);\n\n return { value, status, error, dispose };\n}\n\nasync function consume<T, C, A>(\n source: StreamSource<C, A>,\n args: A,\n signal: AbortSignal,\n fold: (acc: T | undefined, chunk: C) => T,\n // Use the public WriteSignal type instead of re-declaring an inline subset, so\n // these stay in lockstep with the signal API (no structural drift).\n value: WriteSignal<T | undefined>,\n status: WriteSignal<StreamStatus>,\n error: WriteSignal<unknown>,\n): Promise<void> {\n // Obtain the iterator EXPLICITLY (not via `for await`'s implicit one) so abort can\n // call `iterator.return()` to trigger an AsyncIterable / async generator's\n // `finally`/cleanup. A `for await` only calls `.return()` when the loop itself exits;\n // if the producer is PARKED (awaiting before the next yield while ignoring `signal`),\n // the loop never advances, so the implicit `.return()` never runs and the task leaks\n // past restart/dispose. Calling `.return()` on abort is the PARTIAL rescue: the\n // parked `await` cannot be force-unwound from outside, but once the generator resumes\n // (its next tick), `.return()` makes it run its `finally` and stop — recovering the\n // common \"generator wakes up after abort\" case. The ReadableStream path is fully\n // rescued via `reader.cancel()` (see `readableToAsyncIterable`).\n let iterator: AsyncIterator<C> | null = null;\n // Guard against returning the SAME iterator twice. `onAbort` is reachable two ways:\n // the abort listener, and the explicit call on line ~163 when abort raced the\n // `await source(...)`. The guard keys on the iterator instance (not a plain \"ran\"\n // flag): the listener firing with iterator still null must NOT consume the single\n // real cleanup that the explicit call performs once the iterator exists. So we only\n // mark an iterator returned once we have actually called `.return()` on it.\n let returned: AsyncIterator<C> | null = null;\n const onAbort = (): void => {\n if (!iterator || iterator === returned) {\n return; // nothing to release yet, or already released this iterator\n }\n returned = iterator;\n // Fire the iterator's cleanup. Swallow any throw/rejection from `.return()` — we\n // are tearing down; a producer that rejects on return must not surface here.\n try {\n void iterator.return?.()?.then?.(undefined, () => {});\n } catch {\n // `.return()` threw synchronously while tearing down — ignore.\n }\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n const produced = await source(args, signal);\n iterator = iterate(produced, signal)[Symbol.asyncIterator]();\n if (signal.aborted) {\n // Aborted while awaiting the source: the abort listener already ran (iterator\n // was still null then), so explicitly release the just-produced iterator now —\n // this fires a generator's finally / a ReadableStream's cancel for the\n // resource we created but will never iterate.\n onAbort();\n return;\n }\n for (;;) {\n const result = await iterator.next();\n if (result.done) {\n break;\n }\n if (signal.aborted) {\n return; // stale chunk from a superseded/disposed run — drop it\n }\n value.set(fold(value.peek(), result.value));\n }\n if (signal.aborted) {\n return; // stream ended but this run was aborted — don't mark done\n }\n status.set(\"done\");\n } catch (e) {\n if (signal.aborted) {\n return; // an abort that surfaced as a throw is not an error\n }\n error.set(e); // keep the last folded value (do not reset)\n status.set(\"error\");\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n}\n\nfunction iterate<C>(produced: StreamProducer<C>, signal: AbortSignal): AsyncIterable<C> {\n // Anything ReadableStream-shaped (has getReader) takes the reader path FIRST —\n // even when it is also async-iterable. On runtimes where ReadableStream\n // implements native async iteration (Node 18+ / Chrome 124+ / Firefox), the\n // spec serializes `iterator.return()` behind the pending `next()`\n // ([[ongoingPromise]]), so the AsyncIterable rescue cannot force-unwind a\n // parked read: abort would hang the consume task until the producer's next\n // chunk (or forever on a silent stream), leaking the underlying source.\n // `reader.cancel()` has no such ordering — routing every getReader-bearing\n // source through it keeps the FULL rescue promised for ReadableStream.\n if (typeof (produced as ReadableStream<C>)?.getReader === \"function\") {\n return readableToAsyncIterable(produced as ReadableStream<C>, signal);\n }\n if (typeof (produced as AsyncIterable<C>)?.[Symbol.asyncIterator] === \"function\") {\n return produced as AsyncIterable<C>;\n }\n throw new TypeError(\n \"streamResource: source must return an AsyncIterable or a ReadableStream (got neither).\",\n );\n}\n\nasync function* readableToAsyncIterable<C>(\n stream: ReadableStream<C>,\n signal: AbortSignal,\n): AsyncGenerator<C> {\n const reader = stream.getReader();\n // A ReadableStream read() does NOT observe an AbortSignal on its own. Without\n // this, a switchMap restart / dispose leaves the previous reader parked in a\n // pending read() forever, leaking the underlying source. Cancelling on abort\n // both releases the source AND settles the pending read() so the for-await\n // unwinds and the finally below can release the lock. Abort is the only\n // early-exit path for this generator (the consumer never calls .return()\n // without aborting), so this is the sole place a non-drained stream is cancelled.\n const onAbort = (): void => {\n void reader.cancel().catch(() => {}); // tearing down; swallow a rejected cancel\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n return;\n }\n yield value as C;\n }\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n reader.releaseLock();\n }\n}\n"],"names":["reportError","err","r","globalThis","console","error","isDev","__WCS_DEV__","emitted","Set","warnDev","code","dedupeKey","message","detail","id","has","add","warn","currentObserver","track","source","_sources","_observers","untrack","node","delete","clear","currentOwner","registerDisposer","disposer","_owned","push","disposeOwned","owner","owned","i","length","pendingEffects","flushScheduled","bufferPool","takeBuffer","pop","schedule","queueMicrotask","flushEffects","MAX_FLUSH_ITERATIONS","describeCycle","batch","live","filter","e","_disposed","lines","map","_devStack","split","slice","join","trim","iterations","lastBatch","size","Error","updateIfNecessary","flushSync","markStale","level","_state","wasClean","_isEffect","observer","ComputedNode","_update","SignalNode","_value","_equals","constructor","value","equals","this","get","peek","set","next","_fn","undefined","_initialized","_running","fn","previous","prevObserver","EffectNode","_cleanup","stack","dispose","_runCleanup","prevOwner","signal","initial","Object","is","computed","effect","createRoot","disposed","hasOwner","onCleanup","DISPOSED_ERROR","Symbol","DisposedError","isDisposedError","readProperty","target","name","DEFAULT_GETTER","bindNode","descriptor","desc","wcBindable","propByName","Map","properties","p","declaredInputs","inputs","declaredCommands","commands","c","signals","removers","assertLive","op","prop","cell","read","getter","handler","event","addEventListener","removeEventListener","on","options","propDesc","fold","_acc","chunk","bindInput","handle","v","before","command","args","TypeError","bindCommand","trigger","mapArgs","primed","remove","nodeSource","bound","run","abortName","abort","once","resource","loading","controller","runner","a","ac","AbortController","produced","Promise","resolve","then","resolved","aborted","streamResource","status","async","iterator","returned","onAbort","return","getReader","stream","reader","cancel","catch","done","releaseLock","readableToAsyncIterable","asyncIterator","iterate","result","consume"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-GLSUraaa.esm.js","sources":["../src/reportError.ts","../src/dev.ts","../src/reactive.ts","../src/bindNode.ts","../src/resource.ts","../src/streamResource.ts"],"sourcesContent":["// Internal error-reporting policy (NOT part of the public API; not re-exported\n// from exports.ts). Single source of truth for \"surface an error without crashing\".\n//\n// Delegates to the platform `reportError` when present — it dispatches a window\n// \"error\" event / logs to the console without aborting the current task — and falls\n// back to `console.error` otherwise. It NEVER re-throws (re-throwing would abort the\n// caller's drain / abort handler) and NEVER swallows silently (that would hide bugs).\nexport function reportError(err: unknown): void {\n const r = (globalThis as { reportError?: (e: unknown) => void }).reportError;\n if (typeof r === \"function\") {\n r(err);\n } else {\n console.error(err);\n }\n}\n","// Opt-in development diagnostics (NOT part of the public API; not re-exported from\n// exports.ts). Surfaces \"silent failure\" modes — duplicate/unkeyed list rows,\n// un-owned effects that leak, no-op onCleanup, reactive cycles — that production\n// otherwise swallows.\n//\n// DEFAULT OFF. Nothing here runs unless the consumer flips `globalThis.__WCS_DEV__`\n// to `true` BEFORE the failing operation. `isDev()` reads the flag at CALL TIME (not\n// module-load time), so a page can toggle it on for a debugging session without a\n// rebuild. See README \"Development mode\".\n//\n// TREE-SHAKING: the package sets `\"sideEffects\": false`, and every export here is a\n// pure function with no module-top-level side effects (the dedupe cache is a lazily-\n// populated Set, not eager work). A production bundler that never references warnDev\n// can drop this module entirely; a bundler that does keep it still pays nothing at\n// runtime because `warnDev` short-circuits on the `isDev()` guard before touching the\n// dedupe Set or formatting a message.\n\n/** Stable warning codes, so docs/tests can reference a warning without matching prose. */\nexport type DevWarningCode =\n | \"DUPLICATE_KEY\"\n | \"NON_PRIMITIVE_KEY\"\n | \"NULLISH_KEY\"\n | \"UNOWNED_EFFECT\"\n | \"UNOWNED_INSERT\"\n | \"ORPHAN_CLEANUP\"\n | \"REACTIVE_CYCLE\";\n\n/**\n * True only when the consumer has opted in via `globalThis.__WCS_DEV__ === true`.\n * Read at call time so the flag can be toggled at runtime. Kept tiny and pure so a\n * production bundle that strips dev branches pays nothing.\n */\nexport function isDev(): boolean {\n return (globalThis as { __WCS_DEV__?: boolean }).__WCS_DEV__ === true;\n}\n\n// Dedupe set: a given (code + dedupe-key) warning is emitted at most once, so a\n// per-row or per-flush failure does not flood the console. Lazily populated; empty\n// and inert until the first dev warning actually fires.\nconst emitted = new Set<string>();\n\n/**\n * Emit a development warning ONCE per `(code, dedupeKey)`, only when dev mode is on.\n * In production (`isDev()` false) this is a no-op that returns before any work.\n *\n * @param code stable warning code (see DevWarningCode)\n * @param dedupeKey extra discriminator folded into the dedupe identity (e.g. the\n * duplicate key value). Same code + same dedupeKey warns once.\n * @param message human-readable explanation; `detail` is appended for context.\n */\nexport function warnDev(\n code: DevWarningCode,\n dedupeKey: string,\n message: string,\n ...detail: unknown[]\n): void {\n if (!isDev()) {\n return;\n }\n const id = code + \"\u0000\" + dedupeKey;\n if (emitted.has(id)) {\n return;\n }\n emitted.add(id);\n console.warn(`[wcstack/signals][${code}] ${message}`, ...detail);\n}\n\n/**\n * Clear the dedupe cache. Test-only seam so each test can re-exercise a warning that\n * is otherwise emitted once per process. Not meant for application use.\n */\nexport function __resetDevWarnings(): void {\n emitted.clear();\n}\n","// Minimal signal core (v1 design notes).\n//\n// A self-contained, zero-dependency reactive primitive whose public API is\n// shaped after the TC39 Signals proposal (State / Computed / effect). The intent\n// is NOT to ship signal-polyfill as a runtime dependency, but to keep an in-house\n// implementation that can later be swapped for the native one without changing\n// call sites. See docs/signals-state-design.md §2 (the \"case C\" decision).\n//\n// Reactivity model: three-color marking (CLEAN / CHECK / DIRTY), pull-validated\n// (after Reactively / Solid). docs §8 (c).\n// - A signal write marks its direct observers DIRTY and their transitive\n// observers CHECK. Effects scheduled on the CLEAN→stale transition run on a\n// coalesced microtask (docs §5-3).\n// - On read/run, `updateIfNecessary` validates a CHECK node by refreshing its\n// computed sources first; it recomputes only if a source actually changed.\n// - EQUALITY SHORT-CIRCUIT: when a computed recomputes to an equal value it does\n// NOT mark its observers DIRTY, so downstream effects/computeds skip work.\n// - dependencies are re-tracked on every run, so conditional deps are pruned.\n\nimport { reportError } from \"./reportError.js\";\nimport { isDev, warnDev } from \"./dev.js\";\n\nexport type Cleanup = () => void;\nexport type Equals<T> = (a: T, b: T) => boolean;\n\nexport interface ReadSignal<T> {\n /** Read the value AND register the current observer as a dependent. */\n get(): T;\n /** Read the value WITHOUT tracking (no dependency edge created). */\n peek(): T;\n}\n\nexport interface WriteSignal<T> extends ReadSignal<T> {\n set(value: T): void;\n}\n\nexport interface EffectHandle {\n dispose(): void;\n}\n\n// Node freshness. CLEAN: up to date. CHECK: a transitive source may have changed\n// — must validate before use. DIRTY: a direct source changed — must recompute.\n// Typed as `number` (not literals) on purpose: `_state` is mutated through\n// aliasing during `updateIfNecessary`, so literal narrowing would wrongly flag\n// the `=== DIRTY` re-check after a source update.\nconst CLEAN: number = 0;\nconst CHECK: number = 1;\nconst DIRTY: number = 2;\n\ninterface Observable {\n _observers: Set<Computation>;\n}\n\ninterface Computation extends Observable {\n _state: number;\n _sources: Set<Observable>;\n _isEffect: boolean;\n _update(): void;\n}\n\n// --- tracking context -------------------------------------------------------\n\nlet currentObserver: Computation | null = null;\n\nfunction track(source: Observable): void {\n if (currentObserver !== null) {\n currentObserver._sources.add(source);\n source._observers.add(currentObserver);\n }\n}\n\nfunction untrack(node: Computation): void {\n for (const source of node._sources) {\n source._observers.delete(node);\n }\n node._sources.clear();\n}\n\n// --- ownership (disposal tree) ----------------------------------------------\n//\n// Orthogonal to dependency tracking. An \"owner\" collects the disposers of the\n// computations (effects, nested cleanups, child roots) created during its run.\n// An effect IS an owner: it owns whatever was created while it ran, and disposes\n// those children before it re-runs or is disposed. This is what lets the DOM\n// layer rebuild a subtree without leaking the previous subtree's effects\n// (docs §8 (d)). Modeled after Solid's reactive ownership.\n\ntype Disposer = () => void;\n\ninterface Owner {\n _owned: Disposer[];\n}\n\nlet currentOwner: Owner | null = null;\n\nfunction registerDisposer(disposer: Disposer): void {\n if (currentOwner !== null) {\n currentOwner._owned.push(disposer);\n }\n}\n\nfunction disposeOwned(owner: Owner): void {\n // Dispose in reverse creation order (children before parents, last-in-first-out).\n const owned = owner._owned;\n for (let i = owned.length - 1; i >= 0; i--) {\n owned[i]();\n }\n owned.length = 0;\n}\n\n// --- effect scheduling (microtask coalesce) ---------------------------------\n\n// The queue NEW work accumulates into (markStale / a computed's DIRTY promotion add\n// here). Each drain pass takes the current queue to iterate and installs a fresh\n// empty one in its place, so effects queued WHILE draining land in the next pass.\nlet pendingEffects = new Set<EffectNode>();\nlet flushScheduled = false;\n\n// Double-buffer free list: rather than `[...pendingEffects]` (a Set→Array copy) per\n// pass, a drain SWAPS the live queue with a recycled empty Set and iterates that Set\n// directly. The swapped-out Set must be a fresh buffer that re-entrancy cannot\n// clobber — a nested flushSync swaps `pendingEffects` again, so the outer pass keeps\n// iterating its OWN buffer (the same isolation the old local-array copy gave). After\n// draining, the buffer is cleared and returned to the pool for reuse.\nconst bufferPool: Set<EffectNode>[] = [];\n\nfunction takeBuffer(): Set<EffectNode> {\n return bufferPool.pop() ?? new Set<EffectNode>();\n}\n\nfunction schedule(): void {\n if (flushScheduled) {\n return;\n }\n flushScheduled = true;\n queueMicrotask(flushEffects);\n}\n\n// Upper bound on drain iterations. A well-behaved graph settles in a handful of\n// passes; exceeding this means an effect keeps dirtying its own dependency with a\n// changing value (a reactive cycle), which would otherwise hang the page. We bail\n// loudly instead of spinning forever (docs §5-3).\nconst MAX_FLUSH_ITERATIONS = 1000;\n\n// Dev-only: build a \"what was still running\" appendix for the cycle bail. Uses the\n// creation stack captured per effect (dev only) so the message points at WHERE the\n// looping effects were defined. Production never calls this (guarded by the dev\n// `lastBatch`), so the stack-capture cost never lands on shipped code.\nfunction describeCycle(batch: EffectNode[]): string {\n const live = batch.filter((e) => !e._disposed);\n const lines = live.map((e, i) => {\n const where = e._devStack ? e._devStack.split(\"\\n\").slice(1, 3).join(\" | \").trim() : \"(no stack)\";\n return ` #${i + 1} ${where}`;\n });\n return (\n `\\n[wcstack/signals] dev diagnostics: ${live.length} effect(s) still re-running on the ` +\n `final pass (likely cycle participants):\\n${lines.join(\"\\n\")}`\n );\n}\n\nfunction flushEffects(): void {\n flushScheduled = false;\n // Drain in a loop: an effect may, while running, dirty a signal that queues\n // further effects. Coalescing collapses multiple writes in one tick into a\n // single run per effect (docs §5-3).\n let iterations = 0;\n // Dev-only: the effects run on the most recent pass, so a cycle bail can point at\n // the culprits. Lazily allocated only in dev (no cost in production).\n let lastBatch: EffectNode[] | null = null;\n while (pendingEffects.size > 0) {\n if (++iterations > MAX_FLUSH_ITERATIONS) {\n // Drop the runaway queue so a single bad effect cannot wedge every later\n // flush, then surface the bug.\n pendingEffects.clear();\n throw new Error(\n \"flushEffects: exceeded \" +\n MAX_FLUSH_ITERATIONS +\n \" iterations — likely a reactive cycle (an effect writes a signal it depends on with an ever-changing value).\" +\n (lastBatch ? describeCycle(lastBatch) : \"\"),\n );\n }\n // Swap the live queue out for a fresh empty buffer, then iterate the swapped-out\n // Set directly (no array copy). New work queued by an effect's body lands in the\n // new `pendingEffects` and is picked up on the next pass — same coalescing as\n // before. `batch` is a local reference, so a re-entrant flushSync (which swaps\n // `pendingEffects` again) cannot clobber this pass's iteration.\n const batch = pendingEffects;\n pendingEffects = takeBuffer();\n if (isDev()) {\n // Record the effects on this pass; on the FINAL (overflowing) pass these are\n // the ones still re-queuing themselves — the cycle participants.\n lastBatch = [...batch];\n }\n for (const node of batch) {\n if (!node._disposed) {\n // Isolate each effect: a throw from one effect's body must NOT abort the\n // batch and silently strand its already-dequeued siblings. We hand the\n // error to reportError (which does not re-throw, so the drain continues)\n // and move on. updateIfNecessary guarantees the node is settled to CLEAN\n // even on throw, so it is not left stuck DIRTY.\n try {\n updateIfNecessary(node);\n } catch (err) {\n reportError(err);\n }\n }\n }\n // Recycle this pass's buffer. Clear AFTER iterating so a node is never dropped\n // mid-walk; the Set is now free for a later takeBuffer().\n batch.clear();\n bufferPool.push(batch);\n }\n}\n\n// Effect/computed errors are surfaced via the shared `reportError` policy\n// (./reportError.ts), called synchronously from the drain loop. It does NOT\n// re-throw (that would abort the drain) and does NOT swallow (that would hide the\n// bug). Re-entrancy is safe: the only re-scheduling path (markStale) defers to a\n// microtask, so reporting here cannot recurse into the running flush.\n\n/**\n * Synchronously flush queued effects. Provided for tests and for callers that\n * need DOM updates applied before reading the DOM back. In normal use effects\n * settle on their own microtask.\n */\nexport function flushSync(): void {\n // Do NOT touch flushScheduled here: a microtask may already be queued. Just\n // drain. flushEffects resets the flag itself; if a queued microtask still fires\n // afterwards it finds an empty queue and is a no-op. Touching the flag from here\n // would let a second flushSync race the queued microtask into a double drain.\n flushEffects();\n}\n\n// --- marking & validation ---------------------------------------------------\n\nfunction markStale(node: Computation, level: number): void {\n if (node._state < level) {\n const wasClean = node._state === CLEAN;\n node._state = level;\n if (wasClean && node._isEffect) {\n pendingEffects.add(node as EffectNode);\n schedule();\n }\n // Observers can only become CHECK from us: they need to re-validate, but\n // whether they truly changed depends on our recomputed value.\n for (const observer of node._observers) {\n markStale(observer, CHECK);\n }\n }\n}\n\nfunction updateIfNecessary(node: Computation): void {\n if (node._state === CLEAN) {\n return;\n }\n if (node._state === CHECK) {\n // A transitive source might have changed. Refresh computed sources; one of\n // them may set us DIRTY (via the equality check in its _update).\n // NOTE: this scans ALL sources each validation (O(sources)); the standard\n // pull-based scheme (Reactively/Solid) does the same. Intentional for v1 —\n // a future optimization could track per-source dirtiness to scan less.\n for (const source of node._sources) {\n if (source instanceof ComputedNode) {\n updateIfNecessary(source);\n if (node._state === DIRTY) {\n break;\n }\n }\n }\n }\n // Settle to CLEAN in a finally: if _update() (a user fn / equals) throws, the\n // node must NOT be left stuck DIRTY, otherwise every later peek/get would\n // re-run the throwing fn forever. The error propagates to the caller (a flush\n // isolates it per-effect; a direct get/peek surfaces it to that caller).\n try {\n if (node._state === DIRTY) {\n node._update();\n }\n } finally {\n node._state = CLEAN;\n }\n}\n\n// --- signal -----------------------------------------------------------------\n\nclass SignalNode<T> implements Observable, WriteSignal<T> {\n _observers = new Set<Computation>();\n private _value: T;\n private _equals: Equals<T>;\n\n constructor(value: T, equals: Equals<T>) {\n this._value = value;\n this._equals = equals;\n }\n\n get(): T {\n track(this);\n return this._value;\n }\n\n peek(): T {\n return this._value;\n }\n\n set(next: T): void {\n if (this._equals(this._value, next)) {\n return;\n }\n this._value = next;\n // No copy needed: markStale only schedules effects (on a microtask) and marks\n // observers — it never runs user code synchronously, so this set is not\n // mutated during the walk. Iterating it directly avoids a per-write allocation.\n for (const observer of this._observers) {\n markStale(observer, DIRTY);\n }\n }\n}\n\n// --- computed ---------------------------------------------------------------\n\nclass ComputedNode<T> implements Computation, ReadSignal<T> {\n _observers = new Set<Computation>();\n _sources = new Set<Observable>();\n _state = DIRTY; // never computed yet\n _isEffect = false;\n private _fn: () => T;\n private _value: T | undefined = undefined;\n private _initialized = false;\n private _running = false;\n private _equals: Equals<T>;\n\n constructor(fn: () => T, equals: Equals<T>) {\n this._fn = fn;\n this._equals = equals;\n // Be owned by the enclosing scope (a parent effect or createRoot). When that\n // scope tears down, untrack this computed so it is removed from its sources'\n // observer sets — otherwise a computed created inside an effect that re-runs\n // would leak a stale ComputedNode into each source on every run (docs §8 (d)).\n // Lazy + no children: teardown is just untrack, no cleanup/owned to dispose.\n registerDisposer(() => untrack(this));\n }\n\n get(): T {\n // Order matters: track BEFORE updateIfNecessary. The current observer must be\n // registered against THIS computed regardless of whether validation recomputes —\n // and recomputation swaps currentObserver to this node, so tracking first records\n // the edge under the OUTER observer (the caller), not this computed's own run.\n track(this);\n updateIfNecessary(this);\n return this._value as T;\n }\n\n peek(): T {\n updateIfNecessary(this);\n return this._value as T;\n }\n\n _update(): void {\n // Cycle guard: if _fn (directly or transitively) reads this computed, get()\n // would recurse into _update and blow the stack with an opaque RangeError.\n // Detect the self-reference and throw a clear error instead. (The effect-level\n // MAX_FLUSH_ITERATIONS guard does not cover this synchronous self-recursion.)\n if (this._running) {\n throw new Error(\"computed: circular dependency — the computed reads itself.\");\n }\n const previous = this._value;\n untrack(this);\n const prevObserver = currentObserver;\n currentObserver = this;\n this._running = true;\n try {\n this._value = this._fn();\n } finally {\n this._running = false;\n currentObserver = prevObserver;\n }\n // Equality short-circuit: only propagate when the value actually changed.\n // Our observers are already CHECK (from the originating markStale); promote\n // them to DIRTY so they recompute, otherwise leave them to settle CLEAN. Skip\n // on the first computation: there is no prior value to compare, and a custom\n // `equals` must not be invoked with the undefined sentinel.\n if (!this._initialized) {\n this._initialized = true;\n } else if (!this._equals(previous as T, this._value as T)) {\n for (const observer of this._observers) {\n observer._state = DIRTY;\n // Normally this computed is refreshed from inside flushEffects while\n // validating an already-scheduled effect, so the observer is in the queue\n // already. But if it is refreshed out-of-band (e.g. a `peek()` outside a\n // flush) an effect observer could otherwise be promoted to DIRTY without\n // being queued, and never re-run. Re-schedule defensively to keep the\n // \"DIRTY effect always runs\" invariant regardless of refresh path.\n if (observer._isEffect && !(observer as EffectNode)._disposed) {\n pendingEffects.add(observer as EffectNode);\n schedule();\n }\n }\n }\n }\n}\n\n// --- effect -----------------------------------------------------------------\n\nclass EffectNode implements Computation, Owner {\n _observers = new Set<Computation>(); // effects are not observed; kept for the interface\n _sources = new Set<Observable>();\n _owned: Disposer[] = [];\n _state = DIRTY;\n _isEffect = true;\n _disposed = false;\n // Dev-only creation stack, used to point a reactive-cycle bail at where the looping\n // effect was defined. Captured only when dev mode is on at construction time, so\n // production effects pay nothing for it.\n _devStack: string | undefined = undefined;\n private _fn: () => Cleanup | void;\n private _cleanup: Cleanup | void = undefined;\n\n constructor(fn: () => Cleanup | void) {\n this._fn = fn;\n if (isDev()) {\n this._devStack = new Error().stack;\n }\n // Be owned by the enclosing owner (a parent effect or a createRoot), so a\n // parent teardown disposes this effect.\n registerDisposer(() => this.dispose());\n updateIfNecessary(this); // initial run (state starts DIRTY)\n }\n\n _update(): void {\n this._runCleanup();\n disposeOwned(this); // tear down children created in the previous run\n untrack(this);\n const prevObserver = currentObserver;\n const prevOwner = currentOwner;\n currentObserver = this;\n currentOwner = this; // children created during the run are owned by this effect\n try {\n this._cleanup = this._fn();\n } finally {\n currentObserver = prevObserver;\n currentOwner = prevOwner;\n }\n }\n\n private _runCleanup(): void {\n if (typeof this._cleanup === \"function\") {\n this._cleanup();\n this._cleanup = undefined;\n }\n }\n\n dispose(): void {\n if (this._disposed) {\n return;\n }\n this._disposed = true;\n this._runCleanup();\n disposeOwned(this);\n untrack(this);\n pendingEffects.delete(this);\n }\n}\n\n// --- public API -------------------------------------------------------------\n\nexport function signal<T>(initial: T, equals: Equals<T> = Object.is): WriteSignal<T> {\n return new SignalNode(initial, equals);\n}\n\nexport function computed<T>(fn: () => T, equals: Equals<T> = Object.is): ReadSignal<T> {\n return new ComputedNode(fn, equals);\n}\n\nexport function effect(fn: () => Cleanup | void): EffectHandle {\n if (isDev() && currentOwner === null) {\n // No enclosing owner: nothing will ever dispose this effect, so it (and the\n // subscriptions it builds) leak. Legitimate top-level effects should be mounted\n // under createRoot / SignalsElement and disposed on teardown. Dev-only warn.\n warnDev(\n \"UNOWNED_EFFECT\",\n \"\",\n \"effect created with no owner — it will never be disposed and may leak. \" +\n \"Wrap it in createRoot(dispose => …) (or a SignalsElement render()) and \" +\n \"dispose on teardown.\",\n );\n }\n return new EffectNode(fn);\n}\n\n/**\n * Run `fn` inside a fresh ownership scope and return its result. Every effect (or\n * nested cleanup) created during `fn` — directly or transitively — is owned by\n * this root; calling the `dispose` passed to `fn` tears them all down.\n *\n * The root is detached: it is NOT auto-disposed by an enclosing owner. The caller\n * holds `dispose` (e.g. a custom element disposes it in disconnectedCallback).\n *\n * If `fn` throws, any effects/cleanups it created BEFORE the throw are disposed\n * before the error propagates — a caller that never received `dispose` (because the\n * call threw) cannot leak the half-built scope.\n */\nexport function createRoot<T>(fn: (dispose: () => void) => T): T {\n const owner: Owner = { _owned: [] };\n let disposed = false;\n const dispose = (): void => {\n if (disposed) {\n return;\n }\n disposed = true;\n disposeOwned(owner);\n };\n const prevOwner = currentOwner;\n const prevObserver = currentObserver;\n currentOwner = owner;\n // Detach the dependency-tracking context too, not just ownership. A root is a\n // fresh reactive boundary: signals read SYNCHRONOUSLY while `fn` builds the scope\n // must not register the OUTER observer as their dependent (Solid's createRoot\n // clears both owner and listener). Without this, a For/Index `each` that reads\n // `index()`/`item()` synchronously in its body would track the outer reconcile\n // effect against the row's idx/item signal, so reordering re-dirties the reconcile\n // effect — a self-loop the equality/MAX_FLUSH guards only paper over.\n currentObserver = null;\n try {\n return fn(dispose);\n } catch (err) {\n dispose(); // tear down whatever fn built before it threw\n throw err;\n } finally {\n currentOwner = prevOwner;\n currentObserver = prevObserver;\n }\n}\n\n/**\n * True when there is a current ownership scope (an enclosing effect or createRoot).\n * Dev-diagnostics seam for the DOM layer to flag un-owned reactive insertions\n * without re-implementing owner tracking. NOT part of the public API.\n */\nexport function hasOwner(): boolean {\n return currentOwner !== null;\n}\n\n/**\n * Register a teardown callback with the current owner. Runs when the owning effect\n * re-runs or is disposed, or when the enclosing root is disposed. A no-op when\n * there is no current owner.\n */\nexport function onCleanup(fn: () => void): void {\n if (currentOwner === null) {\n // No owner → registerDisposer drops `fn` on the floor: the cleanup never runs.\n // Silent in production; flag it in dev so the missing teardown is visible.\n warnDev(\n \"ORPHAN_CLEANUP\",\n \"\",\n \"onCleanup called with no owner — the cleanup will never run. \" +\n \"Call it inside an effect, createRoot, or a SignalsElement render().\",\n );\n }\n registerDisposer(fn);\n}\n","// wc-bindable → signal adapter. The crux of the design.\n//\n// Any async-IO node in wcstack speaks the wc-bindable protocol:\n// - properties: outputs — the node dispatches `event` on change; the value is\n// read via `getter(event)`, defaulting to `e.detail` (wc-bindable\n// SPEC § Default Getter). The property `name` is read only for the\n// initial seed, where no event exists yet (SPEC § Initial Sync).\n// - inputs: settable surface — write `node[name] = value`.\n// - commands: invocable methods — call `node[name](...args)`.\n// The node has NO idea whether the observer behind the binding is a proxy (state)\n// or a signal. So a single adapter that maps these surfaces onto signals makes every\n// existing node plug into the signal core unchanged. See docs/signals-state-design.md\n// §3, and the four mappings of §3-1:\n//\n// element → signal | property (latest snapshot) → read signal [`signals`]\n// element → signal | event-token (per-emit stream) → folded signal [`on`]\n// signal → element | input (write-back) → effect → prop [`bindInput`]\n// signal → element | command-token (start/cancel) → emit on change [`bindCommand`]\n//\n// `signals` is the STATE view of a property (equality-guarded — same value = no\n// update). `on` is the OCCURRENCE view of the same event (a stream — every emit\n// updates, even with an equal value), folded latest-by-default.\n\nimport { signal, effect, WriteSignal, ReadSignal } from \"./reactive.js\";\nimport { reportError } from \"./reportError.js\";\nimport type { ResourceSource } from \"./resource.js\";\n\nexport interface WcBindableProperty<V = unknown> {\n readonly name: string;\n readonly event: string;\n readonly getter?: (event: Event) => V;\n}\n\n// Structurally a SUBSET of the real `IWcBindable` (which additionally carries\n// `protocol` / `version`, and richer input/command entries). A real descriptor is\n// therefore assignable to this type — see __tests__/bindNode.compat.test.ts.\nexport interface WcBindableDescriptor {\n readonly properties: readonly WcBindableProperty[];\n readonly inputs?: readonly { readonly name: string }[];\n readonly commands?: readonly { readonly name: string }[];\n}\n\n// --- typed node shape (D1) ---------------------------------------------------\n//\n// `bindNode` is the package's centerpiece, yet without a type argument every output\n// was `ReadSignal<unknown>` and every `set`/`command` took `unknown` — the feature\n// was effectively untyped in consumer code. `NodeShape` is an OPT-IN description of a\n// node's reactive surface that drives the return type: declare the property value\n// types, the input value types, and the command signatures ONCE, and `bound.signals`,\n// `on`, `set`, `bindInput`, `command`, and `bindCommand` are all typed from it.\n//\n// interface FetchShape extends NodeShape {\n// signals: { value: Person[]; loading: boolean };\n// inputs: { url: string };\n// commands: { fetch: (url: string) => Promise<Person[]>; abort: () => void };\n// }\n// const bound = bindNode<FetchShape>(el, desc);\n// bound.signals.value.get(); // ReadSignal<Person[]> — key & type checked\n// bound.set(\"url\", \"/api/people\"); // string enforced\n// bound.command(\"fetch\", \"/api\"); // arg/return inferred\n//\n// The runtime is UNCHANGED — these types are erased; the adapter still validates names\n// against the descriptor at runtime. The default shape below keeps every existing\n// untyped call site compiling exactly as before (all-`unknown`, string keys).\n// Open command map: a command name → any call signature. `(...args: any[])` (NOT\n// `never[]`) is the constraint a USER shape's commands must satisfy — and, crucially,\n// the OPEN fallback used when a shape omits `commands`. `Parameters<(...args: any[])\n// => unknown>` is `any[]`, so an untyped `command(\"run\", \"/api\")` accepts any args,\n// preserving the pre-generic behaviour (symmetric with `inputs` → `unknown`). A shape\n// that DECLARES a precise signature (e.g. `(url: string) => Promise<X>`) still gets\n// exact arg/return checking on the typed path — the loose constraint only widens the\n// DEFAULT, never a declared command. (`no-explicit-any` is disabled for this package.)\ntype OpenCommandMap = Record<string, (...args: any[]) => unknown>;\n\nexport interface NodeShape {\n /** propertyName → latest-snapshot value type (what `signals[name]` / `on(name)` carry). */\n signals?: Record<string, unknown>;\n /** inputName → settable value type (what `set` / `bindInput` accept). */\n inputs?: Record<string, unknown>;\n /** commandName → its call signature (what `command` / `bindCommand` invoke). */\n commands?: OpenCommandMap;\n}\n\n// Back-compat default: a fully-open shape so `bindNode(target, desc)` with no type\n// argument behaves as before (signals keyed by arbitrary string → unknown; any\n// input/command name; unknown values; any command args). Stronger typing is opt-in\n// via the type arg.\nexport interface DefaultNodeShape extends NodeShape {\n signals: Record<string, unknown>;\n inputs: Record<string, unknown>;\n commands: OpenCommandMap;\n}\n\n// Resolve the three sub-maps with sane fallbacks when a shape omits one of them.\ntype SignalsOf<S extends NodeShape> = S[\"signals\"] extends Record<string, unknown>\n ? S[\"signals\"]\n : Record<string, unknown>;\ntype InputsOf<S extends NodeShape> = S[\"inputs\"] extends Record<string, unknown>\n ? S[\"inputs\"]\n : Record<string, unknown>;\ntype CommandsOf<S extends NodeShape> = S[\"commands\"] extends OpenCommandMap ? S[\"commands\"] : OpenCommandMap;\n\n/** Options for `on` — how each event emission folds into the stream signal. */\nexport interface EventStreamOptions<T, C> {\n /** Combine the running value with each emission. Default: latest (replace). */\n fold?: (acc: T | undefined, chunk: C) => T;\n /** Seed value before the first emission. */\n initial?: T;\n}\n\nexport interface BoundNode<S extends NodeShape = DefaultNodeShape> {\n /**\n * Output properties as read-only signals (latest value), keyed by property name and\n * typed from the node shape: `bound.signals.value.get()` carries the declared value\n * type (no longer `unknown`). Keys are the declared property names.\n */\n readonly signals: { readonly [K in keyof SignalsOf<S>]: ReadSignal<SignalsOf<S>[K]> };\n /**\n * An event-token STREAM for a declared property's event: a read signal that folds\n * every emission (latest by default). Unlike `signals[prop]`, it updates on EVERY\n * emit — even when the derived value is equal — because it models occurrences, not\n * state. The chunk per emit is `getter(event)` if the property declares one, else\n * the property's current value.\n *\n * With no fold the value type defaults to the property's declared snapshot type; a\n * `fold` may reshape it (explicit `T`/`C` type args override).\n */\n on<K extends keyof SignalsOf<S>>(prop: K): ReadSignal<SignalsOf<S>[K] | undefined>;\n on<T, C = SignalsOf<S>[keyof SignalsOf<S>]>(\n prop: keyof SignalsOf<S>,\n options: EventStreamOptions<T, C>,\n ): ReadSignal<T | undefined>;\n /** Write a declared input on the node (imperative); value type checked against the shape. */\n set<K extends keyof InputsOf<S>>(name: K, value: InputsOf<S>[K]): void;\n /**\n * Reactively write a declared input from a signal: an effect mirrors `source` into\n * `node[name]`. A same-value guard (`node[name] !== v`) skips redundant writes, so\n * a property whose write re-dispatches an event cannot feed back into an infinite\n * loop. `undefined` follows the wc-bindable applier profile (A2): it is not written\n * while the source has had no other value (the first evaluation, or after another\n * `undefined`), so the element keeps its own initial state; after a value it is\n * written, so the element restores that state. Returns a disposer; the effect is\n * also torn down by `dispose()` (neither writes anything).\n */\n bindInput<K extends keyof InputsOf<S>>(name: K, source: ReadSignal<InputsOf<S>[K]>): () => void;\n /** Invoke a declared command on the node (imperative); args/return typed from the shape. */\n command<K extends keyof CommandsOf<S>>(name: K, ...args: Parameters<CommandsOf<S>[K]>): ReturnType<CommandsOf<S>[K]>;\n /**\n * Command-token: invoke a declared command whenever `trigger` CHANGES. The initial\n * value does not fire (subscribe-without-firing), so a command like `abort` is not\n * triggered on mount. `mapArgs` shapes the call arguments from the trigger value\n * (default: the value itself as a single argument). Returns a disposer; also torn\n * down by `dispose()`.\n */\n bindCommand<K extends keyof CommandsOf<S>, V = unknown>(\n name: K,\n trigger: ReadSignal<V>,\n mapArgs?: (value: V) => Parameters<CommandsOf<S>[K]>,\n ): () => void;\n /**\n * Detach all listeners/effects and make the adapter inert. After dispose the output\n * signals/streams stop updating and `set`/`command`/`bindInput`/`bindCommand`/`on`\n * throw (use-after-dispose). Idempotent.\n */\n dispose(): void;\n}\n\n// Internal target type. `Record<string, any>` is needed to index arbitrary declared\n// members (target[name]) inside the adapter, but it is DELIBERATELY NOT in the public\n// `bindNode` signature — exposing it there erased the caller's element type and let any\n// stray member access type-check. Kept private; the public entry takes `EventTarget`.\ntype NodeTarget = EventTarget & Record<string, any>;\n\n// Internal, fully-loose surface the adapter object is built against (string keys,\n// `unknown` values). The public `BoundNode<S>` is a stricter projection of this same\n// runtime object; `bindNode` casts the built `UntypedBoundNode` to `BoundNode<S>` at\n// return. Kept private so the loose `unknown`-typed methods never leak to callers.\ninterface UntypedBoundNode {\n readonly signals: Record<string, ReadSignal<unknown>>;\n on<T = unknown, C = unknown>(prop: string, options?: EventStreamOptions<T, C>): ReadSignal<T | undefined>;\n set(name: string, value: unknown): void;\n bindInput(name: string, source: ReadSignal<unknown>): () => void;\n command(name: string, ...args: unknown[]): unknown;\n bindCommand(name: string, trigger: ReadSignal<unknown>, mapArgs?: (value: unknown) => unknown[]): () => void;\n dispose(): void;\n}\n\n// The error thrown by `assertLive` when an adapter method is called after dispose.\n// Carries a brand symbol so callers that need to distinguish a post-dispose throw\n// (e.g. nodeSource's abort bridge) can test it structurally instead of matching the\n// message string — the message is free to change without silently breaking the\n// guard. The brand survives even if a consumer's bundler duplicates the class (e.g.\n// `instanceof` across realms), since it's a Symbol on the instance, not a class\n// identity check.\nconst DISPOSED_ERROR = Symbol(\"bindNode.disposed\");\n\n/**\n * Error thrown when a {@link BoundNode} method is invoked after the adapter has been\n * disposed. Every surface that mutates or subscribes — `on` / `set` / `bindInput` /\n * `command` / `bindCommand` — throws this once `dispose()` has run, so a\n * use-after-dispose is loud instead of silently writing/invoking on an inert adapter.\n *\n * Prefer {@link isDisposedError} over `instanceof` for the check: the brand survives a\n * bundler that duplicates the class across realms, where `instanceof` would not. Use it\n * to make teardown-order races robust — e.g. swallow a post-dispose throw while letting\n * any other error surface:\n *\n * ```ts\n * try {\n * bound.command(\"abort\");\n * } catch (err) {\n * if (!isDisposedError(err)) throw err; // expected during teardown; ignore\n * }\n * ```\n */\nexport class DisposedError extends Error {\n readonly [DISPOSED_ERROR] = true;\n}\n\n/**\n * True if `err` is a {@link DisposedError} — i.e. a use-after-dispose error thrown by a\n * {@link BoundNode} method after `dispose()`. Brand-based (checks a Symbol on the\n * instance), so it stays correct even if a consumer's bundler duplicates the\n * `DisposedError` class across module realms, where `err instanceof DisposedError`\n * could be false.\n */\nexport function isDisposedError(err: unknown): boolean {\n return typeof err === \"object\" && err !== null && (err as Record<symbol, unknown>)[DISPOSED_ERROR] === true;\n}\n\nfunction readProperty(target: NodeTarget, name: string): unknown {\n return target[name];\n}\n\n// wc-bindable SPEC § Default Getter: when a property omits `getter`, an adapter MUST\n// read `e.detail`. This adapter used to read the property instead (#238), which\n// diverged from `@wcstack/state` and `@wc-bindable/core` — the same element could\n// bind correctly in one adapter and not the other. Only the initial seed reads the\n// property (SPEC § Initial Sync: there is no event to derive from at bind time).\nconst DEFAULT_GETTER = (e: Event): unknown => (e as CustomEvent).detail;\n\n/**\n * Adapt a wc-bindable node into signals. The public `target` parameter is a plain\n * `EventTarget` (the internal `Record<string, any>` indexing is cast away here, not\n * leaked to callers — see {@link NodeTarget}). Pass an optional {@link NodeShape} type\n * argument to type the result; omit it for the back-compat all-`unknown` shape.\n *\n * @example typed\n * interface FetchShape extends NodeShape {\n * signals: { value: Person[]; loading: boolean };\n * inputs: { url: string };\n * commands: { fetch: (url: string) => Promise<Person[]>; abort: () => void };\n * }\n * const bound = bindNode<FetchShape>(fetchEl, FetchCore.wcBindable);\n * bound.signals.value.get(); // ReadSignal<Person[]>\n * bound.set(\"url\", \"/api/people\"); // string enforced\n */\nexport function bindNode<S extends NodeShape = DefaultNodeShape>(\n target: EventTarget,\n descriptor?: WcBindableDescriptor,\n): BoundNode<S> {\n // The Record<string, any> indexing surface is needed internally to read/write/invoke\n // declared members by name; keep it private to this body.\n const node = target as NodeTarget;\n const desc = descriptor ?? (node.constructor as { wcBindable?: WcBindableDescriptor }).wcBindable;\n if (!desc) {\n throw new Error(\"bindNode: no wc-bindable descriptor provided and none found on target.constructor.wcBindable\");\n }\n\n // Name → declared-entry lookups so set/command/on reject names the node never\n // declared, instead of silently writing/invoking/listening on an arbitrary member.\n const propByName = new Map(desc.properties.map((p) => [p.name, p]));\n const declaredInputs = new Set((desc.inputs ?? []).map((i) => i.name));\n const declaredCommands = new Set((desc.commands ?? []).map((c) => c.name));\n\n const signals: Record<string, WriteSignal<unknown>> = {};\n const removers: Array<() => void> = [];\n let disposed = false;\n\n const assertLive = (op: string, name: string): void => {\n if (disposed) {\n throw new DisposedError(`bindNode.${op}: \"${name}\" called after dispose (the adapter is inert).`);\n }\n };\n\n for (const prop of desc.properties) {\n // Seed with the node's current value so the signal is valid before the first\n // event fires (e.g. FetchCore.value === null at rest).\n const cell = signal<unknown>(readProperty(node, prop.name));\n signals[prop.name] = cell;\n\n const read = prop.getter ?? DEFAULT_GETTER;\n const handler = (event: Event): void => {\n cell.set(read(event));\n };\n node.addEventListener(prop.event, handler);\n removers.push(() => node.removeEventListener(prop.event, handler));\n // Re-seed AFTER subscribing: the initial read above is a snapshot taken before\n // the listener was attached, so a value change in that gap would be missed.\n // Reading the property once more now closes the race (the equality guard makes\n // it a no-op when nothing changed). Note this is the property snapshot, not a\n // getter(event) — there is no event to derive from at bind time, and the SPEC's\n // default getter (`e.detail`) applies to events only.\n cell.set(readProperty(node, prop.name));\n }\n\n // Implemented against a string-keyed, all-`unknown` surface (`UntypedBoundNode`); the\n // generic typing in `BoundNode<S>` is a compile-time projection over the SAME\n // runtime, so the object is built once and cast to `BoundNode<S>` at return. Runtime\n // name validation against the descriptor is unchanged.\n const bound: UntypedBoundNode = {\n signals,\n\n on<T = unknown, C = unknown>(prop: string, options: EventStreamOptions<T, C> = {}): ReadSignal<T | undefined> {\n assertLive(\"on\", prop);\n const propDesc = propByName.get(prop);\n if (!propDesc) {\n throw new Error(`bindNode.on: \"${prop}\" is not a declared property on this node.`);\n }\n const fold = options.fold ?? ((_acc: T | undefined, chunk: C) => chunk as unknown as T);\n // equals: () => false — a stream notifies on EVERY emit, even an equal value.\n const cell = signal<T | undefined>(options.initial, () => false);\n const read = propDesc.getter ?? DEFAULT_GETTER;\n const handler = (event: Event): void => {\n const chunk = read(event) as C;\n cell.set(fold(cell.peek(), chunk));\n };\n node.addEventListener(propDesc.event, handler);\n removers.push(() => node.removeEventListener(propDesc.event, handler));\n return cell;\n },\n\n set(name: string, value: unknown): void {\n assertLive(\"set\", name);\n if (!declaredInputs.has(name)) {\n throw new Error(`bindNode.set: \"${name}\" is not a declared input on this node.`);\n }\n node[name] = value;\n },\n\n bindInput(name: string, source: ReadSignal<unknown>): () => void {\n assertLive(\"bindInput\", name);\n if (!declaredInputs.has(name)) {\n throw new Error(`bindNode.bindInput: \"${name}\" is not a declared input on this node.`);\n }\n // The previous evaluation, written or not. Its initial `undefined` stands for \"no\n // previous evaluation\", which A2 treats the same as a previous `undefined`.\n let previous: unknown = undefined;\n const handle = effect(() => {\n const v = source.get();\n const before = previous;\n previous = v;\n // A2: an `undefined` that follows no value means \"no value yet\" — writing it\n // would overwrite the element's own initial state (its default, or the markup)\n if (v === undefined && before === undefined) return;\n if (node[name] !== v) {\n node[name] = v; // same-value guard above breaks write→event→write loops\n }\n });\n removers.push(() => handle.dispose());\n return () => handle.dispose();\n },\n\n command(name: string, ...args: unknown[]): unknown {\n assertLive(\"command\", name);\n if (!declaredCommands.has(name)) {\n throw new Error(`bindNode.command: \"${name}\" is not a declared command on this node.`);\n }\n if (typeof node[name] !== \"function\") {\n throw new TypeError(`bindNode.command: \"${name}\" is declared but not a function on the node.`);\n }\n return node[name](...args);\n },\n\n bindCommand(name: string, trigger: ReadSignal<unknown>, mapArgs?: (value: unknown) => unknown[]): () => void {\n assertLive(\"bindCommand\", name);\n if (!declaredCommands.has(name)) {\n throw new Error(`bindNode.bindCommand: \"${name}\" is not a declared command on this node.`);\n }\n // Fail fast at bind time (not on first change), so a wrong name surfaces when\n // the subscription is set up rather than silently later inside a flush.\n if (typeof node[name] !== \"function\") {\n throw new TypeError(`bindNode.bindCommand: \"${name}\" is declared but not a function on the node.`);\n }\n let primed = false;\n const handle = effect(() => {\n const v = trigger.get();\n if (!primed) {\n primed = true; // subscribe on mount without firing (emit on CHANGE only)\n return;\n }\n const args = mapArgs ? mapArgs(v) : [v];\n node[name](...args);\n });\n removers.push(() => handle.dispose());\n return () => handle.dispose();\n },\n\n dispose(): void {\n if (disposed) {\n return; // idempotent\n }\n disposed = true;\n for (const remove of removers) {\n remove();\n }\n },\n };\n\n return bound as unknown as BoundNode<S>;\n}\n\n/**\n * Build a `resource` source from a wc-bindable node, bridging the resource's\n * AbortSignal to the node's cancel command (default `\"abort\"`) before delegating to\n * `run`. This generalizes the earlier hand-wired `sig → core.abort()` bridge (docs §8\n * (e), §5-2): wrap the result in `resource({ args })` and any node that declares an\n * abort command gets switchMap-style cancel/restart for free — an `args` change\n * aborts the in-flight call (firing the node's abort command, which cancels its real\n * AbortController) and starts the next.\n *\n * The node's own value/loading/error stay available via `bound.signals`; `resource`\n * here is used for the cancel/restart lifecycle, not to re-derive that triad.\n *\n * @example\n * const bound = bindNode(fetchEl);\n * const r = resource(\n * nodeSource(bound, (b, id) => b.command(\"fetch\", `/api/${id}`)),\n * { args: () => id.get() },\n * );\n */\nexport function nodeSource<T, A = void, S extends NodeShape = DefaultNodeShape>(\n bound: BoundNode<S>,\n run: (node: BoundNode<S>, args: A) => Promise<T> | T,\n options: { abort?: string } = {},\n): ResourceSource<T, A> {\n const abortName = options.abort ?? \"abort\";\n return (args, signal) => {\n // Honor the resource's cancel by invoking the node's abort command. `once`:\n // each resource run gets a fresh AbortSignal, so the listener fires at most once.\n //\n // GUARD: the abort listener runs SYNCHRONOUSLY inside AbortController.abort().\n // If `bound` was already disposed (e.g. the adapter and the resource share an\n // owner and the adapter's disposer ran first), `command` throws assertLive —\n // and a throw out of an abort listener surfaces as an unhandled exception during\n // the synchronous abort() call. Swallow a post-dispose throw so teardown order is\n // robust; report any OTHER error via the platform reporter without breaking abort.\n signal.addEventListener(\n \"abort\",\n () => {\n try {\n // `abortName` is a runtime string (validated by the adapter); the cast lets\n // it reach `command` regardless of the typed command-name union of `S`.\n (bound.command as (name: string, ...args: unknown[]) => unknown)(abortName);\n } catch (err) {\n // Brand-based check (not a message regex): a post-dispose throw is\n // expected here and swallowed; any OTHER error is reported.\n if (!isDisposedError(err)) {\n reportError(err);\n }\n }\n },\n { once: true },\n );\n return run(bound, args);\n };\n}\n","// Async resource (v1 design notes).\n//\n// Adapts an async producer into a reactive `{ value, loading, error }` triad —\n// the same shape FetchCore exposes (value/loading/error). When the resource's\n// `args` (a reactive getter) change, the in-flight request is aborted and a fresh\n// one starts: dependency-driven cancel/restart, i.e. RxJS switchMap.\n//\n// This is the signals-side counterpart of the state-side `$streams` adapter\n// (docs/state-stream-type-design.md §4-1). The hard part — making sure a stale\n// response from a superseded request never lands on the new state — is handled\n// here by checking `signal.aborted` before committing any result.\n\nimport { signal, effect, onCleanup, ReadSignal } from \"./reactive.js\";\n\nexport interface ResourceState<T> {\n value: ReadSignal<T | undefined>;\n // Progress is a BOOLEAN here, intentionally different from streamResource's\n // `status` enum. A resource is single-shot (one request → resolve/reject), so\n // its progress is binary: in-flight or not. A stream is continuous (idle →\n // active → done/error), so it needs the enum. The \"shared contract\" with the\n // state-side `$streams` adapter covers the cancel/restart SEMANTICS, not this\n // progress representation. (docs §8 (a)).\n loading: ReadSignal<boolean>;\n /** Last error, or `null` when there is none. Initial value is `null`; a (re)start resets it to `null`. */\n error: ReadSignal<unknown>;\n /** Abort any in-flight request and stop reacting to `args` changes. */\n dispose(): void;\n}\n\nexport interface ResourceOptions<T, A> {\n /** Reactive inputs. Reading signals here wires restart-on-change. */\n args?: () => A;\n /** Seed value before the first resolution. */\n initial?: T;\n}\n\nexport type ResourceSource<T, A> = (args: A, signal: AbortSignal) => Promise<T> | T;\n\nexport function resource<T, A = void>(\n source: ResourceSource<T, A>,\n options: ResourceOptions<T, A> = {},\n): ResourceState<T> {\n const value = signal<T | undefined>(options.initial);\n const loading = signal<boolean>(false);\n const error = signal<unknown>(null);\n\n let controller: AbortController | null = null;\n\n const runner = effect(() => {\n // Reading args() inside the effect is what subscribes us to its signals, so a\n // change re-runs this body — that IS the restart trigger.\n //\n // NOTE (effect-internal writes): this effect writes loading/error/value, which\n // are signals OTHER effects may observe. A write that actually CHANGES the value\n // marks those observers stale and queues them on the same flush; the drain loop\n // then runs them in the same tick. (A same-value write — e.g. error.set(null)\n // when error is already null — is a no-op via the equality guard and notifies\n // nothing.) We never read these signals inside THIS effect, so we don't dirty\n // our own dependency — no cycle. (docs §8 (a)).\n const a = (options.args ? options.args() : undefined) as A;\n\n // Abort the previous request before starting the next (switchMap).\n controller?.abort();\n const ac = new AbortController();\n controller = ac;\n\n loading.set(true);\n error.set(null);\n\n // Call the source SYNCHRONOUSLY (so it receives ac.signal immediately and can\n // wire its abort listener before any teardown), but guard it with try/catch so\n // a synchronous throw is normalized into the same error/loading state as a\n // rejected promise. Without the guard a sync throw would escape the effect body\n // (and, on the initial run, the resource() call itself), leaving loading stuck\n // true and error unset.\n let produced: Promise<T> | T;\n try {\n produced = source(a, ac.signal);\n } catch (err) {\n error.set(err);\n loading.set(false);\n return;\n }\n\n // Settle via two-arm `.then` rather than `await` + try/catch (streamResource's\n // shape): a resource is SINGLE-SHOT — one resolve/reject lands once — so a flat\n // resolved/rejected pair maps cleanly. A stream is a CONTINUOUS loop whose chunks\n // and termination must share one error path, which an `await` loop in try/catch\n // expresses; that structure would be needless here.\n Promise.resolve(produced).then(\n (resolved) => {\n // Drop the result if this request was superseded/disposed: committing it\n // would let a stale response overwrite the newer request's state.\n if (ac.signal.aborted) {\n return;\n }\n value.set(resolved);\n loading.set(false);\n },\n (err) => {\n if (ac.signal.aborted) {\n return;\n }\n error.set(err);\n loading.set(false);\n },\n );\n });\n\n const dispose = (): void => {\n controller?.abort();\n runner.dispose();\n };\n\n // Auto-dispose with the enclosing owner (createRoot / parent effect), so a\n // resource created inside a component is aborted on unmount. No-op when there\n // is no owner — the caller then disposes manually.\n onCleanup(dispose);\n\n return { value, loading, error, dispose };\n}\n","// Stream resource (docs §8 (a)). The signals-side counterpart of the state-side\n// `$streams` adapter (docs/state-stream-type-design.md).\n//\n// Adapts a continuous async flow (async iterable / ReadableStream / async\n// generator) into a single reactive value by FOLDING each chunk:\n// - latest (default): replace — value becomes the last chunk.\n// - reduce: accumulate — value = fold(acc, chunk), needs `initial`.\n// When the resource's `args` change, the in-flight stream is aborted and a fresh\n// one starts (switchMap), with `value` reset to `initial`.\n//\n// Deliberate non-goal: backpressure. The fold result IS the buffer; demand does\n// not flow back to the producer. Unbounded accumulation of an infinite stream is\n// a footgun — bound the fold (latest / count / last-N / window) for live streams.\n// This mirrors the state `$streams` norm and is what lets the impedance mismatch\n// be resolved honestly (state-stream §0, §4-3).\n//\n// Shared contract with state `$streams` (settled in v1):\n// - source(args, signal) receives an AbortSignal; honoring it drives restart.\n// - restart RESETS value to `initial`; error KEEPS the last value.\n// - status companion: \"idle\" | \"active\" | \"done\" | \"error\".\n// - async iterable is the lingua franca; anything getReader-bearing\n// (ReadableStream) is read via getReader() — see rescue levels below.\n//\n// CONTRACT (cooperative cancellation — STRONG REQUIREMENT): the `source` MUST honor\n// the `AbortSignal` it is given. Honoring it is what drives switchMap restart/dispose;\n// a source that ignores it cannot be reliably cancelled.\n//\n// Rescue levels on abort:\n// - ReadableStream: FULLY rescued. A parked read() is force-unwound via\n// reader.cancel(), which both releases the underlying source and settles the\n// pending read() so the loop unwinds. Anything getReader-bearing is routed\n// through this path even when it is ALSO natively async-iterable (Node 18+ /\n// Chrome 124+ / Firefox): the spec serializes asyncIterator.return() behind\n// the pending next() ([[ongoingPromise]]), so the AsyncIterable rescue below\n// cannot force-unwind a parked read on a native ReadableStream.\n// - AsyncIterable / async generator: PARTIALLY rescued. On abort we call\n// iterator.return() to trigger the generator's finally/cleanup. But a parked\n// `await` (the producer stalling before its next yield while IGNORING `signal`)\n// cannot be force-unwound from outside — return() only takes effect when the\n// generator next resumes. So a source that parks forever and never observes\n// `signal` still leaks its consume() task. Honor `signal` to bound this.\n// The `if (signal.aborted) return` check only runs after a chunk arrives, not while\n// parked — it drops stale chunks but is not, by itself, a cancellation mechanism.\n\nimport { signal, effect, onCleanup, ReadSignal, WriteSignal } from \"./reactive.js\";\n\nexport type StreamStatus = \"idle\" | \"active\" | \"done\" | \"error\";\n\nexport interface StreamResourceState<T> {\n value: ReadSignal<T | undefined>;\n // Progress is an ENUM here, intentionally different from resource's `loading`\n // boolean: a continuous stream moves idle → active → done/error, which a boolean\n // cannot express. The cancel/restart contract is shared with `resource` and the\n // state-side `$streams` adapter; the progress representation is not.\n status: ReadSignal<StreamStatus>;\n /** Last error, or `null` when there is none. Initial value is `null`; a (re)start resets it to `null`. */\n error: ReadSignal<unknown>;\n /** Abort the in-flight stream and stop reacting to `args`. */\n dispose(): void;\n}\n\nexport interface StreamResourceOptions<T, C, A> {\n /** Reactive inputs. Reading signals here wires restart-on-change. */\n args?: () => A;\n /** Combine the running value with each chunk. Default: latest (replace). */\n fold?: (acc: T | undefined, chunk: C) => T;\n /** Seed value, and the value `value` is reset to on each (re)start. */\n initial?: T;\n}\n\nexport type StreamProducer<C> = AsyncIterable<C> | ReadableStream<C>;\n\n/**\n * Produce a stream for the current `args`. The `signal` aborts on restart/dispose —\n * the source **MUST honor it** (this is a hard contract, not a suggestion): a\n * `ReadableStream` is fully cancelled via `reader.cancel()`, and an async generator\n * should observe `signal` (e.g. reject/break on `signal.aborted`) so a parked `await`\n * can unwind. On abort the adapter also calls the iterator's `return()` to run a\n * generator's `finally`, but a generator that parks forever while ignoring `signal`\n * cannot be force-unwound and will leak. See the module header's CONTRACT.\n */\nexport type StreamSource<C, A> = (\n args: A,\n signal: AbortSignal,\n) => StreamProducer<C> | Promise<StreamProducer<C>>;\n\nexport function streamResource<T, C = T, A = void>(\n source: StreamSource<C, A>,\n options: StreamResourceOptions<T, C, A> = {},\n): StreamResourceState<T> {\n const fold = options.fold ?? ((_acc: T | undefined, chunk: C) => chunk as unknown as T);\n const value = signal<T | undefined>(options.initial);\n const status = signal<StreamStatus>(\"idle\");\n const error = signal<unknown>(null);\n\n let controller: AbortController | null = null;\n\n const runner = effect(() => {\n // NOTE (effect-internal writes): like `resource`, this effect writes\n // value/status/error — signals other effects may observe. Those observers are\n // queued on the same flush and run in the same tick. This effect never reads\n // those signals, so it does not dirty its own dependency (no cycle). The async\n // `consume` writes them later from microtasks/timers, outside this flush.\n const a = (options.args ? options.args() : undefined) as A;\n\n controller?.abort();\n const ac = new AbortController();\n controller = ac;\n\n // Reset for the new run: a restart starts the fold from `initial`.\n value.set(options.initial);\n error.set(null);\n status.set(\"active\");\n\n void consume(source, a, ac.signal, fold, value, status, error);\n });\n\n const dispose = (): void => {\n controller?.abort();\n runner.dispose();\n };\n onCleanup(dispose);\n\n return { value, status, error, dispose };\n}\n\nasync function consume<T, C, A>(\n source: StreamSource<C, A>,\n args: A,\n signal: AbortSignal,\n fold: (acc: T | undefined, chunk: C) => T,\n // Use the public WriteSignal type instead of re-declaring an inline subset, so\n // these stay in lockstep with the signal API (no structural drift).\n value: WriteSignal<T | undefined>,\n status: WriteSignal<StreamStatus>,\n error: WriteSignal<unknown>,\n): Promise<void> {\n // Obtain the iterator EXPLICITLY (not via `for await`'s implicit one) so abort can\n // call `iterator.return()` to trigger an AsyncIterable / async generator's\n // `finally`/cleanup. A `for await` only calls `.return()` when the loop itself exits;\n // if the producer is PARKED (awaiting before the next yield while ignoring `signal`),\n // the loop never advances, so the implicit `.return()` never runs and the task leaks\n // past restart/dispose. Calling `.return()` on abort is the PARTIAL rescue: the\n // parked `await` cannot be force-unwound from outside, but once the generator resumes\n // (its next tick), `.return()` makes it run its `finally` and stop — recovering the\n // common \"generator wakes up after abort\" case. The ReadableStream path is fully\n // rescued via `reader.cancel()` (see `readableToAsyncIterable`).\n let iterator: AsyncIterator<C> | null = null;\n // Guard against returning the SAME iterator twice. `onAbort` is reachable two ways:\n // the abort listener, and the explicit call on line ~163 when abort raced the\n // `await source(...)`. The guard keys on the iterator instance (not a plain \"ran\"\n // flag): the listener firing with iterator still null must NOT consume the single\n // real cleanup that the explicit call performs once the iterator exists. So we only\n // mark an iterator returned once we have actually called `.return()` on it.\n let returned: AsyncIterator<C> | null = null;\n const onAbort = (): void => {\n if (!iterator || iterator === returned) {\n return; // nothing to release yet, or already released this iterator\n }\n returned = iterator;\n // Fire the iterator's cleanup. Swallow any throw/rejection from `.return()` — we\n // are tearing down; a producer that rejects on return must not surface here.\n try {\n void iterator.return?.()?.then?.(undefined, () => {});\n } catch {\n // `.return()` threw synchronously while tearing down — ignore.\n }\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n const produced = await source(args, signal);\n iterator = iterate(produced, signal)[Symbol.asyncIterator]();\n if (signal.aborted) {\n // Aborted while awaiting the source: the abort listener already ran (iterator\n // was still null then), so explicitly release the just-produced iterator now —\n // this fires a generator's finally / a ReadableStream's cancel for the\n // resource we created but will never iterate.\n onAbort();\n return;\n }\n for (;;) {\n const result = await iterator.next();\n if (result.done) {\n break;\n }\n if (signal.aborted) {\n return; // stale chunk from a superseded/disposed run — drop it\n }\n value.set(fold(value.peek(), result.value));\n }\n if (signal.aborted) {\n return; // stream ended but this run was aborted — don't mark done\n }\n status.set(\"done\");\n } catch (e) {\n if (signal.aborted) {\n return; // an abort that surfaced as a throw is not an error\n }\n error.set(e); // keep the last folded value (do not reset)\n status.set(\"error\");\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n}\n\nfunction iterate<C>(produced: StreamProducer<C>, signal: AbortSignal): AsyncIterable<C> {\n // Anything ReadableStream-shaped (has getReader) takes the reader path FIRST —\n // even when it is also async-iterable. On runtimes where ReadableStream\n // implements native async iteration (Node 18+ / Chrome 124+ / Firefox), the\n // spec serializes `iterator.return()` behind the pending `next()`\n // ([[ongoingPromise]]), so the AsyncIterable rescue cannot force-unwind a\n // parked read: abort would hang the consume task until the producer's next\n // chunk (or forever on a silent stream), leaking the underlying source.\n // `reader.cancel()` has no such ordering — routing every getReader-bearing\n // source through it keeps the FULL rescue promised for ReadableStream.\n if (typeof (produced as ReadableStream<C>)?.getReader === \"function\") {\n return readableToAsyncIterable(produced as ReadableStream<C>, signal);\n }\n if (typeof (produced as AsyncIterable<C>)?.[Symbol.asyncIterator] === \"function\") {\n return produced as AsyncIterable<C>;\n }\n throw new TypeError(\n \"streamResource: source must return an AsyncIterable or a ReadableStream (got neither).\",\n );\n}\n\nasync function* readableToAsyncIterable<C>(\n stream: ReadableStream<C>,\n signal: AbortSignal,\n): AsyncGenerator<C> {\n const reader = stream.getReader();\n // A ReadableStream read() does NOT observe an AbortSignal on its own. Without\n // this, a switchMap restart / dispose leaves the previous reader parked in a\n // pending read() forever, leaking the underlying source. Cancelling on abort\n // both releases the source AND settles the pending read() so the for-await\n // unwinds and the finally below can release the lock. Abort is the only\n // early-exit path for this generator (the consumer never calls .return()\n // without aborting), so this is the sole place a non-drained stream is cancelled.\n const onAbort = (): void => {\n void reader.cancel().catch(() => {}); // tearing down; swallow a rejected cancel\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n return;\n }\n yield value as C;\n }\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n reader.releaseLock();\n }\n}\n"],"names":[],"mappings":"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"}
package/dist/dom.d.ts CHANGED
@@ -123,7 +123,11 @@ interface BoundNode<S extends NodeShape = DefaultNodeShape> {
123
123
  * Reactively write a declared input from a signal: an effect mirrors `source` into
124
124
  * `node[name]`. A same-value guard (`node[name] !== v`) skips redundant writes, so
125
125
  * a property whose write re-dispatches an event cannot feed back into an infinite
126
- * loop. Returns a disposer; the effect is also torn down by `dispose()`.
126
+ * loop. `undefined` follows the wc-bindable applier profile (A2): it is not written
127
+ * while the source has had no other value (the first evaluation, or after another
128
+ * `undefined`), so the element keeps its own initial state; after a value it is
129
+ * written, so the element restores that state. Returns a disposer; the effect is
130
+ * also torn down by `dispose()` (neither writes anything).
127
131
  */
128
132
  bindInput<K extends keyof InputsOf<S>>(name: K, source: ReadSignal<InputsOf<S>[K]>): () => void;
129
133
  /** Invoke a declared command on the node (imperative); args/return typed from the shape. */
package/dist/dom.esm.js CHANGED
@@ -1,5 +1,5 @@
1
- import { e as effect, o as onCleanup, b as bindNode, g as isDev, h as hasOwner, w as warnDev, s as signal, a as createRoot } from './core-Db6kZPd_.esm.js';
2
- export { D as DisposedError, c as computed, f as flushSync, i as isDisposedError, n as nodeSource, r as resource, d as streamResource } from './core-Db6kZPd_.esm.js';
1
+ import { e as effect, o as onCleanup, b as bindNode, g as isDev, h as hasOwner, w as warnDev, s as signal, a as createRoot } from './core-GLSUraaa.esm.js';
2
+ export { D as DisposedError, c as computed, f as flushSync, i as isDisposedError, n as nodeSource, r as resource, d as streamResource } from './core-GLSUraaa.esm.js';
3
3
 
4
4
  // Fine-grained hyperscript (v1 design notes). The "step before JSX" (docs §4-1).
5
5
  //
@@ -1,2 +1,2 @@
1
- import{e,o as t,b as o,g as s,h as l,w as a,s as u,a as p}from"./core-3xbxch0d.esm.min.js";export{D as DisposedError,c as computed,f as flushSync,i as isDisposedError,n as nodeSource,r as resource,d as streamResource}from"./core-3xbxch0d.esm.min.js";const m=Symbol("signals.Fragment");function h(e){return"object"==typeof e&&null!==e&&"function"==typeof e.get&&"function"==typeof e.peek}function g(e,t,...n){if(e===m){const e=document.createDocumentFragment();return N(e,n),e}if("function"==typeof e){const o=e({...t??{},children:n});return Array.isArray(o)?function(e){const t=document.createDocumentFragment();return N(t,e),t}(o):o}const o=function(e){return w.has(e)?document.createElementNS(y,e):document.createElement(e)}(e);if(t)for(const e in t)C(o,e,t[e]);return N(o,n),o}const y="http://www.w3.org/2000/svg",w=new Set(["svg","g","path","rect","circle","ellipse","line","polyline","polygon","text","tspan","defs","use","symbol","marker","linearGradient","radialGradient","stop","clipPath","mask","pattern","image","foreignObject"]);function b(e,t){return x(t,e),t}const v={for:"htmlFor",tabindex:"tabIndex",colspan:"colSpan",rowspan:"rowSpan",readonly:"readOnly",maxlength:"maxLength",minlength:"minLength",contenteditable:"contentEditable"},k=new WeakMap;function E(e,t){const n=Object.getPrototypeOf(e);let o=k.get(n);void 0===o&&(o=new Map,k.set(n,o));let s=o.get(t);return void 0===s&&(s=function(e,t){let n=e;for(;null!==n;){const e=Object.getOwnPropertyDescriptor(n,t);if(e)return"set"in e?"function"==typeof e.set:!1!==e.writable;n=Object.getPrototypeOf(n)}return!1}(e,t),o.set(t,s)),s}function C(n,o,s){if(/^on[A-Z]/.test(o)&&"function"==typeof s){const e=o.slice(2).toLowerCase(),r=s;return n.addEventListener(e,r),void t(()=>n.removeEventListener(e,r))}"function"!=typeof s?h(s)?e(()=>S(n,o,s.get())):S(n,o,s):e(()=>S(n,o,s()))}function S(e,t,n){if("style"===t)return void function(e,t){if(null==t)return void e.removeAttribute("style");if("string"==typeof t)return void(e.style.cssText=t);e.style.cssText="";const n=e.style,o=t;for(const e in o)e.includes("-")?n.setProperty(e,o[e]):n[e]=o[e]}(e,n);if("class"===t||"className"===t)return void e.setAttribute("class",null==n||!1===n?"":String(n));const o=v[t]??t;E(e,o)?e[o]=null==n?"":n:null!=n&&!1!==n?!0!==n?e.setAttribute(t,String(n)):e.setAttribute(t,""):e.removeAttribute(t)}function N(e,t){for(const n of t)x(e,n)}function x(e,t){var n;if(null!=t&&"boolean"!=typeof t)if(Array.isArray(t))for(const n of t)x(e,n);else"object"!=typeof(n=t)||null===n||!0!==n.__wcsList?t instanceof Node?e.appendChild(t):"function"!=typeof t?h(t)?O(e,()=>t.get()):e.appendChild(document.createTextNode(String(t))):O(e,t):t.mount(e)}function O(n,o){s()&&!l()&&a("UNOWNED_INSERT","","reactive child inserted with no owner — its update effect will never be disposed and may leak. Mount under createRoot / SignalsElement.");const r=document.createComment("");n.appendChild(r);let i=[];e(()=>{const e=o();if(("string"==typeof e||"number"==typeof e)&&1===i.length){const t=i[0];if(3===t.nodeType)return void(t.data=String(e))}const t=function(e){const t=[],n=e=>{null!=e&&"boolean"!=typeof e&&(Array.isArray(e)?e.forEach(n):e instanceof Node?t.push(e):t.push(document.createTextNode(String(e))))};return n(e),t}(e);for(const e of i)e.parentNode?.removeChild(e);const n=r.parentNode;if(n)for(const e of t)n.insertBefore(e,r);i=t}),t(()=>{i=[]})}function M(e){return h(e)?()=>e.get():e}function _(e,t,n,o){const s=n.length;if(0===s)return;const r=new Map;for(let e=0;e<o.length;e++)r.set(o[e],e);const i=new Array(s);for(let e=0;e<s;e++){const t=r.get(n[e]);i[e]=void 0===t?-1:t}const c=function(e){const t=e.length,n=[],o=new Array(t).fill(-1);for(let s=0;s<t;s++){const t=e[s];if(t<0)continue;let r=0,i=n.length;for(;r<i;){const o=r+i>>1;e[n[o]]<t?r=o+1:i=o}r>0&&(o[s]=n[r-1]),n[r]=s}const s=[];let r=n.length>0?n[n.length-1]:-1;for(;r>=0;)s.push(r),r=o[r];return s.reverse(),s}(i);let l=c.length-1,a=t;for(let t=s-1;t>=0;t--){const o=n[t];l>=0&&c[l]===t?l--:e.insertBefore(o,a),a=o}}function A(e,t,n){null==e||"number"==typeof e&&Number.isNaN(e)?a("NULLISH_KEY",String(e),"For: a key is null/undefined/NaN. Such keys collide under SameValueZero, so rows silently merge/drop. Provide a stable unique `key`.",{key:e}):n||"object"!=typeof e&&"function"!=typeof e||a("NON_PRIMITIVE_KEY",typeof e,"For: a non-primitive item is used as its own key (no `key` option given). Object identity changes across renders, so every row is rebuilt and per-row state is lost. Pass `{ key: item => item.id }`.",{item:t})}function L(n,o,r){const i=M(n),c=r?.key;return{__wcsList:!0,mount(n){const r=document.createComment("for");n.appendChild(r);let l=new Map,d=[];const f=(e,t)=>{const n=u(t);let s;const r=p(t=>(s=o(e,()=>n.get()),t));return{node:s,dispose:r,idx:n}};e(()=>{const e=i()??[],t=[],n=new Set,o=s();for(let s=0;s<e.length;s++){const r=c?c(e[s],s):e[s];if(o&&A(r,e[s],void 0!==c),n.has(r))throw o&&a("DUPLICATE_KEY",String(r),`For: duplicate key "${String(r)}" at index ${s} — keys must be unique. Rendering throws and the list stops updating until keys are fixed.`,{key:r,index:s}),new Error(`For: duplicate key "${String(r)}" — keys must be unique.`);n.add(r),t.push(r)}const u=new Map,p=[],m=[];try{for(let n=0;n<e.length;n++){const o=t[n];let s=l.get(o);s?s.idx.peek()!==n&&s.idx.set(n):(s=f(e[n],n),m.push(s)),u.set(o,s),p.push(s.node)}}catch(e){for(const e of m)e.dispose();throw e}const h=r.parentNode;if(l.size>0&&u.size===m.length)if(null!==h&&h.lastChild===r&&d.length>0&&h.firstChild===d[0]&&h.childNodes.length===l.size+1){h.textContent="",h.appendChild(r);for(const e of l.values())e.dispose()}else for(const e of l.values())e.node.remove(),e.dispose();else for(const[e,t]of l)u.has(e)||(t.node.remove(),t.dispose());h&&_(h,r,p,d),d=p,l=u}),t(()=>{for(const e of l.values())e.dispose();l.clear()})}}}function P(n,o){const s=M(n);return{__wcsList:!0,mount(n){const r=document.createComment("index");n.appendChild(r);const i=[];e(()=>{const e=s()??[],t=r.parentNode;for(let n=0;n<e.length;n++)if(n<i.length)i[n].item.set(e[n]);else{const s=u(e[n]);let c;const l=p(e=>(c=o(()=>s.get(),n),e));i.push({node:c,dispose:l,item:s}),t?.insertBefore(c,r)}if(e.length<i.length){if(0===e.length&&null!==t&&t.lastChild===r&&t.firstChild===i[0].node&&t.childNodes.length===i.length+1){t.textContent="",t.appendChild(r);for(const e of i)e.dispose()}else for(let t=e.length;t<i.length;t++)i[t].node.remove(),i[t].dispose();i.length=e.length}}),t(()=>{for(const e of i)e.dispose();i.length=0})}}}let j=null;function T(){if(null!==j)return j;if("undefined"==typeof HTMLElement)throw new Error("@wcstack/signals/dom: SignalsElement requires a DOM (HTMLElement is not defined). The `./dom` entry's element/render surface is browser-only; in SSR/Node/worker code import the headless core from `@wcstack/signals` instead, or inject a DOM (e.g. happy-dom) before constructing custom elements.");class e extends HTMLElement{_dispose=null;getMountPoint(){return this}connectedCallback(){if(null!==this._dispose)return;const e=this.getMountPoint();this._dispose=p(t=>(e.appendChild(this.render()),t))}disconnectedCallback(){if(null===this._dispose)return;this._dispose(),this._dispose=null;const e=this.getMountPoint();for(;e.firstChild;)e.removeChild(e.firstChild)}}return j=e,j}const R=new Proxy(function(){},{construct:(e,t,n)=>Reflect.construct(T(),t,n),get:(e,t,n)=>Reflect.get(T(),t,n),has:(e,t)=>Reflect.has(T(),t),apply(){throw new TypeError("SignalsElement is a class and cannot be invoked without `new`/`extends`.")},getPrototypeOf:()=>Reflect.getPrototypeOf(T())});function F(e,t={}){if("undefined"==typeof document||"undefined"==typeof customElements)throw new Error("@wcstack/signals/dom: mountNode requires a DOM (document/customElements is not defined). It creates and connects a live element, so it is browser-only; inject a DOM (e.g. happy-dom) before calling, or keep headless work on `@wcstack/signals`.");const{attrs:n,parent:s,descriptor:r}=t,i=e.toLowerCase();if(!r&&!customElements.get(i))throw new Error(`mountNode: "<${i}>" is not defined. Import its defining module first (e.g. a side-effect \`import "@wcstack/<pkg>/auto"\` in this module — evaluation order then guarantees the definition). For a tag loaded by someone else, await customElements.whenDefined("${i}") or gate with <wcs-defined> before mounting. Binding a non-custom-element tag requires an explicit \`descriptor\`.`);const c=document.createElement(i);if(n)for(const[e,t]of Object.entries(n))!1!==t&&c.setAttribute(e,!0===t?"":String(t));const l=o(c,r);(s??document.body).appendChild(c);const a=()=>{l.dispose(),c.remove()};return{...l,el:c,unmount:a,dispose:a}}export{L as For,m as Fragment,P as Index,R as SignalsElement,o as bindNode,p as createRoot,T as createSignalsElement,e as effect,g as h,F as mountNode,t as onCleanup,b as render,u as signal};
1
+ import{e,o as t,b as o,g as s,h as l,w as a,s as u,a as p}from"./core-CUHdCgQ2.esm.min.js";export{D as DisposedError,c as computed,f as flushSync,i as isDisposedError,n as nodeSource,r as resource,d as streamResource}from"./core-CUHdCgQ2.esm.min.js";const m=Symbol("signals.Fragment");function h(e){return"object"==typeof e&&null!==e&&"function"==typeof e.get&&"function"==typeof e.peek}function g(e,t,...n){if(e===m){const e=document.createDocumentFragment();return N(e,n),e}if("function"==typeof e){const o=e({...t??{},children:n});return Array.isArray(o)?function(e){const t=document.createDocumentFragment();return N(t,e),t}(o):o}const o=function(e){return w.has(e)?document.createElementNS(y,e):document.createElement(e)}(e);if(t)for(const e in t)C(o,e,t[e]);return N(o,n),o}const y="http://www.w3.org/2000/svg",w=new Set(["svg","g","path","rect","circle","ellipse","line","polyline","polygon","text","tspan","defs","use","symbol","marker","linearGradient","radialGradient","stop","clipPath","mask","pattern","image","foreignObject"]);function b(e,t){return x(t,e),t}const v={for:"htmlFor",tabindex:"tabIndex",colspan:"colSpan",rowspan:"rowSpan",readonly:"readOnly",maxlength:"maxLength",minlength:"minLength",contenteditable:"contentEditable"},k=new WeakMap;function E(e,t){const n=Object.getPrototypeOf(e);let o=k.get(n);void 0===o&&(o=new Map,k.set(n,o));let s=o.get(t);return void 0===s&&(s=function(e,t){let n=e;for(;null!==n;){const e=Object.getOwnPropertyDescriptor(n,t);if(e)return"set"in e?"function"==typeof e.set:!1!==e.writable;n=Object.getPrototypeOf(n)}return!1}(e,t),o.set(t,s)),s}function C(n,o,s){if(/^on[A-Z]/.test(o)&&"function"==typeof s){const e=o.slice(2).toLowerCase(),r=s;return n.addEventListener(e,r),void t(()=>n.removeEventListener(e,r))}"function"!=typeof s?h(s)?e(()=>S(n,o,s.get())):S(n,o,s):e(()=>S(n,o,s()))}function S(e,t,n){if("style"===t)return void function(e,t){if(null==t)return void e.removeAttribute("style");if("string"==typeof t)return void(e.style.cssText=t);e.style.cssText="";const n=e.style,o=t;for(const e in o)e.includes("-")?n.setProperty(e,o[e]):n[e]=o[e]}(e,n);if("class"===t||"className"===t)return void e.setAttribute("class",null==n||!1===n?"":String(n));const o=v[t]??t;E(e,o)?e[o]=null==n?"":n:null!=n&&!1!==n?!0!==n?e.setAttribute(t,String(n)):e.setAttribute(t,""):e.removeAttribute(t)}function N(e,t){for(const n of t)x(e,n)}function x(e,t){var n;if(null!=t&&"boolean"!=typeof t)if(Array.isArray(t))for(const n of t)x(e,n);else"object"!=typeof(n=t)||null===n||!0!==n.__wcsList?t instanceof Node?e.appendChild(t):"function"!=typeof t?h(t)?O(e,()=>t.get()):e.appendChild(document.createTextNode(String(t))):O(e,t):t.mount(e)}function O(n,o){s()&&!l()&&a("UNOWNED_INSERT","","reactive child inserted with no owner — its update effect will never be disposed and may leak. Mount under createRoot / SignalsElement.");const r=document.createComment("");n.appendChild(r);let i=[];e(()=>{const e=o();if(("string"==typeof e||"number"==typeof e)&&1===i.length){const t=i[0];if(3===t.nodeType)return void(t.data=String(e))}const t=function(e){const t=[],n=e=>{null!=e&&"boolean"!=typeof e&&(Array.isArray(e)?e.forEach(n):e instanceof Node?t.push(e):t.push(document.createTextNode(String(e))))};return n(e),t}(e);for(const e of i)e.parentNode?.removeChild(e);const n=r.parentNode;if(n)for(const e of t)n.insertBefore(e,r);i=t}),t(()=>{i=[]})}function M(e){return h(e)?()=>e.get():e}function _(e,t,n,o){const s=n.length;if(0===s)return;const r=new Map;for(let e=0;e<o.length;e++)r.set(o[e],e);const i=new Array(s);for(let e=0;e<s;e++){const t=r.get(n[e]);i[e]=void 0===t?-1:t}const c=function(e){const t=e.length,n=[],o=new Array(t).fill(-1);for(let s=0;s<t;s++){const t=e[s];if(t<0)continue;let r=0,i=n.length;for(;r<i;){const o=r+i>>1;e[n[o]]<t?r=o+1:i=o}r>0&&(o[s]=n[r-1]),n[r]=s}const s=[];let r=n.length>0?n[n.length-1]:-1;for(;r>=0;)s.push(r),r=o[r];return s.reverse(),s}(i);let l=c.length-1,a=t;for(let t=s-1;t>=0;t--){const o=n[t];l>=0&&c[l]===t?l--:e.insertBefore(o,a),a=o}}function A(e,t,n){null==e||"number"==typeof e&&Number.isNaN(e)?a("NULLISH_KEY",String(e),"For: a key is null/undefined/NaN. Such keys collide under SameValueZero, so rows silently merge/drop. Provide a stable unique `key`.",{key:e}):n||"object"!=typeof e&&"function"!=typeof e||a("NON_PRIMITIVE_KEY",typeof e,"For: a non-primitive item is used as its own key (no `key` option given). Object identity changes across renders, so every row is rebuilt and per-row state is lost. Pass `{ key: item => item.id }`.",{item:t})}function L(n,o,r){const i=M(n),c=r?.key;return{__wcsList:!0,mount(n){const r=document.createComment("for");n.appendChild(r);let l=new Map,d=[];const f=(e,t)=>{const n=u(t);let s;const r=p(t=>(s=o(e,()=>n.get()),t));return{node:s,dispose:r,idx:n}};e(()=>{const e=i()??[],t=[],n=new Set,o=s();for(let s=0;s<e.length;s++){const r=c?c(e[s],s):e[s];if(o&&A(r,e[s],void 0!==c),n.has(r))throw o&&a("DUPLICATE_KEY",String(r),`For: duplicate key "${String(r)}" at index ${s} — keys must be unique. Rendering throws and the list stops updating until keys are fixed.`,{key:r,index:s}),new Error(`For: duplicate key "${String(r)}" — keys must be unique.`);n.add(r),t.push(r)}const u=new Map,p=[],m=[];try{for(let n=0;n<e.length;n++){const o=t[n];let s=l.get(o);s?s.idx.peek()!==n&&s.idx.set(n):(s=f(e[n],n),m.push(s)),u.set(o,s),p.push(s.node)}}catch(e){for(const e of m)e.dispose();throw e}const h=r.parentNode;if(l.size>0&&u.size===m.length)if(null!==h&&h.lastChild===r&&d.length>0&&h.firstChild===d[0]&&h.childNodes.length===l.size+1){h.textContent="",h.appendChild(r);for(const e of l.values())e.dispose()}else for(const e of l.values())e.node.remove(),e.dispose();else for(const[e,t]of l)u.has(e)||(t.node.remove(),t.dispose());h&&_(h,r,p,d),d=p,l=u}),t(()=>{for(const e of l.values())e.dispose();l.clear()})}}}function P(n,o){const s=M(n);return{__wcsList:!0,mount(n){const r=document.createComment("index");n.appendChild(r);const i=[];e(()=>{const e=s()??[],t=r.parentNode;for(let n=0;n<e.length;n++)if(n<i.length)i[n].item.set(e[n]);else{const s=u(e[n]);let c;const l=p(e=>(c=o(()=>s.get(),n),e));i.push({node:c,dispose:l,item:s}),t?.insertBefore(c,r)}if(e.length<i.length){if(0===e.length&&null!==t&&t.lastChild===r&&t.firstChild===i[0].node&&t.childNodes.length===i.length+1){t.textContent="",t.appendChild(r);for(const e of i)e.dispose()}else for(let t=e.length;t<i.length;t++)i[t].node.remove(),i[t].dispose();i.length=e.length}}),t(()=>{for(const e of i)e.dispose();i.length=0})}}}let j=null;function T(){if(null!==j)return j;if("undefined"==typeof HTMLElement)throw new Error("@wcstack/signals/dom: SignalsElement requires a DOM (HTMLElement is not defined). The `./dom` entry's element/render surface is browser-only; in SSR/Node/worker code import the headless core from `@wcstack/signals` instead, or inject a DOM (e.g. happy-dom) before constructing custom elements.");class e extends HTMLElement{_dispose=null;getMountPoint(){return this}connectedCallback(){if(null!==this._dispose)return;const e=this.getMountPoint();this._dispose=p(t=>(e.appendChild(this.render()),t))}disconnectedCallback(){if(null===this._dispose)return;this._dispose(),this._dispose=null;const e=this.getMountPoint();for(;e.firstChild;)e.removeChild(e.firstChild)}}return j=e,j}const R=new Proxy(function(){},{construct:(e,t,n)=>Reflect.construct(T(),t,n),get:(e,t,n)=>Reflect.get(T(),t,n),has:(e,t)=>Reflect.has(T(),t),apply(){throw new TypeError("SignalsElement is a class and cannot be invoked without `new`/`extends`.")},getPrototypeOf:()=>Reflect.getPrototypeOf(T())});function F(e,t={}){if("undefined"==typeof document||"undefined"==typeof customElements)throw new Error("@wcstack/signals/dom: mountNode requires a DOM (document/customElements is not defined). It creates and connects a live element, so it is browser-only; inject a DOM (e.g. happy-dom) before calling, or keep headless work on `@wcstack/signals`.");const{attrs:n,parent:s,descriptor:r}=t,i=e.toLowerCase();if(!r&&!customElements.get(i))throw new Error(`mountNode: "<${i}>" is not defined. Import its defining module first (e.g. a side-effect \`import "@wcstack/<pkg>/auto"\` in this module — evaluation order then guarantees the definition). For a tag loaded by someone else, await customElements.whenDefined("${i}") or gate with <wcs-defined> before mounting. Binding a non-custom-element tag requires an explicit \`descriptor\`.`);const c=document.createElement(i);if(n)for(const[e,t]of Object.entries(n))!1!==t&&c.setAttribute(e,!0===t?"":String(t));const l=o(c,r);(s??document.body).appendChild(c);const a=()=>{l.dispose(),c.remove()};return{...l,el:c,unmount:a,dispose:a}}export{L as For,m as Fragment,P as Index,R as SignalsElement,o as bindNode,p as createRoot,T as createSignalsElement,e as effect,g as h,F as mountNode,t as onCleanup,b as render,u as signal};
2
2
  //# sourceMappingURL=dom.esm.min.js.map
package/dist/index.d.ts CHANGED
@@ -153,7 +153,11 @@ interface BoundNode<S extends NodeShape = DefaultNodeShape> {
153
153
  * Reactively write a declared input from a signal: an effect mirrors `source` into
154
154
  * `node[name]`. A same-value guard (`node[name] !== v`) skips redundant writes, so
155
155
  * a property whose write re-dispatches an event cannot feed back into an infinite
156
- * loop. Returns a disposer; the effect is also torn down by `dispose()`.
156
+ * loop. `undefined` follows the wc-bindable applier profile (A2): it is not written
157
+ * while the source has had no other value (the first evaluation, or after another
158
+ * `undefined`), so the element keeps its own initial state; after a value it is
159
+ * written, so the element restores that state. Returns a disposer; the effect is
160
+ * also torn down by `dispose()` (neither writes anything).
157
161
  */
158
162
  bindInput<K extends keyof InputsOf<S>>(name: K, source: ReadSignal<InputsOf<S>[K]>): () => void;
159
163
  /** Invoke a declared command on the node (imperative); args/return typed from the shape. */
package/dist/index.esm.js CHANGED
@@ -1,2 +1,2 @@
1
- export { D as DisposedError, b as bindNode, c as computed, a as createRoot, e as effect, f as flushSync, i as isDisposedError, n as nodeSource, o as onCleanup, r as resource, s as signal, d as streamResource } from './core-Db6kZPd_.esm.js';
1
+ export { D as DisposedError, b as bindNode, c as computed, a as createRoot, e as effect, f as flushSync, i as isDisposedError, n as nodeSource, o as onCleanup, r as resource, s as signal, d as streamResource } from './core-GLSUraaa.esm.js';
2
2
  //# sourceMappingURL=index.esm.js.map
@@ -1,2 +1,2 @@
1
- export{D as DisposedError,b as bindNode,c as computed,a as createRoot,e as effect,f as flushSync,i as isDisposedError,n as nodeSource,o as onCleanup,r as resource,s as signal,d as streamResource}from"./core-3xbxch0d.esm.min.js";
1
+ export{D as DisposedError,b as bindNode,c as computed,a as createRoot,e as effect,f as flushSync,i as isDisposedError,n as nodeSource,o as onCleanup,r as resource,s as signal,d as streamResource}from"./core-CUHdCgQ2.esm.min.js";
2
2
  //# sourceMappingURL=index.esm.min.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/signals",
3
- "version": "4.0.0-rc.8",
3
+ "version": "4.0.0-rc.9",
4
4
  "description": "Signals-based lightweight reactive core with async-IO resource adapters and a wc-bindable → signal bridge for Web Components. Zero runtime dependencies.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1,2 +0,0 @@
1
- function e(e){const t=globalThis.reportError;"function"==typeof t?t(e):console.error(e)}function t(){return!0===globalThis.__WCS_DEV__}const n=new Set;function s(e,s,o,...r){if(!t())return;const i=e+"\0"+s;n.has(i)||(n.add(i),console.warn(`[wcstack/signals][${e}] ${o}`,...r))}let o=null;function r(e){null!==o&&(o._sources.add(e),e._observers.add(o))}function i(e){for(const t of e._sources)t._observers.delete(e);e._sources.clear()}let a=null;function c(e){null!==a&&a._owned.push(e)}function d(e){const t=e._owned;for(let e=t.length-1;e>=0;e--)t[e]();t.length=0}let l=new Set,u=!1;const f=[];function h(){return f.pop()??new Set}function _(){u||(u=!0,queueMicrotask(b))}const p=1e3;function w(e){const t=e.filter(e=>!e._disposed),n=t.map((e,t)=>` #${t+1} ${e._devStack?e._devStack.split("\n").slice(1,3).join(" | ").trim():"(no stack)"}`);return`\n[wcstack/signals] dev diagnostics: ${t.length} effect(s) still re-running on the final pass (likely cycle participants):\n${n.join("\n")}`}function b(){u=!1;let n=0,s=null;for(;l.size>0;){if(++n>p)throw l.clear(),new Error("flushEffects: exceeded "+p+" iterations — likely a reactive cycle (an effect writes a signal it depends on with an ever-changing value)."+(s?w(s):""));const o=l;l=h(),t()&&(s=[...o]);for(const t of o)if(!t._disposed)try{y(t)}catch(t){e(t)}o.clear(),f.push(o)}}function v(){b()}function m(e,t){if(e._state<t){const n=0===e._state;e._state=t,n&&e._isEffect&&(l.add(e),_());for(const t of e._observers)m(t,1)}}function y(e){if(0!==e._state){if(1===e._state)for(const t of e._sources)if(t instanceof E&&(y(t),2===e._state))break;try{2===e._state&&e._update()}finally{e._state=0}}}class g{_observers=new Set;_value;_equals;constructor(e,t){this._value=e,this._equals=t}get(){return r(this),this._value}peek(){return this._value}set(e){if(!this._equals(this._value,e)){this._value=e;for(const e of this._observers)m(e,2)}}}class E{_observers=new Set;_sources=new Set;_state=2;_isEffect=!1;_fn;_value=void 0;_initialized=!1;_running=!1;_equals;constructor(e,t){this._fn=e,this._equals=t,c(()=>i(this))}get(){return r(this),y(this),this._value}peek(){return y(this),this._value}_update(){if(this._running)throw new Error("computed: circular dependency — the computed reads itself.");const e=this._value;i(this);const t=o;o=this,this._running=!0;try{this._value=this._fn()}finally{this._running=!1,o=t}if(this._initialized){if(!this._equals(e,this._value))for(const e of this._observers)e._state=2,e._isEffect&&!e._disposed&&(l.add(e),_())}else this._initialized=!0}}class S{_observers=new Set;_sources=new Set;_owned=[];_state=2;_isEffect=!0;_disposed=!1;_devStack=void 0;_fn;_cleanup=void 0;constructor(e){this._fn=e,t()&&(this._devStack=(new Error).stack),c(()=>this.dispose()),y(this)}_update(){this._runCleanup(),d(this),i(this);const e=o,t=a;o=this,a=this;try{this._cleanup=this._fn()}finally{o=e,a=t}}_runCleanup(){"function"==typeof this._cleanup&&(this._cleanup(),this._cleanup=void 0)}dispose(){this._disposed||(this._disposed=!0,this._runCleanup(),d(this),i(this),l.delete(this))}}function k(e,t=Object.is){return new g(e,t)}function $(e,t=Object.is){return new E(e,t)}function C(e){return t()&&null===a&&s("UNOWNED_EFFECT","","effect created with no owner — it will never be disposed and may leak. Wrap it in createRoot(dispose => …) (or a SignalsElement render()) and dispose on teardown."),new S(e)}function N(e){const t={_owned:[]};let n=!1;const s=()=>{n||(n=!0,d(t))},r=a,i=o;a=t,o=null;try{return e(s)}catch(e){throw s(),e}finally{a=r,o=i}}function L(){return null!==a}function q(e){null===a&&s("ORPHAN_CLEANUP","","onCleanup called with no owner — the cleanup will never run. Call it inside an effect, createRoot, or a SignalsElement render()."),c(e)}const R=Symbol("bindNode.disposed");class I extends Error{[R]=!0}function T(e){return"object"==typeof e&&null!==e&&!0===e[R]}function j(e,t){return e[t]}const A=e=>e.detail;function x(e,t){const n=e,s=t??n.constructor.wcBindable;if(!s)throw new Error("bindNode: no wc-bindable descriptor provided and none found on target.constructor.wcBindable");const o=new Map(s.properties.map(e=>[e.name,e])),r=new Set((s.inputs??[]).map(e=>e.name)),i=new Set((s.commands??[]).map(e=>e.name)),a={},c=[];let d=!1;const l=(e,t)=>{if(d)throw new I(`bindNode.${e}: "${t}" called after dispose (the adapter is inert).`)};for(const e of s.properties){const t=k(j(n,e.name));a[e.name]=t;const s=e.getter??A,o=e=>{t.set(s(e))};n.addEventListener(e.event,o),c.push(()=>n.removeEventListener(e.event,o)),t.set(j(n,e.name))}return{signals:a,on(e,t={}){l("on",e);const s=o.get(e);if(!s)throw new Error(`bindNode.on: "${e}" is not a declared property on this node.`);const r=t.fold??((e,t)=>t),i=k(t.initial,()=>!1),a=s.getter??A,d=e=>{const t=a(e);i.set(r(i.peek(),t))};return n.addEventListener(s.event,d),c.push(()=>n.removeEventListener(s.event,d)),i},set(e,t){if(l("set",e),!r.has(e))throw new Error(`bindNode.set: "${e}" is not a declared input on this node.`);n[e]=t},bindInput(e,t){if(l("bindInput",e),!r.has(e))throw new Error(`bindNode.bindInput: "${e}" is not a declared input on this node.`);const s=C(()=>{const s=t.get();n[e]!==s&&(n[e]=s)});return c.push(()=>s.dispose()),()=>s.dispose()},command(e,...t){if(l("command",e),!i.has(e))throw new Error(`bindNode.command: "${e}" is not a declared command on this node.`);if("function"!=typeof n[e])throw new TypeError(`bindNode.command: "${e}" is declared but not a function on the node.`);return n[e](...t)},bindCommand(e,t,s){if(l("bindCommand",e),!i.has(e))throw new Error(`bindNode.bindCommand: "${e}" is not a declared command on this node.`);if("function"!=typeof n[e])throw new TypeError(`bindNode.bindCommand: "${e}" is declared but not a function on the node.`);let o=!1;const r=C(()=>{const r=t.get();if(!o)return void(o=!0);const i=s?s(r):[r];n[e](...i)});return c.push(()=>r.dispose()),()=>r.dispose()},dispose(){if(!d){d=!0;for(const e of c)e()}}}}function z(t,n,s={}){const o=s.abort??"abort";return(s,r)=>(r.addEventListener("abort",()=>{try{t.command(o)}catch(t){T(t)||e(t)}},{once:!0}),n(t,s))}function O(e,t={}){const n=k(t.initial),s=k(!1),o=k(null);let r=null;const i=C(()=>{const i=t.args?t.args():void 0;r?.abort();const a=new AbortController;let c;r=a,s.set(!0),o.set(null);try{c=e(i,a.signal)}catch(e){return o.set(e),void s.set(!1)}Promise.resolve(c).then(e=>{a.signal.aborted||(n.set(e),s.set(!1))},e=>{a.signal.aborted||(o.set(e),s.set(!1))})}),a=()=>{r?.abort(),i.dispose()};return q(a),{value:n,loading:s,error:o,dispose:a}}function D(e,t={}){const n=t.fold??((e,t)=>t),s=k(t.initial),o=k("idle"),r=k(null);let i=null;const a=C(()=>{const a=t.args?t.args():void 0;i?.abort();const c=new AbortController;i=c,s.set(t.initial),r.set(null),o.set("active"),async function(e,t,n,s,o,r,i){let a=null,c=null;const d=()=>{if(a&&a!==c){c=a;try{a.return?.()?.then?.(void 0,()=>{})}catch{}}};n.addEventListener("abort",d,{once:!0});try{const i=await e(t,n);if(a=function(e,t){if("function"==typeof e?.getReader)return async function*(e,t){const n=e.getReader(),s=()=>{n.cancel().catch(()=>{})};t.addEventListener("abort",s,{once:!0});try{for(;;){const{done:e,value:t}=await n.read();if(e)return;yield t}}finally{t.removeEventListener("abort",s),n.releaseLock()}}(e,t);if("function"==typeof e?.[Symbol.asyncIterator])return e;throw new TypeError("streamResource: source must return an AsyncIterable or a ReadableStream (got neither).")}(i,n)[Symbol.asyncIterator](),n.aborted)return void d();for(;;){const e=await a.next();if(e.done)break;if(n.aborted)return;o.set(s(o.peek(),e.value))}if(n.aborted)return;r.set("done")}catch(e){if(n.aborted)return;i.set(e),r.set("error")}finally{n.removeEventListener("abort",d)}}(e,a,c.signal,n,s,o,r)}),c=()=>{i?.abort(),a.dispose()};return q(c),{value:s,status:o,error:r,dispose:c}}export{I as D,N as a,x as b,$ as c,D as d,C as e,v as f,t as g,L as h,T as i,z as n,q as o,O as r,k as s,s as w};
2
- //# sourceMappingURL=core-3xbxch0d.esm.min.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"core-3xbxch0d.esm.min.js","sources":["../src/reportError.ts","../src/dev.ts","../src/reactive.ts","../src/bindNode.ts","../src/resource.ts","../src/streamResource.ts"],"sourcesContent":["// Internal error-reporting policy (NOT part of the public API; not re-exported\n// from exports.ts). Single source of truth for \"surface an error without crashing\".\n//\n// Delegates to the platform `reportError` when present — it dispatches a window\n// \"error\" event / logs to the console without aborting the current task — and falls\n// back to `console.error` otherwise. It NEVER re-throws (re-throwing would abort the\n// caller's drain / abort handler) and NEVER swallows silently (that would hide bugs).\nexport function reportError(err: unknown): void {\n const r = (globalThis as { reportError?: (e: unknown) => void }).reportError;\n if (typeof r === \"function\") {\n r(err);\n } else {\n console.error(err);\n }\n}\n","// Opt-in development diagnostics (NOT part of the public API; not re-exported from\n// exports.ts). Surfaces \"silent failure\" modes — duplicate/unkeyed list rows,\n// un-owned effects that leak, no-op onCleanup, reactive cycles — that production\n// otherwise swallows.\n//\n// DEFAULT OFF. Nothing here runs unless the consumer flips `globalThis.__WCS_DEV__`\n// to `true` BEFORE the failing operation. `isDev()` reads the flag at CALL TIME (not\n// module-load time), so a page can toggle it on for a debugging session without a\n// rebuild. See README \"Development mode\".\n//\n// TREE-SHAKING: the package sets `\"sideEffects\": false`, and every export here is a\n// pure function with no module-top-level side effects (the dedupe cache is a lazily-\n// populated Set, not eager work). A production bundler that never references warnDev\n// can drop this module entirely; a bundler that does keep it still pays nothing at\n// runtime because `warnDev` short-circuits on the `isDev()` guard before touching the\n// dedupe Set or formatting a message.\n\n/** Stable warning codes, so docs/tests can reference a warning without matching prose. */\nexport type DevWarningCode =\n | \"DUPLICATE_KEY\"\n | \"NON_PRIMITIVE_KEY\"\n | \"NULLISH_KEY\"\n | \"UNOWNED_EFFECT\"\n | \"UNOWNED_INSERT\"\n | \"ORPHAN_CLEANUP\"\n | \"REACTIVE_CYCLE\";\n\n/**\n * True only when the consumer has opted in via `globalThis.__WCS_DEV__ === true`.\n * Read at call time so the flag can be toggled at runtime. Kept tiny and pure so a\n * production bundle that strips dev branches pays nothing.\n */\nexport function isDev(): boolean {\n return (globalThis as { __WCS_DEV__?: boolean }).__WCS_DEV__ === true;\n}\n\n// Dedupe set: a given (code + dedupe-key) warning is emitted at most once, so a\n// per-row or per-flush failure does not flood the console. Lazily populated; empty\n// and inert until the first dev warning actually fires.\nconst emitted = new Set<string>();\n\n/**\n * Emit a development warning ONCE per `(code, dedupeKey)`, only when dev mode is on.\n * In production (`isDev()` false) this is a no-op that returns before any work.\n *\n * @param code stable warning code (see DevWarningCode)\n * @param dedupeKey extra discriminator folded into the dedupe identity (e.g. the\n * duplicate key value). Same code + same dedupeKey warns once.\n * @param message human-readable explanation; `detail` is appended for context.\n */\nexport function warnDev(\n code: DevWarningCode,\n dedupeKey: string,\n message: string,\n ...detail: unknown[]\n): void {\n if (!isDev()) {\n return;\n }\n const id = code + \"\u0000\" + dedupeKey;\n if (emitted.has(id)) {\n return;\n }\n emitted.add(id);\n console.warn(`[wcstack/signals][${code}] ${message}`, ...detail);\n}\n\n/**\n * Clear the dedupe cache. Test-only seam so each test can re-exercise a warning that\n * is otherwise emitted once per process. Not meant for application use.\n */\nexport function __resetDevWarnings(): void {\n emitted.clear();\n}\n","// Minimal signal core (v1 design notes).\n//\n// A self-contained, zero-dependency reactive primitive whose public API is\n// shaped after the TC39 Signals proposal (State / Computed / effect). The intent\n// is NOT to ship signal-polyfill as a runtime dependency, but to keep an in-house\n// implementation that can later be swapped for the native one without changing\n// call sites. See docs/signals-state-design.md §2 (the \"case C\" decision).\n//\n// Reactivity model: three-color marking (CLEAN / CHECK / DIRTY), pull-validated\n// (after Reactively / Solid). docs §8 (c).\n// - A signal write marks its direct observers DIRTY and their transitive\n// observers CHECK. Effects scheduled on the CLEAN→stale transition run on a\n// coalesced microtask (docs §5-3).\n// - On read/run, `updateIfNecessary` validates a CHECK node by refreshing its\n// computed sources first; it recomputes only if a source actually changed.\n// - EQUALITY SHORT-CIRCUIT: when a computed recomputes to an equal value it does\n// NOT mark its observers DIRTY, so downstream effects/computeds skip work.\n// - dependencies are re-tracked on every run, so conditional deps are pruned.\n\nimport { reportError } from \"./reportError.js\";\nimport { isDev, warnDev } from \"./dev.js\";\n\nexport type Cleanup = () => void;\nexport type Equals<T> = (a: T, b: T) => boolean;\n\nexport interface ReadSignal<T> {\n /** Read the value AND register the current observer as a dependent. */\n get(): T;\n /** Read the value WITHOUT tracking (no dependency edge created). */\n peek(): T;\n}\n\nexport interface WriteSignal<T> extends ReadSignal<T> {\n set(value: T): void;\n}\n\nexport interface EffectHandle {\n dispose(): void;\n}\n\n// Node freshness. CLEAN: up to date. CHECK: a transitive source may have changed\n// — must validate before use. DIRTY: a direct source changed — must recompute.\n// Typed as `number` (not literals) on purpose: `_state` is mutated through\n// aliasing during `updateIfNecessary`, so literal narrowing would wrongly flag\n// the `=== DIRTY` re-check after a source update.\nconst CLEAN: number = 0;\nconst CHECK: number = 1;\nconst DIRTY: number = 2;\n\ninterface Observable {\n _observers: Set<Computation>;\n}\n\ninterface Computation extends Observable {\n _state: number;\n _sources: Set<Observable>;\n _isEffect: boolean;\n _update(): void;\n}\n\n// --- tracking context -------------------------------------------------------\n\nlet currentObserver: Computation | null = null;\n\nfunction track(source: Observable): void {\n if (currentObserver !== null) {\n currentObserver._sources.add(source);\n source._observers.add(currentObserver);\n }\n}\n\nfunction untrack(node: Computation): void {\n for (const source of node._sources) {\n source._observers.delete(node);\n }\n node._sources.clear();\n}\n\n// --- ownership (disposal tree) ----------------------------------------------\n//\n// Orthogonal to dependency tracking. An \"owner\" collects the disposers of the\n// computations (effects, nested cleanups, child roots) created during its run.\n// An effect IS an owner: it owns whatever was created while it ran, and disposes\n// those children before it re-runs or is disposed. This is what lets the DOM\n// layer rebuild a subtree without leaking the previous subtree's effects\n// (docs §8 (d)). Modeled after Solid's reactive ownership.\n\ntype Disposer = () => void;\n\ninterface Owner {\n _owned: Disposer[];\n}\n\nlet currentOwner: Owner | null = null;\n\nfunction registerDisposer(disposer: Disposer): void {\n if (currentOwner !== null) {\n currentOwner._owned.push(disposer);\n }\n}\n\nfunction disposeOwned(owner: Owner): void {\n // Dispose in reverse creation order (children before parents, last-in-first-out).\n const owned = owner._owned;\n for (let i = owned.length - 1; i >= 0; i--) {\n owned[i]();\n }\n owned.length = 0;\n}\n\n// --- effect scheduling (microtask coalesce) ---------------------------------\n\n// The queue NEW work accumulates into (markStale / a computed's DIRTY promotion add\n// here). Each drain pass takes the current queue to iterate and installs a fresh\n// empty one in its place, so effects queued WHILE draining land in the next pass.\nlet pendingEffects = new Set<EffectNode>();\nlet flushScheduled = false;\n\n// Double-buffer free list: rather than `[...pendingEffects]` (a Set→Array copy) per\n// pass, a drain SWAPS the live queue with a recycled empty Set and iterates that Set\n// directly. The swapped-out Set must be a fresh buffer that re-entrancy cannot\n// clobber — a nested flushSync swaps `pendingEffects` again, so the outer pass keeps\n// iterating its OWN buffer (the same isolation the old local-array copy gave). After\n// draining, the buffer is cleared and returned to the pool for reuse.\nconst bufferPool: Set<EffectNode>[] = [];\n\nfunction takeBuffer(): Set<EffectNode> {\n return bufferPool.pop() ?? new Set<EffectNode>();\n}\n\nfunction schedule(): void {\n if (flushScheduled) {\n return;\n }\n flushScheduled = true;\n queueMicrotask(flushEffects);\n}\n\n// Upper bound on drain iterations. A well-behaved graph settles in a handful of\n// passes; exceeding this means an effect keeps dirtying its own dependency with a\n// changing value (a reactive cycle), which would otherwise hang the page. We bail\n// loudly instead of spinning forever (docs §5-3).\nconst MAX_FLUSH_ITERATIONS = 1000;\n\n// Dev-only: build a \"what was still running\" appendix for the cycle bail. Uses the\n// creation stack captured per effect (dev only) so the message points at WHERE the\n// looping effects were defined. Production never calls this (guarded by the dev\n// `lastBatch`), so the stack-capture cost never lands on shipped code.\nfunction describeCycle(batch: EffectNode[]): string {\n const live = batch.filter((e) => !e._disposed);\n const lines = live.map((e, i) => {\n const where = e._devStack ? e._devStack.split(\"\\n\").slice(1, 3).join(\" | \").trim() : \"(no stack)\";\n return ` #${i + 1} ${where}`;\n });\n return (\n `\\n[wcstack/signals] dev diagnostics: ${live.length} effect(s) still re-running on the ` +\n `final pass (likely cycle participants):\\n${lines.join(\"\\n\")}`\n );\n}\n\nfunction flushEffects(): void {\n flushScheduled = false;\n // Drain in a loop: an effect may, while running, dirty a signal that queues\n // further effects. Coalescing collapses multiple writes in one tick into a\n // single run per effect (docs §5-3).\n let iterations = 0;\n // Dev-only: the effects run on the most recent pass, so a cycle bail can point at\n // the culprits. Lazily allocated only in dev (no cost in production).\n let lastBatch: EffectNode[] | null = null;\n while (pendingEffects.size > 0) {\n if (++iterations > MAX_FLUSH_ITERATIONS) {\n // Drop the runaway queue so a single bad effect cannot wedge every later\n // flush, then surface the bug.\n pendingEffects.clear();\n throw new Error(\n \"flushEffects: exceeded \" +\n MAX_FLUSH_ITERATIONS +\n \" iterations — likely a reactive cycle (an effect writes a signal it depends on with an ever-changing value).\" +\n (lastBatch ? describeCycle(lastBatch) : \"\"),\n );\n }\n // Swap the live queue out for a fresh empty buffer, then iterate the swapped-out\n // Set directly (no array copy). New work queued by an effect's body lands in the\n // new `pendingEffects` and is picked up on the next pass — same coalescing as\n // before. `batch` is a local reference, so a re-entrant flushSync (which swaps\n // `pendingEffects` again) cannot clobber this pass's iteration.\n const batch = pendingEffects;\n pendingEffects = takeBuffer();\n if (isDev()) {\n // Record the effects on this pass; on the FINAL (overflowing) pass these are\n // the ones still re-queuing themselves — the cycle participants.\n lastBatch = [...batch];\n }\n for (const node of batch) {\n if (!node._disposed) {\n // Isolate each effect: a throw from one effect's body must NOT abort the\n // batch and silently strand its already-dequeued siblings. We hand the\n // error to reportError (which does not re-throw, so the drain continues)\n // and move on. updateIfNecessary guarantees the node is settled to CLEAN\n // even on throw, so it is not left stuck DIRTY.\n try {\n updateIfNecessary(node);\n } catch (err) {\n reportError(err);\n }\n }\n }\n // Recycle this pass's buffer. Clear AFTER iterating so a node is never dropped\n // mid-walk; the Set is now free for a later takeBuffer().\n batch.clear();\n bufferPool.push(batch);\n }\n}\n\n// Effect/computed errors are surfaced via the shared `reportError` policy\n// (./reportError.ts), called synchronously from the drain loop. It does NOT\n// re-throw (that would abort the drain) and does NOT swallow (that would hide the\n// bug). Re-entrancy is safe: the only re-scheduling path (markStale) defers to a\n// microtask, so reporting here cannot recurse into the running flush.\n\n/**\n * Synchronously flush queued effects. Provided for tests and for callers that\n * need DOM updates applied before reading the DOM back. In normal use effects\n * settle on their own microtask.\n */\nexport function flushSync(): void {\n // Do NOT touch flushScheduled here: a microtask may already be queued. Just\n // drain. flushEffects resets the flag itself; if a queued microtask still fires\n // afterwards it finds an empty queue and is a no-op. Touching the flag from here\n // would let a second flushSync race the queued microtask into a double drain.\n flushEffects();\n}\n\n// --- marking & validation ---------------------------------------------------\n\nfunction markStale(node: Computation, level: number): void {\n if (node._state < level) {\n const wasClean = node._state === CLEAN;\n node._state = level;\n if (wasClean && node._isEffect) {\n pendingEffects.add(node as EffectNode);\n schedule();\n }\n // Observers can only become CHECK from us: they need to re-validate, but\n // whether they truly changed depends on our recomputed value.\n for (const observer of node._observers) {\n markStale(observer, CHECK);\n }\n }\n}\n\nfunction updateIfNecessary(node: Computation): void {\n if (node._state === CLEAN) {\n return;\n }\n if (node._state === CHECK) {\n // A transitive source might have changed. Refresh computed sources; one of\n // them may set us DIRTY (via the equality check in its _update).\n // NOTE: this scans ALL sources each validation (O(sources)); the standard\n // pull-based scheme (Reactively/Solid) does the same. Intentional for v1 —\n // a future optimization could track per-source dirtiness to scan less.\n for (const source of node._sources) {\n if (source instanceof ComputedNode) {\n updateIfNecessary(source);\n if (node._state === DIRTY) {\n break;\n }\n }\n }\n }\n // Settle to CLEAN in a finally: if _update() (a user fn / equals) throws, the\n // node must NOT be left stuck DIRTY, otherwise every later peek/get would\n // re-run the throwing fn forever. The error propagates to the caller (a flush\n // isolates it per-effect; a direct get/peek surfaces it to that caller).\n try {\n if (node._state === DIRTY) {\n node._update();\n }\n } finally {\n node._state = CLEAN;\n }\n}\n\n// --- signal -----------------------------------------------------------------\n\nclass SignalNode<T> implements Observable, WriteSignal<T> {\n _observers = new Set<Computation>();\n private _value: T;\n private _equals: Equals<T>;\n\n constructor(value: T, equals: Equals<T>) {\n this._value = value;\n this._equals = equals;\n }\n\n get(): T {\n track(this);\n return this._value;\n }\n\n peek(): T {\n return this._value;\n }\n\n set(next: T): void {\n if (this._equals(this._value, next)) {\n return;\n }\n this._value = next;\n // No copy needed: markStale only schedules effects (on a microtask) and marks\n // observers — it never runs user code synchronously, so this set is not\n // mutated during the walk. Iterating it directly avoids a per-write allocation.\n for (const observer of this._observers) {\n markStale(observer, DIRTY);\n }\n }\n}\n\n// --- computed ---------------------------------------------------------------\n\nclass ComputedNode<T> implements Computation, ReadSignal<T> {\n _observers = new Set<Computation>();\n _sources = new Set<Observable>();\n _state = DIRTY; // never computed yet\n _isEffect = false;\n private _fn: () => T;\n private _value: T | undefined = undefined;\n private _initialized = false;\n private _running = false;\n private _equals: Equals<T>;\n\n constructor(fn: () => T, equals: Equals<T>) {\n this._fn = fn;\n this._equals = equals;\n // Be owned by the enclosing scope (a parent effect or createRoot). When that\n // scope tears down, untrack this computed so it is removed from its sources'\n // observer sets — otherwise a computed created inside an effect that re-runs\n // would leak a stale ComputedNode into each source on every run (docs §8 (d)).\n // Lazy + no children: teardown is just untrack, no cleanup/owned to dispose.\n registerDisposer(() => untrack(this));\n }\n\n get(): T {\n // Order matters: track BEFORE updateIfNecessary. The current observer must be\n // registered against THIS computed regardless of whether validation recomputes —\n // and recomputation swaps currentObserver to this node, so tracking first records\n // the edge under the OUTER observer (the caller), not this computed's own run.\n track(this);\n updateIfNecessary(this);\n return this._value as T;\n }\n\n peek(): T {\n updateIfNecessary(this);\n return this._value as T;\n }\n\n _update(): void {\n // Cycle guard: if _fn (directly or transitively) reads this computed, get()\n // would recurse into _update and blow the stack with an opaque RangeError.\n // Detect the self-reference and throw a clear error instead. (The effect-level\n // MAX_FLUSH_ITERATIONS guard does not cover this synchronous self-recursion.)\n if (this._running) {\n throw new Error(\"computed: circular dependency — the computed reads itself.\");\n }\n const previous = this._value;\n untrack(this);\n const prevObserver = currentObserver;\n currentObserver = this;\n this._running = true;\n try {\n this._value = this._fn();\n } finally {\n this._running = false;\n currentObserver = prevObserver;\n }\n // Equality short-circuit: only propagate when the value actually changed.\n // Our observers are already CHECK (from the originating markStale); promote\n // them to DIRTY so they recompute, otherwise leave them to settle CLEAN. Skip\n // on the first computation: there is no prior value to compare, and a custom\n // `equals` must not be invoked with the undefined sentinel.\n if (!this._initialized) {\n this._initialized = true;\n } else if (!this._equals(previous as T, this._value as T)) {\n for (const observer of this._observers) {\n observer._state = DIRTY;\n // Normally this computed is refreshed from inside flushEffects while\n // validating an already-scheduled effect, so the observer is in the queue\n // already. But if it is refreshed out-of-band (e.g. a `peek()` outside a\n // flush) an effect observer could otherwise be promoted to DIRTY without\n // being queued, and never re-run. Re-schedule defensively to keep the\n // \"DIRTY effect always runs\" invariant regardless of refresh path.\n if (observer._isEffect && !(observer as EffectNode)._disposed) {\n pendingEffects.add(observer as EffectNode);\n schedule();\n }\n }\n }\n }\n}\n\n// --- effect -----------------------------------------------------------------\n\nclass EffectNode implements Computation, Owner {\n _observers = new Set<Computation>(); // effects are not observed; kept for the interface\n _sources = new Set<Observable>();\n _owned: Disposer[] = [];\n _state = DIRTY;\n _isEffect = true;\n _disposed = false;\n // Dev-only creation stack, used to point a reactive-cycle bail at where the looping\n // effect was defined. Captured only when dev mode is on at construction time, so\n // production effects pay nothing for it.\n _devStack: string | undefined = undefined;\n private _fn: () => Cleanup | void;\n private _cleanup: Cleanup | void = undefined;\n\n constructor(fn: () => Cleanup | void) {\n this._fn = fn;\n if (isDev()) {\n this._devStack = new Error().stack;\n }\n // Be owned by the enclosing owner (a parent effect or a createRoot), so a\n // parent teardown disposes this effect.\n registerDisposer(() => this.dispose());\n updateIfNecessary(this); // initial run (state starts DIRTY)\n }\n\n _update(): void {\n this._runCleanup();\n disposeOwned(this); // tear down children created in the previous run\n untrack(this);\n const prevObserver = currentObserver;\n const prevOwner = currentOwner;\n currentObserver = this;\n currentOwner = this; // children created during the run are owned by this effect\n try {\n this._cleanup = this._fn();\n } finally {\n currentObserver = prevObserver;\n currentOwner = prevOwner;\n }\n }\n\n private _runCleanup(): void {\n if (typeof this._cleanup === \"function\") {\n this._cleanup();\n this._cleanup = undefined;\n }\n }\n\n dispose(): void {\n if (this._disposed) {\n return;\n }\n this._disposed = true;\n this._runCleanup();\n disposeOwned(this);\n untrack(this);\n pendingEffects.delete(this);\n }\n}\n\n// --- public API -------------------------------------------------------------\n\nexport function signal<T>(initial: T, equals: Equals<T> = Object.is): WriteSignal<T> {\n return new SignalNode(initial, equals);\n}\n\nexport function computed<T>(fn: () => T, equals: Equals<T> = Object.is): ReadSignal<T> {\n return new ComputedNode(fn, equals);\n}\n\nexport function effect(fn: () => Cleanup | void): EffectHandle {\n if (isDev() && currentOwner === null) {\n // No enclosing owner: nothing will ever dispose this effect, so it (and the\n // subscriptions it builds) leak. Legitimate top-level effects should be mounted\n // under createRoot / SignalsElement and disposed on teardown. Dev-only warn.\n warnDev(\n \"UNOWNED_EFFECT\",\n \"\",\n \"effect created with no owner — it will never be disposed and may leak. \" +\n \"Wrap it in createRoot(dispose => …) (or a SignalsElement render()) and \" +\n \"dispose on teardown.\",\n );\n }\n return new EffectNode(fn);\n}\n\n/**\n * Run `fn` inside a fresh ownership scope and return its result. Every effect (or\n * nested cleanup) created during `fn` — directly or transitively — is owned by\n * this root; calling the `dispose` passed to `fn` tears them all down.\n *\n * The root is detached: it is NOT auto-disposed by an enclosing owner. The caller\n * holds `dispose` (e.g. a custom element disposes it in disconnectedCallback).\n *\n * If `fn` throws, any effects/cleanups it created BEFORE the throw are disposed\n * before the error propagates — a caller that never received `dispose` (because the\n * call threw) cannot leak the half-built scope.\n */\nexport function createRoot<T>(fn: (dispose: () => void) => T): T {\n const owner: Owner = { _owned: [] };\n let disposed = false;\n const dispose = (): void => {\n if (disposed) {\n return;\n }\n disposed = true;\n disposeOwned(owner);\n };\n const prevOwner = currentOwner;\n const prevObserver = currentObserver;\n currentOwner = owner;\n // Detach the dependency-tracking context too, not just ownership. A root is a\n // fresh reactive boundary: signals read SYNCHRONOUSLY while `fn` builds the scope\n // must not register the OUTER observer as their dependent (Solid's createRoot\n // clears both owner and listener). Without this, a For/Index `each` that reads\n // `index()`/`item()` synchronously in its body would track the outer reconcile\n // effect against the row's idx/item signal, so reordering re-dirties the reconcile\n // effect — a self-loop the equality/MAX_FLUSH guards only paper over.\n currentObserver = null;\n try {\n return fn(dispose);\n } catch (err) {\n dispose(); // tear down whatever fn built before it threw\n throw err;\n } finally {\n currentOwner = prevOwner;\n currentObserver = prevObserver;\n }\n}\n\n/**\n * True when there is a current ownership scope (an enclosing effect or createRoot).\n * Dev-diagnostics seam for the DOM layer to flag un-owned reactive insertions\n * without re-implementing owner tracking. NOT part of the public API.\n */\nexport function hasOwner(): boolean {\n return currentOwner !== null;\n}\n\n/**\n * Register a teardown callback with the current owner. Runs when the owning effect\n * re-runs or is disposed, or when the enclosing root is disposed. A no-op when\n * there is no current owner.\n */\nexport function onCleanup(fn: () => void): void {\n if (currentOwner === null) {\n // No owner → registerDisposer drops `fn` on the floor: the cleanup never runs.\n // Silent in production; flag it in dev so the missing teardown is visible.\n warnDev(\n \"ORPHAN_CLEANUP\",\n \"\",\n \"onCleanup called with no owner — the cleanup will never run. \" +\n \"Call it inside an effect, createRoot, or a SignalsElement render().\",\n );\n }\n registerDisposer(fn);\n}\n","// wc-bindable → signal adapter. The crux of the design.\n//\n// Any async-IO node in wcstack speaks the wc-bindable protocol:\n// - properties: outputs — the node dispatches `event` on change; the value is\n// read via `getter(event)`, defaulting to `e.detail` (wc-bindable\n// SPEC § Default Getter). The property `name` is read only for the\n// initial seed, where no event exists yet (SPEC § Initial Sync).\n// - inputs: settable surface — write `node[name] = value`.\n// - commands: invocable methods — call `node[name](...args)`.\n// The node has NO idea whether the observer behind the binding is a proxy (state)\n// or a signal. So a single adapter that maps these surfaces onto signals makes every\n// existing node plug into the signal core unchanged. See docs/signals-state-design.md\n// §3, and the four mappings of §3-1:\n//\n// element → signal | property (latest snapshot) → read signal [`signals`]\n// element → signal | event-token (per-emit stream) → folded signal [`on`]\n// signal → element | input (write-back) → effect → prop [`bindInput`]\n// signal → element | command-token (start/cancel) → emit on change [`bindCommand`]\n//\n// `signals` is the STATE view of a property (equality-guarded — same value = no\n// update). `on` is the OCCURRENCE view of the same event (a stream — every emit\n// updates, even with an equal value), folded latest-by-default.\n\nimport { signal, effect, WriteSignal, ReadSignal } from \"./reactive.js\";\nimport { reportError } from \"./reportError.js\";\nimport type { ResourceSource } from \"./resource.js\";\n\nexport interface WcBindableProperty<V = unknown> {\n readonly name: string;\n readonly event: string;\n readonly getter?: (event: Event) => V;\n}\n\n// Structurally a SUBSET of the real `IWcBindable` (which additionally carries\n// `protocol` / `version`, and richer input/command entries). A real descriptor is\n// therefore assignable to this type — see __tests__/bindNode.compat.test.ts.\nexport interface WcBindableDescriptor {\n readonly properties: readonly WcBindableProperty[];\n readonly inputs?: readonly { readonly name: string }[];\n readonly commands?: readonly { readonly name: string }[];\n}\n\n// --- typed node shape (D1) ---------------------------------------------------\n//\n// `bindNode` is the package's centerpiece, yet without a type argument every output\n// was `ReadSignal<unknown>` and every `set`/`command` took `unknown` — the feature\n// was effectively untyped in consumer code. `NodeShape` is an OPT-IN description of a\n// node's reactive surface that drives the return type: declare the property value\n// types, the input value types, and the command signatures ONCE, and `bound.signals`,\n// `on`, `set`, `bindInput`, `command`, and `bindCommand` are all typed from it.\n//\n// interface FetchShape extends NodeShape {\n// signals: { value: Person[]; loading: boolean };\n// inputs: { url: string };\n// commands: { fetch: (url: string) => Promise<Person[]>; abort: () => void };\n// }\n// const bound = bindNode<FetchShape>(el, desc);\n// bound.signals.value.get(); // ReadSignal<Person[]> — key & type checked\n// bound.set(\"url\", \"/api/people\"); // string enforced\n// bound.command(\"fetch\", \"/api\"); // arg/return inferred\n//\n// The runtime is UNCHANGED — these types are erased; the adapter still validates names\n// against the descriptor at runtime. The default shape below keeps every existing\n// untyped call site compiling exactly as before (all-`unknown`, string keys).\n// Open command map: a command name → any call signature. `(...args: any[])` (NOT\n// `never[]`) is the constraint a USER shape's commands must satisfy — and, crucially,\n// the OPEN fallback used when a shape omits `commands`. `Parameters<(...args: any[])\n// => unknown>` is `any[]`, so an untyped `command(\"run\", \"/api\")` accepts any args,\n// preserving the pre-generic behaviour (symmetric with `inputs` → `unknown`). A shape\n// that DECLARES a precise signature (e.g. `(url: string) => Promise<X>`) still gets\n// exact arg/return checking on the typed path — the loose constraint only widens the\n// DEFAULT, never a declared command. (`no-explicit-any` is disabled for this package.)\ntype OpenCommandMap = Record<string, (...args: any[]) => unknown>;\n\nexport interface NodeShape {\n /** propertyName → latest-snapshot value type (what `signals[name]` / `on(name)` carry). */\n signals?: Record<string, unknown>;\n /** inputName → settable value type (what `set` / `bindInput` accept). */\n inputs?: Record<string, unknown>;\n /** commandName → its call signature (what `command` / `bindCommand` invoke). */\n commands?: OpenCommandMap;\n}\n\n// Back-compat default: a fully-open shape so `bindNode(target, desc)` with no type\n// argument behaves as before (signals keyed by arbitrary string → unknown; any\n// input/command name; unknown values; any command args). Stronger typing is opt-in\n// via the type arg.\nexport interface DefaultNodeShape extends NodeShape {\n signals: Record<string, unknown>;\n inputs: Record<string, unknown>;\n commands: OpenCommandMap;\n}\n\n// Resolve the three sub-maps with sane fallbacks when a shape omits one of them.\ntype SignalsOf<S extends NodeShape> = S[\"signals\"] extends Record<string, unknown>\n ? S[\"signals\"]\n : Record<string, unknown>;\ntype InputsOf<S extends NodeShape> = S[\"inputs\"] extends Record<string, unknown>\n ? S[\"inputs\"]\n : Record<string, unknown>;\ntype CommandsOf<S extends NodeShape> = S[\"commands\"] extends OpenCommandMap ? S[\"commands\"] : OpenCommandMap;\n\n/** Options for `on` — how each event emission folds into the stream signal. */\nexport interface EventStreamOptions<T, C> {\n /** Combine the running value with each emission. Default: latest (replace). */\n fold?: (acc: T | undefined, chunk: C) => T;\n /** Seed value before the first emission. */\n initial?: T;\n}\n\nexport interface BoundNode<S extends NodeShape = DefaultNodeShape> {\n /**\n * Output properties as read-only signals (latest value), keyed by property name and\n * typed from the node shape: `bound.signals.value.get()` carries the declared value\n * type (no longer `unknown`). Keys are the declared property names.\n */\n readonly signals: { readonly [K in keyof SignalsOf<S>]: ReadSignal<SignalsOf<S>[K]> };\n /**\n * An event-token STREAM for a declared property's event: a read signal that folds\n * every emission (latest by default). Unlike `signals[prop]`, it updates on EVERY\n * emit — even when the derived value is equal — because it models occurrences, not\n * state. The chunk per emit is `getter(event)` if the property declares one, else\n * the property's current value.\n *\n * With no fold the value type defaults to the property's declared snapshot type; a\n * `fold` may reshape it (explicit `T`/`C` type args override).\n */\n on<K extends keyof SignalsOf<S>>(prop: K): ReadSignal<SignalsOf<S>[K] | undefined>;\n on<T, C = SignalsOf<S>[keyof SignalsOf<S>]>(\n prop: keyof SignalsOf<S>,\n options: EventStreamOptions<T, C>,\n ): ReadSignal<T | undefined>;\n /** Write a declared input on the node (imperative); value type checked against the shape. */\n set<K extends keyof InputsOf<S>>(name: K, value: InputsOf<S>[K]): void;\n /**\n * Reactively write a declared input from a signal: an effect mirrors `source` into\n * `node[name]`. A same-value guard (`node[name] !== v`) skips redundant writes, so\n * a property whose write re-dispatches an event cannot feed back into an infinite\n * loop. Returns a disposer; the effect is also torn down by `dispose()`.\n */\n bindInput<K extends keyof InputsOf<S>>(name: K, source: ReadSignal<InputsOf<S>[K]>): () => void;\n /** Invoke a declared command on the node (imperative); args/return typed from the shape. */\n command<K extends keyof CommandsOf<S>>(name: K, ...args: Parameters<CommandsOf<S>[K]>): ReturnType<CommandsOf<S>[K]>;\n /**\n * Command-token: invoke a declared command whenever `trigger` CHANGES. The initial\n * value does not fire (subscribe-without-firing), so a command like `abort` is not\n * triggered on mount. `mapArgs` shapes the call arguments from the trigger value\n * (default: the value itself as a single argument). Returns a disposer; also torn\n * down by `dispose()`.\n */\n bindCommand<K extends keyof CommandsOf<S>, V = unknown>(\n name: K,\n trigger: ReadSignal<V>,\n mapArgs?: (value: V) => Parameters<CommandsOf<S>[K]>,\n ): () => void;\n /**\n * Detach all listeners/effects and make the adapter inert. After dispose the output\n * signals/streams stop updating and `set`/`command`/`bindInput`/`bindCommand`/`on`\n * throw (use-after-dispose). Idempotent.\n */\n dispose(): void;\n}\n\n// Internal target type. `Record<string, any>` is needed to index arbitrary declared\n// members (target[name]) inside the adapter, but it is DELIBERATELY NOT in the public\n// `bindNode` signature — exposing it there erased the caller's element type and let any\n// stray member access type-check. Kept private; the public entry takes `EventTarget`.\ntype NodeTarget = EventTarget & Record<string, any>;\n\n// Internal, fully-loose surface the adapter object is built against (string keys,\n// `unknown` values). The public `BoundNode<S>` is a stricter projection of this same\n// runtime object; `bindNode` casts the built `UntypedBoundNode` to `BoundNode<S>` at\n// return. Kept private so the loose `unknown`-typed methods never leak to callers.\ninterface UntypedBoundNode {\n readonly signals: Record<string, ReadSignal<unknown>>;\n on<T = unknown, C = unknown>(prop: string, options?: EventStreamOptions<T, C>): ReadSignal<T | undefined>;\n set(name: string, value: unknown): void;\n bindInput(name: string, source: ReadSignal<unknown>): () => void;\n command(name: string, ...args: unknown[]): unknown;\n bindCommand(name: string, trigger: ReadSignal<unknown>, mapArgs?: (value: unknown) => unknown[]): () => void;\n dispose(): void;\n}\n\n// The error thrown by `assertLive` when an adapter method is called after dispose.\n// Carries a brand symbol so callers that need to distinguish a post-dispose throw\n// (e.g. nodeSource's abort bridge) can test it structurally instead of matching the\n// message string — the message is free to change without silently breaking the\n// guard. The brand survives even if a consumer's bundler duplicates the class (e.g.\n// `instanceof` across realms), since it's a Symbol on the instance, not a class\n// identity check.\nconst DISPOSED_ERROR = Symbol(\"bindNode.disposed\");\n\n/**\n * Error thrown when a {@link BoundNode} method is invoked after the adapter has been\n * disposed. Every surface that mutates or subscribes — `on` / `set` / `bindInput` /\n * `command` / `bindCommand` — throws this once `dispose()` has run, so a\n * use-after-dispose is loud instead of silently writing/invoking on an inert adapter.\n *\n * Prefer {@link isDisposedError} over `instanceof` for the check: the brand survives a\n * bundler that duplicates the class across realms, where `instanceof` would not. Use it\n * to make teardown-order races robust — e.g. swallow a post-dispose throw while letting\n * any other error surface:\n *\n * ```ts\n * try {\n * bound.command(\"abort\");\n * } catch (err) {\n * if (!isDisposedError(err)) throw err; // expected during teardown; ignore\n * }\n * ```\n */\nexport class DisposedError extends Error {\n readonly [DISPOSED_ERROR] = true;\n}\n\n/**\n * True if `err` is a {@link DisposedError} — i.e. a use-after-dispose error thrown by a\n * {@link BoundNode} method after `dispose()`. Brand-based (checks a Symbol on the\n * instance), so it stays correct even if a consumer's bundler duplicates the\n * `DisposedError` class across module realms, where `err instanceof DisposedError`\n * could be false.\n */\nexport function isDisposedError(err: unknown): boolean {\n return typeof err === \"object\" && err !== null && (err as Record<symbol, unknown>)[DISPOSED_ERROR] === true;\n}\n\nfunction readProperty(target: NodeTarget, name: string): unknown {\n return target[name];\n}\n\n// wc-bindable SPEC § Default Getter: when a property omits `getter`, an adapter MUST\n// read `e.detail`. This adapter used to read the property instead (#238), which\n// diverged from `@wcstack/state` and `@wc-bindable/core` — the same element could\n// bind correctly in one adapter and not the other. Only the initial seed reads the\n// property (SPEC § Initial Sync: there is no event to derive from at bind time).\nconst DEFAULT_GETTER = (e: Event): unknown => (e as CustomEvent).detail;\n\n/**\n * Adapt a wc-bindable node into signals. The public `target` parameter is a plain\n * `EventTarget` (the internal `Record<string, any>` indexing is cast away here, not\n * leaked to callers — see {@link NodeTarget}). Pass an optional {@link NodeShape} type\n * argument to type the result; omit it for the back-compat all-`unknown` shape.\n *\n * @example typed\n * interface FetchShape extends NodeShape {\n * signals: { value: Person[]; loading: boolean };\n * inputs: { url: string };\n * commands: { fetch: (url: string) => Promise<Person[]>; abort: () => void };\n * }\n * const bound = bindNode<FetchShape>(fetchEl, FetchCore.wcBindable);\n * bound.signals.value.get(); // ReadSignal<Person[]>\n * bound.set(\"url\", \"/api/people\"); // string enforced\n */\nexport function bindNode<S extends NodeShape = DefaultNodeShape>(\n target: EventTarget,\n descriptor?: WcBindableDescriptor,\n): BoundNode<S> {\n // The Record<string, any> indexing surface is needed internally to read/write/invoke\n // declared members by name; keep it private to this body.\n const node = target as NodeTarget;\n const desc = descriptor ?? (node.constructor as { wcBindable?: WcBindableDescriptor }).wcBindable;\n if (!desc) {\n throw new Error(\"bindNode: no wc-bindable descriptor provided and none found on target.constructor.wcBindable\");\n }\n\n // Name → declared-entry lookups so set/command/on reject names the node never\n // declared, instead of silently writing/invoking/listening on an arbitrary member.\n const propByName = new Map(desc.properties.map((p) => [p.name, p]));\n const declaredInputs = new Set((desc.inputs ?? []).map((i) => i.name));\n const declaredCommands = new Set((desc.commands ?? []).map((c) => c.name));\n\n const signals: Record<string, WriteSignal<unknown>> = {};\n const removers: Array<() => void> = [];\n let disposed = false;\n\n const assertLive = (op: string, name: string): void => {\n if (disposed) {\n throw new DisposedError(`bindNode.${op}: \"${name}\" called after dispose (the adapter is inert).`);\n }\n };\n\n for (const prop of desc.properties) {\n // Seed with the node's current value so the signal is valid before the first\n // event fires (e.g. FetchCore.value === null at rest).\n const cell = signal<unknown>(readProperty(node, prop.name));\n signals[prop.name] = cell;\n\n const read = prop.getter ?? DEFAULT_GETTER;\n const handler = (event: Event): void => {\n cell.set(read(event));\n };\n node.addEventListener(prop.event, handler);\n removers.push(() => node.removeEventListener(prop.event, handler));\n // Re-seed AFTER subscribing: the initial read above is a snapshot taken before\n // the listener was attached, so a value change in that gap would be missed.\n // Reading the property once more now closes the race (the equality guard makes\n // it a no-op when nothing changed). Note this is the property snapshot, not a\n // getter(event) — there is no event to derive from at bind time, and the SPEC's\n // default getter (`e.detail`) applies to events only.\n cell.set(readProperty(node, prop.name));\n }\n\n // Implemented against a string-keyed, all-`unknown` surface (`UntypedBoundNode`); the\n // generic typing in `BoundNode<S>` is a compile-time projection over the SAME\n // runtime, so the object is built once and cast to `BoundNode<S>` at return. Runtime\n // name validation against the descriptor is unchanged.\n const bound: UntypedBoundNode = {\n signals,\n\n on<T = unknown, C = unknown>(prop: string, options: EventStreamOptions<T, C> = {}): ReadSignal<T | undefined> {\n assertLive(\"on\", prop);\n const propDesc = propByName.get(prop);\n if (!propDesc) {\n throw new Error(`bindNode.on: \"${prop}\" is not a declared property on this node.`);\n }\n const fold = options.fold ?? ((_acc: T | undefined, chunk: C) => chunk as unknown as T);\n // equals: () => false — a stream notifies on EVERY emit, even an equal value.\n const cell = signal<T | undefined>(options.initial, () => false);\n const read = propDesc.getter ?? DEFAULT_GETTER;\n const handler = (event: Event): void => {\n const chunk = read(event) as C;\n cell.set(fold(cell.peek(), chunk));\n };\n node.addEventListener(propDesc.event, handler);\n removers.push(() => node.removeEventListener(propDesc.event, handler));\n return cell;\n },\n\n set(name: string, value: unknown): void {\n assertLive(\"set\", name);\n if (!declaredInputs.has(name)) {\n throw new Error(`bindNode.set: \"${name}\" is not a declared input on this node.`);\n }\n node[name] = value;\n },\n\n bindInput(name: string, source: ReadSignal<unknown>): () => void {\n assertLive(\"bindInput\", name);\n if (!declaredInputs.has(name)) {\n throw new Error(`bindNode.bindInput: \"${name}\" is not a declared input on this node.`);\n }\n const handle = effect(() => {\n const v = source.get();\n if (node[name] !== v) {\n node[name] = v; // same-value guard above breaks write→event→write loops\n }\n });\n removers.push(() => handle.dispose());\n return () => handle.dispose();\n },\n\n command(name: string, ...args: unknown[]): unknown {\n assertLive(\"command\", name);\n if (!declaredCommands.has(name)) {\n throw new Error(`bindNode.command: \"${name}\" is not a declared command on this node.`);\n }\n if (typeof node[name] !== \"function\") {\n throw new TypeError(`bindNode.command: \"${name}\" is declared but not a function on the node.`);\n }\n return node[name](...args);\n },\n\n bindCommand(name: string, trigger: ReadSignal<unknown>, mapArgs?: (value: unknown) => unknown[]): () => void {\n assertLive(\"bindCommand\", name);\n if (!declaredCommands.has(name)) {\n throw new Error(`bindNode.bindCommand: \"${name}\" is not a declared command on this node.`);\n }\n // Fail fast at bind time (not on first change), so a wrong name surfaces when\n // the subscription is set up rather than silently later inside a flush.\n if (typeof node[name] !== \"function\") {\n throw new TypeError(`bindNode.bindCommand: \"${name}\" is declared but not a function on the node.`);\n }\n let primed = false;\n const handle = effect(() => {\n const v = trigger.get();\n if (!primed) {\n primed = true; // subscribe on mount without firing (emit on CHANGE only)\n return;\n }\n const args = mapArgs ? mapArgs(v) : [v];\n node[name](...args);\n });\n removers.push(() => handle.dispose());\n return () => handle.dispose();\n },\n\n dispose(): void {\n if (disposed) {\n return; // idempotent\n }\n disposed = true;\n for (const remove of removers) {\n remove();\n }\n },\n };\n\n return bound as unknown as BoundNode<S>;\n}\n\n/**\n * Build a `resource` source from a wc-bindable node, bridging the resource's\n * AbortSignal to the node's cancel command (default `\"abort\"`) before delegating to\n * `run`. This generalizes the earlier hand-wired `sig → core.abort()` bridge (docs §8\n * (e), §5-2): wrap the result in `resource({ args })` and any node that declares an\n * abort command gets switchMap-style cancel/restart for free — an `args` change\n * aborts the in-flight call (firing the node's abort command, which cancels its real\n * AbortController) and starts the next.\n *\n * The node's own value/loading/error stay available via `bound.signals`; `resource`\n * here is used for the cancel/restart lifecycle, not to re-derive that triad.\n *\n * @example\n * const bound = bindNode(fetchEl);\n * const r = resource(\n * nodeSource(bound, (b, id) => b.command(\"fetch\", `/api/${id}`)),\n * { args: () => id.get() },\n * );\n */\nexport function nodeSource<T, A = void, S extends NodeShape = DefaultNodeShape>(\n bound: BoundNode<S>,\n run: (node: BoundNode<S>, args: A) => Promise<T> | T,\n options: { abort?: string } = {},\n): ResourceSource<T, A> {\n const abortName = options.abort ?? \"abort\";\n return (args, signal) => {\n // Honor the resource's cancel by invoking the node's abort command. `once`:\n // each resource run gets a fresh AbortSignal, so the listener fires at most once.\n //\n // GUARD: the abort listener runs SYNCHRONOUSLY inside AbortController.abort().\n // If `bound` was already disposed (e.g. the adapter and the resource share an\n // owner and the adapter's disposer ran first), `command` throws assertLive —\n // and a throw out of an abort listener surfaces as an unhandled exception during\n // the synchronous abort() call. Swallow a post-dispose throw so teardown order is\n // robust; report any OTHER error via the platform reporter without breaking abort.\n signal.addEventListener(\n \"abort\",\n () => {\n try {\n // `abortName` is a runtime string (validated by the adapter); the cast lets\n // it reach `command` regardless of the typed command-name union of `S`.\n (bound.command as (name: string, ...args: unknown[]) => unknown)(abortName);\n } catch (err) {\n // Brand-based check (not a message regex): a post-dispose throw is\n // expected here and swallowed; any OTHER error is reported.\n if (!isDisposedError(err)) {\n reportError(err);\n }\n }\n },\n { once: true },\n );\n return run(bound, args);\n };\n}\n","// Async resource (v1 design notes).\n//\n// Adapts an async producer into a reactive `{ value, loading, error }` triad —\n// the same shape FetchCore exposes (value/loading/error). When the resource's\n// `args` (a reactive getter) change, the in-flight request is aborted and a fresh\n// one starts: dependency-driven cancel/restart, i.e. RxJS switchMap.\n//\n// This is the signals-side counterpart of the state-side `$streams` adapter\n// (docs/state-stream-type-design.md §4-1). The hard part — making sure a stale\n// response from a superseded request never lands on the new state — is handled\n// here by checking `signal.aborted` before committing any result.\n\nimport { signal, effect, onCleanup, ReadSignal } from \"./reactive.js\";\n\nexport interface ResourceState<T> {\n value: ReadSignal<T | undefined>;\n // Progress is a BOOLEAN here, intentionally different from streamResource's\n // `status` enum. A resource is single-shot (one request → resolve/reject), so\n // its progress is binary: in-flight or not. A stream is continuous (idle →\n // active → done/error), so it needs the enum. The \"shared contract\" with the\n // state-side `$streams` adapter covers the cancel/restart SEMANTICS, not this\n // progress representation. (docs §8 (a)).\n loading: ReadSignal<boolean>;\n /** Last error, or `null` when there is none. Initial value is `null`; a (re)start resets it to `null`. */\n error: ReadSignal<unknown>;\n /** Abort any in-flight request and stop reacting to `args` changes. */\n dispose(): void;\n}\n\nexport interface ResourceOptions<T, A> {\n /** Reactive inputs. Reading signals here wires restart-on-change. */\n args?: () => A;\n /** Seed value before the first resolution. */\n initial?: T;\n}\n\nexport type ResourceSource<T, A> = (args: A, signal: AbortSignal) => Promise<T> | T;\n\nexport function resource<T, A = void>(\n source: ResourceSource<T, A>,\n options: ResourceOptions<T, A> = {},\n): ResourceState<T> {\n const value = signal<T | undefined>(options.initial);\n const loading = signal<boolean>(false);\n const error = signal<unknown>(null);\n\n let controller: AbortController | null = null;\n\n const runner = effect(() => {\n // Reading args() inside the effect is what subscribes us to its signals, so a\n // change re-runs this body — that IS the restart trigger.\n //\n // NOTE (effect-internal writes): this effect writes loading/error/value, which\n // are signals OTHER effects may observe. A write that actually CHANGES the value\n // marks those observers stale and queues them on the same flush; the drain loop\n // then runs them in the same tick. (A same-value write — e.g. error.set(null)\n // when error is already null — is a no-op via the equality guard and notifies\n // nothing.) We never read these signals inside THIS effect, so we don't dirty\n // our own dependency — no cycle. (docs §8 (a)).\n const a = (options.args ? options.args() : undefined) as A;\n\n // Abort the previous request before starting the next (switchMap).\n controller?.abort();\n const ac = new AbortController();\n controller = ac;\n\n loading.set(true);\n error.set(null);\n\n // Call the source SYNCHRONOUSLY (so it receives ac.signal immediately and can\n // wire its abort listener before any teardown), but guard it with try/catch so\n // a synchronous throw is normalized into the same error/loading state as a\n // rejected promise. Without the guard a sync throw would escape the effect body\n // (and, on the initial run, the resource() call itself), leaving loading stuck\n // true and error unset.\n let produced: Promise<T> | T;\n try {\n produced = source(a, ac.signal);\n } catch (err) {\n error.set(err);\n loading.set(false);\n return;\n }\n\n // Settle via two-arm `.then` rather than `await` + try/catch (streamResource's\n // shape): a resource is SINGLE-SHOT — one resolve/reject lands once — so a flat\n // resolved/rejected pair maps cleanly. A stream is a CONTINUOUS loop whose chunks\n // and termination must share one error path, which an `await` loop in try/catch\n // expresses; that structure would be needless here.\n Promise.resolve(produced).then(\n (resolved) => {\n // Drop the result if this request was superseded/disposed: committing it\n // would let a stale response overwrite the newer request's state.\n if (ac.signal.aborted) {\n return;\n }\n value.set(resolved);\n loading.set(false);\n },\n (err) => {\n if (ac.signal.aborted) {\n return;\n }\n error.set(err);\n loading.set(false);\n },\n );\n });\n\n const dispose = (): void => {\n controller?.abort();\n runner.dispose();\n };\n\n // Auto-dispose with the enclosing owner (createRoot / parent effect), so a\n // resource created inside a component is aborted on unmount. No-op when there\n // is no owner — the caller then disposes manually.\n onCleanup(dispose);\n\n return { value, loading, error, dispose };\n}\n","// Stream resource (docs §8 (a)). The signals-side counterpart of the state-side\n// `$streams` adapter (docs/state-stream-type-design.md).\n//\n// Adapts a continuous async flow (async iterable / ReadableStream / async\n// generator) into a single reactive value by FOLDING each chunk:\n// - latest (default): replace — value becomes the last chunk.\n// - reduce: accumulate — value = fold(acc, chunk), needs `initial`.\n// When the resource's `args` change, the in-flight stream is aborted and a fresh\n// one starts (switchMap), with `value` reset to `initial`.\n//\n// Deliberate non-goal: backpressure. The fold result IS the buffer; demand does\n// not flow back to the producer. Unbounded accumulation of an infinite stream is\n// a footgun — bound the fold (latest / count / last-N / window) for live streams.\n// This mirrors the state `$streams` norm and is what lets the impedance mismatch\n// be resolved honestly (state-stream §0, §4-3).\n//\n// Shared contract with state `$streams` (settled in v1):\n// - source(args, signal) receives an AbortSignal; honoring it drives restart.\n// - restart RESETS value to `initial`; error KEEPS the last value.\n// - status companion: \"idle\" | \"active\" | \"done\" | \"error\".\n// - async iterable is the lingua franca; anything getReader-bearing\n// (ReadableStream) is read via getReader() — see rescue levels below.\n//\n// CONTRACT (cooperative cancellation — STRONG REQUIREMENT): the `source` MUST honor\n// the `AbortSignal` it is given. Honoring it is what drives switchMap restart/dispose;\n// a source that ignores it cannot be reliably cancelled.\n//\n// Rescue levels on abort:\n// - ReadableStream: FULLY rescued. A parked read() is force-unwound via\n// reader.cancel(), which both releases the underlying source and settles the\n// pending read() so the loop unwinds. Anything getReader-bearing is routed\n// through this path even when it is ALSO natively async-iterable (Node 18+ /\n// Chrome 124+ / Firefox): the spec serializes asyncIterator.return() behind\n// the pending next() ([[ongoingPromise]]), so the AsyncIterable rescue below\n// cannot force-unwind a parked read on a native ReadableStream.\n// - AsyncIterable / async generator: PARTIALLY rescued. On abort we call\n// iterator.return() to trigger the generator's finally/cleanup. But a parked\n// `await` (the producer stalling before its next yield while IGNORING `signal`)\n// cannot be force-unwound from outside — return() only takes effect when the\n// generator next resumes. So a source that parks forever and never observes\n// `signal` still leaks its consume() task. Honor `signal` to bound this.\n// The `if (signal.aborted) return` check only runs after a chunk arrives, not while\n// parked — it drops stale chunks but is not, by itself, a cancellation mechanism.\n\nimport { signal, effect, onCleanup, ReadSignal, WriteSignal } from \"./reactive.js\";\n\nexport type StreamStatus = \"idle\" | \"active\" | \"done\" | \"error\";\n\nexport interface StreamResourceState<T> {\n value: ReadSignal<T | undefined>;\n // Progress is an ENUM here, intentionally different from resource's `loading`\n // boolean: a continuous stream moves idle → active → done/error, which a boolean\n // cannot express. The cancel/restart contract is shared with `resource` and the\n // state-side `$streams` adapter; the progress representation is not.\n status: ReadSignal<StreamStatus>;\n /** Last error, or `null` when there is none. Initial value is `null`; a (re)start resets it to `null`. */\n error: ReadSignal<unknown>;\n /** Abort the in-flight stream and stop reacting to `args`. */\n dispose(): void;\n}\n\nexport interface StreamResourceOptions<T, C, A> {\n /** Reactive inputs. Reading signals here wires restart-on-change. */\n args?: () => A;\n /** Combine the running value with each chunk. Default: latest (replace). */\n fold?: (acc: T | undefined, chunk: C) => T;\n /** Seed value, and the value `value` is reset to on each (re)start. */\n initial?: T;\n}\n\nexport type StreamProducer<C> = AsyncIterable<C> | ReadableStream<C>;\n\n/**\n * Produce a stream for the current `args`. The `signal` aborts on restart/dispose —\n * the source **MUST honor it** (this is a hard contract, not a suggestion): a\n * `ReadableStream` is fully cancelled via `reader.cancel()`, and an async generator\n * should observe `signal` (e.g. reject/break on `signal.aborted`) so a parked `await`\n * can unwind. On abort the adapter also calls the iterator's `return()` to run a\n * generator's `finally`, but a generator that parks forever while ignoring `signal`\n * cannot be force-unwound and will leak. See the module header's CONTRACT.\n */\nexport type StreamSource<C, A> = (\n args: A,\n signal: AbortSignal,\n) => StreamProducer<C> | Promise<StreamProducer<C>>;\n\nexport function streamResource<T, C = T, A = void>(\n source: StreamSource<C, A>,\n options: StreamResourceOptions<T, C, A> = {},\n): StreamResourceState<T> {\n const fold = options.fold ?? ((_acc: T | undefined, chunk: C) => chunk as unknown as T);\n const value = signal<T | undefined>(options.initial);\n const status = signal<StreamStatus>(\"idle\");\n const error = signal<unknown>(null);\n\n let controller: AbortController | null = null;\n\n const runner = effect(() => {\n // NOTE (effect-internal writes): like `resource`, this effect writes\n // value/status/error — signals other effects may observe. Those observers are\n // queued on the same flush and run in the same tick. This effect never reads\n // those signals, so it does not dirty its own dependency (no cycle). The async\n // `consume` writes them later from microtasks/timers, outside this flush.\n const a = (options.args ? options.args() : undefined) as A;\n\n controller?.abort();\n const ac = new AbortController();\n controller = ac;\n\n // Reset for the new run: a restart starts the fold from `initial`.\n value.set(options.initial);\n error.set(null);\n status.set(\"active\");\n\n void consume(source, a, ac.signal, fold, value, status, error);\n });\n\n const dispose = (): void => {\n controller?.abort();\n runner.dispose();\n };\n onCleanup(dispose);\n\n return { value, status, error, dispose };\n}\n\nasync function consume<T, C, A>(\n source: StreamSource<C, A>,\n args: A,\n signal: AbortSignal,\n fold: (acc: T | undefined, chunk: C) => T,\n // Use the public WriteSignal type instead of re-declaring an inline subset, so\n // these stay in lockstep with the signal API (no structural drift).\n value: WriteSignal<T | undefined>,\n status: WriteSignal<StreamStatus>,\n error: WriteSignal<unknown>,\n): Promise<void> {\n // Obtain the iterator EXPLICITLY (not via `for await`'s implicit one) so abort can\n // call `iterator.return()` to trigger an AsyncIterable / async generator's\n // `finally`/cleanup. A `for await` only calls `.return()` when the loop itself exits;\n // if the producer is PARKED (awaiting before the next yield while ignoring `signal`),\n // the loop never advances, so the implicit `.return()` never runs and the task leaks\n // past restart/dispose. Calling `.return()` on abort is the PARTIAL rescue: the\n // parked `await` cannot be force-unwound from outside, but once the generator resumes\n // (its next tick), `.return()` makes it run its `finally` and stop — recovering the\n // common \"generator wakes up after abort\" case. The ReadableStream path is fully\n // rescued via `reader.cancel()` (see `readableToAsyncIterable`).\n let iterator: AsyncIterator<C> | null = null;\n // Guard against returning the SAME iterator twice. `onAbort` is reachable two ways:\n // the abort listener, and the explicit call on line ~163 when abort raced the\n // `await source(...)`. The guard keys on the iterator instance (not a plain \"ran\"\n // flag): the listener firing with iterator still null must NOT consume the single\n // real cleanup that the explicit call performs once the iterator exists. So we only\n // mark an iterator returned once we have actually called `.return()` on it.\n let returned: AsyncIterator<C> | null = null;\n const onAbort = (): void => {\n if (!iterator || iterator === returned) {\n return; // nothing to release yet, or already released this iterator\n }\n returned = iterator;\n // Fire the iterator's cleanup. Swallow any throw/rejection from `.return()` — we\n // are tearing down; a producer that rejects on return must not surface here.\n try {\n void iterator.return?.()?.then?.(undefined, () => {});\n } catch {\n // `.return()` threw synchronously while tearing down — ignore.\n }\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n const produced = await source(args, signal);\n iterator = iterate(produced, signal)[Symbol.asyncIterator]();\n if (signal.aborted) {\n // Aborted while awaiting the source: the abort listener already ran (iterator\n // was still null then), so explicitly release the just-produced iterator now —\n // this fires a generator's finally / a ReadableStream's cancel for the\n // resource we created but will never iterate.\n onAbort();\n return;\n }\n for (;;) {\n const result = await iterator.next();\n if (result.done) {\n break;\n }\n if (signal.aborted) {\n return; // stale chunk from a superseded/disposed run — drop it\n }\n value.set(fold(value.peek(), result.value));\n }\n if (signal.aborted) {\n return; // stream ended but this run was aborted — don't mark done\n }\n status.set(\"done\");\n } catch (e) {\n if (signal.aborted) {\n return; // an abort that surfaced as a throw is not an error\n }\n error.set(e); // keep the last folded value (do not reset)\n status.set(\"error\");\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n}\n\nfunction iterate<C>(produced: StreamProducer<C>, signal: AbortSignal): AsyncIterable<C> {\n // Anything ReadableStream-shaped (has getReader) takes the reader path FIRST —\n // even when it is also async-iterable. On runtimes where ReadableStream\n // implements native async iteration (Node 18+ / Chrome 124+ / Firefox), the\n // spec serializes `iterator.return()` behind the pending `next()`\n // ([[ongoingPromise]]), so the AsyncIterable rescue cannot force-unwind a\n // parked read: abort would hang the consume task until the producer's next\n // chunk (or forever on a silent stream), leaking the underlying source.\n // `reader.cancel()` has no such ordering — routing every getReader-bearing\n // source through it keeps the FULL rescue promised for ReadableStream.\n if (typeof (produced as ReadableStream<C>)?.getReader === \"function\") {\n return readableToAsyncIterable(produced as ReadableStream<C>, signal);\n }\n if (typeof (produced as AsyncIterable<C>)?.[Symbol.asyncIterator] === \"function\") {\n return produced as AsyncIterable<C>;\n }\n throw new TypeError(\n \"streamResource: source must return an AsyncIterable or a ReadableStream (got neither).\",\n );\n}\n\nasync function* readableToAsyncIterable<C>(\n stream: ReadableStream<C>,\n signal: AbortSignal,\n): AsyncGenerator<C> {\n const reader = stream.getReader();\n // A ReadableStream read() does NOT observe an AbortSignal on its own. Without\n // this, a switchMap restart / dispose leaves the previous reader parked in a\n // pending read() forever, leaking the underlying source. Cancelling on abort\n // both releases the source AND settles the pending read() so the for-await\n // unwinds and the finally below can release the lock. Abort is the only\n // early-exit path for this generator (the consumer never calls .return()\n // without aborting), so this is the sole place a non-drained stream is cancelled.\n const onAbort = (): void => {\n void reader.cancel().catch(() => {}); // tearing down; swallow a rejected cancel\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n return;\n }\n yield value as C;\n }\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n reader.releaseLock();\n }\n}\n"],"names":["reportError","err","r","globalThis","console","error","isDev","__WCS_DEV__","emitted","Set","warnDev","code","dedupeKey","message","detail","id","has","add","warn","currentObserver","track","source","_sources","_observers","untrack","node","delete","clear","currentOwner","registerDisposer","disposer","_owned","push","disposeOwned","owner","owned","i","length","pendingEffects","flushScheduled","bufferPool","takeBuffer","pop","schedule","queueMicrotask","flushEffects","MAX_FLUSH_ITERATIONS","describeCycle","batch","live","filter","e","_disposed","lines","map","_devStack","split","slice","join","trim","iterations","lastBatch","size","Error","updateIfNecessary","flushSync","markStale","level","_state","wasClean","_isEffect","observer","ComputedNode","_update","SignalNode","_value","_equals","constructor","value","equals","this","get","peek","set","next","_fn","undefined","_initialized","_running","fn","previous","prevObserver","EffectNode","_cleanup","stack","dispose","_runCleanup","prevOwner","signal","initial","Object","is","computed","effect","createRoot","disposed","hasOwner","onCleanup","DISPOSED_ERROR","Symbol","DisposedError","isDisposedError","readProperty","target","name","DEFAULT_GETTER","bindNode","descriptor","desc","wcBindable","propByName","Map","properties","p","declaredInputs","inputs","declaredCommands","commands","c","signals","removers","assertLive","op","prop","cell","read","getter","handler","event","addEventListener","removeEventListener","on","options","propDesc","fold","_acc","chunk","bindInput","handle","v","command","args","TypeError","bindCommand","trigger","mapArgs","primed","remove","nodeSource","bound","run","abortName","abort","once","resource","loading","controller","runner","a","ac","AbortController","produced","Promise","resolve","then","resolved","aborted","streamResource","status","async","iterator","returned","onAbort","return","getReader","stream","reader","cancel","catch","done","releaseLock","readableToAsyncIterable","asyncIterator","iterate","result","consume"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"core-Db6kZPd_.esm.js","sources":["../src/reportError.ts","../src/dev.ts","../src/reactive.ts","../src/bindNode.ts","../src/resource.ts","../src/streamResource.ts"],"sourcesContent":["// Internal error-reporting policy (NOT part of the public API; not re-exported\n// from exports.ts). Single source of truth for \"surface an error without crashing\".\n//\n// Delegates to the platform `reportError` when present — it dispatches a window\n// \"error\" event / logs to the console without aborting the current task — and falls\n// back to `console.error` otherwise. It NEVER re-throws (re-throwing would abort the\n// caller's drain / abort handler) and NEVER swallows silently (that would hide bugs).\nexport function reportError(err: unknown): void {\n const r = (globalThis as { reportError?: (e: unknown) => void }).reportError;\n if (typeof r === \"function\") {\n r(err);\n } else {\n console.error(err);\n }\n}\n","// Opt-in development diagnostics (NOT part of the public API; not re-exported from\n// exports.ts). Surfaces \"silent failure\" modes — duplicate/unkeyed list rows,\n// un-owned effects that leak, no-op onCleanup, reactive cycles — that production\n// otherwise swallows.\n//\n// DEFAULT OFF. Nothing here runs unless the consumer flips `globalThis.__WCS_DEV__`\n// to `true` BEFORE the failing operation. `isDev()` reads the flag at CALL TIME (not\n// module-load time), so a page can toggle it on for a debugging session without a\n// rebuild. See README \"Development mode\".\n//\n// TREE-SHAKING: the package sets `\"sideEffects\": false`, and every export here is a\n// pure function with no module-top-level side effects (the dedupe cache is a lazily-\n// populated Set, not eager work). A production bundler that never references warnDev\n// can drop this module entirely; a bundler that does keep it still pays nothing at\n// runtime because `warnDev` short-circuits on the `isDev()` guard before touching the\n// dedupe Set or formatting a message.\n\n/** Stable warning codes, so docs/tests can reference a warning without matching prose. */\nexport type DevWarningCode =\n | \"DUPLICATE_KEY\"\n | \"NON_PRIMITIVE_KEY\"\n | \"NULLISH_KEY\"\n | \"UNOWNED_EFFECT\"\n | \"UNOWNED_INSERT\"\n | \"ORPHAN_CLEANUP\"\n | \"REACTIVE_CYCLE\";\n\n/**\n * True only when the consumer has opted in via `globalThis.__WCS_DEV__ === true`.\n * Read at call time so the flag can be toggled at runtime. Kept tiny and pure so a\n * production bundle that strips dev branches pays nothing.\n */\nexport function isDev(): boolean {\n return (globalThis as { __WCS_DEV__?: boolean }).__WCS_DEV__ === true;\n}\n\n// Dedupe set: a given (code + dedupe-key) warning is emitted at most once, so a\n// per-row or per-flush failure does not flood the console. Lazily populated; empty\n// and inert until the first dev warning actually fires.\nconst emitted = new Set<string>();\n\n/**\n * Emit a development warning ONCE per `(code, dedupeKey)`, only when dev mode is on.\n * In production (`isDev()` false) this is a no-op that returns before any work.\n *\n * @param code stable warning code (see DevWarningCode)\n * @param dedupeKey extra discriminator folded into the dedupe identity (e.g. the\n * duplicate key value). Same code + same dedupeKey warns once.\n * @param message human-readable explanation; `detail` is appended for context.\n */\nexport function warnDev(\n code: DevWarningCode,\n dedupeKey: string,\n message: string,\n ...detail: unknown[]\n): void {\n if (!isDev()) {\n return;\n }\n const id = code + \"\u0000\" + dedupeKey;\n if (emitted.has(id)) {\n return;\n }\n emitted.add(id);\n console.warn(`[wcstack/signals][${code}] ${message}`, ...detail);\n}\n\n/**\n * Clear the dedupe cache. Test-only seam so each test can re-exercise a warning that\n * is otherwise emitted once per process. Not meant for application use.\n */\nexport function __resetDevWarnings(): void {\n emitted.clear();\n}\n","// Minimal signal core (v1 design notes).\n//\n// A self-contained, zero-dependency reactive primitive whose public API is\n// shaped after the TC39 Signals proposal (State / Computed / effect). The intent\n// is NOT to ship signal-polyfill as a runtime dependency, but to keep an in-house\n// implementation that can later be swapped for the native one without changing\n// call sites. See docs/signals-state-design.md §2 (the \"case C\" decision).\n//\n// Reactivity model: three-color marking (CLEAN / CHECK / DIRTY), pull-validated\n// (after Reactively / Solid). docs §8 (c).\n// - A signal write marks its direct observers DIRTY and their transitive\n// observers CHECK. Effects scheduled on the CLEAN→stale transition run on a\n// coalesced microtask (docs §5-3).\n// - On read/run, `updateIfNecessary` validates a CHECK node by refreshing its\n// computed sources first; it recomputes only if a source actually changed.\n// - EQUALITY SHORT-CIRCUIT: when a computed recomputes to an equal value it does\n// NOT mark its observers DIRTY, so downstream effects/computeds skip work.\n// - dependencies are re-tracked on every run, so conditional deps are pruned.\n\nimport { reportError } from \"./reportError.js\";\nimport { isDev, warnDev } from \"./dev.js\";\n\nexport type Cleanup = () => void;\nexport type Equals<T> = (a: T, b: T) => boolean;\n\nexport interface ReadSignal<T> {\n /** Read the value AND register the current observer as a dependent. */\n get(): T;\n /** Read the value WITHOUT tracking (no dependency edge created). */\n peek(): T;\n}\n\nexport interface WriteSignal<T> extends ReadSignal<T> {\n set(value: T): void;\n}\n\nexport interface EffectHandle {\n dispose(): void;\n}\n\n// Node freshness. CLEAN: up to date. CHECK: a transitive source may have changed\n// — must validate before use. DIRTY: a direct source changed — must recompute.\n// Typed as `number` (not literals) on purpose: `_state` is mutated through\n// aliasing during `updateIfNecessary`, so literal narrowing would wrongly flag\n// the `=== DIRTY` re-check after a source update.\nconst CLEAN: number = 0;\nconst CHECK: number = 1;\nconst DIRTY: number = 2;\n\ninterface Observable {\n _observers: Set<Computation>;\n}\n\ninterface Computation extends Observable {\n _state: number;\n _sources: Set<Observable>;\n _isEffect: boolean;\n _update(): void;\n}\n\n// --- tracking context -------------------------------------------------------\n\nlet currentObserver: Computation | null = null;\n\nfunction track(source: Observable): void {\n if (currentObserver !== null) {\n currentObserver._sources.add(source);\n source._observers.add(currentObserver);\n }\n}\n\nfunction untrack(node: Computation): void {\n for (const source of node._sources) {\n source._observers.delete(node);\n }\n node._sources.clear();\n}\n\n// --- ownership (disposal tree) ----------------------------------------------\n//\n// Orthogonal to dependency tracking. An \"owner\" collects the disposers of the\n// computations (effects, nested cleanups, child roots) created during its run.\n// An effect IS an owner: it owns whatever was created while it ran, and disposes\n// those children before it re-runs or is disposed. This is what lets the DOM\n// layer rebuild a subtree without leaking the previous subtree's effects\n// (docs §8 (d)). Modeled after Solid's reactive ownership.\n\ntype Disposer = () => void;\n\ninterface Owner {\n _owned: Disposer[];\n}\n\nlet currentOwner: Owner | null = null;\n\nfunction registerDisposer(disposer: Disposer): void {\n if (currentOwner !== null) {\n currentOwner._owned.push(disposer);\n }\n}\n\nfunction disposeOwned(owner: Owner): void {\n // Dispose in reverse creation order (children before parents, last-in-first-out).\n const owned = owner._owned;\n for (let i = owned.length - 1; i >= 0; i--) {\n owned[i]();\n }\n owned.length = 0;\n}\n\n// --- effect scheduling (microtask coalesce) ---------------------------------\n\n// The queue NEW work accumulates into (markStale / a computed's DIRTY promotion add\n// here). Each drain pass takes the current queue to iterate and installs a fresh\n// empty one in its place, so effects queued WHILE draining land in the next pass.\nlet pendingEffects = new Set<EffectNode>();\nlet flushScheduled = false;\n\n// Double-buffer free list: rather than `[...pendingEffects]` (a Set→Array copy) per\n// pass, a drain SWAPS the live queue with a recycled empty Set and iterates that Set\n// directly. The swapped-out Set must be a fresh buffer that re-entrancy cannot\n// clobber — a nested flushSync swaps `pendingEffects` again, so the outer pass keeps\n// iterating its OWN buffer (the same isolation the old local-array copy gave). After\n// draining, the buffer is cleared and returned to the pool for reuse.\nconst bufferPool: Set<EffectNode>[] = [];\n\nfunction takeBuffer(): Set<EffectNode> {\n return bufferPool.pop() ?? new Set<EffectNode>();\n}\n\nfunction schedule(): void {\n if (flushScheduled) {\n return;\n }\n flushScheduled = true;\n queueMicrotask(flushEffects);\n}\n\n// Upper bound on drain iterations. A well-behaved graph settles in a handful of\n// passes; exceeding this means an effect keeps dirtying its own dependency with a\n// changing value (a reactive cycle), which would otherwise hang the page. We bail\n// loudly instead of spinning forever (docs §5-3).\nconst MAX_FLUSH_ITERATIONS = 1000;\n\n// Dev-only: build a \"what was still running\" appendix for the cycle bail. Uses the\n// creation stack captured per effect (dev only) so the message points at WHERE the\n// looping effects were defined. Production never calls this (guarded by the dev\n// `lastBatch`), so the stack-capture cost never lands on shipped code.\nfunction describeCycle(batch: EffectNode[]): string {\n const live = batch.filter((e) => !e._disposed);\n const lines = live.map((e, i) => {\n const where = e._devStack ? e._devStack.split(\"\\n\").slice(1, 3).join(\" | \").trim() : \"(no stack)\";\n return ` #${i + 1} ${where}`;\n });\n return (\n `\\n[wcstack/signals] dev diagnostics: ${live.length} effect(s) still re-running on the ` +\n `final pass (likely cycle participants):\\n${lines.join(\"\\n\")}`\n );\n}\n\nfunction flushEffects(): void {\n flushScheduled = false;\n // Drain in a loop: an effect may, while running, dirty a signal that queues\n // further effects. Coalescing collapses multiple writes in one tick into a\n // single run per effect (docs §5-3).\n let iterations = 0;\n // Dev-only: the effects run on the most recent pass, so a cycle bail can point at\n // the culprits. Lazily allocated only in dev (no cost in production).\n let lastBatch: EffectNode[] | null = null;\n while (pendingEffects.size > 0) {\n if (++iterations > MAX_FLUSH_ITERATIONS) {\n // Drop the runaway queue so a single bad effect cannot wedge every later\n // flush, then surface the bug.\n pendingEffects.clear();\n throw new Error(\n \"flushEffects: exceeded \" +\n MAX_FLUSH_ITERATIONS +\n \" iterations — likely a reactive cycle (an effect writes a signal it depends on with an ever-changing value).\" +\n (lastBatch ? describeCycle(lastBatch) : \"\"),\n );\n }\n // Swap the live queue out for a fresh empty buffer, then iterate the swapped-out\n // Set directly (no array copy). New work queued by an effect's body lands in the\n // new `pendingEffects` and is picked up on the next pass — same coalescing as\n // before. `batch` is a local reference, so a re-entrant flushSync (which swaps\n // `pendingEffects` again) cannot clobber this pass's iteration.\n const batch = pendingEffects;\n pendingEffects = takeBuffer();\n if (isDev()) {\n // Record the effects on this pass; on the FINAL (overflowing) pass these are\n // the ones still re-queuing themselves — the cycle participants.\n lastBatch = [...batch];\n }\n for (const node of batch) {\n if (!node._disposed) {\n // Isolate each effect: a throw from one effect's body must NOT abort the\n // batch and silently strand its already-dequeued siblings. We hand the\n // error to reportError (which does not re-throw, so the drain continues)\n // and move on. updateIfNecessary guarantees the node is settled to CLEAN\n // even on throw, so it is not left stuck DIRTY.\n try {\n updateIfNecessary(node);\n } catch (err) {\n reportError(err);\n }\n }\n }\n // Recycle this pass's buffer. Clear AFTER iterating so a node is never dropped\n // mid-walk; the Set is now free for a later takeBuffer().\n batch.clear();\n bufferPool.push(batch);\n }\n}\n\n// Effect/computed errors are surfaced via the shared `reportError` policy\n// (./reportError.ts), called synchronously from the drain loop. It does NOT\n// re-throw (that would abort the drain) and does NOT swallow (that would hide the\n// bug). Re-entrancy is safe: the only re-scheduling path (markStale) defers to a\n// microtask, so reporting here cannot recurse into the running flush.\n\n/**\n * Synchronously flush queued effects. Provided for tests and for callers that\n * need DOM updates applied before reading the DOM back. In normal use effects\n * settle on their own microtask.\n */\nexport function flushSync(): void {\n // Do NOT touch flushScheduled here: a microtask may already be queued. Just\n // drain. flushEffects resets the flag itself; if a queued microtask still fires\n // afterwards it finds an empty queue and is a no-op. Touching the flag from here\n // would let a second flushSync race the queued microtask into a double drain.\n flushEffects();\n}\n\n// --- marking & validation ---------------------------------------------------\n\nfunction markStale(node: Computation, level: number): void {\n if (node._state < level) {\n const wasClean = node._state === CLEAN;\n node._state = level;\n if (wasClean && node._isEffect) {\n pendingEffects.add(node as EffectNode);\n schedule();\n }\n // Observers can only become CHECK from us: they need to re-validate, but\n // whether they truly changed depends on our recomputed value.\n for (const observer of node._observers) {\n markStale(observer, CHECK);\n }\n }\n}\n\nfunction updateIfNecessary(node: Computation): void {\n if (node._state === CLEAN) {\n return;\n }\n if (node._state === CHECK) {\n // A transitive source might have changed. Refresh computed sources; one of\n // them may set us DIRTY (via the equality check in its _update).\n // NOTE: this scans ALL sources each validation (O(sources)); the standard\n // pull-based scheme (Reactively/Solid) does the same. Intentional for v1 —\n // a future optimization could track per-source dirtiness to scan less.\n for (const source of node._sources) {\n if (source instanceof ComputedNode) {\n updateIfNecessary(source);\n if (node._state === DIRTY) {\n break;\n }\n }\n }\n }\n // Settle to CLEAN in a finally: if _update() (a user fn / equals) throws, the\n // node must NOT be left stuck DIRTY, otherwise every later peek/get would\n // re-run the throwing fn forever. The error propagates to the caller (a flush\n // isolates it per-effect; a direct get/peek surfaces it to that caller).\n try {\n if (node._state === DIRTY) {\n node._update();\n }\n } finally {\n node._state = CLEAN;\n }\n}\n\n// --- signal -----------------------------------------------------------------\n\nclass SignalNode<T> implements Observable, WriteSignal<T> {\n _observers = new Set<Computation>();\n private _value: T;\n private _equals: Equals<T>;\n\n constructor(value: T, equals: Equals<T>) {\n this._value = value;\n this._equals = equals;\n }\n\n get(): T {\n track(this);\n return this._value;\n }\n\n peek(): T {\n return this._value;\n }\n\n set(next: T): void {\n if (this._equals(this._value, next)) {\n return;\n }\n this._value = next;\n // No copy needed: markStale only schedules effects (on a microtask) and marks\n // observers — it never runs user code synchronously, so this set is not\n // mutated during the walk. Iterating it directly avoids a per-write allocation.\n for (const observer of this._observers) {\n markStale(observer, DIRTY);\n }\n }\n}\n\n// --- computed ---------------------------------------------------------------\n\nclass ComputedNode<T> implements Computation, ReadSignal<T> {\n _observers = new Set<Computation>();\n _sources = new Set<Observable>();\n _state = DIRTY; // never computed yet\n _isEffect = false;\n private _fn: () => T;\n private _value: T | undefined = undefined;\n private _initialized = false;\n private _running = false;\n private _equals: Equals<T>;\n\n constructor(fn: () => T, equals: Equals<T>) {\n this._fn = fn;\n this._equals = equals;\n // Be owned by the enclosing scope (a parent effect or createRoot). When that\n // scope tears down, untrack this computed so it is removed from its sources'\n // observer sets — otherwise a computed created inside an effect that re-runs\n // would leak a stale ComputedNode into each source on every run (docs §8 (d)).\n // Lazy + no children: teardown is just untrack, no cleanup/owned to dispose.\n registerDisposer(() => untrack(this));\n }\n\n get(): T {\n // Order matters: track BEFORE updateIfNecessary. The current observer must be\n // registered against THIS computed regardless of whether validation recomputes —\n // and recomputation swaps currentObserver to this node, so tracking first records\n // the edge under the OUTER observer (the caller), not this computed's own run.\n track(this);\n updateIfNecessary(this);\n return this._value as T;\n }\n\n peek(): T {\n updateIfNecessary(this);\n return this._value as T;\n }\n\n _update(): void {\n // Cycle guard: if _fn (directly or transitively) reads this computed, get()\n // would recurse into _update and blow the stack with an opaque RangeError.\n // Detect the self-reference and throw a clear error instead. (The effect-level\n // MAX_FLUSH_ITERATIONS guard does not cover this synchronous self-recursion.)\n if (this._running) {\n throw new Error(\"computed: circular dependency — the computed reads itself.\");\n }\n const previous = this._value;\n untrack(this);\n const prevObserver = currentObserver;\n currentObserver = this;\n this._running = true;\n try {\n this._value = this._fn();\n } finally {\n this._running = false;\n currentObserver = prevObserver;\n }\n // Equality short-circuit: only propagate when the value actually changed.\n // Our observers are already CHECK (from the originating markStale); promote\n // them to DIRTY so they recompute, otherwise leave them to settle CLEAN. Skip\n // on the first computation: there is no prior value to compare, and a custom\n // `equals` must not be invoked with the undefined sentinel.\n if (!this._initialized) {\n this._initialized = true;\n } else if (!this._equals(previous as T, this._value as T)) {\n for (const observer of this._observers) {\n observer._state = DIRTY;\n // Normally this computed is refreshed from inside flushEffects while\n // validating an already-scheduled effect, so the observer is in the queue\n // already. But if it is refreshed out-of-band (e.g. a `peek()` outside a\n // flush) an effect observer could otherwise be promoted to DIRTY without\n // being queued, and never re-run. Re-schedule defensively to keep the\n // \"DIRTY effect always runs\" invariant regardless of refresh path.\n if (observer._isEffect && !(observer as EffectNode)._disposed) {\n pendingEffects.add(observer as EffectNode);\n schedule();\n }\n }\n }\n }\n}\n\n// --- effect -----------------------------------------------------------------\n\nclass EffectNode implements Computation, Owner {\n _observers = new Set<Computation>(); // effects are not observed; kept for the interface\n _sources = new Set<Observable>();\n _owned: Disposer[] = [];\n _state = DIRTY;\n _isEffect = true;\n _disposed = false;\n // Dev-only creation stack, used to point a reactive-cycle bail at where the looping\n // effect was defined. Captured only when dev mode is on at construction time, so\n // production effects pay nothing for it.\n _devStack: string | undefined = undefined;\n private _fn: () => Cleanup | void;\n private _cleanup: Cleanup | void = undefined;\n\n constructor(fn: () => Cleanup | void) {\n this._fn = fn;\n if (isDev()) {\n this._devStack = new Error().stack;\n }\n // Be owned by the enclosing owner (a parent effect or a createRoot), so a\n // parent teardown disposes this effect.\n registerDisposer(() => this.dispose());\n updateIfNecessary(this); // initial run (state starts DIRTY)\n }\n\n _update(): void {\n this._runCleanup();\n disposeOwned(this); // tear down children created in the previous run\n untrack(this);\n const prevObserver = currentObserver;\n const prevOwner = currentOwner;\n currentObserver = this;\n currentOwner = this; // children created during the run are owned by this effect\n try {\n this._cleanup = this._fn();\n } finally {\n currentObserver = prevObserver;\n currentOwner = prevOwner;\n }\n }\n\n private _runCleanup(): void {\n if (typeof this._cleanup === \"function\") {\n this._cleanup();\n this._cleanup = undefined;\n }\n }\n\n dispose(): void {\n if (this._disposed) {\n return;\n }\n this._disposed = true;\n this._runCleanup();\n disposeOwned(this);\n untrack(this);\n pendingEffects.delete(this);\n }\n}\n\n// --- public API -------------------------------------------------------------\n\nexport function signal<T>(initial: T, equals: Equals<T> = Object.is): WriteSignal<T> {\n return new SignalNode(initial, equals);\n}\n\nexport function computed<T>(fn: () => T, equals: Equals<T> = Object.is): ReadSignal<T> {\n return new ComputedNode(fn, equals);\n}\n\nexport function effect(fn: () => Cleanup | void): EffectHandle {\n if (isDev() && currentOwner === null) {\n // No enclosing owner: nothing will ever dispose this effect, so it (and the\n // subscriptions it builds) leak. Legitimate top-level effects should be mounted\n // under createRoot / SignalsElement and disposed on teardown. Dev-only warn.\n warnDev(\n \"UNOWNED_EFFECT\",\n \"\",\n \"effect created with no owner — it will never be disposed and may leak. \" +\n \"Wrap it in createRoot(dispose => …) (or a SignalsElement render()) and \" +\n \"dispose on teardown.\",\n );\n }\n return new EffectNode(fn);\n}\n\n/**\n * Run `fn` inside a fresh ownership scope and return its result. Every effect (or\n * nested cleanup) created during `fn` — directly or transitively — is owned by\n * this root; calling the `dispose` passed to `fn` tears them all down.\n *\n * The root is detached: it is NOT auto-disposed by an enclosing owner. The caller\n * holds `dispose` (e.g. a custom element disposes it in disconnectedCallback).\n *\n * If `fn` throws, any effects/cleanups it created BEFORE the throw are disposed\n * before the error propagates — a caller that never received `dispose` (because the\n * call threw) cannot leak the half-built scope.\n */\nexport function createRoot<T>(fn: (dispose: () => void) => T): T {\n const owner: Owner = { _owned: [] };\n let disposed = false;\n const dispose = (): void => {\n if (disposed) {\n return;\n }\n disposed = true;\n disposeOwned(owner);\n };\n const prevOwner = currentOwner;\n const prevObserver = currentObserver;\n currentOwner = owner;\n // Detach the dependency-tracking context too, not just ownership. A root is a\n // fresh reactive boundary: signals read SYNCHRONOUSLY while `fn` builds the scope\n // must not register the OUTER observer as their dependent (Solid's createRoot\n // clears both owner and listener). Without this, a For/Index `each` that reads\n // `index()`/`item()` synchronously in its body would track the outer reconcile\n // effect against the row's idx/item signal, so reordering re-dirties the reconcile\n // effect — a self-loop the equality/MAX_FLUSH guards only paper over.\n currentObserver = null;\n try {\n return fn(dispose);\n } catch (err) {\n dispose(); // tear down whatever fn built before it threw\n throw err;\n } finally {\n currentOwner = prevOwner;\n currentObserver = prevObserver;\n }\n}\n\n/**\n * True when there is a current ownership scope (an enclosing effect or createRoot).\n * Dev-diagnostics seam for the DOM layer to flag un-owned reactive insertions\n * without re-implementing owner tracking. NOT part of the public API.\n */\nexport function hasOwner(): boolean {\n return currentOwner !== null;\n}\n\n/**\n * Register a teardown callback with the current owner. Runs when the owning effect\n * re-runs or is disposed, or when the enclosing root is disposed. A no-op when\n * there is no current owner.\n */\nexport function onCleanup(fn: () => void): void {\n if (currentOwner === null) {\n // No owner → registerDisposer drops `fn` on the floor: the cleanup never runs.\n // Silent in production; flag it in dev so the missing teardown is visible.\n warnDev(\n \"ORPHAN_CLEANUP\",\n \"\",\n \"onCleanup called with no owner — the cleanup will never run. \" +\n \"Call it inside an effect, createRoot, or a SignalsElement render().\",\n );\n }\n registerDisposer(fn);\n}\n","// wc-bindable → signal adapter. The crux of the design.\n//\n// Any async-IO node in wcstack speaks the wc-bindable protocol:\n// - properties: outputs — the node dispatches `event` on change; the value is\n// read via `getter(event)`, defaulting to `e.detail` (wc-bindable\n// SPEC § Default Getter). The property `name` is read only for the\n// initial seed, where no event exists yet (SPEC § Initial Sync).\n// - inputs: settable surface — write `node[name] = value`.\n// - commands: invocable methods — call `node[name](...args)`.\n// The node has NO idea whether the observer behind the binding is a proxy (state)\n// or a signal. So a single adapter that maps these surfaces onto signals makes every\n// existing node plug into the signal core unchanged. See docs/signals-state-design.md\n// §3, and the four mappings of §3-1:\n//\n// element → signal | property (latest snapshot) → read signal [`signals`]\n// element → signal | event-token (per-emit stream) → folded signal [`on`]\n// signal → element | input (write-back) → effect → prop [`bindInput`]\n// signal → element | command-token (start/cancel) → emit on change [`bindCommand`]\n//\n// `signals` is the STATE view of a property (equality-guarded — same value = no\n// update). `on` is the OCCURRENCE view of the same event (a stream — every emit\n// updates, even with an equal value), folded latest-by-default.\n\nimport { signal, effect, WriteSignal, ReadSignal } from \"./reactive.js\";\nimport { reportError } from \"./reportError.js\";\nimport type { ResourceSource } from \"./resource.js\";\n\nexport interface WcBindableProperty<V = unknown> {\n readonly name: string;\n readonly event: string;\n readonly getter?: (event: Event) => V;\n}\n\n// Structurally a SUBSET of the real `IWcBindable` (which additionally carries\n// `protocol` / `version`, and richer input/command entries). A real descriptor is\n// therefore assignable to this type — see __tests__/bindNode.compat.test.ts.\nexport interface WcBindableDescriptor {\n readonly properties: readonly WcBindableProperty[];\n readonly inputs?: readonly { readonly name: string }[];\n readonly commands?: readonly { readonly name: string }[];\n}\n\n// --- typed node shape (D1) ---------------------------------------------------\n//\n// `bindNode` is the package's centerpiece, yet without a type argument every output\n// was `ReadSignal<unknown>` and every `set`/`command` took `unknown` — the feature\n// was effectively untyped in consumer code. `NodeShape` is an OPT-IN description of a\n// node's reactive surface that drives the return type: declare the property value\n// types, the input value types, and the command signatures ONCE, and `bound.signals`,\n// `on`, `set`, `bindInput`, `command`, and `bindCommand` are all typed from it.\n//\n// interface FetchShape extends NodeShape {\n// signals: { value: Person[]; loading: boolean };\n// inputs: { url: string };\n// commands: { fetch: (url: string) => Promise<Person[]>; abort: () => void };\n// }\n// const bound = bindNode<FetchShape>(el, desc);\n// bound.signals.value.get(); // ReadSignal<Person[]> — key & type checked\n// bound.set(\"url\", \"/api/people\"); // string enforced\n// bound.command(\"fetch\", \"/api\"); // arg/return inferred\n//\n// The runtime is UNCHANGED — these types are erased; the adapter still validates names\n// against the descriptor at runtime. The default shape below keeps every existing\n// untyped call site compiling exactly as before (all-`unknown`, string keys).\n// Open command map: a command name → any call signature. `(...args: any[])` (NOT\n// `never[]`) is the constraint a USER shape's commands must satisfy — and, crucially,\n// the OPEN fallback used when a shape omits `commands`. `Parameters<(...args: any[])\n// => unknown>` is `any[]`, so an untyped `command(\"run\", \"/api\")` accepts any args,\n// preserving the pre-generic behaviour (symmetric with `inputs` → `unknown`). A shape\n// that DECLARES a precise signature (e.g. `(url: string) => Promise<X>`) still gets\n// exact arg/return checking on the typed path — the loose constraint only widens the\n// DEFAULT, never a declared command. (`no-explicit-any` is disabled for this package.)\ntype OpenCommandMap = Record<string, (...args: any[]) => unknown>;\n\nexport interface NodeShape {\n /** propertyName → latest-snapshot value type (what `signals[name]` / `on(name)` carry). */\n signals?: Record<string, unknown>;\n /** inputName → settable value type (what `set` / `bindInput` accept). */\n inputs?: Record<string, unknown>;\n /** commandName → its call signature (what `command` / `bindCommand` invoke). */\n commands?: OpenCommandMap;\n}\n\n// Back-compat default: a fully-open shape so `bindNode(target, desc)` with no type\n// argument behaves as before (signals keyed by arbitrary string → unknown; any\n// input/command name; unknown values; any command args). Stronger typing is opt-in\n// via the type arg.\nexport interface DefaultNodeShape extends NodeShape {\n signals: Record<string, unknown>;\n inputs: Record<string, unknown>;\n commands: OpenCommandMap;\n}\n\n// Resolve the three sub-maps with sane fallbacks when a shape omits one of them.\ntype SignalsOf<S extends NodeShape> = S[\"signals\"] extends Record<string, unknown>\n ? S[\"signals\"]\n : Record<string, unknown>;\ntype InputsOf<S extends NodeShape> = S[\"inputs\"] extends Record<string, unknown>\n ? S[\"inputs\"]\n : Record<string, unknown>;\ntype CommandsOf<S extends NodeShape> = S[\"commands\"] extends OpenCommandMap ? S[\"commands\"] : OpenCommandMap;\n\n/** Options for `on` — how each event emission folds into the stream signal. */\nexport interface EventStreamOptions<T, C> {\n /** Combine the running value with each emission. Default: latest (replace). */\n fold?: (acc: T | undefined, chunk: C) => T;\n /** Seed value before the first emission. */\n initial?: T;\n}\n\nexport interface BoundNode<S extends NodeShape = DefaultNodeShape> {\n /**\n * Output properties as read-only signals (latest value), keyed by property name and\n * typed from the node shape: `bound.signals.value.get()` carries the declared value\n * type (no longer `unknown`). Keys are the declared property names.\n */\n readonly signals: { readonly [K in keyof SignalsOf<S>]: ReadSignal<SignalsOf<S>[K]> };\n /**\n * An event-token STREAM for a declared property's event: a read signal that folds\n * every emission (latest by default). Unlike `signals[prop]`, it updates on EVERY\n * emit — even when the derived value is equal — because it models occurrences, not\n * state. The chunk per emit is `getter(event)` if the property declares one, else\n * the property's current value.\n *\n * With no fold the value type defaults to the property's declared snapshot type; a\n * `fold` may reshape it (explicit `T`/`C` type args override).\n */\n on<K extends keyof SignalsOf<S>>(prop: K): ReadSignal<SignalsOf<S>[K] | undefined>;\n on<T, C = SignalsOf<S>[keyof SignalsOf<S>]>(\n prop: keyof SignalsOf<S>,\n options: EventStreamOptions<T, C>,\n ): ReadSignal<T | undefined>;\n /** Write a declared input on the node (imperative); value type checked against the shape. */\n set<K extends keyof InputsOf<S>>(name: K, value: InputsOf<S>[K]): void;\n /**\n * Reactively write a declared input from a signal: an effect mirrors `source` into\n * `node[name]`. A same-value guard (`node[name] !== v`) skips redundant writes, so\n * a property whose write re-dispatches an event cannot feed back into an infinite\n * loop. Returns a disposer; the effect is also torn down by `dispose()`.\n */\n bindInput<K extends keyof InputsOf<S>>(name: K, source: ReadSignal<InputsOf<S>[K]>): () => void;\n /** Invoke a declared command on the node (imperative); args/return typed from the shape. */\n command<K extends keyof CommandsOf<S>>(name: K, ...args: Parameters<CommandsOf<S>[K]>): ReturnType<CommandsOf<S>[K]>;\n /**\n * Command-token: invoke a declared command whenever `trigger` CHANGES. The initial\n * value does not fire (subscribe-without-firing), so a command like `abort` is not\n * triggered on mount. `mapArgs` shapes the call arguments from the trigger value\n * (default: the value itself as a single argument). Returns a disposer; also torn\n * down by `dispose()`.\n */\n bindCommand<K extends keyof CommandsOf<S>, V = unknown>(\n name: K,\n trigger: ReadSignal<V>,\n mapArgs?: (value: V) => Parameters<CommandsOf<S>[K]>,\n ): () => void;\n /**\n * Detach all listeners/effects and make the adapter inert. After dispose the output\n * signals/streams stop updating and `set`/`command`/`bindInput`/`bindCommand`/`on`\n * throw (use-after-dispose). Idempotent.\n */\n dispose(): void;\n}\n\n// Internal target type. `Record<string, any>` is needed to index arbitrary declared\n// members (target[name]) inside the adapter, but it is DELIBERATELY NOT in the public\n// `bindNode` signature — exposing it there erased the caller's element type and let any\n// stray member access type-check. Kept private; the public entry takes `EventTarget`.\ntype NodeTarget = EventTarget & Record<string, any>;\n\n// Internal, fully-loose surface the adapter object is built against (string keys,\n// `unknown` values). The public `BoundNode<S>` is a stricter projection of this same\n// runtime object; `bindNode` casts the built `UntypedBoundNode` to `BoundNode<S>` at\n// return. Kept private so the loose `unknown`-typed methods never leak to callers.\ninterface UntypedBoundNode {\n readonly signals: Record<string, ReadSignal<unknown>>;\n on<T = unknown, C = unknown>(prop: string, options?: EventStreamOptions<T, C>): ReadSignal<T | undefined>;\n set(name: string, value: unknown): void;\n bindInput(name: string, source: ReadSignal<unknown>): () => void;\n command(name: string, ...args: unknown[]): unknown;\n bindCommand(name: string, trigger: ReadSignal<unknown>, mapArgs?: (value: unknown) => unknown[]): () => void;\n dispose(): void;\n}\n\n// The error thrown by `assertLive` when an adapter method is called after dispose.\n// Carries a brand symbol so callers that need to distinguish a post-dispose throw\n// (e.g. nodeSource's abort bridge) can test it structurally instead of matching the\n// message string — the message is free to change without silently breaking the\n// guard. The brand survives even if a consumer's bundler duplicates the class (e.g.\n// `instanceof` across realms), since it's a Symbol on the instance, not a class\n// identity check.\nconst DISPOSED_ERROR = Symbol(\"bindNode.disposed\");\n\n/**\n * Error thrown when a {@link BoundNode} method is invoked after the adapter has been\n * disposed. Every surface that mutates or subscribes — `on` / `set` / `bindInput` /\n * `command` / `bindCommand` — throws this once `dispose()` has run, so a\n * use-after-dispose is loud instead of silently writing/invoking on an inert adapter.\n *\n * Prefer {@link isDisposedError} over `instanceof` for the check: the brand survives a\n * bundler that duplicates the class across realms, where `instanceof` would not. Use it\n * to make teardown-order races robust — e.g. swallow a post-dispose throw while letting\n * any other error surface:\n *\n * ```ts\n * try {\n * bound.command(\"abort\");\n * } catch (err) {\n * if (!isDisposedError(err)) throw err; // expected during teardown; ignore\n * }\n * ```\n */\nexport class DisposedError extends Error {\n readonly [DISPOSED_ERROR] = true;\n}\n\n/**\n * True if `err` is a {@link DisposedError} — i.e. a use-after-dispose error thrown by a\n * {@link BoundNode} method after `dispose()`. Brand-based (checks a Symbol on the\n * instance), so it stays correct even if a consumer's bundler duplicates the\n * `DisposedError` class across module realms, where `err instanceof DisposedError`\n * could be false.\n */\nexport function isDisposedError(err: unknown): boolean {\n return typeof err === \"object\" && err !== null && (err as Record<symbol, unknown>)[DISPOSED_ERROR] === true;\n}\n\nfunction readProperty(target: NodeTarget, name: string): unknown {\n return target[name];\n}\n\n// wc-bindable SPEC § Default Getter: when a property omits `getter`, an adapter MUST\n// read `e.detail`. This adapter used to read the property instead (#238), which\n// diverged from `@wcstack/state` and `@wc-bindable/core` — the same element could\n// bind correctly in one adapter and not the other. Only the initial seed reads the\n// property (SPEC § Initial Sync: there is no event to derive from at bind time).\nconst DEFAULT_GETTER = (e: Event): unknown => (e as CustomEvent).detail;\n\n/**\n * Adapt a wc-bindable node into signals. The public `target` parameter is a plain\n * `EventTarget` (the internal `Record<string, any>` indexing is cast away here, not\n * leaked to callers — see {@link NodeTarget}). Pass an optional {@link NodeShape} type\n * argument to type the result; omit it for the back-compat all-`unknown` shape.\n *\n * @example typed\n * interface FetchShape extends NodeShape {\n * signals: { value: Person[]; loading: boolean };\n * inputs: { url: string };\n * commands: { fetch: (url: string) => Promise<Person[]>; abort: () => void };\n * }\n * const bound = bindNode<FetchShape>(fetchEl, FetchCore.wcBindable);\n * bound.signals.value.get(); // ReadSignal<Person[]>\n * bound.set(\"url\", \"/api/people\"); // string enforced\n */\nexport function bindNode<S extends NodeShape = DefaultNodeShape>(\n target: EventTarget,\n descriptor?: WcBindableDescriptor,\n): BoundNode<S> {\n // The Record<string, any> indexing surface is needed internally to read/write/invoke\n // declared members by name; keep it private to this body.\n const node = target as NodeTarget;\n const desc = descriptor ?? (node.constructor as { wcBindable?: WcBindableDescriptor }).wcBindable;\n if (!desc) {\n throw new Error(\"bindNode: no wc-bindable descriptor provided and none found on target.constructor.wcBindable\");\n }\n\n // Name → declared-entry lookups so set/command/on reject names the node never\n // declared, instead of silently writing/invoking/listening on an arbitrary member.\n const propByName = new Map(desc.properties.map((p) => [p.name, p]));\n const declaredInputs = new Set((desc.inputs ?? []).map((i) => i.name));\n const declaredCommands = new Set((desc.commands ?? []).map((c) => c.name));\n\n const signals: Record<string, WriteSignal<unknown>> = {};\n const removers: Array<() => void> = [];\n let disposed = false;\n\n const assertLive = (op: string, name: string): void => {\n if (disposed) {\n throw new DisposedError(`bindNode.${op}: \"${name}\" called after dispose (the adapter is inert).`);\n }\n };\n\n for (const prop of desc.properties) {\n // Seed with the node's current value so the signal is valid before the first\n // event fires (e.g. FetchCore.value === null at rest).\n const cell = signal<unknown>(readProperty(node, prop.name));\n signals[prop.name] = cell;\n\n const read = prop.getter ?? DEFAULT_GETTER;\n const handler = (event: Event): void => {\n cell.set(read(event));\n };\n node.addEventListener(prop.event, handler);\n removers.push(() => node.removeEventListener(prop.event, handler));\n // Re-seed AFTER subscribing: the initial read above is a snapshot taken before\n // the listener was attached, so a value change in that gap would be missed.\n // Reading the property once more now closes the race (the equality guard makes\n // it a no-op when nothing changed). Note this is the property snapshot, not a\n // getter(event) — there is no event to derive from at bind time, and the SPEC's\n // default getter (`e.detail`) applies to events only.\n cell.set(readProperty(node, prop.name));\n }\n\n // Implemented against a string-keyed, all-`unknown` surface (`UntypedBoundNode`); the\n // generic typing in `BoundNode<S>` is a compile-time projection over the SAME\n // runtime, so the object is built once and cast to `BoundNode<S>` at return. Runtime\n // name validation against the descriptor is unchanged.\n const bound: UntypedBoundNode = {\n signals,\n\n on<T = unknown, C = unknown>(prop: string, options: EventStreamOptions<T, C> = {}): ReadSignal<T | undefined> {\n assertLive(\"on\", prop);\n const propDesc = propByName.get(prop);\n if (!propDesc) {\n throw new Error(`bindNode.on: \"${prop}\" is not a declared property on this node.`);\n }\n const fold = options.fold ?? ((_acc: T | undefined, chunk: C) => chunk as unknown as T);\n // equals: () => false — a stream notifies on EVERY emit, even an equal value.\n const cell = signal<T | undefined>(options.initial, () => false);\n const read = propDesc.getter ?? DEFAULT_GETTER;\n const handler = (event: Event): void => {\n const chunk = read(event) as C;\n cell.set(fold(cell.peek(), chunk));\n };\n node.addEventListener(propDesc.event, handler);\n removers.push(() => node.removeEventListener(propDesc.event, handler));\n return cell;\n },\n\n set(name: string, value: unknown): void {\n assertLive(\"set\", name);\n if (!declaredInputs.has(name)) {\n throw new Error(`bindNode.set: \"${name}\" is not a declared input on this node.`);\n }\n node[name] = value;\n },\n\n bindInput(name: string, source: ReadSignal<unknown>): () => void {\n assertLive(\"bindInput\", name);\n if (!declaredInputs.has(name)) {\n throw new Error(`bindNode.bindInput: \"${name}\" is not a declared input on this node.`);\n }\n const handle = effect(() => {\n const v = source.get();\n if (node[name] !== v) {\n node[name] = v; // same-value guard above breaks write→event→write loops\n }\n });\n removers.push(() => handle.dispose());\n return () => handle.dispose();\n },\n\n command(name: string, ...args: unknown[]): unknown {\n assertLive(\"command\", name);\n if (!declaredCommands.has(name)) {\n throw new Error(`bindNode.command: \"${name}\" is not a declared command on this node.`);\n }\n if (typeof node[name] !== \"function\") {\n throw new TypeError(`bindNode.command: \"${name}\" is declared but not a function on the node.`);\n }\n return node[name](...args);\n },\n\n bindCommand(name: string, trigger: ReadSignal<unknown>, mapArgs?: (value: unknown) => unknown[]): () => void {\n assertLive(\"bindCommand\", name);\n if (!declaredCommands.has(name)) {\n throw new Error(`bindNode.bindCommand: \"${name}\" is not a declared command on this node.`);\n }\n // Fail fast at bind time (not on first change), so a wrong name surfaces when\n // the subscription is set up rather than silently later inside a flush.\n if (typeof node[name] !== \"function\") {\n throw new TypeError(`bindNode.bindCommand: \"${name}\" is declared but not a function on the node.`);\n }\n let primed = false;\n const handle = effect(() => {\n const v = trigger.get();\n if (!primed) {\n primed = true; // subscribe on mount without firing (emit on CHANGE only)\n return;\n }\n const args = mapArgs ? mapArgs(v) : [v];\n node[name](...args);\n });\n removers.push(() => handle.dispose());\n return () => handle.dispose();\n },\n\n dispose(): void {\n if (disposed) {\n return; // idempotent\n }\n disposed = true;\n for (const remove of removers) {\n remove();\n }\n },\n };\n\n return bound as unknown as BoundNode<S>;\n}\n\n/**\n * Build a `resource` source from a wc-bindable node, bridging the resource's\n * AbortSignal to the node's cancel command (default `\"abort\"`) before delegating to\n * `run`. This generalizes the earlier hand-wired `sig → core.abort()` bridge (docs §8\n * (e), §5-2): wrap the result in `resource({ args })` and any node that declares an\n * abort command gets switchMap-style cancel/restart for free — an `args` change\n * aborts the in-flight call (firing the node's abort command, which cancels its real\n * AbortController) and starts the next.\n *\n * The node's own value/loading/error stay available via `bound.signals`; `resource`\n * here is used for the cancel/restart lifecycle, not to re-derive that triad.\n *\n * @example\n * const bound = bindNode(fetchEl);\n * const r = resource(\n * nodeSource(bound, (b, id) => b.command(\"fetch\", `/api/${id}`)),\n * { args: () => id.get() },\n * );\n */\nexport function nodeSource<T, A = void, S extends NodeShape = DefaultNodeShape>(\n bound: BoundNode<S>,\n run: (node: BoundNode<S>, args: A) => Promise<T> | T,\n options: { abort?: string } = {},\n): ResourceSource<T, A> {\n const abortName = options.abort ?? \"abort\";\n return (args, signal) => {\n // Honor the resource's cancel by invoking the node's abort command. `once`:\n // each resource run gets a fresh AbortSignal, so the listener fires at most once.\n //\n // GUARD: the abort listener runs SYNCHRONOUSLY inside AbortController.abort().\n // If `bound` was already disposed (e.g. the adapter and the resource share an\n // owner and the adapter's disposer ran first), `command` throws assertLive —\n // and a throw out of an abort listener surfaces as an unhandled exception during\n // the synchronous abort() call. Swallow a post-dispose throw so teardown order is\n // robust; report any OTHER error via the platform reporter without breaking abort.\n signal.addEventListener(\n \"abort\",\n () => {\n try {\n // `abortName` is a runtime string (validated by the adapter); the cast lets\n // it reach `command` regardless of the typed command-name union of `S`.\n (bound.command as (name: string, ...args: unknown[]) => unknown)(abortName);\n } catch (err) {\n // Brand-based check (not a message regex): a post-dispose throw is\n // expected here and swallowed; any OTHER error is reported.\n if (!isDisposedError(err)) {\n reportError(err);\n }\n }\n },\n { once: true },\n );\n return run(bound, args);\n };\n}\n","// Async resource (v1 design notes).\n//\n// Adapts an async producer into a reactive `{ value, loading, error }` triad —\n// the same shape FetchCore exposes (value/loading/error). When the resource's\n// `args` (a reactive getter) change, the in-flight request is aborted and a fresh\n// one starts: dependency-driven cancel/restart, i.e. RxJS switchMap.\n//\n// This is the signals-side counterpart of the state-side `$streams` adapter\n// (docs/state-stream-type-design.md §4-1). The hard part — making sure a stale\n// response from a superseded request never lands on the new state — is handled\n// here by checking `signal.aborted` before committing any result.\n\nimport { signal, effect, onCleanup, ReadSignal } from \"./reactive.js\";\n\nexport interface ResourceState<T> {\n value: ReadSignal<T | undefined>;\n // Progress is a BOOLEAN here, intentionally different from streamResource's\n // `status` enum. A resource is single-shot (one request → resolve/reject), so\n // its progress is binary: in-flight or not. A stream is continuous (idle →\n // active → done/error), so it needs the enum. The \"shared contract\" with the\n // state-side `$streams` adapter covers the cancel/restart SEMANTICS, not this\n // progress representation. (docs §8 (a)).\n loading: ReadSignal<boolean>;\n /** Last error, or `null` when there is none. Initial value is `null`; a (re)start resets it to `null`. */\n error: ReadSignal<unknown>;\n /** Abort any in-flight request and stop reacting to `args` changes. */\n dispose(): void;\n}\n\nexport interface ResourceOptions<T, A> {\n /** Reactive inputs. Reading signals here wires restart-on-change. */\n args?: () => A;\n /** Seed value before the first resolution. */\n initial?: T;\n}\n\nexport type ResourceSource<T, A> = (args: A, signal: AbortSignal) => Promise<T> | T;\n\nexport function resource<T, A = void>(\n source: ResourceSource<T, A>,\n options: ResourceOptions<T, A> = {},\n): ResourceState<T> {\n const value = signal<T | undefined>(options.initial);\n const loading = signal<boolean>(false);\n const error = signal<unknown>(null);\n\n let controller: AbortController | null = null;\n\n const runner = effect(() => {\n // Reading args() inside the effect is what subscribes us to its signals, so a\n // change re-runs this body — that IS the restart trigger.\n //\n // NOTE (effect-internal writes): this effect writes loading/error/value, which\n // are signals OTHER effects may observe. A write that actually CHANGES the value\n // marks those observers stale and queues them on the same flush; the drain loop\n // then runs them in the same tick. (A same-value write — e.g. error.set(null)\n // when error is already null — is a no-op via the equality guard and notifies\n // nothing.) We never read these signals inside THIS effect, so we don't dirty\n // our own dependency — no cycle. (docs §8 (a)).\n const a = (options.args ? options.args() : undefined) as A;\n\n // Abort the previous request before starting the next (switchMap).\n controller?.abort();\n const ac = new AbortController();\n controller = ac;\n\n loading.set(true);\n error.set(null);\n\n // Call the source SYNCHRONOUSLY (so it receives ac.signal immediately and can\n // wire its abort listener before any teardown), but guard it with try/catch so\n // a synchronous throw is normalized into the same error/loading state as a\n // rejected promise. Without the guard a sync throw would escape the effect body\n // (and, on the initial run, the resource() call itself), leaving loading stuck\n // true and error unset.\n let produced: Promise<T> | T;\n try {\n produced = source(a, ac.signal);\n } catch (err) {\n error.set(err);\n loading.set(false);\n return;\n }\n\n // Settle via two-arm `.then` rather than `await` + try/catch (streamResource's\n // shape): a resource is SINGLE-SHOT — one resolve/reject lands once — so a flat\n // resolved/rejected pair maps cleanly. A stream is a CONTINUOUS loop whose chunks\n // and termination must share one error path, which an `await` loop in try/catch\n // expresses; that structure would be needless here.\n Promise.resolve(produced).then(\n (resolved) => {\n // Drop the result if this request was superseded/disposed: committing it\n // would let a stale response overwrite the newer request's state.\n if (ac.signal.aborted) {\n return;\n }\n value.set(resolved);\n loading.set(false);\n },\n (err) => {\n if (ac.signal.aborted) {\n return;\n }\n error.set(err);\n loading.set(false);\n },\n );\n });\n\n const dispose = (): void => {\n controller?.abort();\n runner.dispose();\n };\n\n // Auto-dispose with the enclosing owner (createRoot / parent effect), so a\n // resource created inside a component is aborted on unmount. No-op when there\n // is no owner — the caller then disposes manually.\n onCleanup(dispose);\n\n return { value, loading, error, dispose };\n}\n","// Stream resource (docs §8 (a)). The signals-side counterpart of the state-side\n// `$streams` adapter (docs/state-stream-type-design.md).\n//\n// Adapts a continuous async flow (async iterable / ReadableStream / async\n// generator) into a single reactive value by FOLDING each chunk:\n// - latest (default): replace — value becomes the last chunk.\n// - reduce: accumulate — value = fold(acc, chunk), needs `initial`.\n// When the resource's `args` change, the in-flight stream is aborted and a fresh\n// one starts (switchMap), with `value` reset to `initial`.\n//\n// Deliberate non-goal: backpressure. The fold result IS the buffer; demand does\n// not flow back to the producer. Unbounded accumulation of an infinite stream is\n// a footgun — bound the fold (latest / count / last-N / window) for live streams.\n// This mirrors the state `$streams` norm and is what lets the impedance mismatch\n// be resolved honestly (state-stream §0, §4-3).\n//\n// Shared contract with state `$streams` (settled in v1):\n// - source(args, signal) receives an AbortSignal; honoring it drives restart.\n// - restart RESETS value to `initial`; error KEEPS the last value.\n// - status companion: \"idle\" | \"active\" | \"done\" | \"error\".\n// - async iterable is the lingua franca; anything getReader-bearing\n// (ReadableStream) is read via getReader() — see rescue levels below.\n//\n// CONTRACT (cooperative cancellation — STRONG REQUIREMENT): the `source` MUST honor\n// the `AbortSignal` it is given. Honoring it is what drives switchMap restart/dispose;\n// a source that ignores it cannot be reliably cancelled.\n//\n// Rescue levels on abort:\n// - ReadableStream: FULLY rescued. A parked read() is force-unwound via\n// reader.cancel(), which both releases the underlying source and settles the\n// pending read() so the loop unwinds. Anything getReader-bearing is routed\n// through this path even when it is ALSO natively async-iterable (Node 18+ /\n// Chrome 124+ / Firefox): the spec serializes asyncIterator.return() behind\n// the pending next() ([[ongoingPromise]]), so the AsyncIterable rescue below\n// cannot force-unwind a parked read on a native ReadableStream.\n// - AsyncIterable / async generator: PARTIALLY rescued. On abort we call\n// iterator.return() to trigger the generator's finally/cleanup. But a parked\n// `await` (the producer stalling before its next yield while IGNORING `signal`)\n// cannot be force-unwound from outside — return() only takes effect when the\n// generator next resumes. So a source that parks forever and never observes\n// `signal` still leaks its consume() task. Honor `signal` to bound this.\n// The `if (signal.aborted) return` check only runs after a chunk arrives, not while\n// parked — it drops stale chunks but is not, by itself, a cancellation mechanism.\n\nimport { signal, effect, onCleanup, ReadSignal, WriteSignal } from \"./reactive.js\";\n\nexport type StreamStatus = \"idle\" | \"active\" | \"done\" | \"error\";\n\nexport interface StreamResourceState<T> {\n value: ReadSignal<T | undefined>;\n // Progress is an ENUM here, intentionally different from resource's `loading`\n // boolean: a continuous stream moves idle → active → done/error, which a boolean\n // cannot express. The cancel/restart contract is shared with `resource` and the\n // state-side `$streams` adapter; the progress representation is not.\n status: ReadSignal<StreamStatus>;\n /** Last error, or `null` when there is none. Initial value is `null`; a (re)start resets it to `null`. */\n error: ReadSignal<unknown>;\n /** Abort the in-flight stream and stop reacting to `args`. */\n dispose(): void;\n}\n\nexport interface StreamResourceOptions<T, C, A> {\n /** Reactive inputs. Reading signals here wires restart-on-change. */\n args?: () => A;\n /** Combine the running value with each chunk. Default: latest (replace). */\n fold?: (acc: T | undefined, chunk: C) => T;\n /** Seed value, and the value `value` is reset to on each (re)start. */\n initial?: T;\n}\n\nexport type StreamProducer<C> = AsyncIterable<C> | ReadableStream<C>;\n\n/**\n * Produce a stream for the current `args`. The `signal` aborts on restart/dispose —\n * the source **MUST honor it** (this is a hard contract, not a suggestion): a\n * `ReadableStream` is fully cancelled via `reader.cancel()`, and an async generator\n * should observe `signal` (e.g. reject/break on `signal.aborted`) so a parked `await`\n * can unwind. On abort the adapter also calls the iterator's `return()` to run a\n * generator's `finally`, but a generator that parks forever while ignoring `signal`\n * cannot be force-unwound and will leak. See the module header's CONTRACT.\n */\nexport type StreamSource<C, A> = (\n args: A,\n signal: AbortSignal,\n) => StreamProducer<C> | Promise<StreamProducer<C>>;\n\nexport function streamResource<T, C = T, A = void>(\n source: StreamSource<C, A>,\n options: StreamResourceOptions<T, C, A> = {},\n): StreamResourceState<T> {\n const fold = options.fold ?? ((_acc: T | undefined, chunk: C) => chunk as unknown as T);\n const value = signal<T | undefined>(options.initial);\n const status = signal<StreamStatus>(\"idle\");\n const error = signal<unknown>(null);\n\n let controller: AbortController | null = null;\n\n const runner = effect(() => {\n // NOTE (effect-internal writes): like `resource`, this effect writes\n // value/status/error — signals other effects may observe. Those observers are\n // queued on the same flush and run in the same tick. This effect never reads\n // those signals, so it does not dirty its own dependency (no cycle). The async\n // `consume` writes them later from microtasks/timers, outside this flush.\n const a = (options.args ? options.args() : undefined) as A;\n\n controller?.abort();\n const ac = new AbortController();\n controller = ac;\n\n // Reset for the new run: a restart starts the fold from `initial`.\n value.set(options.initial);\n error.set(null);\n status.set(\"active\");\n\n void consume(source, a, ac.signal, fold, value, status, error);\n });\n\n const dispose = (): void => {\n controller?.abort();\n runner.dispose();\n };\n onCleanup(dispose);\n\n return { value, status, error, dispose };\n}\n\nasync function consume<T, C, A>(\n source: StreamSource<C, A>,\n args: A,\n signal: AbortSignal,\n fold: (acc: T | undefined, chunk: C) => T,\n // Use the public WriteSignal type instead of re-declaring an inline subset, so\n // these stay in lockstep with the signal API (no structural drift).\n value: WriteSignal<T | undefined>,\n status: WriteSignal<StreamStatus>,\n error: WriteSignal<unknown>,\n): Promise<void> {\n // Obtain the iterator EXPLICITLY (not via `for await`'s implicit one) so abort can\n // call `iterator.return()` to trigger an AsyncIterable / async generator's\n // `finally`/cleanup. A `for await` only calls `.return()` when the loop itself exits;\n // if the producer is PARKED (awaiting before the next yield while ignoring `signal`),\n // the loop never advances, so the implicit `.return()` never runs and the task leaks\n // past restart/dispose. Calling `.return()` on abort is the PARTIAL rescue: the\n // parked `await` cannot be force-unwound from outside, but once the generator resumes\n // (its next tick), `.return()` makes it run its `finally` and stop — recovering the\n // common \"generator wakes up after abort\" case. The ReadableStream path is fully\n // rescued via `reader.cancel()` (see `readableToAsyncIterable`).\n let iterator: AsyncIterator<C> | null = null;\n // Guard against returning the SAME iterator twice. `onAbort` is reachable two ways:\n // the abort listener, and the explicit call on line ~163 when abort raced the\n // `await source(...)`. The guard keys on the iterator instance (not a plain \"ran\"\n // flag): the listener firing with iterator still null must NOT consume the single\n // real cleanup that the explicit call performs once the iterator exists. So we only\n // mark an iterator returned once we have actually called `.return()` on it.\n let returned: AsyncIterator<C> | null = null;\n const onAbort = (): void => {\n if (!iterator || iterator === returned) {\n return; // nothing to release yet, or already released this iterator\n }\n returned = iterator;\n // Fire the iterator's cleanup. Swallow any throw/rejection from `.return()` — we\n // are tearing down; a producer that rejects on return must not surface here.\n try {\n void iterator.return?.()?.then?.(undefined, () => {});\n } catch {\n // `.return()` threw synchronously while tearing down — ignore.\n }\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n const produced = await source(args, signal);\n iterator = iterate(produced, signal)[Symbol.asyncIterator]();\n if (signal.aborted) {\n // Aborted while awaiting the source: the abort listener already ran (iterator\n // was still null then), so explicitly release the just-produced iterator now —\n // this fires a generator's finally / a ReadableStream's cancel for the\n // resource we created but will never iterate.\n onAbort();\n return;\n }\n for (;;) {\n const result = await iterator.next();\n if (result.done) {\n break;\n }\n if (signal.aborted) {\n return; // stale chunk from a superseded/disposed run — drop it\n }\n value.set(fold(value.peek(), result.value));\n }\n if (signal.aborted) {\n return; // stream ended but this run was aborted — don't mark done\n }\n status.set(\"done\");\n } catch (e) {\n if (signal.aborted) {\n return; // an abort that surfaced as a throw is not an error\n }\n error.set(e); // keep the last folded value (do not reset)\n status.set(\"error\");\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n}\n\nfunction iterate<C>(produced: StreamProducer<C>, signal: AbortSignal): AsyncIterable<C> {\n // Anything ReadableStream-shaped (has getReader) takes the reader path FIRST —\n // even when it is also async-iterable. On runtimes where ReadableStream\n // implements native async iteration (Node 18+ / Chrome 124+ / Firefox), the\n // spec serializes `iterator.return()` behind the pending `next()`\n // ([[ongoingPromise]]), so the AsyncIterable rescue cannot force-unwind a\n // parked read: abort would hang the consume task until the producer's next\n // chunk (or forever on a silent stream), leaking the underlying source.\n // `reader.cancel()` has no such ordering — routing every getReader-bearing\n // source through it keeps the FULL rescue promised for ReadableStream.\n if (typeof (produced as ReadableStream<C>)?.getReader === \"function\") {\n return readableToAsyncIterable(produced as ReadableStream<C>, signal);\n }\n if (typeof (produced as AsyncIterable<C>)?.[Symbol.asyncIterator] === \"function\") {\n return produced as AsyncIterable<C>;\n }\n throw new TypeError(\n \"streamResource: source must return an AsyncIterable or a ReadableStream (got neither).\",\n );\n}\n\nasync function* readableToAsyncIterable<C>(\n stream: ReadableStream<C>,\n signal: AbortSignal,\n): AsyncGenerator<C> {\n const reader = stream.getReader();\n // A ReadableStream read() does NOT observe an AbortSignal on its own. Without\n // this, a switchMap restart / dispose leaves the previous reader parked in a\n // pending read() forever, leaking the underlying source. Cancelling on abort\n // both releases the source AND settles the pending read() so the for-await\n // unwinds and the finally below can release the lock. Abort is the only\n // early-exit path for this generator (the consumer never calls .return()\n // without aborting), so this is the sole place a non-drained stream is cancelled.\n const onAbort = (): void => {\n void reader.cancel().catch(() => {}); // tearing down; swallow a rejected cancel\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n return;\n }\n yield value as C;\n }\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n reader.releaseLock();\n }\n}\n"],"names":[],"mappings":"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"}