@weftui/dom 0.26.0 → 0.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # @weftui/dom
2
+
3
+ > The DOM renderer for [Weft](https://weftui.dev) — mount and hydrate in the browser, render to string or stream on the server.
4
+
5
+ Takes a [`@weftui/core`](https://weftui.dev/docs/reference/core) node tree and renders it to real DOM on the client or to HTML on the server. There is no virtual DOM and no diffing — streams patch the tree in place. The same tree renders to HTML with `renderToStringHydratable` and `hydrate()`s flash-free on the client.
6
+
7
+ Two entry points: `@weftui/dom/client` for the browser, `@weftui/dom/server` for Node.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @weftui/core @weftui/dom effect
13
+ ```
14
+
15
+ `effect` is a peer dependency; `@weftui/core` is required to author the tree.
16
+
17
+ ## Key exports
18
+
19
+ ### `@weftui/dom/client`
20
+
21
+ | Export | What it does |
22
+ | ------------------------------- | ---------------------------------------------------------------------------------------- |
23
+ | `mount(node, target)` | Renders a node into `target` for a fresh (non-SSR) page and starts all streams. |
24
+ | `hydrate(node, target)` | Adopts server-rendered DOM **in place** and resumes reactivity — the flash-free path. |
25
+ | `mountScoped` / `hydrateScoped` | Scope-aware variants that register teardown as a finalizer on an ambient `Scope`. |
26
+ | `MountHandle` | Handle returned by mount/hydrate; `unmount()` tears the reactive tree down (idempotent). |
27
+
28
+ ### `@weftui/dom/server`
29
+
30
+ | Export | What it does |
31
+ | -------------------------------- | -------------------------------------------------------------------------------- |
32
+ | `renderToString` | Renders a node to a complete HTML string (static, non-hydrated). |
33
+ | `renderToStringHydratable` | Same, plus the hydration markers `hydrate` needs. Pair with `hydrate`. |
34
+ | `renderToStream` / `…Hydratable` | Streaming variants — emit HTML chunks as the tree resolves, for progressive SSR. |
35
+ | `renderToHydratableShell` | Produces the document scaffold for servers that assemble the shell separately. |
36
+
37
+ The package root re-exports the renderer error types: `HydrationMismatchError`, `UnsupportedNodeTypeError`, `RenderError`, `StreamSubscriptionError`.
38
+
39
+ ## Example
40
+
41
+ ```typescript
42
+ import { h } from "@weftui/core";
43
+ import { mount } from "@weftui/dom/client";
44
+ import { Effect, SubscriptionRef } from "effect";
45
+
46
+ const Counter = () =>
47
+ Effect.gen(function* () {
48
+ const count = yield* SubscriptionRef.make(0);
49
+ return yield* h.button({ onclick: () => SubscriptionRef.update(count, (n) => n + 1) }, [
50
+ count.changes,
51
+ ]);
52
+ });
53
+
54
+ void Effect.runPromise(mount(Counter(), document.getElementById("root")!));
55
+ ```
56
+
57
+ ## Documentation
58
+
59
+ - Full docs: **https://weftui.dev**
60
+ - `@weftui/dom` API reference: **https://weftui.dev/docs/reference/dom**
61
+ - Server-side rendering guide: **https://weftui.dev/docs/how-to/render-on-the-server**
62
+ - Bundled with this package: see the [`./docs`](./docs) directory in `node_modules/@weftui/dom/docs` — the complete tutorial, how-to, explanation, and reference tree ships on disk for offline and agent use.
63
+
64
+ ## License
65
+
66
+ MIT © Stef van Wijchen
@@ -1 +1 @@
1
- import{i as e,n as t,o as n,r,s as i,t as a}from"../data-Bk62ePeP.js";import{a as o,c as s,d as c,f as l,i as u,l as d,m as f,n as p,o as m,p as h,s as g,u as _}from"../boundary-replay-BR26_puM.js";import{Cause as v,Context as y,Deferred as b,Effect as x,ExecutionStrategy as S,Exit as C,Fiber as w,HashMap as T,HashSet as E,Layer as ee,ManagedRuntime as te,Option as D,Ref as O,Schema as ne,Scope as k,Stream as A,SubscriptionRef as j,pipe as M}from"effect";import{AppRpcClientTag as re,FAILURE_BOUNDARY as ie,FRAGMENT as ae,LIST as oe,SERVER_BOUNDARY as se,SUSPENSE_BOUNDARY as ce,Source as le,getElementDescriptor as ue,isStream as N,toStream as P}from"@weftui/core";function F(){return x.gen(function*(){let e=yield*r;return++e.streamIdCounter.current})}const de=F;let fe=0;const I=()=>++fe;function pe(e){if(e.length<=2||!e.startsWith(`on`))return!1;let t=e[2];return t!==void 0&&t>=`a`&&t<=`z`}function L(e,t){return x.gen(function*(){for(let[n,r]of Object.entries(t))if(n!==`children`){if(pe(n)){yield*be(e,n,r);continue}if(n===`ref`&&typeof r==`object`&&O.RefTypeId in r){yield*O.set(r,D.some(e));continue}if(n===`style`){yield*ve(e,r);continue}me(e,n)?yield*he(e,n,r):yield*ge(e,n,r)}})}function me(e,t){if(t.startsWith(`data-`)||t.startsWith(`aria-`))return!1;let n=Object.getPrototypeOf(e);for(;n!==null;){if(Object.hasOwn(n,t))return!0;n=Object.getPrototypeOf(n)}return t in e}function he(e,t,n){return x.gen(function*(){N(n)||x.isEffect(n)?yield*z(P(n),n=>{n==null?delete e[t]:e[t]=n},`property:${t}`):n!=null&&(e[t]=n)})}function ge(e,t,n){return x.gen(function*(){if(N(n)||x.isEffect(n))yield*z(P(n),n=>{if(n==null)e.removeAttribute(t);else{let r=_e(n);r!==void 0&&(typeof n==`boolean`?n?e.setAttribute(t,``):e.removeAttribute(t):e.setAttribute(t,r))}},`attribute:${t}`);else{let r=_e(n);r!==void 0&&(typeof n==`boolean`?n?e.setAttribute(t,``):e.removeAttribute(t):e.setAttribute(t,r))}})}function _e(e){if(e!=null)return String(e)}function ve(e,t){return x.gen(function*(){if(N(t)||x.isEffect(t)){yield*z(P(t),t=>{if(typeof t==`string`)e.setAttribute(`style`,t);else if(typeof t==`object`&&t){e.style.cssText=``;for(let[n,r]of Object.entries(t))r!=null&&e.style.setProperty(R(n),String(r))}},`style`);return}if(typeof t==`string`){e.setAttribute(`style`,t);return}typeof t==`object`&&t&&(yield*ye(e,t))})}function ye(e,t){return x.gen(function*(){for(let[n,r]of Object.entries(t))N(r)||x.isEffect(r)?yield*z(P(r),t=>{t!=null&&e.style.setProperty(R(n),String(t))},`style.${n}`):r!=null&&e.style.setProperty(R(n),String(r))})}function R(e){return e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}function z(e,t,n){return x.gen(function*(){let n=yield*r,i=yield*x.serviceOption(a),o=A.runForEach(e,e=>x.sync(()=>void t(e))),s=yield*x.forkIn(o,n.scope);yield*M(w.await(s),x.flatMap(e=>C.isFailure(e)&&D.isSome(i)?i.value.reportError(e.cause):x.void),x.forkIn(n.scope))})}function be(e,t,n){return x.gen(function*(){let i=yield*r,a=t.slice(2).toLowerCase(),o=null,s=()=>{o&&=(e.removeEventListener(a,o),null)},c=n=>{s(),!(n==null||n===!1)&&typeof n==`function`&&(o=e=>{let r=n(e);x.isEffect(r)&&i.runtime.runFork(M(r,x.catchAll(e=>process.env.NODE_ENV===`development`?x.logError(`Event handler error: ${t}`,{error:e}):x.void)))},e.addEventListener(a,o))};yield*k.addFinalizer(i.scope,x.sync(s)),N(n)||x.isEffect(n)?yield*z(P(n),e=>c(e),`event:${t}`):c(n)})}function xe(e,t,n,r,i,a){return x.gen(function*(){let o=yield*b.await(t).pipe(x.flip,x.catchAll(()=>x.interrupt)),s=e.match(o);if(yield*k.close(n,C.void),s===null)return D.isSome(r)?yield*r.value.reportError(o):yield*x.logError(`Unhandled error escaped the outermost Boundary`,o);K(i,a);let c=yield*B(s),l=a.parentNode;if(l!==null&&c!==null)if(Array.isArray(c))for(let e of c)l.insertBefore(e,a);else l.insertBefore(c,a)})}function Se(e){return x.gen(function*(){let t=yield*r,n=yield*x.serviceOption(a),i=I(),s=document.createComment(o(i)),c=document.createComment(u(i)),l=yield*k.fork(t.scope,S.sequential),d={...t,scope:l},f=yield*b.make(),p=yield*M(V(e.children),x.provideService(a,{reportError:e=>b.fail(f,e).pipe(x.asVoid)}),x.provideService(r,d),x.provideService(k.Scope,l),x.catchAllCause(t=>{let n=e.match(t);return n===null?x.failCause(t):M(k.close(l,C.void),x.flatMap(()=>B(n)),x.map(e=>e===null?[]:Array.isArray(e)?e:[e]))}));return yield*x.forkIn(xe(e,f,l,n,s,c),t.scope),[s,...p,c]})}function Ce(e){return x.gen(function*(){let t=yield*r,i=yield*O.make(1),a=yield*b.make(),o=M(O.updateAndGet(i,e=>e-1),x.flatMap(e=>e<=0?x.asVoid(b.succeed(a,void 0)):x.void)),s={register:O.update(i,e=>e+1),settle:o},c=e.children,l=yield*B((c===void 0?[]:Array.isArray(c)?c:[c]).map(e=>x.isEffect(e)||N(e)?{type:()=>e,props:{}}:e)).pipe(x.provideService(n,s)),u=l===null?[]:Array.isArray(l)?l:[l];yield*o;let d=yield*b.poll(a);if(D.isSome(d))return u;let p=yield*de(),m=document.createComment(f(p)),g=document.createComment(h(p)),_=yield*B(e.fallback??null),v=[];_!==null&&(Array.isArray(_)?v.push(..._):v.push(_));let y=document.createDocumentFragment();for(let e of u)y.appendChild(e);let S=x.gen(function*(){yield*b.await(a),K(m,g);let e=g.parentNode;e!==null&&(e.insertBefore(y,g),m.remove(),g.remove())});return yield*x.forkIn(S,t.scope),[m,...v,g]})}function we(t){return x.gen(function*(){let n=yield*r,i=yield*x.serviceOption(re);if(D.isNone(i))return yield*x.fail(new e({cause:void 0,message:`Boundary.rpc "${t.tag}" was mounted client-first without an AppRpcClient in context. Mount the app under @weftui/router (RouterLive), which provides the rpc client, so the boundary can resolve its data.`}));let a=i.value,s=I(),c=document.createComment(o(s)),l=document.createComment(u(s)),d=yield*B(t.fallback??null),f=[];d!==null&&(Array.isArray(d)?f.push(...d):f.push(d));let p=x.gen(function*(){let e=yield*a.call(t.tag,t.payload()),n=yield*Xe(t.tag,t.payload,e,i),r=yield*B(t.render(n));K(c,l);let o=l.parentNode;if(o!==null&&r!==null)if(Array.isArray(r))for(let e of r)o.insertBefore(e,l);else o.insertBefore(r,l)}).pipe(x.catchAllCause(e=>x.logError(`[weft] Boundary.rpc "${t.tag}" mount failed to resolve; fallback left in place.`,e)));return yield*x.forkIn(p,n.scope),[c,...f,l]})}function B(e){return x.gen(function*(){if(typeof e==`string`||typeof e==`number`||typeof e==`bigint`)return document.createTextNode(String(e));if(typeof e==`boolean`||e==null)return null;if(N(e)||x.isEffect(e)){let t=ue(e);if(t!==void 0)return yield*B(t);if(x.isEffect(e)){let t=x.runSyncExit(e);if(C.isSuccess(t))return yield*B(t.value)}return yield*H(P(e))}if(typeof e==`object`&&Symbol.iterator in e&&!(`type`in e))return yield*V(Te(e));if(typeof e==`object`&&`type`in e&&!(Symbol.iterator in e)){let{type:t,props:n}=e;return t===ae?yield*Ee(n):t===ce?yield*Ce(n):t===se?yield*we(n):t===ie?yield*Se(n):t===oe?yield*Ne(n):typeof t==`string`?yield*De(t,n):typeof t==`function`?yield*Oe(t,n):yield*x.fail(new i({type:t,message:`Invalid Renderable type: expected string, FRAGMENT, or function, got ${typeof t}`}))}return null})}function Te(e){let t=[];function n(e){if(N(e)||x.isEffect(e)){t.push(e);return}if(typeof e==`object`&&e&&Symbol.iterator in e&&!(`type`in e))for(let t of e)n(t);else t.push(e)}return n(e),t}function V(e){return x.gen(function*(){let t=[];for(let n of e)if(N(n)||x.isEffect(n)){let e=yield*H(P(n));t.push(...e)}else{let e=yield*B(n);e!==null&&(Array.isArray(e)?t.push(...e):t.push(e))}return t})}function Ee(e){return x.gen(function*(){let t=`children`in e?e.children:void 0;return t===void 0?[]:yield*V(Array.isArray(t)?t:[t])})}function De(e,t){return x.gen(function*(){let n=document.createElement(e);yield*L(n,t);let r=`children`in t?t.children:void 0;if(r!==void 0){let e=Array.isArray(r)?r:[r];for(let t of e)if(N(t)||x.isEffect(t)){let e=yield*H(P(t));for(let t of e)n.appendChild(t)}else{let e=yield*B(t);if(e!==null)if(Array.isArray(e))for(let t of e)n.appendChild(t);else n.appendChild(e)}}return n})}function Oe(e,t){return x.gen(function*(){let i=e(t);if(N(i)||x.isEffect(i)){let e=yield*r,t=yield*k.fork(e.scope,S.sequential),a={...e,scope:t},o=yield*x.serviceOption(n),s=P(i);return D.isSome(o)&&(yield*o.value.register,s=M(s,A.zipWithIndex,A.flatMap(([e,t])=>t===0?A.fromEffect(x.as(o.value.settle,e)):A.make(e)))),yield*H(s).pipe(x.provideService(r,a),x.provideService(k.Scope,t))}return yield*B(i)})}function H(e){return x.gen(function*(){let t=yield*r,[n,i]=ke(yield*F()),o=null,s=A.runForEach(e,e=>x.gen(function*(){o!==null&&(yield*k.close(o,C.void)),o=yield*k.fork(t.scope,S.sequential);let a={...t,scope:o};yield*U(n,i,e).pipe(x.provideService(r,a),x.provideService(k.Scope,o))})),c=yield*x.forkIn(s,t.scope),l=yield*x.serviceOption(a);return yield*M(w.await(c),x.flatMap(e=>C.isFailure(e)&&D.isSome(l)?l.value.reportError(e.cause):x.void),x.forkIn(t.scope)),[n,i]})}function ke(e){return[document.createComment(l(e)),document.createComment(c(e))]}function U(e,t,n){return x.gen(function*(){let r=e.nextSibling,i=r!==null&&r!==t&&r.nextSibling===t;if(i&&r.nodeType===rt&&W(n)){let e=String(n);r.data!==e&&(r.data=e);return}if(i&&r.nodeType===X){let e=G(n);if(e!==void 0&&typeof e.type==`string`&&r.tagName.toLowerCase()===e.type.toLowerCase()){yield*Ae(r,e);return}}K(e,t);let a=yield*B(n),o=e.parentNode;if(o!==null&&a!==null)if(Array.isArray(a))for(let e of a)o.insertBefore(e,t);else o.insertBefore(a,t)})}function W(e){return typeof e==`string`||typeof e==`number`||typeof e==`bigint`}function G(e){let t=ue(e);if(t!==void 0)return t;if(typeof e==`object`&&e&&`type`in e&&!(Symbol.iterator in e)&&!N(e)&&!x.isEffect(e))return e}function Ae(e,t){return x.gen(function*(){yield*L(e,t.props);let n=t.props.children,r=n===void 0?[]:Array.isArray(n)?n:[n];if(!(yield*je(e,r))){for(;e.firstChild!==null;)e.firstChild.remove();yield*Me(e,r)}})}function je(e,t){return x.gen(function*(){let n=Array.from(e.childNodes);if(n.length!==t.length)return!1;for(let e=0;e<t.length;e++){let r=t[e],i=n[e];if(W(r)){if(i.nodeType!==rt)return!1}else{let e=G(r);if(e===void 0||typeof e.type!=`string`||i.nodeType!==X||i.tagName.toLowerCase()!==e.type.toLowerCase())return!1}}for(let e=0;e<t.length;e++){let r=t[e],i=n[e];if(W(r)){let e=String(r);i.data!==e&&(i.data=e)}else yield*Ae(i,G(r))}return!0})}function Me(e,t){return x.gen(function*(){for(let n of t)if(N(n)||x.isEffect(n)){let t=yield*H(P(n));for(let n of t)e.appendChild(n)}else{let t=yield*B(n);if(t!==null)if(Array.isArray(t))for(let n of t)e.appendChild(n);else e.appendChild(t)}})}function K(e,t){let n=e.nextSibling;for(;n!==null&&n!==t;){let e=n.nextSibling;n.remove(),n=e}}function Ne(e){return x.gen(function*(){let t=yield*r,{of:n,by:i,render:o}=e,[s,c]=ke(yield*F()),l=yield*k.fork(t.scope,S.sequential),u=(yield*le.toSubscribable(n).pipe(x.provideService(k.Scope,l))).changes,d={records:T.empty(),order:[]},f=A.runForEach(u,e=>x.gen(function*(){d=yield*q(Array.from(e),i,o,d,l,c,t)})),p=yield*x.forkIn(f,l),m=yield*x.serviceOption(a);return yield*M(w.await(p),x.flatMap(e=>C.isFailure(e)&&D.isSome(m)?m.value.reportError(e.cause):x.void),x.forkIn(t.scope)),[s,c]})}function Pe(t,n){return x.gen(function*(){let r=[],i=E.empty();for(let a=0;a<t.length;a++){let o=n===void 0?t[a]:n(t[a],a);if(E.has(i,o))return yield*x.fail(new e({cause:o,message:`List.each: duplicate key ${Re(o)} in a single emission; keys must be unique (set a stable \`by\`).`}));i=E.add(i,o),r.push(o)}return r})}function q(e,t,n,r,i,a,o){return x.gen(function*(){let s=yield*Pe(e,t),c=T.empty();r.order.forEach((e,t)=>{c=T.set(c,e,t)});let l=[],u=[];for(let t=0;t<e.length;t++){let a=s[t],d=T.get(r.records,a);if(D.isSome(d))l.push(d.value),u.push(D.getOrElse(T.get(c,a),()=>-1));else{let r=yield*Fe(a,e[t],t,n,i,o);l.push(r),u.push(-1)}}let d=E.empty();for(let e of s)d=E.add(d,e);for(let e of r.order)if(!E.has(d,e)){let t=T.get(r.records,e);D.isSome(t)&&(yield*k.close(t.value.scope,C.void),Le(t.value.startMarker,t.value.endMarker))}let f=ze(u),p=a.parentNode;if(p!==null)for(let e=l.length-1;e>=0;e--){let t=l[e];if(u[e]!==-1&&f.has(e))continue;let n=e+1<l.length?l[e+1].startMarker:a,r=u[e]===-1?[t.startMarker,...t.nodes,t.endMarker]:Ie(t.startMarker,t.endMarker);for(let e of r)p.insertBefore(e,n)}let m=T.empty();for(let e of l)m=T.set(m,e.key,e);return{records:m,order:s}})}function Fe(e,t,n,i,a,o){return x.gen(function*(){let s=yield*k.fork(a,S.sequential),c={...o,scope:s},l=yield*F(),u=document.createComment(g(l)),d=document.createComment(m(l)),f=yield*B(i(t,n)).pipe(x.provideService(r,c),x.provideService(k.Scope,s));return{key:e,scope:s,startMarker:u,endMarker:d,nodes:f===null?[]:Array.isArray(f)?f:[f]}})}function Ie(e,t){let n=[],r=e;for(;r!==null&&(n.push(r),r!==t);)r=r.nextSibling;return n}function Le(e,t){let n=e;for(;n!==null;){let e=n.nextSibling;if(n.remove(),n===t)break;n=e}}function Re(e){if(typeof e==`string`)return JSON.stringify(e);if(typeof e==`object`&&e)try{return JSON.stringify(e)}catch{return Object.prototype.toString.call(e)}return String(e)}function ze(e){let t=e.length,n=[],r=Array.from({length:t},()=>-1);for(let i=0;i<t;i++){let t=e[i];if(t===-1)continue;let a=0,o=n.length;for(;a<o;){let r=a+o>>1;e[n[r]]<t?a=r+1:o=r}a>0&&(r[i]=n[a-1]),n[a]=i}let i=new Set,a=n.length>0?n[n.length-1]:-1;for(;a!==-1;)i.add(a),a=r[a];return i}function Be(e,t){return x.gen(function*(){let n=yield*x.context(),i=yield*x.serviceOption(k.Scope),a=yield*k.make(),o=te.make(ee.succeedContext(y.add(n,k.Scope,a))),s={runtime:o,scope:a,streamIdCounter:{current:0}},c=x.zipRight(k.close(a,C.void),x.promise(()=>o.dispose()));t.innerHTML=``;let l=yield*B(e).pipe(x.provideService(r,s),x.provideService(k.Scope,a),x.tapError(()=>c));if(l!==null)if(Array.isArray(l))for(let e of l)t.appendChild(e);else t.appendChild(l);let u=!1,d={unmount:()=>x.gen(function*(){u||(u=!0,yield*k.close(a,C.void),yield*x.promise(()=>o.dispose()))})};return D.isSome(i)&&(yield*k.addFinalizer(i.value,d.unmount())),d})}function Ve(e,t){return x.gen(function*(){let n=yield*x.context(),i=yield*x.serviceOption(k.Scope),a=yield*k.make(),o=te.make(ee.succeedContext(y.add(n,k.Scope,a))),s=yield*Ue(),c={runtime:o,scope:a,streamIdCounter:{current:0},hydrationReady:s};it(t,c.streamIdCounter);let l=x.zipRight(k.close(a,C.void),x.promise(()=>o.dispose()));yield*J(e,t.firstChild,`root`).pipe(x.provideService(r,c),x.provideService(k.Scope,a),x.tapError(()=>l)),yield*s.settle,yield*s.awaitReady;let u=!1,d={unmount:()=>x.gen(function*(){u||(u=!0,yield*k.close(a,C.void),yield*x.promise(()=>o.dispose()))})};return D.isSome(i)&&(yield*k.addFinalizer(i.value,d.unmount())),d})}function J(e,t,n){return x.gen(function*(){if(typeof e==`string`||typeof e==`number`||typeof e==`bigint`)return yield*He(String(e),t,n);if(typeof e==`boolean`||e==null)return t;if(N(e)||x.isEffect(e)){let r=ue(e);if(r!==void 0)return yield*J(r,t,n);if(x.isEffect(e)){let r=x.runSyncExit(e);if(C.isSuccess(r))return yield*J(r.value,t,n)}return yield*Ge(P(e),t,n)}if(typeof e==`object`&&Symbol.iterator in e&&!(`type`in e)){let r=t,i=0;for(let t of e)r=yield*J(t,r,`${n}[${i}]`),i++;return r}if(typeof e==`object`&&`type`in e&&!(Symbol.iterator in e)){let{type:r,props:a}=e;if(r===ae)return yield*Y(a,t,n);if(r===ce){if(t!==null&&t.nodeType===Z){let e=_(t);if(e!==null&&e.kind===`start`)return yield*qe(t,n)}return yield*Y(a,t,n)}return r===ie?yield*Ye(a,t,n):r===se?yield*Ze(a,t,n):r===oe?yield*$e(a,t,n):typeof r==`string`?yield*Qe(r,a,t,n):typeof r==`function`?yield*J(r(a),t,n):yield*x.fail(new i({type:r,message:`Invalid Renderable type during hydration at ${n}: expected string, FRAGMENT, or function, got ${typeof r}`}))}return t})}function He(e,t,n){return x.gen(function*(){if(e.length===0)return t;if(t===null||t.nodeType!==rt)return yield*$(`text ${JSON.stringify(e)}`,Q(t),n);let r=t;return r.data.startsWith(e)?r.data.length>e.length?r.splitText(e.length):r.nextSibling:yield*$(`text ${JSON.stringify(e)}`,Q(t),n)})}function Ue(){return x.gen(function*(){let e=yield*O.make(1),t=yield*b.make(),n=M(O.updateAndGet(e,e=>e-1),x.flatMap(e=>e<=0?x.asVoid(b.succeed(t,void 0)):x.void));return{register:O.update(e,e=>e+1),settle:n,awaitReady:b.await(t)}})}function We(e){let t=!1;return x.suspend(()=>t||e===void 0?x.void:(t=!0,e.settle))}function Ge(e,t,n){return x.gen(function*(){let i=yield*r;if(t===null||t.nodeType!==Z)return yield*$(`reactive region start marker`,Q(t),n);let o=t,s=d(o);if(s===null||s.kind!==`start`)return yield*$(`reactive region start marker`,Q(t),n);let c=at(o);if(c===null)return yield*$(`reactive region end marker`,`unterminated region starting at ${JSON.stringify(o.data)}`,n);let l=We(i.hydrationReady),u=!0,f=null,p=A.runForEach(e,e=>x.gen(function*(){f!==null&&(yield*k.close(f,C.void)),f=yield*k.fork(i.scope,S.sequential);let t={...i,scope:f};yield*x.gen(function*(){u?(u=!1,yield*Ke(e,o,c,n),yield*l):yield*U(o,c,e)}).pipe(x.provideService(r,t),x.provideService(k.Scope,f))})).pipe(x.ensuring(l));i.hydrationReady!==void 0&&(yield*i.hydrationReady.register);let m=yield*x.forkIn(p,i.scope),h=yield*x.serviceOption(a);return yield*M(w.await(m),x.flatMap(e=>C.isFailure(e)&&D.isSome(h)?h.value.reportError(e.cause):x.void),x.forkIn(i.scope)),c.nextSibling})}function Ke(e,t,n,r){return x.gen(function*(){let i=yield*J(e,t.nextSibling,`${r}<resume>`).pipe(x.map(e=>e===n?null:`adopted content did not align with the end marker`),x.catchTag(`HydrationMismatchError`,e=>x.succeed(`expected ${e.expected}, found ${e.actual} at ${e.path}`)));i!==null&&(console.error(`[weft] hydrate: reactive region at ${r} diverged from server output (${i}); patching.`),yield*U(t,n,e))})}function qe(e,t){return x.gen(function*(){let n=Je(e);if(n===null)return yield*$(`substituted suspense end marker`,`unterminated region starting at ${JSON.stringify(e.data)}`,t);let r=null;for(let t=e.nextSibling;t!==null&&t!==n;t=t.nextSibling)if(t.nodeType===X&&t.tagName===`SCRIPT`&&t.getAttribute(`type`)===`application/json`&&t.hasAttribute(`data-weft-suspense-failure`)){r=t;break}let i=null;if(r!==null){let e=r.textContent??``;i=yield*x.try({try:()=>JSON.parse(e),catch:e=>e}).pipe(x.catchAll(()=>x.succeed(null)))}if(typeof i!=`object`||!i||!(`error`in i))return console.error(`[weft] hydrate: substituted suspense region at ${t} has no decodable failure sentinel; leaving its static content.`),n.nextSibling;let o=yield*x.serviceOption(a);return D.isNone(o)?(console.error(`[weft] hydrate: substituted suspense region at ${t} has no enclosing Boundary to replay its failure to; leaving its static content.`,i.error),n.nextSibling):(yield*o.value.reportError(v.fail(i.error)),n.nextSibling)})}function Je(e){let t=0,n=e.nextSibling;for(;n!==null;){if(n.nodeType===Z){let e=_(n);if(e!==null)if(e.kind===`start`)t++;else if(t===0)return n;else t--}n=n.nextSibling}return null}function Ye(e,t,n){return x.gen(function*(){if(t===null||t.nodeType!==X||t.tagName!==`SCRIPT`||t.getAttribute(`type`)!==`application/json`||!t.hasAttribute(`data-weft-boundary-failure`)){let i=yield*r,s=yield*x.serviceOption(a),c=yield*k.fork(i.scope,S.sequential),l={...i,scope:c},d=yield*b.make(),f=yield*Y(e,t,n).pipe(x.provideService(a,{reportError:e=>b.fail(d,e).pipe(x.asVoid)}),x.provideService(r,l),x.provideService(k.Scope,c)),p=t?.parentNode??null;if(t===null||t===f||p===null)return console.error(`[weft] hydrate: boundary at ${n} adopted an empty extent; live failure recovery is not installed for it.`),f;let m=I(),h=document.createComment(o(m)),g=document.createComment(u(m));return p.insertBefore(h,t),f===null?p.appendChild(g):p.insertBefore(g,f),yield*x.forkIn(xe(e,d,c,s,h,g),i.scope),f}let i=t,s=i.textContent??``,c=yield*x.gen(function*(){let t=yield*x.try({try:()=>JSON.parse(s),catch:e=>e}),n=p(e.children)[t.index];if(n===void 0)return null;let r=yield*ne.decodeUnknown(n.errorSchema)(t.error);return e.match(v.fail(r))}).pipe(x.catchAll(e=>(console.error(`[weft] hydrate: boundary failure payload at ${n} failed to decode; cannot replay.`,e),x.succeed(null))));if(c===null)return yield*$(`replayable boundary failure`,`undecodable failure payload`,n);let l=yield*J(c,i.nextSibling,n);return i.remove(),l})}function Xe(e,t,n,r){return x.gen(function*(){let i=yield*j.make(n),a=yield*j.make(!1),o=yield*j.make(D.none());return{value:i,refetch:D.match(r,{onNone:()=>x.void,onSome:n=>x.gen(function*(){(yield*j.get(a))||(yield*j.set(a,!0),yield*x.gen(function*(){let r=yield*x.exit(n.call(e,t()));C.isSuccess(r)?(yield*j.set(i,r.value),yield*j.set(o,D.none())):yield*j.set(o,D.some(v.squash(r.cause)))}).pipe(x.ensuring(j.set(a,!1))))})}),pending:a,error:o}})}function Ze(e,t,n){return x.gen(function*(){if(t===null||t.nodeType!==X||t.tagName!==`SCRIPT`||t.getAttribute(`type`)!==`application/json`)return yield*$(`server boundary payload <script type="application/json">`,Q(t),n);if(t.hasAttribute(`data-weft-boundary-failure`))return yield*$(`server boundary success payload <script type="application/json">`,`boundary failure payload`,n);let r=t,i=r.textContent??``,a=yield*x.try({try:()=>JSON.parse(i),catch:e=>e}).pipe(x.flatMap(t=>ne.decodeUnknown(e.successSchema)(t)),x.catchAll(e=>(console.error(`[weft] hydrate: server boundary payload at ${n} failed to decode; cannot replay.`,e),$(`decodable server boundary payload`,`undecodable payload`,n)))),o=yield*x.serviceOption(re),s=yield*Xe(e.tag,e.payload,a,o),c=yield*J(e.render(s),r.nextSibling,n);return r.remove(),c})}function Qe(e,t,n,r){return x.gen(function*(){if(n===null||n.nodeType!==X)return yield*$(`<${e}>`,Q(n),r);let i=n;return i.tagName.toLowerCase()===e.toLowerCase()?(yield*L(i,t),yield*Y(t,i.firstChild,`${r} > ${e}`),i.nextSibling):yield*$(`<${e}>`,Q(n),r)})}function Y(e,t,n){return x.gen(function*(){let r=`children`in e?e.children:void 0;if(r===void 0)return t;let i=Array.isArray(r)?r:[r],a=t,o=0;for(let e of i)a=yield*J(e,a,`${n}[${o}]`),o++;return a})}function $e(e,t,n){return x.gen(function*(){let i=yield*r,{of:o,by:s,render:c}=e;if(t===null||t.nodeType!==Z)return yield*$(`list region start marker`,Q(t),n);let l=t,u=d(l);if(u===null||u.kind!==`start`)return yield*$(`list region start marker`,Q(t),n);let f=at(l);if(f===null)return yield*$(`list region end marker`,`unterminated region starting at ${JSON.stringify(l.data)}`,n);let p=et(l,f),m=yield*k.fork(i.scope,S.sequential),h=(yield*le.toSubscribable(o).pipe(x.provideService(k.Scope,m))).changes,g={records:T.empty(),order:[]},_=!0,v=We(i.hydrationReady),y=A.runForEach(h,e=>x.gen(function*(){let t=Array.from(e);_?(_=!1,g=yield*tt(t,s,c,p,m,l,f,i,n),yield*v):g=yield*q(t,s,c,g,m,f,i)})).pipe(x.ensuring(v));i.hydrationReady!==void 0&&(yield*i.hydrationReady.register);let b=yield*x.forkIn(y,m),E=yield*x.serviceOption(a);return yield*M(w.await(b),x.flatMap(e=>C.isFailure(e)&&D.isSome(E)?E.value.reportError(e.cause):x.void),x.forkIn(i.scope)),f.nextSibling})}function et(e,t){let n=[],r=e.nextSibling;for(;r!==null&&r!==t;){let e=r.nodeType===Z?s(r):null;if(e===null||e.kind!==`start`){r=r.nextSibling;continue}let i=r,a=[],o=0,c=null,l=i.nextSibling;for(;l!==null&&l!==t;){if(l.nodeType===Z){let e=s(l);if(e!==null)if(e.kind===`start`)o++;else if(o===0){c=l;break}else o--}a.push(l),l=l.nextSibling}if(c===null)break;n.push({startMarker:i,endMarker:c,nodes:a}),r=c.nextSibling}return n}function tt(e,t,n,r,i,a,o,s,c){return x.gen(function*(){let l=yield*Pe(e,t);if(l.length!==r.length)return console.error(`[weft] hydrate: list region at ${c} had ${r.length} server item(s) but the first emission has ${l.length}; rebuilding.`),K(a,o),yield*q(e,t,n,{records:T.empty(),order:[]},i,o,s);let u=[];for(let t=0;t<e.length;t++){let a=yield*nt(l[t],e[t],t,n,r[t],i,s,c);u.push(a)}let d=T.empty();for(let e of u)d=T.set(d,e.key,e);return{records:d,order:l}})}function nt(e,t,n,i,a,o,s,c){return x.gen(function*(){let l=yield*k.fork(o,S.sequential),u={...s,scope:l},d=i(t,n),f=yield*J(d,a.startMarker.nextSibling,`${c}<item>`).pipe(x.provideService(r,u),x.provideService(k.Scope,l),x.map(e=>e===a.endMarker?null:`adopted item content did not align with its end marker`),x.catchTag(`HydrationMismatchError`,e=>x.succeed(`expected ${e.expected}, found ${e.actual} at ${e.path}`)));if(f===null)return{key:e,scope:l,startMarker:a.startMarker,endMarker:a.endMarker,nodes:a.nodes};console.error(`[weft] hydrate: list item at ${c} diverged from server output (${f}); patching.`),yield*k.close(l,C.void);let p=yield*k.fork(o,S.sequential),m={...s,scope:p};K(a.startMarker,a.endMarker);let h=yield*B(d).pipe(x.provideService(r,m),x.provideService(k.Scope,p)),g=h===null?[]:Array.isArray(h)?h:[h],_=a.endMarker.parentNode;if(_!==null)for(let e of g)_.insertBefore(e,a.endMarker);return{key:e,scope:p,startMarker:a.startMarker,endMarker:a.endMarker,nodes:g}})}const X=1,rt=3,Z=8;function it(e,t){let n=document.createTreeWalker(e,128),r=t.current;for(let e=n.nextNode();e!==null;e=n.nextNode()){let t=e,n=d(t)??_(t)??s(t);n!==null&&n.id>r&&(r=n.id)}t.current=r}function at(e){let t=0,n=e.nextSibling;for(;n!==null;){if(n.nodeType===8){let e=d(n);if(e!==null)if(e.kind===`start`)t++;else if(t===0)return n;else t--}n=n.nextSibling}return null}function Q(e){if(e===null)return`end of children`;switch(e.nodeType){case 1:return`<${e.tagName.toLowerCase()}>`;case 3:return`text ${JSON.stringify(e.data)}`;case 8:return`comment ${JSON.stringify(e.data)}`;default:return`node(type ${e.nodeType})`}}function $(e,n,r){return x.fail(new t({expected:e,actual:n,path:r}))}function ot(e,t){return M(Be(e,t),x.tap(e=>x.addFinalizer(()=>e.unmount())))}function st(e,t){return M(Ve(e,t),x.tap(e=>x.addFinalizer(()=>e.unmount())))}export{Ve as hydrate,st as hydrateScoped,Be as mount,ot as mountScoped};
1
+ import{i as e,n as t,o as n,r,s as i,t as a}from"../data-Bk62ePeP.js";import{a as o,c as s,d as c,f as l,i as u,l as d,m as f,n as p,o as m,p as h,s as g,u as _}from"../boundary-replay-BR26_puM.js";import{Cause as v,Context as y,Deferred as b,Effect as x,ExecutionStrategy as S,Exit as C,Fiber as ee,FiberRef as te,HashMap as w,HashSet as T,Layer as ne,LogLevel as re,ManagedRuntime as ie,Option as E,Ref as D,Schema as ae,Scope as O,Stream as k,SubscriptionRef as A,pipe as j}from"effect";import{AppRpcClientTag as oe,FAILURE_BOUNDARY as se,FRAGMENT as ce,LIST as le,SERVER_BOUNDARY as ue,SUSPENSE_BOUNDARY as de,Source as fe,getElementDescriptor as pe,isStream as M,toStream as N}from"@weftui/core";function P(){return x.gen(function*(){let e=yield*r;return++e.streamIdCounter.current})}const me=P;let he=0;const F=()=>++he;function ge(e){if(e.length<=2||!e.startsWith(`on`))return!1;let t=e[2];return t!==void 0&&t>=`a`&&t<=`z`}function I(e,t){return x.gen(function*(){for(let[n,r]of Object.entries(t))if(n!==`children`){if(ge(n)){yield*Ce(e,n,r);continue}if(n===`ref`&&typeof r==`object`&&D.RefTypeId in r){yield*D.set(r,E.some(e));continue}if(n===`style`){yield*xe(e,r);continue}_e(e,n)?yield*ve(e,n,r):yield*ye(e,n,r)}})}function _e(e,t){if(t.startsWith(`data-`)||t.startsWith(`aria-`))return!1;let n=Object.getPrototypeOf(e);for(;n!==null;){if(Object.hasOwn(n,t))return!0;n=Object.getPrototypeOf(n)}return t in e}function ve(e,t,n){return x.gen(function*(){M(n)||x.isEffect(n)?yield*z(N(n),n=>{n==null?delete e[t]:e[t]=n},`property:${t}`):n!=null&&(e[t]=n)})}function ye(e,t,n){return x.gen(function*(){if(M(n)||x.isEffect(n))yield*z(N(n),n=>{if(n==null)e.removeAttribute(t);else{let r=be(n);r!==void 0&&(typeof n==`boolean`?n?e.setAttribute(t,``):e.removeAttribute(t):e.setAttribute(t,r))}},`attribute:${t}`);else{let r=be(n);r!==void 0&&(typeof n==`boolean`?n?e.setAttribute(t,``):e.removeAttribute(t):e.setAttribute(t,r))}})}function be(e){if(e!=null)return String(e)}function xe(e,t){return x.gen(function*(){if(M(t)||x.isEffect(t)){yield*z(N(t),t=>{if(typeof t==`string`)e.setAttribute(`style`,t);else if(typeof t==`object`&&t){e.style.cssText=``;for(let[n,r]of Object.entries(t))r!=null&&e.style.setProperty(L(n),String(r))}},`style`);return}if(typeof t==`string`){e.setAttribute(`style`,t);return}typeof t==`object`&&t&&(yield*Se(e,t))})}function Se(e,t){return x.gen(function*(){for(let[n,r]of Object.entries(t))M(r)||x.isEffect(r)?yield*z(N(r),t=>{t!=null&&e.style.setProperty(L(n),String(t))},`style.${n}`):r!=null&&e.style.setProperty(L(n),String(r))})}function L(e){return e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}function R(e,t,n){return x.gen(function*(){let r=yield*x.serviceOption(a);if(E.isNone(r)){let r=yield*te.get(te.unhandledErrorLogLevel);return yield*j(x.forkIn(x.withUnhandledErrorLogLevel(e,r),t),x.withUnhandledErrorLogLevel(E.some(re.Error)),x.annotateLogs(`weft.region`,n))}let i=yield*x.forkIn(e,t);return yield*j(ee.await(i),x.flatMap(e=>C.isFailure(e)?r.value.reportError(e.cause):x.void),x.forkIn(t)),i})}function z(e,t,n){return x.gen(function*(){let i=yield*r;yield*R(k.runForEach(e,e=>x.sync(()=>void t(e))),i.scope,n)})}function Ce(e,t,n){return x.gen(function*(){let i=yield*r,a=t.slice(2).toLowerCase(),o=null,s=()=>{o&&=(e.removeEventListener(a,o),null)},c=n=>{s(),!(n==null||n===!1)&&typeof n==`function`&&(o=e=>{let r=n(e);x.isEffect(r)&&i.runtime.runFork(j(r,x.catchAll(e=>process.env.NODE_ENV===`development`?x.logError(`Event handler error: ${t}`,{error:e}):x.void)))},e.addEventListener(a,o))};yield*O.addFinalizer(i.scope,x.sync(s)),M(n)||x.isEffect(n)?yield*z(N(n),e=>c(e),`event:${t}`):c(n)})}function we(e,t,n,r,i,a){return x.gen(function*(){let o=yield*b.await(t).pipe(x.flip,x.catchAll(()=>x.interrupt)),s=e.match(o);if(yield*O.close(n,C.void),s===null)return E.isSome(r)?yield*r.value.reportError(o):yield*x.logError(`Unhandled error escaped the outermost Boundary`,o);K(i,a);let c=yield*B(s),l=a.parentNode;if(l!==null&&c!==null)if(Array.isArray(c))for(let e of c)l.insertBefore(e,a);else l.insertBefore(c,a)})}function Te(e){return x.gen(function*(){let t=yield*r,n=yield*x.serviceOption(a),i=F(),s=document.createComment(o(i)),c=document.createComment(u(i)),l=yield*O.fork(t.scope,S.sequential),d={...t,scope:l},f=yield*b.make(),p=yield*j(V(e.children),x.provideService(a,{reportError:e=>b.fail(f,e).pipe(x.asVoid)}),x.provideService(r,d),x.provideService(O.Scope,l),x.catchAllCause(t=>{let n=e.match(t);return n===null?x.failCause(t):j(O.close(l,C.void),x.flatMap(()=>B(n)),x.map(e=>e===null?[]:Array.isArray(e)?e:[e]))}));return yield*x.forkIn(we(e,f,l,n,s,c),t.scope),[s,...p,c]})}function Ee(e){return x.gen(function*(){let t=yield*r,i=yield*D.make(1),a=yield*b.make(),o=j(D.updateAndGet(i,e=>e-1),x.flatMap(e=>e<=0?x.asVoid(b.succeed(a,void 0)):x.void)),s={register:D.update(i,e=>e+1),settle:o},c=e.children,l=yield*B((c===void 0?[]:Array.isArray(c)?c:[c]).map(e=>x.isEffect(e)||M(e)?{type:()=>e,props:{}}:e)).pipe(x.provideService(n,s)),u=l===null?[]:Array.isArray(l)?l:[l];yield*o;let d=yield*b.poll(a);if(E.isSome(d))return u;let p=yield*me(),m=document.createComment(f(p)),g=document.createComment(h(p)),_=yield*B(e.fallback??null),v=[];_!==null&&(Array.isArray(_)?v.push(..._):v.push(_));let y=document.createDocumentFragment();for(let e of u)y.appendChild(e);let S=x.gen(function*(){yield*b.await(a),K(m,g);let e=g.parentNode;e!==null&&(e.insertBefore(y,g),m.remove(),g.remove())});return yield*x.forkIn(S,t.scope),[m,...v,g]})}function De(t){return x.gen(function*(){let n=yield*r,i=yield*x.serviceOption(oe);if(E.isNone(i))return yield*x.fail(new e({cause:void 0,message:`Boundary.rpc "${t.tag}" was mounted client-first without an AppRpcClient in context. Mount the app under @weftui/router (RouterLive), which provides the rpc client, so the boundary can resolve its data.`}));let a=i.value,s=F(),c=document.createComment(o(s)),l=document.createComment(u(s)),d=yield*B(t.fallback??null),f=[];d!==null&&(Array.isArray(d)?f.push(...d):f.push(d));let p=x.gen(function*(){let e=yield*a.call(t.tag,t.payload()),n=yield*$e(t.tag,t.payload,e,i),r=yield*B(t.render(n));K(c,l);let o=l.parentNode;if(o!==null&&r!==null)if(Array.isArray(r))for(let e of r)o.insertBefore(e,l);else o.insertBefore(r,l)}).pipe(x.catchAllCause(e=>x.logError(`[weft] Boundary.rpc "${t.tag}" mount failed to resolve; fallback left in place.`,e)));return yield*x.forkIn(p,n.scope),[c,...f,l]})}function B(e){return x.gen(function*(){if(typeof e==`string`||typeof e==`number`||typeof e==`bigint`)return document.createTextNode(String(e));if(typeof e==`boolean`||e==null)return null;if(M(e)||x.isEffect(e)){let t=pe(e);if(t!==void 0)return yield*B(t);if(x.isEffect(e)){let t=x.runSyncExit(e);if(C.isSuccess(t))return yield*B(t.value)}return yield*H(N(e))}if(typeof e==`object`&&Symbol.iterator in e&&!(`type`in e))return yield*V(Oe(e));if(typeof e==`object`&&`type`in e&&!(Symbol.iterator in e)){let{type:t,props:n}=e;return t===ce?yield*ke(n):t===de?yield*Ee(n):t===ue?yield*De(n):t===se?yield*Te(n):t===le?yield*Ie(n):typeof t==`string`?yield*Ae(t,n):typeof t==`function`?yield*je(t,n):yield*x.fail(new i({type:t,message:`Invalid Renderable type: expected string, FRAGMENT, or function, got ${typeof t}`}))}return null})}function Oe(e){let t=[];function n(e){if(M(e)||x.isEffect(e)){t.push(e);return}if(typeof e==`object`&&e&&Symbol.iterator in e&&!(`type`in e))for(let t of e)n(t);else t.push(e)}return n(e),t}function V(e){return x.gen(function*(){let t=[];for(let n of e)if(M(n)||x.isEffect(n)){let e=yield*H(N(n));t.push(...e)}else{let e=yield*B(n);e!==null&&(Array.isArray(e)?t.push(...e):t.push(e))}return t})}function ke(e){return x.gen(function*(){let t=`children`in e?e.children:void 0;return t===void 0?[]:yield*V(Array.isArray(t)?t:[t])})}function Ae(e,t){return x.gen(function*(){let n=document.createElement(e);yield*I(n,t);let r=`children`in t?t.children:void 0;if(r!==void 0){let e=Array.isArray(r)?r:[r];for(let t of e)if(M(t)||x.isEffect(t)){let e=yield*H(N(t));for(let t of e)n.appendChild(t)}else{let e=yield*B(t);if(e!==null)if(Array.isArray(e))for(let t of e)n.appendChild(t);else n.appendChild(e)}}return n})}function je(e,t){return x.gen(function*(){let i=e(t);if(M(i)||x.isEffect(i)){let e=yield*r,t=yield*O.fork(e.scope,S.sequential),a={...e,scope:t},o=yield*x.serviceOption(n),s=N(i);return E.isSome(o)&&(yield*o.value.register,s=j(s,k.zipWithIndex,k.flatMap(([e,t])=>t===0?k.fromEffect(x.as(o.value.settle,e)):k.make(e)))),yield*H(s).pipe(x.provideService(r,a),x.provideService(O.Scope,t))}return yield*B(i)})}function H(e){return x.gen(function*(){let t=yield*r,n=yield*P(),[i,a]=Me(n),o=null;return yield*R(k.runForEach(e,e=>x.gen(function*(){o!==null&&(yield*O.close(o,C.void)),o=yield*O.fork(t.scope,S.sequential);let n={...t,scope:o};yield*U(i,a,e).pipe(x.provideService(r,n),x.provideService(O.Scope,o))})),t.scope,`child:stream-${n}`),[i,a]})}function Me(e){return[document.createComment(l(e)),document.createComment(c(e))]}function U(e,t,n){return x.gen(function*(){let r=e.nextSibling,i=r!==null&&r!==t&&r.nextSibling===t;if(i&&r.nodeType===ot&&W(n)){let e=String(n);r.data!==e&&(r.data=e);return}if(i&&r.nodeType===X){let e=G(n);if(e!==void 0&&typeof e.type==`string`&&r.tagName.toLowerCase()===e.type.toLowerCase()){yield*Ne(r,e);return}}K(e,t);let a=yield*B(n),o=e.parentNode;if(o!==null&&a!==null)if(Array.isArray(a))for(let e of a)o.insertBefore(e,t);else o.insertBefore(a,t)})}function W(e){return typeof e==`string`||typeof e==`number`||typeof e==`bigint`}function G(e){let t=pe(e);if(t!==void 0)return t;if(typeof e==`object`&&e&&`type`in e&&!(Symbol.iterator in e)&&!M(e)&&!x.isEffect(e))return e}function Ne(e,t){return x.gen(function*(){yield*I(e,t.props);let n=t.props.children,r=n===void 0?[]:Array.isArray(n)?n:[n];if(!(yield*Pe(e,r))){for(;e.firstChild!==null;)e.firstChild.remove();yield*Fe(e,r)}})}function Pe(e,t){return x.gen(function*(){let n=Array.from(e.childNodes);if(n.length!==t.length)return!1;for(let e=0;e<t.length;e++){let r=t[e],i=n[e];if(W(r)){if(i.nodeType!==ot)return!1}else{let e=G(r);if(e===void 0||typeof e.type!=`string`||i.nodeType!==X||i.tagName.toLowerCase()!==e.type.toLowerCase())return!1}}for(let e=0;e<t.length;e++){let r=t[e],i=n[e];if(W(r)){let e=String(r);i.data!==e&&(i.data=e)}else yield*Ne(i,G(r))}return!0})}function Fe(e,t){return x.gen(function*(){for(let n of t)if(M(n)||x.isEffect(n)){let t=yield*H(N(n));for(let n of t)e.appendChild(n)}else{let t=yield*B(n);if(t!==null)if(Array.isArray(t))for(let n of t)e.appendChild(n);else e.appendChild(t)}})}function K(e,t){let n=e.nextSibling;for(;n!==null&&n!==t;){let e=n.nextSibling;n.remove(),n=e}}function Ie(e){return x.gen(function*(){let t=yield*r,{of:n,by:i,render:a}=e,o=yield*P(),[s,c]=Me(o),l=yield*O.fork(t.scope,S.sequential),u=(yield*fe.toSubscribable(n).pipe(x.provideService(O.Scope,l))).changes,d={records:w.empty(),order:[]};return yield*R(k.runForEach(u,e=>x.gen(function*(){d=yield*q(Array.from(e),i,a,d,l,c,t)})),l,`list:stream-${o}`),[s,c]})}function Le(t,n){return x.gen(function*(){let r=[],i=T.empty();for(let a=0;a<t.length;a++){let o=n===void 0?t[a]:n(t[a],a);if(T.has(i,o))return yield*x.fail(new e({cause:o,message:`List.each: duplicate key ${Ve(o)} in a single emission; keys must be unique (set a stable \`by\`).`}));i=T.add(i,o),r.push(o)}return r})}function q(e,t,n,r,i,a,o){return x.gen(function*(){let s=yield*Le(e,t),c=w.empty();r.order.forEach((e,t)=>{c=w.set(c,e,t)});let l=[],u=[];for(let t=0;t<e.length;t++){let a=s[t],d=w.get(r.records,a);if(E.isSome(d))l.push(d.value),u.push(E.getOrElse(w.get(c,a),()=>-1));else{let r=yield*Re(a,e[t],t,n,i,o);l.push(r),u.push(-1)}}let d=T.empty();for(let e of s)d=T.add(d,e);for(let e of r.order)if(!T.has(d,e)){let t=w.get(r.records,e);E.isSome(t)&&(yield*O.close(t.value.scope,C.void),Be(t.value.startMarker,t.value.endMarker))}let f=He(u),p=a.parentNode;if(p!==null)for(let e=l.length-1;e>=0;e--){let t=l[e];if(u[e]!==-1&&f.has(e))continue;let n=e+1<l.length?l[e+1].startMarker:a,r=u[e]===-1?[t.startMarker,...t.nodes,t.endMarker]:ze(t.startMarker,t.endMarker);for(let e of r)p.insertBefore(e,n)}let m=w.empty();for(let e of l)m=w.set(m,e.key,e);return{records:m,order:s}})}function Re(e,t,n,i,a,o){return x.gen(function*(){let s=yield*O.fork(a,S.sequential),c={...o,scope:s},l=yield*P(),u=document.createComment(g(l)),d=document.createComment(m(l)),f=yield*B(i(t,n)).pipe(x.provideService(r,c),x.provideService(O.Scope,s));return{key:e,scope:s,startMarker:u,endMarker:d,nodes:f===null?[]:Array.isArray(f)?f:[f]}})}function ze(e,t){let n=[],r=e;for(;r!==null&&(n.push(r),r!==t);)r=r.nextSibling;return n}function Be(e,t){let n=e;for(;n!==null;){let e=n.nextSibling;if(n.remove(),n===t)break;n=e}}function Ve(e){if(typeof e==`string`)return JSON.stringify(e);if(typeof e==`object`&&e)try{return JSON.stringify(e)}catch{return Object.prototype.toString.call(e)}return String(e)}function He(e){let t=e.length,n=[],r=Array.from({length:t},()=>-1);for(let i=0;i<t;i++){let t=e[i];if(t===-1)continue;let a=0,o=n.length;for(;a<o;){let r=a+o>>1;e[n[r]]<t?a=r+1:o=r}a>0&&(r[i]=n[a-1]),n[a]=i}let i=new Set,a=n.length>0?n[n.length-1]:-1;for(;a!==-1;)i.add(a),a=r[a];return i}function Ue(e,t){return x.gen(function*(){let n=yield*x.context(),i=yield*x.serviceOption(O.Scope),a=yield*O.make(),o=ie.make(ne.succeedContext(y.add(n,O.Scope,a))),s={runtime:o,scope:a,streamIdCounter:{current:0}},c=x.zipRight(O.close(a,C.void),x.promise(()=>o.dispose()));t.innerHTML=``;let l=yield*B(e).pipe(x.provideService(r,s),x.provideService(O.Scope,a),x.tapError(()=>c));if(l!==null)if(Array.isArray(l))for(let e of l)t.appendChild(e);else t.appendChild(l);let u=!1,d={unmount:()=>x.gen(function*(){u||(u=!0,yield*O.close(a,C.void),yield*x.promise(()=>o.dispose()))})};return E.isSome(i)&&(yield*O.addFinalizer(i.value,d.unmount())),d})}function We(e,t){return x.gen(function*(){let n=yield*x.context(),i=yield*x.serviceOption(O.Scope),a=yield*O.make(),o=ie.make(ne.succeedContext(y.add(n,O.Scope,a))),s=yield*Ke(),c={runtime:o,scope:a,streamIdCounter:{current:0},hydrationReady:s};st(t,c.streamIdCounter);let l=x.zipRight(O.close(a,C.void),x.promise(()=>o.dispose()));yield*J(e,t.firstChild,`root`).pipe(x.provideService(r,c),x.provideService(O.Scope,a),x.tapError(()=>l)),yield*s.settle,yield*s.awaitReady;let u=!1,d={unmount:()=>x.gen(function*(){u||(u=!0,yield*O.close(a,C.void),yield*x.promise(()=>o.dispose()))})};return E.isSome(i)&&(yield*O.addFinalizer(i.value,d.unmount())),d})}function J(e,t,n){return x.gen(function*(){if(typeof e==`string`||typeof e==`number`||typeof e==`bigint`)return yield*Ge(String(e),t,n);if(typeof e==`boolean`||e==null)return t;if(M(e)||x.isEffect(e)){let r=pe(e);if(r!==void 0)return yield*J(r,t,n);if(x.isEffect(e)){let r=x.runSyncExit(e);if(C.isSuccess(r))return yield*J(r.value,t,n)}return yield*Je(N(e),t,n)}if(typeof e==`object`&&Symbol.iterator in e&&!(`type`in e)){let r=t,i=0;for(let t of e)r=yield*J(t,r,`${n}[${i}]`),i++;return r}if(typeof e==`object`&&`type`in e&&!(Symbol.iterator in e)){let{type:r,props:a}=e;if(r===ce)return yield*Y(a,t,n);if(r===de){if(t!==null&&t.nodeType===Z){let e=_(t);if(e!==null&&e.kind===`start`)return yield*Xe(t,n)}return yield*Y(a,t,n)}return r===se?yield*Qe(a,t,n):r===ue?yield*et(a,t,n):r===le?yield*nt(a,t,n):typeof r==`string`?yield*tt(r,a,t,n):typeof r==`function`?yield*J(r(a),t,n):yield*x.fail(new i({type:r,message:`Invalid Renderable type during hydration at ${n}: expected string, FRAGMENT, or function, got ${typeof r}`}))}return t})}function Ge(e,t,n){return x.gen(function*(){if(e.length===0)return t;if(t===null||t.nodeType!==ot)return yield*$(`text ${JSON.stringify(e)}`,Q(t),n);let r=t;return r.data.startsWith(e)?r.data.length>e.length?r.splitText(e.length):r.nextSibling:yield*$(`text ${JSON.stringify(e)}`,Q(t),n)})}function Ke(){return x.gen(function*(){let e=yield*D.make(1),t=yield*b.make(),n=j(D.updateAndGet(e,e=>e-1),x.flatMap(e=>e<=0?x.asVoid(b.succeed(t,void 0)):x.void));return{register:D.update(e,e=>e+1),settle:n,awaitReady:b.await(t)}})}function qe(e){let t=!1;return x.suspend(()=>t||e===void 0?x.void:(t=!0,e.settle))}function Je(e,t,n){return x.gen(function*(){let i=yield*r;if(t===null||t.nodeType!==Z)return yield*$(`reactive region start marker`,Q(t),n);let a=t,o=d(a);if(o===null||o.kind!==`start`)return yield*$(`reactive region start marker`,Q(t),n);let s=ct(a);if(s===null)return yield*$(`reactive region end marker`,`unterminated region starting at ${JSON.stringify(a.data)}`,n);let c=qe(i.hydrationReady),l=!0,u=null,f=k.runForEach(e,e=>x.gen(function*(){u!==null&&(yield*O.close(u,C.void)),u=yield*O.fork(i.scope,S.sequential);let t={...i,scope:u};yield*x.gen(function*(){l?(l=!1,yield*Ye(e,a,s,n),yield*c):yield*U(a,s,e)}).pipe(x.provideService(r,t),x.provideService(O.Scope,u))})).pipe(x.ensuring(c));return i.hydrationReady!==void 0&&(yield*i.hydrationReady.register),yield*R(f,i.scope,`hydrate:stream-${o.id} (${n})`),s.nextSibling})}function Ye(e,t,n,r){return x.gen(function*(){let i=yield*J(e,t.nextSibling,`${r}<resume>`).pipe(x.map(e=>e===n?null:`adopted content did not align with the end marker`),x.catchTag(`HydrationMismatchError`,e=>x.succeed(`expected ${e.expected}, found ${e.actual} at ${e.path}`)));i!==null&&(console.error(`[weft] hydrate: reactive region at ${r} diverged from server output (${i}); patching.`),yield*U(t,n,e))})}function Xe(e,t){return x.gen(function*(){let n=Ze(e);if(n===null)return yield*$(`substituted suspense end marker`,`unterminated region starting at ${JSON.stringify(e.data)}`,t);let r=null;for(let t=e.nextSibling;t!==null&&t!==n;t=t.nextSibling)if(t.nodeType===X&&t.tagName===`SCRIPT`&&t.getAttribute(`type`)===`application/json`&&t.hasAttribute(`data-weft-suspense-failure`)){r=t;break}let i=null;if(r!==null){let e=r.textContent??``;i=yield*x.try({try:()=>JSON.parse(e),catch:e=>e}).pipe(x.catchAll(()=>x.succeed(null)))}if(typeof i!=`object`||!i||!(`error`in i))return console.error(`[weft] hydrate: substituted suspense region at ${t} has no decodable failure sentinel; leaving its static content.`),n.nextSibling;let o=yield*x.serviceOption(a);return E.isNone(o)?(console.error(`[weft] hydrate: substituted suspense region at ${t} has no enclosing Boundary to replay its failure to; leaving its static content.`,i.error),n.nextSibling):(yield*o.value.reportError(v.fail(i.error)),n.nextSibling)})}function Ze(e){let t=0,n=e.nextSibling;for(;n!==null;){if(n.nodeType===Z){let e=_(n);if(e!==null)if(e.kind===`start`)t++;else if(t===0)return n;else t--}n=n.nextSibling}return null}function Qe(e,t,n){return x.gen(function*(){if(t===null||t.nodeType!==X||t.tagName!==`SCRIPT`||t.getAttribute(`type`)!==`application/json`||!t.hasAttribute(`data-weft-boundary-failure`)){let i=yield*r,s=yield*x.serviceOption(a),c=yield*O.fork(i.scope,S.sequential),l={...i,scope:c},d=yield*b.make(),f=yield*Y(e,t,n).pipe(x.provideService(a,{reportError:e=>b.fail(d,e).pipe(x.asVoid)}),x.provideService(r,l),x.provideService(O.Scope,c)),p=t?.parentNode??null;if(t===null||t===f||p===null)return console.error(`[weft] hydrate: boundary at ${n} adopted an empty extent; live failure recovery is not installed for it.`),f;let m=F(),h=document.createComment(o(m)),g=document.createComment(u(m));return p.insertBefore(h,t),f===null?p.appendChild(g):p.insertBefore(g,f),yield*x.forkIn(we(e,d,c,s,h,g),i.scope),f}let i=t,s=i.textContent??``,c=yield*x.gen(function*(){let t=yield*x.try({try:()=>JSON.parse(s),catch:e=>e}),n=p(e.children)[t.index];if(n===void 0)return null;let r=yield*ae.decodeUnknown(n.errorSchema)(t.error);return e.match(v.fail(r))}).pipe(x.catchAll(e=>(console.error(`[weft] hydrate: boundary failure payload at ${n} failed to decode; cannot replay.`,e),x.succeed(null))));if(c===null)return yield*$(`replayable boundary failure`,`undecodable failure payload`,n);let l=yield*J(c,i.nextSibling,n);return i.remove(),l})}function $e(e,t,n,r){return x.gen(function*(){let i=yield*A.make(n),a=yield*A.make(!1),o=yield*A.make(E.none());return{value:i,refetch:E.match(r,{onNone:()=>x.void,onSome:n=>x.gen(function*(){(yield*A.get(a))||(yield*A.set(a,!0),yield*x.gen(function*(){let r=yield*x.exit(n.call(e,t()));C.isSuccess(r)?(yield*A.set(i,r.value),yield*A.set(o,E.none())):yield*A.set(o,E.some(v.squash(r.cause)))}).pipe(x.ensuring(A.set(a,!1))))})}),pending:a,error:o}})}function et(e,t,n){return x.gen(function*(){if(t===null||t.nodeType!==X||t.tagName!==`SCRIPT`||t.getAttribute(`type`)!==`application/json`)return yield*$(`server boundary payload <script type="application/json">`,Q(t),n);if(t.hasAttribute(`data-weft-boundary-failure`))return yield*$(`server boundary success payload <script type="application/json">`,`boundary failure payload`,n);let r=t,i=r.textContent??``,a=yield*x.try({try:()=>JSON.parse(i),catch:e=>e}).pipe(x.flatMap(t=>ae.decodeUnknown(e.successSchema)(t)),x.catchAll(e=>(console.error(`[weft] hydrate: server boundary payload at ${n} failed to decode; cannot replay.`,e),$(`decodable server boundary payload`,`undecodable payload`,n)))),o=yield*x.serviceOption(oe),s=yield*$e(e.tag,e.payload,a,o),c=yield*J(e.render(s),r.nextSibling,n);return r.remove(),c})}function tt(e,t,n,r){return x.gen(function*(){if(n===null||n.nodeType!==X)return yield*$(`<${e}>`,Q(n),r);let i=n;return i.tagName.toLowerCase()===e.toLowerCase()?(yield*I(i,t),yield*Y(t,i.firstChild,`${r} > ${e}`),i.nextSibling):yield*$(`<${e}>`,Q(n),r)})}function Y(e,t,n){return x.gen(function*(){let r=`children`in e?e.children:void 0;if(r===void 0)return t;let i=Array.isArray(r)?r:[r],a=t,o=0;for(let e of i)a=yield*J(e,a,`${n}[${o}]`),o++;return a})}function nt(e,t,n){return x.gen(function*(){let i=yield*r,{of:a,by:o,render:s}=e;if(t===null||t.nodeType!==Z)return yield*$(`list region start marker`,Q(t),n);let c=t,l=d(c);if(l===null||l.kind!==`start`)return yield*$(`list region start marker`,Q(t),n);let u=ct(c);if(u===null)return yield*$(`list region end marker`,`unterminated region starting at ${JSON.stringify(c.data)}`,n);let f=rt(c,u),p=yield*O.fork(i.scope,S.sequential),m=(yield*fe.toSubscribable(a).pipe(x.provideService(O.Scope,p))).changes,h={records:w.empty(),order:[]},g=!0,_=qe(i.hydrationReady),v=k.runForEach(m,e=>x.gen(function*(){let t=Array.from(e);g?(g=!1,h=yield*it(t,o,s,f,p,c,u,i,n),yield*_):h=yield*q(t,o,s,h,p,u,i)})).pipe(x.ensuring(_));return i.hydrationReady!==void 0&&(yield*i.hydrationReady.register),yield*R(v,p,`hydrate:list-${l.id} (${n})`),u.nextSibling})}function rt(e,t){let n=[],r=e.nextSibling;for(;r!==null&&r!==t;){let e=r.nodeType===Z?s(r):null;if(e===null||e.kind!==`start`){r=r.nextSibling;continue}let i=r,a=[],o=0,c=null,l=i.nextSibling;for(;l!==null&&l!==t;){if(l.nodeType===Z){let e=s(l);if(e!==null)if(e.kind===`start`)o++;else if(o===0){c=l;break}else o--}a.push(l),l=l.nextSibling}if(c===null)break;n.push({startMarker:i,endMarker:c,nodes:a}),r=c.nextSibling}return n}function it(e,t,n,r,i,a,o,s,c){return x.gen(function*(){let l=yield*Le(e,t);if(l.length!==r.length)return console.error(`[weft] hydrate: list region at ${c} had ${r.length} server item(s) but the first emission has ${l.length}; rebuilding.`),K(a,o),yield*q(e,t,n,{records:w.empty(),order:[]},i,o,s);let u=[];for(let t=0;t<e.length;t++){let a=yield*at(l[t],e[t],t,n,r[t],i,s,c);u.push(a)}let d=w.empty();for(let e of u)d=w.set(d,e.key,e);return{records:d,order:l}})}function at(e,t,n,i,a,o,s,c){return x.gen(function*(){let l=yield*O.fork(o,S.sequential),u={...s,scope:l},d=i(t,n),f=yield*J(d,a.startMarker.nextSibling,`${c}<item>`).pipe(x.provideService(r,u),x.provideService(O.Scope,l),x.map(e=>e===a.endMarker?null:`adopted item content did not align with its end marker`),x.catchTag(`HydrationMismatchError`,e=>x.succeed(`expected ${e.expected}, found ${e.actual} at ${e.path}`)));if(f===null)return{key:e,scope:l,startMarker:a.startMarker,endMarker:a.endMarker,nodes:a.nodes};console.error(`[weft] hydrate: list item at ${c} diverged from server output (${f}); patching.`),yield*O.close(l,C.void);let p=yield*O.fork(o,S.sequential),m={...s,scope:p};K(a.startMarker,a.endMarker);let h=yield*B(d).pipe(x.provideService(r,m),x.provideService(O.Scope,p)),g=h===null?[]:Array.isArray(h)?h:[h],_=a.endMarker.parentNode;if(_!==null)for(let e of g)_.insertBefore(e,a.endMarker);return{key:e,scope:p,startMarker:a.startMarker,endMarker:a.endMarker,nodes:g}})}const X=1,ot=3,Z=8;function st(e,t){let n=document.createTreeWalker(e,128),r=t.current;for(let e=n.nextNode();e!==null;e=n.nextNode()){let t=e,n=d(t)??_(t)??s(t);n!==null&&n.id>r&&(r=n.id)}t.current=r}function ct(e){let t=0,n=e.nextSibling;for(;n!==null;){if(n.nodeType===8){let e=d(n);if(e!==null)if(e.kind===`start`)t++;else if(t===0)return n;else t--}n=n.nextSibling}return null}function Q(e){if(e===null)return`end of children`;switch(e.nodeType){case 1:return`<${e.tagName.toLowerCase()}>`;case 3:return`text ${JSON.stringify(e.data)}`;case 8:return`comment ${JSON.stringify(e.data)}`;default:return`node(type ${e.nodeType})`}}function $(e,n,r){return x.fail(new t({expected:e,actual:n,path:r}))}function lt(e,t){return j(Ue(e,t),x.tap(e=>x.addFinalizer(()=>e.unmount())))}function ut(e,t){return j(We(e,t),x.tap(e=>x.addFinalizer(()=>e.unmount())))}export{We as hydrate,ut as hydrateScoped,Ue as mount,lt as mountScoped};
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: Boundaries and Suspense
3
+ order: 4
4
+ section: explanation
5
+ description: How Weft models failure, async, and server data as boundary nodes in the same tree — failure-catch variants, Boundary.suspend, and Boundary.rpc, and how their E/R channels behave.
6
+ ---
7
+
8
+ # Boundaries and Suspense
9
+
10
+ A **boundary** is a node that intercepts something flowing through the tree — an error, a pending async child, or a server-resolved value — and decides what the DOM shows in its place. Because a boundary is itself a `Node<E, R>` ([nodes are Effects](https://weftui.dev/docs/explanation/rendering-model)), it composes exactly like any other element: you nest it, and its children's channels flow through it under a transformation the boundary defines.
11
+
12
+ The `Boundary` namespace has three kinds. This page is the conceptual map; the [core reference](https://weftui.dev/docs/reference/core#boundary-namespace) has the full signatures.
13
+
14
+ ## Failure boundaries
15
+
16
+ A component's `E` channel accumulates up the tree. A **failure boundary** is where you _discharge_ some of that `E`: it wraps children and, if one of them fails, renders a fallback instead of letting the failure propagate to the mount.
17
+
18
+ ```typescript
19
+ import { Boundary, h } from "@weftui/core";
20
+
21
+ Boundary.catchAll({ fallback: (e) => h.div({ class: "error" }, `Failed: ${e.message}`) }, [
22
+ RiskyWidget(),
23
+ ]);
24
+ ```
25
+
26
+ There are six failure-catch variants, mirroring Effect's own error operators so the mental model transfers directly:
27
+
28
+ | Variant | Catches |
29
+ | ------------------------ | ------------------------------------------ |
30
+ | `catchAll` | every failure in `E` |
31
+ | `catchAllCause` | the full `Cause` (defects included) |
32
+ | `catchTag` / `catchTags` | one / several tagged errors by `_tag` |
33
+ | `catchSome` / `catchIf` | a selected subset, by `Option` / predicate |
34
+
35
+ The channel algebra is the whole reason they exist: `catchTag("Foo", …)` removes `Foo` from the children's `E` and adds whatever the fallback needs — so the type of the boundary node reflects exactly which failures are still live and which were handled. An unhandled failure re-raises to the **nearest enclosing** boundary; if none catches it at **mount time**, mounting fails. Boundaries nest, so an inner `catchTag` can handle a specific case while an outer `catchAll` sweeps the rest.
36
+
37
+ ### Post-mount failures with no enclosing boundary
38
+
39
+ The routing above describes what happens while a node is being built. Once mounted, a reactive region — an attribute, child, or list stream, or a hydrated equivalent — keeps running for the lifetime of its scope, and it can still fail later: a `Stream` backing a `Boundary.rpc` resource might raise `RouterNotFound` after a client-side navigation, for instance. If a `BoundaryContext` encloses the region, the failure routes to it exactly as above, and the boundary's fallback swaps in.
40
+
41
+ If no boundary encloses it, there is nothing to swap to. Weft does not synthesize one: the region's DOM keeps its last rendered content, and the subscription fiber's failure exit is left **unobserved**. The Effect runtime itself then reports it — `"Fiber terminated with an unhandled error"` — because Weft raises that fiber's `FiberRef.unhandledErrorLogLevel` from the ambient default (`Debug`) to `LogLevel.Error` and annotates the log with `weft.region`, identifying the failing region by kind and identity (e.g. `attribute:class`, `child:stream-3`, `list:stream-2`, `hydrate:stream-1 (/products/42)`). This fires for typed failures and defects alike, in both dev and prod, exactly once per failing region. Interruption — the ordinary case of unmount tearing down the region's scope — is never reported; only genuine failures are.
42
+
43
+ This is deliberate: rather than a Weft-specific error-reporting config, visibility is controlled by the same knobs any Effect program uses — `Logger.withMinimumLogLevel` to filter it, `Effect.withUnhandledErrorLogLevel` to change how loudly (or quietly) unhandled fiber exits are reported elsewhere in your program. A stream that can fail and has no enclosing boundary is a stream whose failures you've chosen not to route into the UI — the log is what tells you that decision has consequences at runtime.
44
+
45
+ ## Suspense boundaries
46
+
47
+ `Boundary.suspend` wraps async children and shows a `fallback` until **all** of them have emitted their first value, then swaps atomically — either everything is visible or nothing is. This prevents partial flicker when sibling async regions resolve at different times.
48
+
49
+ ```typescript
50
+ import { Boundary, h } from "@weftui/core";
51
+
52
+ Boundary.suspend({ fallback: h.div({ class: "spinner" }, "Loading…") }, [
53
+ AsyncCard({ id: 1 }),
54
+ AsyncCard({ id: 2 }),
55
+ ]);
56
+ ```
57
+
58
+ A suspense boundary is transparent to the type channels: its node is `Node<ChildrenE, ChildrenR>` — the children's `E`/`R` pass straight through, exactly as they would for a plain `h.*` parent. It changes _timing_ (when the children become visible), not _types_.
59
+
60
+ On the server, `renderToStreamHydratable` emits the fallback inline and appends patch scripts as children resolve; on the client, `hydrate` sees through the boundary and adopts the already-resolved DOM directly.
61
+
62
+ > **Note.** There is no `Suspense` export — the API is `Boundary.suspend(props, children)`. Reach for it for async that loads **on the client**; for data that must resolve on the **server** and hydrate without a second request, use `Boundary.rpc` (below).
63
+
64
+ ## The rpc boundary
65
+
66
+ `Boundary.rpc` is the server-data boundary: it resolves one `Rpc` on the server, serializes the result into the HTML, replays it on the client during `hydrate` (no second request, no flash), and then keeps the region live for `refetch`. Conceptually it is the same idea as the other boundaries — a node that decides what renders in a subtree — but the thing it intercepts is a **round-trip to a server handler**, and instead of a children array it takes a `render` function that receives a reactive [`Resource`](https://weftui.dev/docs/reference/core#resourcea).
67
+
68
+ ```typescript
69
+ import { Boundary, h } from "@weftui/core";
70
+ import { Stream } from "effect";
71
+
72
+ Boundary.rpc(
73
+ GetStock,
74
+ () => ({ id: productId }),
75
+ (resource) => h.span([Stream.map(resource.value.changes, (s) => String(s.units))]),
76
+ { fallback: h.p("loading…") },
77
+ );
78
+ ```
79
+
80
+ Unlike the failure and suspense boundaries, `Boundary.rpc` is not self-contained: it resolves through the ambient [`AppRpcClientTag`](https://weftui.dev/docs/reference/core#apprpcclienttag) seam that `@weftui/router` provides on both sides. Its channel behavior is also distinct — the rpc's typed `error` schema joins the node's `E` (replayable through an enclosing failure boundary), while `render`'s `R` passes through untouched. The full model — the contract/handler split, the four lifecycles, typed-failure replay — is a **how-to**, not repeated here: [Load Data with RPC](https://weftui.dev/docs/how-to/load-data-with-rpc).
81
+
82
+ ## One tree, three interceptors
83
+
84
+ The unifying idea: failure, async pending state, and server data are not three separate subsystems bolted onto the renderer. They are three **boundary nodes** in the one tree, each intercepting a different thing flowing through it, each with channel behavior you can read off its type. That is why they nest freely — a `Boundary.catchTag` can wrap a `Boundary.rpc` to catch its typed failure, and a `Boundary.suspend` can wrap async siblings that themselves contain rpc boundaries.
85
+
86
+ ## See also
87
+
88
+ - [The Rendering Model](https://weftui.dev/docs/explanation/rendering-model) — why a boundary is just a node in a static tree
89
+ - [`Boundary` API reference](https://weftui.dev/docs/reference/core#boundary-namespace) — every variant's signature and channel algebra
90
+ - [Load Data with RPC](https://weftui.dev/docs/how-to/load-data-with-rpc) — the full `Boundary.rpc` walkthrough and its four lifecycles
91
+ - [Render on the Server](https://weftui.dev/docs/how-to/render-on-the-server) — how suspense and rpc boundaries stream and hydrate
@@ -0,0 +1,164 @@
1
+ ---
2
+ title: The Combinator API
3
+ order: 2
4
+ section: explanation
5
+ description: How h, h.fragment, and Component.gen / Component.make work; why Node is an Effect; how E and R accumulate through a tree.
6
+ ---
7
+
8
+ # The Combinator API
9
+
10
+ Weft builds UI trees by calling builder functions. Because component return types stay as generic `Effect.Effect<ElementDescriptor, E, R>`, the error channel (`E`) and requirements channel (`R`) propagate through the entire tree — visible to the type checker, satisfiable at the mount boundary. JSX collapses every component's return type to an opaque `JSX.Element`, erasing both channels; the combinator API exists specifically to keep them intact.
11
+
12
+ ## Nodes are Effects
13
+
14
+ `Node<E, R>` is defined as:
15
+
16
+ ```typescript
17
+ type Node<E = never, R = never> = Effect.Effect<ElementDescriptor, E, R>;
18
+ ```
19
+
20
+ Nodes are first-class Effects. Everything in the Effect ecosystem works on them directly:
21
+
22
+ ```typescript
23
+ import { h } from "@weftui/core";
24
+ import { Effect } from "effect";
25
+
26
+ // yield* in Effect.gen — R propagates into the generator's context
27
+ const node = yield * h.div({ class: "container" }, "Hello");
28
+
29
+ // pipe — chain Effect operators directly
30
+ const provided = pipe(h.div(userStream), Effect.provide(UserServiceLive));
31
+
32
+ // Effect.flatMap — sequence node creation with async logic
33
+ const card = pipe(
34
+ fetchCard(id),
35
+ Effect.flatMap((data) => h.div({ class: "card" }, data.title)),
36
+ );
37
+ ```
38
+
39
+ ## The `h` namespace
40
+
41
+ `h` is a proxy object where every property is an element builder. Access any HTML or SVG tag name as `h.tagName`:
42
+
43
+ ```typescript
44
+ import { h } from "@weftui/core";
45
+
46
+ h.div({ class: "container" }, [h.span("Hello"), h.p("World")]);
47
+ h.input({ type: "text", placeholder: "Search..." });
48
+ h.button({ type: "button", onclick: () => handleClick() }, "Submit");
49
+ ```
50
+
51
+ Each builder accepts these call signatures:
52
+
53
+ ```typescript
54
+ // props + children array
55
+ h.div(props, children: Node[])
56
+
57
+ // props + single string or number child
58
+ h.div(props, child: string | number)
59
+
60
+ // props only
61
+ h.div(props)
62
+
63
+ // children only (no props)
64
+ h.div(children: Node[])
65
+
66
+ // single string or number child
67
+ h.div("five")
68
+ h.div(5)
69
+
70
+ // no props, no children
71
+ h.div()
72
+ ```
73
+
74
+ ### How `E` and `R` accumulate
75
+
76
+ Reactive prop values (any `Stream`, `Effect`, or `Subscribable`) contribute their channels to the node:
77
+
78
+ ```typescript
79
+ declare const colorStream: Stream.Stream<string, never, ThemeService>;
80
+
81
+ // Node<never, ThemeService> — R comes from the stream prop
82
+ const box = h.div({ style: { color: colorStream } }, "Hello");
83
+ ```
84
+
85
+ Children contribute their channels too, and siblings union their channels:
86
+
87
+ ```typescript
88
+ declare const nodeA: Node<never, ServiceA>;
89
+ declare const nodeB: Node<never, ServiceB>;
90
+
91
+ // Node<never, ServiceA | ServiceB>
92
+ const parent = h.div([nodeA, nodeB]);
93
+ ```
94
+
95
+ Static values (strings, numbers, plain functions) contribute `never` to both channels.
96
+
97
+ ## `h.fragment`
98
+
99
+ `h.fragment` groups children without emitting a wrapper element. Use it when a component needs to return multiple sibling nodes:
100
+
101
+ ```typescript
102
+ import { h } from "@weftui/core";
103
+
104
+ // Renders as three adjacent <td> elements with no wrapping element
105
+ const TableRow = ({ user }: { user: User }) =>
106
+ h.fragment([h.td(user.name), h.td(user.role), h.td(user.status)]);
107
+ ```
108
+
109
+ ## Custom components with `Component.gen` / `Component.make`
110
+
111
+ Plain functions work fine for simple components, but the `Component` factories provide type-level wiring so the caller's reactive prop types contribute their `E`/`R` to the returned node. Pick `Component.make` for a plain-function body and `Component.gen` for a generator body (when you need `yield*` to set up local state or pull from services).
112
+
113
+ ```typescript
114
+ import { Component, h } from "@weftui/core";
115
+ import { Stream } from "effect";
116
+
117
+ interface ButtonProps {
118
+ label: string | Stream.Stream<string>;
119
+ onclick?: () => void;
120
+ }
121
+
122
+ const Button = Component.make((props: ButtonProps) =>
123
+ h.button({ onclick: props.onclick }, [props.label]),
124
+ );
125
+
126
+ // When called with a stream prop, the stream's R flows into the node type:
127
+ declare const labelStream: Stream.Stream<string, never, I18nService>;
128
+
129
+ // Node<never, I18nService>
130
+ const btn = Button({ label: labelStream });
131
+ ```
132
+
133
+ Components also accept an optional `children` argument, either as `readonly Renderable[]` or as a `(input) => readonly Renderable[]` function (render-prop pattern). `E`/`R` from children — including the array returned by a function-children call — accumulate on the resulting node.
134
+
135
+ Without `Component`, a plain function's return type is fixed at definition time and does not reflect the caller's reactive prop types.
136
+
137
+ See [component-authoring.md](https://weftui.dev/docs/how-to/author-components) for a full walkthrough.
138
+
139
+ ## Suspense boundaries
140
+
141
+ `Boundary.suspend` wraps async children and shows a fallback until all of them have emitted their first value:
142
+
143
+ ```typescript
144
+ import { Boundary, h } from "@weftui/core";
145
+
146
+ Boundary.suspend({ fallback: h.div({ class: "spinner" }, "Loading...") }, [
147
+ AsyncCard({ id: 1 }),
148
+ AsyncCard({ id: 2 }),
149
+ ]);
150
+ ```
151
+
152
+ The fallback is replaced atomically — either all children are visible or none are. This prevents partial flicker when multiple async siblings resolve at different times. The boundary's node type is `Node<ChildrenE, ChildrenR>`: the children's `E`/`R` channels accumulate onto it, exactly as they would for a plain `h.*` parent.
153
+
154
+ On the server, `renderToStreamHydratable` emits the fallback inline and appends patch scripts as children resolve. On the client, `hydrate` sees through `Boundary.suspend` boundaries and adopts the already-resolved DOM directly.
155
+
156
+ `Boundary.suspend` is one of the boundary combinators — see the [core reference](https://weftui.dev/docs/reference/core#boundarysuspend) for the full `Boundary.*` surface, including the failure-catch variants and `Boundary.rpc`.
157
+
158
+ ## See also
159
+
160
+ - [The Rendering Model](https://weftui.dev/docs/explanation/rendering-model) — why a `Node` is an `Effect` and how the tree renders
161
+ - [Reactive Primitives](https://weftui.dev/docs/explanation/reactive-primitives) — the `Source` vocabulary that reactive props and children accept
162
+ - [Boundaries and Suspense](https://weftui.dev/docs/explanation/boundaries-and-suspense) — the boundary combinators as tree nodes
163
+ - [Author Components](https://weftui.dev/docs/how-to/author-components) — `Component.gen` / `Component.make` in practice
164
+ - [`@weftui/core` reference](https://weftui.dev/docs/reference/core)
@@ -0,0 +1,162 @@
1
+ ---
2
+ title: Reactive Primitives
3
+ order: 3
4
+ section: explanation
5
+ description: The Source<A, E, R> vocabulary; Stream, Effect, and Subscribable as prop values and children; derived streams, reactive styles, and NoPropValue.
6
+ ---
7
+
8
+ # Reactive Primitives
9
+
10
+ The unified `Source` vocabulary is what lets static values, Effects, Streams, and Subscribables be used interchangeably wherever reactivity is supported — props, children, and style values all accept the same type.
11
+
12
+ Weft accepts a `Source` for prop values and children. Any of these is valid wherever reactivity is supported:
13
+
14
+ - A plain static value (`string`, `number`, `boolean`, ...)
15
+ - An `Effect.Effect<A, E, R>` — runs once and resolves to a value
16
+ - A `Stream.Stream<A, E, R>` — each emission replaces the previous value
17
+ - A `Subscribable<A, E, R>` — like a hot stream; already has a "current value"
18
+
19
+ The `Source<A, E, R>` type captures this union:
20
+
21
+ ```typescript
22
+ type Source<A, E, R> = A | Effect.Effect<A, E, R> | Stream.Stream<A, E, R> | Subscribable<A, E, R>;
23
+ ```
24
+
25
+ ## Static values
26
+
27
+ Static props behave exactly as you'd expect — set once and never updated:
28
+
29
+ ```typescript
30
+ h.div({ class: "container", id: "root" }, "Hello");
31
+ ```
32
+
33
+ ## Effect props
34
+
35
+ When a prop value is an `Effect`, it runs once and the resulting value is applied:
36
+
37
+ ```typescript
38
+ const username = Effect.map(fetchProfile(), (p) => p.name);
39
+
40
+ // Renders the username once it resolves
41
+ h.span([username]);
42
+ ```
43
+
44
+ The `E` and `R` channels of the Effect flow into the node's own channels.
45
+
46
+ ## Stream props and children
47
+
48
+ Streams are the primary reactive primitive. Each emission replaces the previous value in the DOM — no diffing, direct DOM update:
49
+
50
+ ```typescript
51
+ import { SubscriptionRef, Stream } from "effect";
52
+
53
+ const count = yield * SubscriptionRef.make(0);
54
+
55
+ // count.changes is a Stream<number> — each new value updates the text node
56
+ h.span([count.changes]);
57
+
58
+ // Stream as a prop — each emission sets the attribute
59
+ const isDisabled = Stream.map(count.changes, (n) => n >= 10);
60
+ h.button({ disabled: isDisabled }, "Submit");
61
+ ```
62
+
63
+ Streams can also supply entire child arrays. Each emission replaces the previous set of children:
64
+
65
+ ```typescript
66
+ const todos = yield * SubscriptionRef.make<string[]>([]);
67
+
68
+ h.ul([Stream.map(todos.changes, (list) => list.map((item) => h.li(item)))]);
69
+ ```
70
+
71
+ ## Derived streams
72
+
73
+ Because `.changes` is a plain `Stream`, the full Stream API applies:
74
+
75
+ ```typescript
76
+ const count = yield * SubscriptionRef.make(0);
77
+
78
+ const doubled = Stream.map(count.changes, (n) => n * 2);
79
+ const formatted = Stream.map(count.changes, (n) => `Count: ${n}`);
80
+ const isHigh = Stream.map(count.changes, (n) => n > 10);
81
+
82
+ h.div([
83
+ h.p([count.changes]),
84
+ h.p([doubled]),
85
+ h.p([formatted]),
86
+ h.p({ style: { color: Stream.map(isHigh, (b) => (b ? "red" : "black")) } }, "Status"),
87
+ ]);
88
+ ```
89
+
90
+ Multiple refs can be combined with `Stream.zipLatestWith`, `Stream.merge`, or other combinators:
91
+
92
+ ```typescript
93
+ const firstName = yield * SubscriptionRef.make("");
94
+ const lastName = yield * SubscriptionRef.make("");
95
+
96
+ const fullName = Stream.zipLatestWith(firstName.changes, lastName.changes, (first, last) =>
97
+ `${first} ${last}`.trim(),
98
+ );
99
+ ```
100
+
101
+ ## Reactive styles
102
+
103
+ The `style` prop accepts the same `Source` vocabulary at any level:
104
+
105
+ ```typescript
106
+ // Individual property as a stream
107
+ h.div({
108
+ style: {
109
+ color: colorStream, // Stream<string>
110
+ opacity: opacityStream, // Stream<number>
111
+ fontWeight: "bold", // static
112
+ },
113
+ });
114
+
115
+ // Entire style object as a stream
116
+ h.div({ style: styleObjectStream });
117
+
118
+ // Combine a whole-object stream with a static property.
119
+ // A whole-object stream replaces every property on each emit, so fold the
120
+ // static value into each emitted object with Stream.map — you cannot spread the
121
+ // Stream itself into a style object (that copies the Stream's internals, not
122
+ // its emitted style keys).
123
+ h.div({
124
+ style: Stream.map(styleObjectStream, (s) => ({
125
+ ...s, // reactive properties
126
+ transition: "all 0.3s", // static, applied on every emit
127
+ })),
128
+ });
129
+
130
+ // For a mix of static and per-property reactive values, use per-property
131
+ // streams alongside static siblings instead:
132
+ h.div({
133
+ style: {
134
+ transform: transformStream, // reactive, per-property
135
+ transition: "all 0.3s", // static
136
+ },
137
+ });
138
+ ```
139
+
140
+ ## NoPropValue
141
+
142
+ When a `Stream` prop ends before emitting, the renderer raises a `NoPropValue` tagged error. This carries an optional `key` field identifying which prop triggered it:
143
+
144
+ ```typescript
145
+ // Handle at the mount boundary if needed. `Effect.catchTag` matches the error
146
+ // by its string tag, so no `NoPropValue` import is required here.
147
+ pipe(
148
+ mount(App(), root),
149
+ Effect.catchTag("NoPropValue", (e) =>
150
+ Effect.logWarning(`Prop stream ended before emitting: ${e.key}`),
151
+ ),
152
+ );
153
+ ```
154
+
155
+ In practice you only encounter `NoPropValue` if you use a finite `Stream` as a prop and it ends before emitting — e.g., `Stream.empty` or `Stream.take(0, stream)`. Most usage with `SubscriptionRef.changes` or infinite streams never raises it.
156
+
157
+ ## See also
158
+
159
+ - [The Rendering Model](https://weftui.dev/docs/explanation/rendering-model) — streams as the weft woven through a static tree
160
+ - [The Combinator API](https://weftui.dev/docs/explanation/combinator-api) — how reactive props and children contribute `E`/`R`
161
+ - [Style Reactively](https://weftui.dev/docs/how-to/style-reactively) and [Render Keyed Lists](https://weftui.dev/docs/how-to/render-keyed-lists) — reactive props and collections in practice
162
+ - [`Source` reference](https://weftui.dev/docs/reference/core#source-namespace) — the `Source` type and `Source.toSubscribable`