@wcstack/state 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +128 -90
- package/README.md +129 -91
- package/dist/auto.min.js +1 -1
- package/dist/auto.min.js.map +1 -1
- package/dist/define.d.ts +11 -7
- package/dist/index.d.ts +58 -15
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/manifest.d.ts +39 -5
- package/dist/manifest.esm.js +315 -253
- package/dist/parser.d.ts +16 -2
- package/dist/parser.esm.js +223 -50
- package/dist/split/chunks/binder.js +1 -1
- package/dist/split/chunks/binder.js.map +1 -1
- package/dist/split/chunks/checkDependency.js +1 -1
- package/dist/split/chunks/declarationAliases.js +2 -0
- package/dist/split/chunks/declarationAliases.js.map +1 -0
- package/dist/split/chunks/declarationHooks.js +2 -0
- package/dist/split/chunks/declarationHooks.js.map +1 -0
- package/dist/split/chunks/featureBridge.js +2 -0
- package/dist/split/chunks/featureBridge.js.map +1 -0
- package/dist/split/chunks/filterRegistry.js +1 -1
- package/dist/split/chunks/filterRegistry.js.map +1 -1
- package/dist/split/chunks/getAllPropertyDescriptors.js +1 -1
- package/dist/split/chunks/getAllPropertyDescriptors.js.map +1 -1
- package/dist/split/chunks/lifecycleHooks.js +1 -1
- package/dist/split/chunks/pathDiagnostics.js +1 -1
- package/dist/split/chunks/pathDiagnostics.js.map +1 -1
- package/dist/split/chunks/registerComponents.js +1 -1
- package/dist/split/chunks/registerComponents.js.map +1 -1
- package/dist/split/chunks/setAll.js +1 -1
- package/dist/split/chunks/setAll.js.map +1 -1
- package/dist/split/chunks/version.js +1 -1
- package/dist/split/core.d.ts +21 -9
- package/dist/split/core.js +1 -1
- package/dist/split/core.js.map +1 -1
- package/dist/split/features/devtools.js +1 -1
- package/dist/split/features/devtools.js.map +1 -1
- package/dist/split/features/diagnostics.js +1 -1
- package/dist/split/features/diagnostics.js.map +1 -1
- package/dist/split/features/formats.js +1 -1
- package/dist/split/features/formats.js.map +1 -1
- package/dist/split/features/recursion.js +1 -1
- package/dist/split/features/recursion.js.map +1 -1
- package/dist/split/features/scopes.js +1 -1
- package/dist/split/features/scopes.js.map +1 -1
- package/dist/split/features/ssr.js +1 -1
- package/dist/split/features/ssr.js.map +1 -1
- package/dist/split/features/temporal.js +1 -1
- package/dist/split/features/temporal.js.map +1 -1
- package/dist/wcs-manifest.json +98 -44
- package/package.json +1 -1
- package/dist/split/chunks/mount.js +0 -2
- package/dist/split/chunks/mount.js.map +0 -1
- package/dist/split/chunks/watchRuntime.js +0 -2
- package/dist/split/chunks/watchRuntime.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{c as t,S as e}from"../chunks/filterRegistry.js";import{P as n,Q as o,R as s,S as r,U as a,V as c,W as i,n as l,m as d,X as u,y as f,Y as p,L as m,o as h,D as N,Z as g,_ as y,v as b,$ as S,a0 as T,a1 as x,a2 as A,a3 as C,a4 as O,a5 as v,a6 as _,a7 as E,a8 as w,a9 as k,k as D,aa as M,ab as P,ac as j,ad as $,ae as W,g as B}from"../chunks/binder.js";import{H,a as q}from"../chunks/registerComponents.js";import{V as F}from"../chunks/version.js";import{W as R,I,g as z}from"../chunks/pathDiagnostics.js";import"../chunks/errorGuidance.js";import"../chunks/lifecycleHooks.js";import"../chunks/setAll.js";import"../chunks/getAllPropertyDescriptors.js";import"../chunks/checkDependency.js";import"../chunks/diagnosticsHooks.js";const J=/^@@wcs-(?:for|if|elseif|else):[^-]/,L=/^@@wcs-(for|if|elseif|else)-start:(.+)$/,V=/^@@wcs-(for|if|elseif|else)-end:(.+)$/,G=/^@@wcs-text-start:(.+)$/;function Q(t){return t.replace(/</g,"\\u003c").replace(/>/g,"\\u003e").replace(/&/g,"\\u0026").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")}class U extends H{_stateData=null;_templates=null;_hydrateProps=null;get version(){return this.getAttribute("version")||""}get stateData(){return null===this._stateData&&(this._stateData=this._loadStateData()),this._stateData}get templates(){return null===this._templates&&(this._templates=this._loadTemplates()),this._templates}get hydrateProps(){return null===this._hydrateProps&&(this._hydrateProps=this._loadHydrateProps()),this._hydrateProps}getTemplate(t){return this.templates.get(t)??null}verifyVersion(){const t=this.version;if(!t)return!0;const e=t.split("."),n=F.split(".");return e[0]===n[0]&&e[1]===n[1]}setStateData(t){this._stateData=t}setHydrateProps(t){this._hydrateProps=t}_loadStateData(){const t=this.querySelector('script[type="application/json"]:not([data-wcs-ssr-props])');if(!t)return{};try{return JSON.parse(t.textContent||"{}")}catch{return{}}}_loadTemplates(){const t=new Map,e=this.querySelectorAll("template[id]");for(const n of e){const e=n.getAttribute("id");e&&t.set(e,n)}return t}_loadHydrateProps(){const t=this.querySelector("script[data-wcs-ssr-props]");if(!t)return{};try{return JSON.parse(t.textContent||"{}")}catch{return{}}}static find(e){const n=t.tagNames.ssr,o=e instanceof Element?e:e instanceof Document?e.documentElement:null;if(!o)return null;return o.querySelector(n)}static extractStateData(t){const e=t.__state;if(!e||"object"!=typeof e)return{};const n={};for(const t of Object.keys(e)){if(t.startsWith("$"))continue;const o=Object.getOwnPropertyDescriptor(e,t);if(void 0!==o&&"function"==typeof o.get)continue;const s=e[t];"function"!=typeof s&&(n[t]=s)}return n}static buildContent(e,c){const i=document.createElement("script");i.setAttribute("type","application/json"),i.textContent=Q(JSON.stringify(c)),e.appendChild(i);const l=n();for(const n of l){const s=o(n);if(!s)continue;const r=document.createElement("template");r.setAttribute("id",n);const a=s.parseBindTextResult,c="else"===a.bindingType?"else:":`${a.bindingType}: ${a.statePathName}`;r.setAttribute(t.bindAttributeName,c);const i=s.fragment.cloneNode(!0);r.content.appendChild(i),e.appendChild(r)}const d=s();if(d.length>0){const t={};for(let e=0;e<d.length;e++){const n=d[e],o=r(n);if(0===o.length)continue;const s=`wcs-ssr-${e}`;n.setAttribute("data-wcs-ssr-id",s);const a={};for(const t of o)a[t.propName]=t.value;t[s]=a}if(Object.keys(t).length>0){const n=document.createElement("script");n.setAttribute("type","application/json"),n.setAttribute("data-wcs-ssr-props",""),n.textContent=Q(JSON.stringify(t)),e.appendChild(n)}}a()}static removeBlockBoundaryComments(t){const e=document.createTreeWalker(t,NodeFilter.SHOW_COMMENT),n=[];for(;e.nextNode();){const t=e.currentNode;(L.test(t.data)||V.test(t.data))&&n.push(t)}for(const t of n)t.remove()}static removeStructuralComments(t){const e=document.createTreeWalker(t,NodeFilter.SHOW_COMMENT),n=[];for(;e.nextNode();){const t=e.currentNode;J.test(t.data)&&n.push(t)}for(const t of n)t.remove()}static restoreTextBindings(t){const e=document.createTreeWalker(t,NodeFilter.SHOW_COMMENT),n=[];for(;e.nextNode();){const t=e.currentNode,o=G.exec(t.data);o&&n.push({comment:t,path:o[1]})}for(const{comment:t,path:e}of n){const n=document.createComment(`@@: ${e}`);t.parentNode.insertBefore(n,t);let o=t.nextSibling;t.remove();const s=`@@wcs-text-end:${e}`;for(;o;){const t=o.nextSibling;if(o.nodeType===Node.COMMENT_NODE&&o.data===s){o.parentNode.removeChild(o);break}o.parentNode.removeChild(o),o=t}}}static cleanupDom(e){const n=document.body,o=e.querySelectorAll(t.tagNames.ssr),s=new Map;for(const t of o){const e=t.querySelectorAll("template[id]");for(const t of e){const e=t.getAttribute("id");e&&s.set(e,t)}}const r=document.createTreeWalker(n,NodeFilter.SHOW_COMMENT),a=[];for(;r.nextNode();){const t=r.currentNode;L.test(t.data)&&a.push(t)}for(const t of a){const e=L.exec(t.data),n=`@@wcs-${e[1]}-end:${e[2]}`;let o=t.nextSibling;for(;o;){const t=o.nextSibling;if(o.nodeType===Node.COMMENT_NODE&&o.data===n){o.remove();break}o.remove(),o=t}t.remove()}U.restoreTextBindings(n);const c=document.createTreeWalker(n,NodeFilter.SHOW_COMMENT),i=[];for(;c.nextNode();){const t=c.currentNode;if(J.test(t.data)){const e=t.data.split(":")[1];i.push({comment:t,uuid:e})}}for(const{comment:e,uuid:n}of i){const o=s.get(n);if(o){const n=document.createElement("template"),s=o.getAttribute(t.bindAttributeName);s&&n.setAttribute(t.bindAttributeName,s);const r=document.importNode(o.content,!0);if(r.childNodes.length>0)n.content.appendChild(r);else for(const t of Array.from(o.childNodes))n.content.appendChild(document.importNode(t,!0));e.parentNode.replaceChild(n,e)}}const l=e.querySelectorAll("[data-wcs-ssr-id]");for(const t of l)t.removeAttribute("data-wcs-ssr-id");for(const t of o)t.remove()}}const X={protocol:"wcs-ssr-snapshot",version:1,build:function(e){const n=t.tagNames.state,o=t.tagNames.ssr,s=e.querySelectorAll(`${n}[enable-ssr]:not([mount]):not([bind-component])`);for(const t of s){const e=t.previousElementSibling;if(null!==e&&e.tagName.toLowerCase()===o)continue;const n=document.createElement(o);n.setAttribute("version",F),U.buildContent(n,U.extractStateData(t)),t.parentNode?.insertBefore(n,t)}}};const Y=new Set(["for","if","elseif","else"]);function Z(t){const e=new j;if(0===t.length)return{bindingInfos:[],subscriberNodes:[],bindingSession:e};const n=t[0].parentNode,o=t[t.length-1].nextSibling,s=document.createElement("div");for(const e of t)s.appendChild(e);const[r,a]=i(s),c=e.initialize(a,{registerAddress:!1,applyOnReconnect:!1});if(n)for(;s.firstChild;)n.insertBefore(s.firstChild,o);return{bindingInfos:c,subscriberNodes:r,bindingSession:e}}function K(t,e,n){for(const o of e)"event"===o.bindingType||Y.has(o.bindingType)||o.node.nodeType===Node.COMMENT_NODE||o.statePathName in I||z(o.statePathName).wildcardCount>n||t.push(o)}function tt(e,n,o){const s={for:t.commentForPrefix,if:t.commentIfPrefix,elseif:t.commentElseIfPrefix,else:t.commentElsePrefix}[n];if(!s)return null;const r=`@@${s}:${o}`,a=document.createTreeWalker(e,NodeFilter.SHOW_COMMENT);for(;a.nextNode();){const t=a.currentNode;if(t.data===r)return t}return null}function et(e,n){const o=e;let s=null;for(const[e,r]of n.templates){const n=r.getAttribute(t.bindAttributeName)||"";let a=N(n)[0];const c=a.bindingType;"else"===c&&s&&(a={...s,outFilters:[...s.outFilters,$()],bindingType:"else"}),"if"===c||"elseif"===c?s=a:"else"===c&&(s=null);const i=document.importNode(r.content,!0),l="for"===c?a.statePathName:void 0;g(i),"string"==typeof l&&y(i,l),b(o,i,l);const d={fragment:i,parseBindTextResult:a,nodeInfos:S(i)};T(e,o,d)}}const nt={hydrate:async function(e){await c(e);const n=e.querySelectorAll(t.tagNames.ssr);for(const t of n){const n=t;if(!n.verifyVersion())return console.warn(`[@wcstack/state] SSR version mismatch: server="${n.version}", client="${F}". Falling back to full render.`),U.cleanupDom(e),!1}for(const t of n)et(e,t);const s=function(t){const e=[],n=document.createTreeWalker(t,NodeFilter.SHOW_COMMENT),o=[];for(;n.nextNode();)o.push(n.currentNode);for(const t of o){const n=L.exec(t.data);if(!n)continue;const o=n[1],s=n[2],r=s.split(":");let a,c,i=null;"for"===o?(a=r[0],c=r[1],i=parseInt(r[2],10)):(a=r[0],c=r.slice(1).join(":"));const l=[];let d=t.nextSibling;const u=`@@wcs-${o}-end:${s}`;for(;d&&(d.nodeType!==Node.COMMENT_NODE||d.data!==u);)l.push(d),d=d.nextSibling;e.push({type:o,uuid:a,path:c,index:i,nodes:l})}return e}(document.body),r=function(t,e){const n=new Map,s=[];for(const o of e){if(0===o.nodes.length)continue;const e=x(o.nodes),{bindingInfos:r,subscriberNodes:a,bindingSession:c}=Z(o.nodes);A(e,c);for(const t of a)t.nodeType===Node.ELEMENT_NODE&&t.setAttribute("data-wcs-completed","");C(e,r),O(e,a);const i=[];for(const t of r)t.statePathName in I&&i.push(t);if(v(e,i),"for"===o.type&&null!==o.index){const a=tt(t,"for",o.uuid);if(a){const t=_(null,o.index);E(a,t,e);const i=o.nodes[o.nodes.length-1];w(a,i),k(a,e);const l=z(o.path+"."+R),d=D(l,t);M(e,d),c.initialize(r,{registerAddress:!0,registerPathInfo:!1,applyOnReconnect:!1}),K(s,r,l.wildcardCount);let u=n.get(o.uuid);u||(u=[],n.set(o.uuid,u)),u.push(t)}}else{const n=tt(t,o.type,o.uuid);n&&(k(n,e),c.initialize(r,{registerAddress:!0,registerPathInfo:!1,applyOnReconnect:!1}),K(s,r,0))}}for(const[e,s]of n){const n=tt(t,"for",e);if(!n)continue;const r=n.getRootNode(),a=o(e);if(!a)continue;const c=a.parseBindTextResult.statePathName,i=f(r);i&&i.createState("readonly",t=>{const e=t[c];Array.isArray(e)&&P(e,s)})}return s}(document.body,s);U.removeBlockBoundaryComments(document.body);const a=[];for(const t of n)t.parentNode&&(a.push({el:t,parent:t.parentNode,next:t.nextSibling}),t.remove());U.restoreTextBindings(document.body);const[N,g]=i(document.body);for(const t of N)if(t.nodeType===Node.ELEMENT_NODE){const e=t;e.hasAttribute("data-wcs-completed")||(l(t,null),e.setAttribute("data-wcs-completed",""))}else l(t,null);const y=[],b=[],S=d(document.body).initialize(g);for(const t of S)if("event"!==t.bindingType)if(Y.has(t.bindingType))b.push(t);else{if(t.statePathName.includes(R))continue;y.push(t)}for(const t of b)if("for"===t.bindingType){const e=u(t),n=t.replaceNode.getRootNode(),o=f(n);o&&o.createState("readonly",n=>{const o=n[t.statePathName];Array.isArray(o)&&(p(e,o),m(e,o))})}h([...y,...r]);for(const{el:t,parent:e,next:n}of a)e.insertBefore(t,n);const T=e.querySelectorAll(t.tagNames.ssr);for(const t of T){const n=t.hydrateProps;for(const[t,o]of Object.entries(n)){const n=e.querySelector(`[data-wcs-ssr-id="${t}"]`);if(n)for(const[t,e]of Object.entries(o))n[t]=e}}const j=e.querySelectorAll("[data-wcs-completed]");for(const t of j)t.removeAttribute("data-wcs-completed");return!0},loadState(t){const e=t.parentNode;if(!e)return null;const n=U.find(e);if(!n)return null;const o=n.stateData;return Object.keys(o).length>0?o:null},async emitSnapshot(e){await B(e.rootNode);const n=U.extractStateData(e),o=document.createElement(t.tagNames.ssr);o.setAttribute("version",F),U.buildContent(o,n),e.parentNode?.insertBefore(o,e)}};let ot=!1;function st(){ot||(ot=!0,W(nt),q(e=>{e.get(t.tagNames.ssr)||e.define(t.tagNames.ssr,U)}),function(){const t=globalThis;void 0===t[e]&&(t[e]=X)}())}const rt={name:"ssr",install(){st()}};export{rt as default,rt as ssr};
|
|
1
|
+
import{c as t,S as e}from"../chunks/filterRegistry.js";import{P as n,Q as s,R as o,S as r,U as a,V as i,W as c,X as l,Y as d,y as u,x as f,Z as p,_ as m,F as h,$ as g,M as b,z as y,K as N,a0 as S,a1 as A,D as w,a2 as x,a3 as v,a4 as C,a5 as T,a6 as D,a7 as _,a8 as E,a9 as P,aa as k,ab as O,ac as j,j as $,ad as M,ae as B,af as q,ag as R,ah as I,g as H}from"../chunks/binder.js";import{H as F,a as z}from"../chunks/registerComponents.js";import{V as W}from"../chunks/version.js";import{W as J,I as V,g as L}from"../chunks/pathDiagnostics.js";import"../chunks/errorGuidance.js";import"../chunks/lifecycleHooks.js";import"../chunks/declarationAliases.js";import"../chunks/setAll.js";import"../chunks/getAllPropertyDescriptors.js";import"../chunks/checkDependency.js";import"../chunks/declarationHooks.js";import"../chunks/diagnosticsHooks.js";const G=/^@@wcs-(?:for|if|elseif|else):[^-]/,K=/^@@wcs-(for|if|elseif|else)-start:(.+)$/,Q=/^@@wcs-(for|if|elseif|else)-end:(.+)$/,U=/^@@wcs-text-start:(.+)$/;function X(t){return t.replace(/</g,"\\u003c").replace(/>/g,"\\u003e").replace(/&/g,"\\u0026").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")}function Y(t,e){const n=document.createTreeWalker(t,NodeFilter.SHOW_COMMENT),s=[];for(;n.nextNode();){const t=n.currentNode;e(t.data)&&s.push(t)}return s}const Z=t=>G.test(t),tt=t=>K.test(t);function et(t){const e=t.fragment.cloneNode(!0);for(const n of t.nodeInfos){const s=a(i(t.fragment,n.nodePath));if(null===s)continue;const o=i(e,n.nodePath);o.parentNode.replaceChild(document.createComment(s),o)}return e}class nt extends F{_stateData=null;_templates=null;_hydrateProps=null;get version(){return this.getAttribute("version")||""}get stateData(){return null===this._stateData&&(this._stateData=this._loadStateData()),this._stateData}get templates(){return null===this._templates&&(this._templates=this._loadTemplates()),this._templates}get hydrateProps(){return null===this._hydrateProps&&(this._hydrateProps=this._loadHydrateProps()),this._hydrateProps}getTemplate(t){return this.templates.get(t)??null}verifyVersion(){const t=this.version;if(!t)return!0;const e=t.split("."),n=W.split(".");return e[0]===n[0]&&e[1]===n[1]}setStateData(t){this._stateData=t}setHydrateProps(t){this._hydrateProps=t}_loadStateData(){const t=this.querySelector('script[type="application/json"]:not([data-wcs-ssr-props])');if(!t)return{};try{return JSON.parse(t.textContent||"{}")}catch{return{}}}_loadTemplates(){const t=new Map,e=this.querySelectorAll("template[id]");for(const n of e){const e=n.getAttribute("id");e&&t.set(e,n)}return t}_loadHydrateProps(){const t=this.querySelector("script[data-wcs-ssr-props]");if(!t)return{};try{return JSON.parse(t.textContent||"{}")}catch{return{}}}static find(e){const n=t.tagNames.ssr,s=e instanceof Element?e:e instanceof Document?e.documentElement:null;if(!s)return null;return s.querySelector(n)}static extractStateData(t){const e=t.__state;if(!e||"object"!=typeof e)return{};const n={};for(const t of Object.keys(e)){if(t.startsWith("$"))continue;const s=Object.getOwnPropertyDescriptor(e,t);if(void 0!==s&&"function"==typeof s.get)continue;const o=e[t];"function"!=typeof o&&(n[t]=o)}return n}static buildContent(e,a,i){const c=i??e.ownerDocument,l=document.createElement("script");l.setAttribute("type","application/json"),l.textContent=X(JSON.stringify(a)),e.appendChild(l);for(const[n,s]of function(t){const e=new Map,n=[t];for(;n.length>0;)for(const t of Y(n.shift(),Z)){const s=t.data,o=s.slice(s.indexOf(":")+1);if(e.has(o))continue;const a=r(o);null!==a&&(e.set(o,a),n.push(a.fragment))}return e}(c)){const o=document.createElement("template");o.setAttribute("id",n);const r=s.parseBindTextResult,a="else"===r.bindingType?"else:":`${r.bindingType}: ${r.statePathName}`;o.setAttribute(t.bindAttributeName,a),o.content.appendChild(et(s)),e.appendChild(o)}const d=e.ownerDocument,u=n();if(u.length>0){const t={};for(let e=0;e<u.length;e++){const n=u[e];if(!n.isConnected||n.ownerDocument!==d)continue;const o=s(n);if(0===o.length)continue;const r=`wcs-ssr-${e}`;n.setAttribute("data-wcs-ssr-id",r);const a={};for(const t of o)a[t.propName]=t.value;t[r]=a}if(Object.keys(t).length>0){const n=document.createElement("script");n.setAttribute("type","application/json"),n.setAttribute("data-wcs-ssr-props",""),n.textContent=X(JSON.stringify(t)),e.appendChild(n)}}o()}static removeBlockBoundaryComments(t){for(const e of Y(t,t=>K.test(t)||Q.test(t)))e.remove()}static removeStructuralComments(t){for(const e of Y(t,Z))e.remove()}static restoreTextBindings(t){for(const e of Y(t,t=>U.test(t))){const t=U.exec(e.data)[1],n=document.createComment(`@@: ${t}`);e.parentNode.insertBefore(n,e);let s=e.nextSibling;e.remove();const o=`@@wcs-text-end:${t}`;for(;s;){const t=s.nextSibling;if(s.nodeType===Node.COMMENT_NODE&&s.data===o){s.parentNode.removeChild(s);break}s.parentNode.removeChild(s),s=t}}}static cleanupDom(e){const n=document.body,s=e.querySelectorAll(t.tagNames.ssr),o=new Map;for(const t of s){const e=t.querySelectorAll("template[id]");for(const t of e){const e=t.getAttribute("id");e&&o.set(e,t)}}for(const t of Y(n,tt)){const e=K.exec(t.data),n=`@@wcs-${e[1]}-end:${e[2]}`;let s=t.nextSibling;for(;s;){const t=s.nextSibling;if(s.nodeType===Node.COMMENT_NODE&&s.data===n){s.remove();break}s.remove(),s=t}t.remove()}nt.restoreTextBindings(n);for(const e of Y(n,Z)){const n=o.get(e.data.split(":")[1]);if(n){const s=document.createElement("template"),o=n.getAttribute(t.bindAttributeName);o&&s.setAttribute(t.bindAttributeName,o);const r=document.importNode(n.content,!0);if(r.childNodes.length>0)s.content.appendChild(r);else for(const t of Array.from(n.childNodes))s.content.appendChild(document.importNode(t,!0));e.parentNode.replaceChild(s,e)}}const r=e.querySelectorAll("[data-wcs-ssr-id]");for(const t of r)t.removeAttribute("data-wcs-ssr-id");for(const t of s)t.remove()}}const st={protocol:"wcs-ssr-snapshot",version:1,build:function(e){const n=t.tagNames.state,s=t.tagNames.ssr,r=e.querySelectorAll(`${n}[enable-ssr]:not([mount]):not([bind-component])`);for(const t of r){const e=t.previousElementSibling;if(null!==e&&e.tagName.toLowerCase()===s)continue;const n=document.createElement(s);n.setAttribute("version",W),nt.buildContent(n,nt.extractStateData(t),t.getRootNode()),t.parentNode?.insertBefore(n,t)}o()},reset:function(){c(),o()}};const ot=new Set(["for","if","elseif","else"]);function rt(t){const e=new q;if(0===t.length)return{bindingInfos:[],subscriberNodes:[],bindingSession:e,deferredSpreads:[]};const n=t[0].parentNode,s=t[t.length-1].nextSibling,o=document.createElement("div");for(const e of t)o.appendChild(e);const[r,a,i]=d(o),c=e.initialize(a,{registerAddress:!1,applyOnReconnect:!1});if(n)for(;o.firstChild;)n.insertBefore(o.firstChild,s);return{bindingInfos:c,subscriberNodes:r,bindingSession:e,deferredSpreads:i}}const at=/^\s*@@\s*(?:wcs-text)?\s*:/;function it(t,e,n){for(const s of e)"event"===s.bindingType||ot.has(s.bindingType)||(s.node.nodeType!==Node.COMMENT_NODE||"text"===s.bindingType&&at.test(s.node.data))&&(s.statePathName in V||L(s.statePathName).wildcardCount>n||t.push(s))}function ct(e,n,s){const o={for:t.commentForPrefix,if:t.commentIfPrefix,elseif:t.commentElseIfPrefix,else:t.commentElsePrefix}[n];if(!o)return null;const r=`@@${o}:${s}`;return Y(e,t=>t===r)[0]??null}function lt(e,n){const s=e;let o=null;for(const[e,r]of n.templates){const n=r.getAttribute(t.bindAttributeName)||"";let a=N(n)[0];const i=a.bindingType;"else"===i&&o&&(a={...o,outFilters:[...o.outFilters,R()],bindingType:"else"}),"if"===i||"elseif"===i?o=a:"else"===i&&(o=null);const c=document.importNode(r.content,!0),l="for"===i?a.statePathName:void 0;S(c),"string"==typeof l&&A(c,l),w(s,c,l);const d={fragment:c,parseBindTextResult:a,nodeInfos:x(c)};v(e,s,d)}}const dt={hydrate:async function(e){await l(e);const n=e.querySelectorAll(t.tagNames.ssr);for(const t of n){const n=t;if(!n.verifyVersion())return console.warn(`[@wcstack/state] SSR version mismatch: server="${n.version}", client="${W}". Falling back to full render.`),nt.cleanupDom(e),!1}for(const t of n)lt(e,t);nt.restoreTextBindings(document.body);const s=function(t){const e=[];for(const n of Y(t,tt)){const t=K.exec(n.data),s=t[1],o=t[2],r=o.split(":");let a,i,c=null;"for"===s?(a=r[0],i=r[1],c=parseInt(r[2],10)):(a=r[0],i=r.slice(1).join(":"));const l=[];let d=n.nextSibling;const u=`@@wcs-${s}-end:${o}`;for(;d&&(d.nodeType!==Node.COMMENT_NODE||d.data!==u);)l.push(d),d=d.nextSibling;e.push({type:s,uuid:a,path:i,index:c,nodes:l})}return e}(document.body),o=function(t,e){const n=new Map,s=[];for(const o of e){if(0===o.nodes.length)continue;const e=C(o.nodes),{bindingInfos:r,subscriberNodes:a,bindingSession:i,deferredSpreads:c}=rt(o.nodes);T(e,i);for(const t of a)t.nodeType===Node.ELEMENT_NODE&&t.setAttribute("data-wcs-completed","");D(e,r),_(e,a);const l=[];for(const t of r)t.statePathName in V&&l.push(t);if(E(e,l),"for"===o.type&&null!==o.index){const a=ct(t,"for",o.uuid);if(a){const t=P(null,o.index);k(a,t,e);const l=o.nodes[o.nodes.length-1];O(a,l),j(a,e);const d=L(o.path+"."+J),u=$(d,t);M(e,u),i.initialize(r,{registerAddress:!0,registerPathInfo:!1,applyOnReconnect:!1}),it(s,r,d.wildcardCount),p(c,u,i);let f=n.get(o.uuid);f||(f=[],n.set(o.uuid,f)),f.push(t)}}else{const n=ct(t,o.type,o.uuid);n&&(j(n,e),i.initialize(r,{registerAddress:!0,registerPathInfo:!1,applyOnReconnect:!1}),it(s,r,0),p(c,null,i))}}for(const[e,s]of n){const n=ct(t,"for",e);if(!n)continue;const o=n.getRootNode(),a=r(e);if(!a)continue;const i=a.parseBindTextResult.statePathName,c=h(o);c&&c.createState("readonly",t=>{const e=t[i];Array.isArray(e)&&B(e,s)})}return s}(document.body,s);nt.removeBlockBoundaryComments(document.body);const a=[];for(const t of n)t.parentNode&&(a.push({el:t,parent:t.parentNode,next:t.nextSibling}),t.remove());const[i,c,N]=d(document.body);for(const t of i)if(t.nodeType===Node.ELEMENT_NODE){const e=t;e.hasAttribute("data-wcs-completed")||(u(t,null),e.setAttribute("data-wcs-completed",""))}else u(t,null);const S=[],A=[],w=f(document.body),x=w.initialize(c);p(N,null,w);for(const t of x)if("event"!==t.bindingType)if(ot.has(t.bindingType))A.push(t);else{if(t.statePathName.includes(J))continue;S.push(t)}for(const t of A)if("for"===t.bindingType){const e=m(t),n=t.replaceNode.getRootNode(),s=h(n);s&&s.createState("readonly",n=>{const s=n[t.statePathName];Array.isArray(s)&&(g(e,s),b(e,s))})}y([...S,...o]);for(const{el:t,parent:e,next:n}of a)e.insertBefore(t,n);const v=new Map;for(const t of e.querySelectorAll("[data-wcs-ssr-id]"))v.set(t.getAttribute("data-wcs-ssr-id"),t);for(const n of e.querySelectorAll(t.tagNames.ssr))for(const[t,e]of Object.entries(n.hydrateProps)){const n=v.get(t);if(n)for(const[t,s]of Object.entries(e))n[t]=s}for(const t of e.querySelectorAll("[data-wcs-completed],[data-wcs-ssr-id]"))t.removeAttribute("data-wcs-completed"),t.removeAttribute("data-wcs-ssr-id");return!0},loadState(t){const e=t.parentNode;if(!e)return null;const n=nt.find(e);if(!n)return null;const s=n.stateData;return Object.keys(s).length>0?s:null},async emitSnapshot(e){await H(e.rootNode);const n=nt.extractStateData(e),s=document.createElement(t.tagNames.ssr);s.setAttribute("version",W),nt.buildContent(s,n,e.rootNode),e.parentNode?.insertBefore(s,e)}};let ut=!1;function ft(){ut||(ut=!0,I(dt),z(e=>{e.get(t.tagNames.ssr)||e.define(t.tagNames.ssr,nt)}),function(){const t=globalThis;void 0===t[e]&&(t[e]=st)}())}const pt={name:"ssr",install(){ft()}};export{pt as default,pt as ssr};
|
|
2
2
|
//# sourceMappingURL=ssr.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr.js","sources":["../../../../../src/ssr/Ssr.ts","../../../../../src/ssr/buildSsrDocument.ts","../../../../../src/ssr/hydrateBindings.ts","../../../../../src/ssr/install.ts","../../../../../src/features/ssr.ts"],"sourcesContent":["import { config } from \"../config\";\nimport { IState } from \"../types\";\nimport { VERSION } from \"../version\";\nimport { getAllFragmentUUIDs, getFragmentInfoByUUID } from \"../structural/fragmentInfoByUUID\";\nimport { getAllSsrPropertyNodes, getSsrProperties, clearSsrPropertyStore } from \"../apply/ssrPropertyStore\";\nimport { HTMLElementBase } from \"../platform/HTMLElementBase\";\n\nexport interface ISsrElement {\n readonly version: string;\n readonly stateData: IState;\n readonly templates: Map<string, HTMLTemplateElement>;\n readonly hydrateProps: Record<string, Record<string, unknown>>;\n getTemplate(uuid: string): HTMLTemplateElement | null;\n verifyVersion(): boolean;\n}\n\n// SSR コメントパターン\nconst SSR_PLACEHOLDER_COMMENT = /^@@wcs-(?:for|if|elseif|else):[^-]/;\nconst SSR_BLOCK_START = /^@@wcs-(for|if|elseif|else)-start:(.+)$/;\nconst SSR_BLOCK_END = /^@@wcs-(for|if|elseif|else)-end:(.+)$/;\nconst SSR_TEXT_START = /^@@wcs-text-start:(.+)$/;\n\nexport {\n SSR_PLACEHOLDER_COMMENT,\n SSR_BLOCK_START,\n SSR_BLOCK_END,\n SSR_TEXT_START,\n};\n\n/**\n * script 要素へ埋め込む JSON を HTML パーサから保護する。\n * HTML 直列化時、script の中身は生のまま出力されるため、state 値に\n * \"</script>\" や \"<!--\" を含む文字列があると script を脱出できてしまう。\n * \"<\" \">\" \"&\" と U+2028/U+2029 を JSON の \\uXXXX エスケープへ置換する\n * (JSON.parse では元の文字列と等価に復元される)。\n */\nfunction escapeJsonForScript(json: string): string {\n return json\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n}\n\nexport class Ssr extends HTMLElementBase implements ISsrElement {\n private _stateData: IState | null = null;\n private _templates: Map<string, HTMLTemplateElement> | null = null;\n private _hydrateProps: Record<string, Record<string, unknown>> | null = null;\n\n get version(): string {\n return this.getAttribute('version') || '';\n }\n\n get stateData(): IState {\n if (this._stateData === null) {\n this._stateData = this._loadStateData();\n }\n return this._stateData;\n }\n\n get templates(): Map<string, HTMLTemplateElement> {\n if (this._templates === null) {\n this._templates = this._loadTemplates();\n }\n return this._templates;\n }\n\n get hydrateProps(): Record<string, Record<string, unknown>> {\n if (this._hydrateProps === null) {\n this._hydrateProps = this._loadHydrateProps();\n }\n return this._hydrateProps;\n }\n\n getTemplate(uuid: string): HTMLTemplateElement | null {\n return this.templates.get(uuid) ?? null;\n }\n\n /**\n * サーバーの SSR バージョンとクライアントの state バージョンを検証する。\n * メジャー・マイナーバージョンが一致すればtrue。\n * version 属性がない場合は検証スキップ(true)。\n */\n verifyVersion(): boolean {\n const serverVersion = this.version;\n if (!serverVersion) return true;\n const serverParts = serverVersion.split('.');\n const clientParts = VERSION.split('.');\n // メジャー・マイナーが一致すれば互換\n return serverParts[0] === clientParts[0] && serverParts[1] === clientParts[1];\n }\n\n setStateData(data: IState): void {\n this._stateData = data;\n }\n\n setHydrateProps(props: Record<string, Record<string, unknown>>): void {\n this._hydrateProps = props;\n }\n\n private _loadStateData(): IState {\n const script = this.querySelector(\n `script[type=\"application/json\"]:not([data-wcs-ssr-props])`\n );\n if (!script) return {};\n try {\n return JSON.parse(script.textContent || '{}');\n } catch {\n return {};\n }\n }\n\n private _loadTemplates(): Map<string, HTMLTemplateElement> {\n const map = new Map<string, HTMLTemplateElement>();\n const templates = this.querySelectorAll<HTMLTemplateElement>('template[id]');\n for (const tpl of templates) {\n const id = tpl.getAttribute('id');\n if (id) {\n map.set(id, tpl);\n }\n }\n return map;\n }\n\n private _loadHydrateProps(): Record<string, Record<string, unknown>> {\n const script = this.querySelector('script[data-wcs-ssr-props]');\n if (!script) return {};\n try {\n return JSON.parse(script.textContent || '{}');\n } catch {\n return {};\n }\n }\n\n static find(root: Node): ISsrElement | null {\n const tagName = config.tagNames.ssr;\n const parentEl = root instanceof Element\n ? root\n : root instanceof Document\n ? root.documentElement\n : null;\n if (!parentEl) return null;\n const el = parentEl.querySelector(tagName);\n return el as ISsrElement | null;\n }\n\n /**\n * stateData と構造テンプレート・プロパティから <wcs-ssr> の中身を構築する。\n * server パッケージの renderToString から呼ばれる。\n */\n /**\n * wcs-state 要素から $ プレフィックスや関数を除いたデータを抽出する。\n */\n static extractStateData(stateEl: Element): Record<string, any> {\n const raw = (stateEl as any).__state;\n if (!raw || typeof raw !== 'object') return {};\n const data: Record<string, any> = {};\n for (const key of Object.keys(raw)) {\n if (key.startsWith('$')) continue;\n // **アクセサは評価しない。** スナップショットが運ぶのはデータで、派生値は\n // クライアントが同じ宣言から作り直す。ここは Object.entries で舐めていたので、\n // own かつ enumerable な getter を**生の state オブジェクト**を this にして\n // 評価していた — proxy の上でしか意味を持たない本体(`this[\"items.*.n\"]` や\n // `this.$getAll(...)`)が、パス getter なら NaN → JSON の null で静かに壊れ、\n // `$getAll` を呼ぶ getter なら TypeError でページ全体の SSR を落としていた\n // (docs/state-recursive-path-impl-plan.md §7)。\n const descriptor = Object.getOwnPropertyDescriptor(raw, key);\n if (descriptor !== undefined && typeof descriptor.get === 'function') continue;\n const value = (raw as Record<string, unknown>)[key];\n if (typeof value !== 'function') {\n data[key] = value;\n }\n }\n return data;\n }\n\n static buildContent(ssrEl: Element, stateData: Record<string, any>): void {\n // 初期データ JSON\n const jsonScript = document.createElement('script');\n jsonScript.setAttribute('type', 'application/json');\n jsonScript.textContent = escapeJsonForScript(JSON.stringify(stateData));\n ssrEl.appendChild(jsonScript);\n\n // UUID で管理されているテンプレートを復元して格納\n const uuids = getAllFragmentUUIDs();\n for (const uuid of uuids) {\n const fragmentInfo = getFragmentInfoByUUID(uuid);\n if (!fragmentInfo) continue;\n\n const tpl = document.createElement('template');\n tpl.setAttribute('id', uuid);\n\n const bindResult = fragmentInfo.parseBindTextResult;\n const bindText = bindResult.bindingType === 'else'\n ? 'else:'\n : `${bindResult.bindingType}: ${bindResult.statePathName}`;\n tpl.setAttribute(config.bindAttributeName, bindText);\n\n const content = fragmentInfo.fragment.cloneNode(true) as DocumentFragment;\n tpl.content.appendChild(content);\n\n ssrEl.appendChild(tpl);\n }\n\n // 属性で代替不可なプロパティをハイドレーション用に格納\n const ssrNodes = getAllSsrPropertyNodes();\n if (ssrNodes.length > 0) {\n const propsData: Record<string, Record<string, unknown>> = {};\n for (let i = 0; i < ssrNodes.length; i++) {\n const node = ssrNodes[i];\n const entries = getSsrProperties(node);\n if (entries.length === 0) continue;\n const id = `wcs-ssr-${i}`;\n (node as Element).setAttribute('data-wcs-ssr-id', id);\n const props: Record<string, unknown> = {};\n for (const entry of entries) {\n props[entry.propName] = entry.value;\n }\n propsData[id] = props;\n }\n if (Object.keys(propsData).length > 0) {\n const propsScript = document.createElement('script');\n propsScript.setAttribute('type', 'application/json');\n propsScript.setAttribute('data-wcs-ssr-props', '');\n propsScript.textContent = escapeJsonForScript(JSON.stringify(propsData));\n ssrEl.appendChild(propsScript);\n }\n }\n\n clearSsrPropertyStore();\n }\n\n /**\n * SSR ブロック境界コメント (@@wcs-*-start/end) を除去する\n */\n static removeBlockBoundaryComments(root: Node): void {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);\n const toRemove: Comment[] = [];\n while (walker.nextNode()) {\n const comment = walker.currentNode as Comment;\n if (SSR_BLOCK_START.test(comment.data) || SSR_BLOCK_END.test(comment.data)) {\n toRemove.push(comment);\n }\n }\n for (const comment of toRemove) {\n comment.remove();\n }\n }\n\n /**\n * SSR の構造プレースホルダーコメント (@@wcs-for:uuid 等) を除去する\n */\n static removeStructuralComments(root: Node): void {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);\n const toRemove: Comment[] = [];\n while (walker.nextNode()) {\n const comment = walker.currentNode as Comment;\n if (SSR_PLACEHOLDER_COMMENT.test(comment.data)) {\n toRemove.push(comment);\n }\n }\n for (const comment of toRemove) {\n comment.remove();\n }\n }\n\n /**\n * SSR テキストバインディングコメントを復元する。\n * <!--@@wcs-text-start:path-->text<!--@@wcs-text-end:path-->\n * → <!--@@: path--> (バインディングシステムが認識する形式)\n */\n static restoreTextBindings(root: Node): void {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);\n const startComments: { comment: Comment, path: string }[] = [];\n\n while (walker.nextNode()) {\n const comment = walker.currentNode as Comment;\n const match = SSR_TEXT_START.exec(comment.data);\n if (match) {\n startComments.push({ comment, path: match[1] });\n }\n }\n\n for (const { comment, path } of startComments) {\n const bindComment = document.createComment(`@@: ${path}`);\n comment.parentNode!.insertBefore(bindComment, comment);\n\n let sibling: Node | null = comment.nextSibling;\n comment.remove();\n\n const endPattern = `@@wcs-text-end:${path}`;\n while (sibling) {\n const next: Node | null = sibling.nextSibling;\n if (sibling.nodeType === Node.COMMENT_NODE && (sibling as Comment).data === endPattern) {\n sibling.parentNode!.removeChild(sibling);\n break;\n }\n sibling.parentNode!.removeChild(sibling);\n sibling = next;\n }\n }\n }\n\n /**\n * SSR DOM をクリーンアップし、buildBindings が動作できる状態に戻す。\n * バージョン不一致時のフォールバック用。\n *\n * 1. SSR ブロック境界コメント間のレンダリング済みノードを除去\n * 2. SSR テキストバインディングを @@: 形式に復元\n * 3. プレースホルダーコメントを <wcs-ssr> 内のテンプレートで差し替え\n * 4. data-wcs-ssr-id 属性を除去\n * 5. <wcs-ssr> を除去\n */\n static cleanupDom(root: Document): void {\n const body = document.body;\n\n // <wcs-ssr> からテンプレート UUID マップを構築(カスタム要素未定義でも動作するよう DOM 直接走査)\n const ssrElements = root.querySelectorAll(config.tagNames.ssr);\n const templateByUuid = new Map<string, HTMLTemplateElement>();\n for (const ssrNode of ssrElements) {\n const templates = ssrNode.querySelectorAll<HTMLTemplateElement>('template[id]');\n for (const tpl of templates) {\n const id = tpl.getAttribute('id');\n if (id) {\n templateByUuid.set(id, tpl);\n }\n }\n }\n\n // SSR ブロック境界コメント間のレンダリング済みノードと境界コメントを除去\n const walker1 = document.createTreeWalker(body, NodeFilter.SHOW_COMMENT);\n const startComments: Comment[] = [];\n while (walker1.nextNode()) {\n const comment = walker1.currentNode as Comment;\n if (SSR_BLOCK_START.test(comment.data)) {\n startComments.push(comment);\n }\n }\n for (const startComment of startComments) {\n const match = SSR_BLOCK_START.exec(startComment.data)!;\n const type = match[1];\n const info = match[2];\n const endPattern = `@@wcs-${type}-end:${info}`;\n let sibling = startComment.nextSibling;\n while (sibling) {\n const next = sibling.nextSibling;\n if (sibling.nodeType === Node.COMMENT_NODE && (sibling as Comment).data === endPattern) {\n sibling.remove();\n break;\n }\n sibling.remove();\n sibling = next;\n }\n startComment.remove();\n }\n\n // SSR テキストバインディングを @@: 形式に復元\n Ssr.restoreTextBindings(body);\n\n // プレースホルダーコメント (@@wcs-for:uuid 等) をテンプレートに差し替え\n const walker2 = document.createTreeWalker(body, NodeFilter.SHOW_COMMENT);\n const placeholders: { comment: Comment, uuid: string }[] = [];\n while (walker2.nextNode()) {\n const comment = walker2.currentNode as Comment;\n if (SSR_PLACEHOLDER_COMMENT.test(comment.data)) {\n const uuid = comment.data.split(':')[1];\n placeholders.push({ comment, uuid });\n }\n }\n for (const { comment, uuid } of placeholders) {\n const tpl = templateByUuid.get(uuid);\n if (tpl) {\n const restored = document.createElement('template') as HTMLTemplateElement;\n const bindAttr = tpl.getAttribute(config.bindAttributeName);\n if (bindAttr) restored.setAttribute(config.bindAttributeName, bindAttr);\n const imported = document.importNode(tpl.content, true);\n if (imported.childNodes.length > 0) {\n restored.content.appendChild(imported);\n } else {\n for (const child of Array.from(tpl.childNodes)) {\n restored.content.appendChild(document.importNode(child, true));\n }\n }\n comment.parentNode!.replaceChild(restored, comment);\n }\n }\n\n // data-wcs-ssr-id 属性を除去\n const ssrIdElements = root.querySelectorAll('[data-wcs-ssr-id]');\n for (const el of ssrIdElements) {\n el.removeAttribute('data-wcs-ssr-id');\n }\n\n // <wcs-ssr> を除去\n for (const el of ssrElements) {\n el.remove();\n }\n }\n}\n","import { config } from \"../config\";\nimport { Ssr } from \"./Ssr\";\nimport { VERSION } from \"../version\";\nimport { IWcsSsrSnapshotBuilder, SSR_SNAPSHOT_BUILDER_KEY } from \"../protocol/ssrSnapshot\";\n\n/**\n * ssr-snapshot プロトコルの提供側(docs/ssr-router-design.md §5)。\n *\n * `<wcs-ssr>` スナップショットを document 全体に対する最終パスとして生成する。\n * connectedCallback 内の inline 生成は「その時点の DOM」しか見えず、router が\n * 後から挿入するルート内容の構造テンプレートを取り逃がすレースがあった\n * (state のロード方式と文書順に依存)。renderToString が全要素の完了と\n * バインディング構築の後にこれを呼ぶことで、スナップショットは常に確定後の\n * DOM を見る。\n *\n * 複数 `enable-ssr` state の意味論は inline 生成と同一に保つ(文書順に生成・\n * fragment レジストリはモジュール共有・props store は生成ごとにクリア)。\n * その整理は本プロトコルの範囲外の既存挙動として引き継ぐ。\n */\nexport function buildSsrDocument(root: Document): void {\n const stateTag = config.tagNames.state;\n const ssrTag = config.tagNames.ssr;\n // スナップショットはルートツリーに 1 本(D14): ボリューム(mount=)と bind-component は\n // 独立ツリー(__state)を持たないため、含めると空の <wcs-ssr> を生成してしまう。\n // Ssr.find は文書先頭一致なので、ルートより前の空スナップショットをルートが掴み、\n // ハイドレーション全体が無言で CSR 退化する\n const stateElements = root.querySelectorAll(`${stateTag}[enable-ssr]:not([mount]):not([bind-component])`);\n for (const stateEl of stateElements) {\n // 既に直前へ生成済み(旧 server との組み合わせで inline 生成された等)なら\n // 何もしない — build() は冪等でなければならない(プロトコル契約)\n const prev = stateEl.previousElementSibling;\n if (prev !== null && prev.tagName.toLowerCase() === ssrTag) {\n continue;\n }\n const ssrEl = document.createElement(ssrTag);\n ssrEl.setAttribute(\"version\", VERSION);\n Ssr.buildContent(ssrEl, Ssr.extractStateData(stateEl));\n stateEl.parentNode?.insertBefore(ssrEl, stateEl);\n }\n}\n\nconst builder: IWcsSsrSnapshotBuilder = {\n protocol: \"wcs-ssr-snapshot\",\n version: 1,\n build: buildSsrDocument,\n};\n\n/**\n * グローバル symbol へ自分を載せる。`bootstrapState` から呼ぶ。\n * binder(registerBinder)と同じ規範 — 既に別のコピーが載っているなら譲る\n * (そのコピーのレジストリが、そのページの正本だからである)。\n */\nexport function registerSsrSnapshotBuilder(): void {\n const globals = globalThis as Record<symbol, unknown>;\n if (globals[SSR_SNAPSHOT_BUILDER_KEY] === undefined) {\n globals[SSR_SNAPSHOT_BUILDER_KEY] = builder;\n }\n}\n\n/** テスト用: 登録を外す */\nexport function _unregisterSsrSnapshotBuilder(): void {\n const globals = globalThis as Record<symbol, unknown>;\n if (globals[SSR_SNAPSHOT_BUILDER_KEY] === builder) {\n delete globals[SSR_SNAPSHOT_BUILDER_KEY];\n }\n}\n","import { getAbsoluteStateAddressByBinding } from \"../binding/getAbsoluteStateAddressByBinding\";\nimport { setLastListValueByAbsoluteStateAddress } from \"../list/lastListValueByAbsoluteStateAddress\";\nimport { setStateListBaseline } from \"../list/stateListBaseline\";\nimport { parseBindTextsForElement } from \"../bindTextParser/parseBindTextsForElement\";\nimport { ParseBindTextResult } from \"../bindTextParser/types\";\nimport { BindingSession, getOrCreateBindingSession } from \"../bindings/BindingSession\";\nimport { collectNodesAndBindingInfos } from \"../bindings/collectNodesAndBindingInfos\";\nimport { setBindingsByContent } from \"../bindings/bindingsByContent\";\nimport { setBindingSessionByContent } from \"../bindings/bindingSessionByContent\";\nimport { setIndexBindingsByContent } from \"../bindings/indexBindingsByContent\";\nimport { setNodesByContent } from \"../bindings/nodesByContent\";\nimport { bindLoopContextToContent } from \"../bindings/bindLoopContextToContent\";\nimport { config } from \"../config\";\nimport { WILDCARD, INDEX_BY_INDEX_NAME } from \"../define\";\nimport { Ssr, SSR_BLOCK_START } from \"./Ssr\";\nimport { getStateElement } from \"../stateElementByName\";\nimport { setLoopContextByNode } from \"../list/loopContextByNode\";\nimport { applyChangeFromBindings } from \"../apply/applyChangeFromBindings\";\nimport { hydrateSetContent, hydrateSetLastNode } from \"../apply/applyChangeToFor\";\nimport { waitForStateInitialize } from \"../waitForStateInitialize\";\nimport { setFragmentInfoByUUID, getFragmentInfoByUUID } from \"../structural/fragmentInfoByUUID\";\nimport { setContentByNode } from \"../structural/contentsByNode\";\nimport { createContentFromNodes } from \"../structural/createContent\";\nimport { collectStructuralFragments } from \"../structural/collectStructuralFragments\";\nimport { createNotFilter } from \"../structural/createNotFilter\";\nimport { getFragmentNodeInfos } from \"../structural/getFragmentNodeInfos\";\nimport { optimizeFragment } from \"../structural/optimizeFragment\";\nimport { expandShorthandPaths } from \"../structural/expandShorthandPaths\";\nimport { createListIndex } from \"../list/createListIndex\";\nimport { IListIndex } from \"../list/types\";\nimport { setListIndexesByList } from \"../list/listIndexesByList\";\nimport { getPathInfo } from \"../address/PathInfo\";\nimport { createStateAddress } from \"../address/StateAddress\";\nimport { IBindingInfo } from \"../types\";\nimport { VERSION } from \"../version\";\n\n// ハイドレーション時にスキップするバインディングタイプ\nconst STRUCTURAL_TYPES = new Set(['for', 'if', 'elseif', 'else']);\n\n\ninterface ISsrBlock {\n type: string; // for, if, elseif, else\n uuid: string;\n path: string;\n index: number | null; // for のみ\n nodes: Node[]; // start〜end 間のノード\n}\n\n/**\n * SSR ブロック境界コメントを走査して、start〜end 間のノードを収集する\n */\nfunction collectSsrBlocks(root: Node): ISsrBlock[] {\n const blocks: ISsrBlock[] = [];\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);\n const startComments: Comment[] = [];\n\n // まず全コメントを収集\n while (walker.nextNode()) {\n startComments.push(walker.currentNode as Comment);\n }\n\n for (const comment of startComments) {\n const startMatch = SSR_BLOCK_START.exec(comment.data);\n if (!startMatch) continue;\n\n const type = startMatch[1];\n const info = startMatch[2]; // \"uuid:path:index\" or \"uuid:path\"\n const parts = info.split(':');\n\n let uuid: string;\n let path: string;\n let index: number | null = null;\n\n if (type === 'for') {\n // uuid:path:index\n uuid = parts[0];\n path = parts[1];\n index = parseInt(parts[2], 10);\n } else {\n // uuid:path\n uuid = parts[0];\n path = parts.slice(1).join(':');\n }\n\n // start と end の間のノードを収集\n const nodes: Node[] = [];\n let sibling = comment.nextSibling;\n const endPattern = `@@wcs-${type}-end:${info}`;\n while (sibling) {\n if (sibling.nodeType === Node.COMMENT_NODE && (sibling as Comment).data === endPattern) {\n break;\n }\n nodes.push(sibling);\n sibling = sibling.nextSibling;\n }\n\n blocks.push({ type, uuid, path, index, nodes });\n }\n\n return blocks;\n}\n\n/**\n * live DOM ノード群からバインディングを収集する。\n * ノードを一時的に DocumentFragment に移動して collectNodesAndBindingInfos を実行し、\n * 元の位置に戻す。\n */\nfunction collectBindingsFromLiveNodes(\n nodes: Node[],\n): { bindingInfos: IBindingInfo[], subscriberNodes: Node[], bindingSession: BindingSession } {\n const bindingSession = new BindingSession();\n if (nodes.length === 0) {\n return { bindingInfos: [], subscriberNodes: [], bindingSession };\n }\n\n // ノードの元の位置を記録\n const parent = nodes[0].parentNode;\n const nextSibling = nodes[nodes.length - 1].nextSibling;\n\n // 一時的に wrapper 要素に移動(collectNodesAndBindingInfos は Element を受け付ける)\n const wrapper = document.createElement('div');\n for (const node of nodes) {\n wrapper.appendChild(node);\n }\n\n // バインディング収集\n const [subscriberNodes, allBindings] = collectNodesAndBindingInfos(wrapper);\n\n const bindingInfos = bindingSession.initialize(allBindings, {\n registerAddress: false,\n applyOnReconnect: false,\n });\n\n // 元の位置に戻す\n if (parent) {\n while (wrapper.firstChild) {\n parent.insertBefore(wrapper.firstChild, nextSibling);\n }\n }\n\n return {\n bindingInfos,\n subscriberNodes,\n bindingSession,\n };\n}\n\n/**\n * ブロックの中で初回値を適用するバインディングを集める(#258 X6 — hydrateBindings の注記)。\n *\n * 除くもの:\n * - 構造バインディング(SSR が描画済み)とイベント。\n * - コメントに乗ったバインディング。ブロックを集める時点のコメントのバインディングは構造の置き場\n * (`<!--@@wcs-for:uuid-->`)だけで、テキストの `@@:` はこの後で `Ssr.restoreTextBindings` が戻す。\n * テンプレートを復帰できなかった入れ子の置き場は text として解釈される(`text: uuid`)ので、\n * 構造の種別だけでは除けない。\n * - 添字のバインディング(`$1` / `$2` …)。state に依存しないので依存辺のための適用が要らず、SSR が\n * 描いた添字のままでよい(入れ子の内側の `$2` は、外側のループ文脈しか持たないので読めもしない)。\n * - ループの深さより多いワイルドカードを持つバインディング。入れ子のブロックの行は外側のブロックの\n * Content に吸収され、外側のループ文脈しか持たない(入れ子ハイドレーションの既知の制限・\n * integration.listLedgerParentKey.test.ts)。\n * コメントと段数の足りないバインディングは、適用しても失敗の報告をハイドレーションのたびに増やすだけになる。\n */\nfunction collectBlockBindings(out: IBindingInfo[], bindings: readonly IBindingInfo[], loopDepth: number): void {\n for (const binding of bindings) {\n if (binding.bindingType === \"event\" || STRUCTURAL_TYPES.has(binding.bindingType)) {\n continue;\n }\n if (binding.node.nodeType === Node.COMMENT_NODE || binding.statePathName in INDEX_BY_INDEX_NAME) {\n continue;\n }\n if (getPathInfo(binding.statePathName).wildcardCount > loopDepth) {\n continue;\n }\n out.push(binding);\n }\n}\n\n/**\n * SSR ブロックの DOM ノードを Content 化し、バインディングを登録する。\n * 戻り値はブロックの中で初回値を適用するバインディング(`collectBlockBindings`)。\n */\nfunction hydrateBlocks(root: Node, blocks: ISsrBlock[]): IBindingInfo[] {\n // for ブロックの listIndex を UUID ごとに収集\n const listIndexesByUuid: Map<string, IListIndex[]> = new Map();\n const blockBindings: IBindingInfo[] = [];\n\n for (const block of blocks) {\n if (block.nodes.length === 0) continue;\n\n const content = createContentFromNodes(block.nodes);\n\n // Content のバインディングを収集\n const { bindingInfos, subscriberNodes, bindingSession } = collectBindingsFromLiveNodes(block.nodes);\n setBindingSessionByContent(content, bindingSession);\n\n // Content 内のノードに data-wcs-completed を付与\n // (メインの collectNodesAndBindingInfos で重複登録されないようにする)\n for (const node of subscriberNodes) {\n if (node.nodeType === Node.ELEMENT_NODE) {\n (node as Element).setAttribute('data-wcs-completed', '');\n }\n }\n setBindingsByContent(content, bindingInfos);\n setNodesByContent(content, subscriberNodes);\n\n const indexBindings: IBindingInfo[] = [];\n for (const binding of bindingInfos) {\n if (binding.statePathName in INDEX_BY_INDEX_NAME) {\n indexBindings.push(binding);\n }\n }\n setIndexBindingsByContent(content, indexBindings);\n\n if (block.type === 'for' && block.index !== null) {\n const placeholderComment = findPlaceholderComment(root, 'for', block.uuid);\n if (placeholderComment) {\n const listIndex = createListIndex(null, block.index);\n hydrateSetContent(placeholderComment, listIndex, content);\n const lastNode = block.nodes[block.nodes.length - 1];\n hydrateSetLastNode(placeholderComment, lastNode);\n setContentByNode(placeholderComment, content);\n\n // ループコンテキストをバインドし、バインディングをアドレスに登録\n const pathInfo = getPathInfo(block.path + '.' + WILDCARD);\n const stateAddress = createStateAddress(pathInfo, listIndex);\n // ILoopContext は IStateAddress + listIndex なので、stateAddress をそのまま使う\n bindLoopContextToContent(content, stateAddress as any);\n\n bindingSession.initialize(bindingInfos, {\n registerAddress: true,\n registerPathInfo: false,\n applyOnReconnect: false,\n });\n collectBlockBindings(blockBindings, bindingInfos, pathInfo.wildcardCount);\n\n // listIndex を UUID ごとに収集(後で setListIndexesByList に渡す)\n let indexes = listIndexesByUuid.get(block.uuid);\n if (!indexes) {\n indexes = [];\n listIndexesByUuid.set(block.uuid, indexes);\n }\n indexes.push(listIndex);\n }\n } else {\n const placeholderComment = findPlaceholderComment(root, block.type, block.uuid);\n if (placeholderComment) {\n setContentByNode(placeholderComment, content);\n\n bindingSession.initialize(bindingInfos, {\n registerAddress: true,\n registerPathInfo: false,\n applyOnReconnect: false,\n });\n // if / elseif / else の中身はループの外(入れ子の行はここに吸収されても段数で除かれる)\n collectBlockBindings(blockBindings, bindingInfos, 0);\n }\n }\n }\n\n // for ブロックの listIndex を state のリスト値に紐づける\n for (const [uuid, indexes] of listIndexesByUuid) {\n const placeholderComment = findPlaceholderComment(root, 'for', uuid);\n if (!placeholderComment) continue;\n // state から現在のリスト値を取得して listIndexes を設定\n const rootNode = placeholderComment.getRootNode() as Node;\n // structuralBindings はまだ登録前なので、getParseBindTextResults を直接使う\n const fragmentInfo = getFragmentInfoByUUID(uuid);\n if (!fragmentInfo) continue;\n const statePathName = fragmentInfo.parseBindTextResult.statePathName;\n const stateElement = getStateElement(rootNode);\n if (!stateElement) continue;\n stateElement.createState(\"readonly\", (state) => {\n const list = state[statePathName];\n if (Array.isArray(list)) {\n setListIndexesByList(list, indexes);\n }\n });\n }\n return blockBindings;\n}\n\nfunction findPlaceholderComment(root: Node, type: string, uuid: string): Comment | null {\n const keywordMap: Record<string, string> = {\n 'for': config.commentForPrefix,\n 'if': config.commentIfPrefix,\n 'elseif': config.commentElseIfPrefix,\n 'else': config.commentElsePrefix,\n };\n const keyword = keywordMap[type];\n if (!keyword) return null;\n\n const pattern = `@@${keyword}:${uuid}`;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);\n while (walker.nextNode()) {\n const comment = walker.currentNode as Comment;\n if (comment.data === pattern) {\n return comment;\n }\n }\n return null;\n}\n\n/**\n * <wcs-ssr> 内のテンプレートを fragmentInfoByUUID に復帰させる。\n */\nfunction restoreFragments(root: Document, ssrEl: Ssr): void {\n const rootNode = root as Node;\n let lastIfParseResult: ParseBindTextResult | null = null;\n\n for (const [uuid, tpl] of ssrEl.templates) {\n const bindText = tpl.getAttribute(config.bindAttributeName) || '';\n const parseBindTextResults = parseBindTextsForElement(bindText);\n let parseBindTextResult = parseBindTextResults[0];\n const bindingType = parseBindTextResult.bindingType;\n\n // else: 直前の if 条件の not → 条件反転(elseif は独自条件のままでよい)\n if (bindingType === 'else' && lastIfParseResult) {\n parseBindTextResult = {\n ...lastIfParseResult,\n outFilters: [...lastIfParseResult.outFilters, createNotFilter()],\n bindingType: 'else',\n };\n }\n\n // if chain の追跡\n if (bindingType === 'if') {\n lastIfParseResult = parseBindTextResult;\n } else if (bindingType === 'elseif') {\n lastIfParseResult = parseBindTextResult;\n } else if (bindingType === 'else') {\n lastIfParseResult = null;\n }\n\n const fragment = document.importNode(tpl.content, true);\n const forPath = bindingType === \"for\" ? parseBindTextResult.statePathName : undefined;\n optimizeFragment(fragment);\n if (typeof forPath === \"string\") {\n expandShorthandPaths(fragment, forPath);\n }\n collectStructuralFragments(rootNode, fragment, forPath);\n\n const fragmentInfo = {\n fragment,\n parseBindTextResult,\n nodeInfos: getFragmentNodeInfos(fragment),\n };\n setFragmentInfoByUUID(uuid, rootNode, fragmentInfo);\n }\n}\n\n/**\n * SSR ハイドレーション用バインディング初期化。\n * バージョン不一致時は DOM をクリーンアップして false を返す\n * (呼び出し元で buildBindings にフォールバック)。\n */\nexport async function hydrateBindings(root: Document): Promise<boolean> {\n await waitForStateInitialize(root);\n\n // バージョン検証\n const ssrElements = root.querySelectorAll(config.tagNames.ssr);\n for (const ssrNode of ssrElements) {\n const ssrEl = ssrNode as Ssr;\n if (!ssrEl.verifyVersion()) {\n console.warn(\n `[@wcstack/state] SSR version mismatch: server=\"${ssrEl.version}\", client=\"${VERSION}\". Falling back to full render.`\n );\n Ssr.cleanupDom(root);\n return false;\n }\n }\n\n // <wcs-ssr> からテンプレートを fragmentInfoByUUID に復帰\n for (const ssrNode of ssrElements) {\n restoreFragments(root, ssrNode as Ssr);\n }\n\n // SSR ブロック境界コメントから既存 DOM を Content 化\n const blocks = collectSsrBlocks(document.body);\n const blockBindings = hydrateBlocks(document.body, blocks);\n\n // ブロック境界コメント (start/end) を除去\n Ssr.removeBlockBoundaryComments(document.body);\n\n // <wcs-ssr> を一時除去(バインディング走査に含めない)\n const ssrParents: { el: Element, parent: Node, next: Node | null }[] = [];\n for (const el of ssrElements) {\n if (el.parentNode) {\n ssrParents.push({ el, parent: el.parentNode, next: el.nextSibling });\n el.remove();\n }\n }\n\n // 構造プレースホルダーコメント (@@wcs-for:uuid 等) は残す\n // → バインディング走査で拾われ、状態変化時の再レンダリングに使われる\n\n // SSR テキストバインディングを @@: 形式に復元\n Ssr.restoreTextBindings(document.body);\n\n // ノードとバインディングを収集\n const [subscriberNodes, allBindings] = collectNodesAndBindingInfos(document.body);\n\n // 収集完了したノードに data-wcs-completed 属性を付与\n // for ブロック内ノード(hydrateBlocks で登録済み)にはループコンテキストをリセットしない\n for (const node of subscriberNodes) {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n if (!el.hasAttribute('data-wcs-completed')) {\n setLoopContextByNode(node, null);\n el.setAttribute('data-wcs-completed', '');\n }\n } else {\n // コメントノード等\n setLoopContextByNode(node, null);\n }\n }\n\n // バインディングを構造系とそれ以外に分離\n const normalBindings: IBindingInfo[] = [];\n const structuralBindings: IBindingInfo[] = [];\n const bindingSession = getOrCreateBindingSession(document.body);\n const initializedBindings = bindingSession.initialize(allBindings);\n\n for (const binding of initializedBindings) {\n if (binding.bindingType === \"event\") continue;\n if (STRUCTURAL_TYPES.has(binding.bindingType)) {\n structuralBindings.push(binding);\n } else if (binding.statePathName.includes(WILDCARD)) {\n // for ブロック内のバインディング → Content のバインディングとして登録済み\n continue;\n } else {\n normalBindings.push(binding);\n }\n }\n\n // for バインディングの lastListValue を初期値として設定\n // (次回の状態変化時に差分計算の基準になる)\n for (const binding of structuralBindings) {\n if (binding.bindingType === 'for') {\n const absAddr = getAbsoluteStateAddressByBinding(binding);\n const rootNode = binding.replaceNode.getRootNode() as Node;\n const stateElement = getStateElement(rootNode);\n if (stateElement) {\n stateElement.createState(\"readonly\", (state) => {\n const value = state[binding.statePathName];\n if (Array.isArray(value)) {\n setLastListValueByAbsoluteStateAddress(absAddr, value);\n // 描画の基準と同時に state 側の基準も進める(E1。applyChangeFromBindings と対称)\n setStateListBaseline(absAddr, value);\n }\n });\n }\n }\n }\n\n // 初回値の適用(構造バインディングは SSR 描画済みなので除く)。SSR ブロック(for の行・if の中身)の\n // 中のバインディングも適用する(#258 X6): getter の依存辺は getter を評価したときにしか張られない\n // ので、適用しないと行の `items.*.double` は `items.*.n` を書いても一度も再評価されず、サーバーが\n // 書いたテキストのまま固まる。値は SSR と同じ state から読むのでテキストは変わらない\n applyChangeFromBindings([...normalBindings, ...blockBindings]);\n\n // <wcs-ssr> を元に戻す\n for (const { el, parent, next } of ssrParents) {\n parent.insertBefore(el, next);\n }\n\n // hydrateProps 復元\n const restoredSsrElements = root.querySelectorAll(config.tagNames.ssr);\n for (const ssrNode of restoredSsrElements) {\n const ssrEl = ssrNode as Ssr;\n const props = ssrEl.hydrateProps;\n for (const [id, propMap] of Object.entries(props)) {\n const target = root.querySelector(`[data-wcs-ssr-id=\"${id}\"]`);\n if (!target) continue;\n for (const [propName, value] of Object.entries(propMap)) {\n (target as any)[propName] = value;\n }\n }\n }\n\n // ハイドレーション中の重複登録防止用属性を除去\n const completedEls = root.querySelectorAll('[data-wcs-completed]');\n for (const el of completedEls) {\n el.removeAttribute('data-wcs-completed');\n }\n\n return true;\n}\n\n/** @internal テスト用 */\nexport const __test = {\n collectSsrBlocks,\n collectBindingsFromLiveNodes,\n hydrateBlocks,\n findPlaceholderComment,\n restoreFragments,\n};\n","/**\n * ssr/install.ts — SSR 機能の install(設計案 H8、S5)。core の受け口(core/ssrHooks.ts と\n * registerComponents の definer)へこの機能の実装を置き、ssr-snapshot プロトコルを提供する。\n * 従来 `State` と `stateElementByName` と `registerComponents` に直書きされていた分岐で、\n * 理由のコメントは元の位置から移してある。\n */\nimport { config } from \"../config\";\nimport type { IStateElement } from \"../components/types\";\nimport { ISsrHooks, setSsrHooks } from \"../core/ssrHooks\";\nimport { registerComponentDefiner } from \"../registerComponents\";\nimport { getBindingsReady } from \"../stateElementByName\";\nimport { VERSION } from \"../version\";\nimport { registerSsrSnapshotBuilder } from \"./buildSsrDocument\";\nimport { hydrateBindings } from \"./hydrateBindings\";\nimport { Ssr } from \"./Ssr\";\n\nexport const ssrFeatureHooks: ISsrHooks = {\n hydrate: hydrateBindings,\n loadState(element) {\n const root = element.parentNode;\n if (!root) return null;\n const ssrEl = Ssr.find(root);\n if (!ssrEl) return null;\n const data = ssrEl.stateData;\n return Object.keys(data).length > 0 ? data : null;\n },\n async emitSnapshot(element) {\n await getBindingsReady((element as unknown as IStateElement).rootNode);\n const stateData = Ssr.extractStateData(element);\n const ssrEl = document.createElement(config.tagNames.ssr);\n ssrEl.setAttribute(\"version\", VERSION);\n Ssr.buildContent(ssrEl, stateData);\n element.parentNode?.insertBefore(ssrEl, element);\n },\n};\n\nlet installed = false;\n/** 冪等。full / auto では `bootstrapState()` が呼ぶ */\nexport function installSsr(): void {\n if (installed) return;\n installed = true;\n setSsrHooks(ssrFeatureHooks);\n registerComponentDefiner((registry) => {\n if (!registry.get(config.tagNames.ssr)) {\n registry.define(config.tagNames.ssr, Ssr);\n }\n });\n // ssr-snapshot プロトコルの提供(docs/ssr-router-design.md §5)。renderToString が\n // `<wcs-ssr>` 生成をサーバー主導の最終パスへ回せるようにする。登録は冪等\n registerSsrSnapshotBuilder();\n}\n","/**\n * features/ssr.ts — サーバー描画とハイドレーション(@wcstack/state/features/ssr)。\n */\nimport type { IStateFeature } from \"../core/features\";\nimport { installSsr } from \"../ssr/install\";\n\nexport const ssr: IStateFeature = {\n name: \"ssr\",\n install(): void {\n installSsr();\n },\n};\nexport default ssr;\n"],"names":["SSR_PLACEHOLDER_COMMENT","SSR_BLOCK_START","SSR_BLOCK_END","SSR_TEXT_START","escapeJsonForScript","json","replace","Ssr","HTMLElementBase","_stateData","_templates","_hydrateProps","version","this","getAttribute","stateData","_loadStateData","templates","_loadTemplates","hydrateProps","_loadHydrateProps","getTemplate","uuid","get","verifyVersion","serverVersion","serverParts","split","clientParts","VERSION","setStateData","data","setHydrateProps","props","script","querySelector","JSON","parse","textContent","map","Map","querySelectorAll","tpl","id","set","find","root","tagName","config","tagNames","ssr","parentEl","Element","Document","documentElement","extractStateData","stateEl","raw","__state","key","Object","keys","startsWith","descriptor","getOwnPropertyDescriptor","undefined","value","buildContent","ssrEl","jsonScript","document","createElement","setAttribute","stringify","appendChild","uuids","getAllFragmentUUIDs","fragmentInfo","getFragmentInfoByUUID","bindResult","parseBindTextResult","bindText","bindingType","statePathName","bindAttributeName","content","fragment","cloneNode","ssrNodes","getAllSsrPropertyNodes","length","propsData","i","node","entries","getSsrProperties","entry","propName","propsScript","clearSsrPropertyStore","removeBlockBoundaryComments","walker","createTreeWalker","NodeFilter","SHOW_COMMENT","toRemove","nextNode","comment","currentNode","test","push","remove","removeStructuralComments","restoreTextBindings","startComments","match","exec","path","bindComment","createComment","parentNode","insertBefore","sibling","nextSibling","endPattern","next","nodeType","Node","COMMENT_NODE","removeChild","cleanupDom","body","ssrElements","templateByUuid","ssrNode","walker1","startComment","walker2","placeholders","restored","bindAttr","imported","importNode","childNodes","child","Array","from","replaceChild","ssrIdElements","el","removeAttribute","builder","protocol","build","stateTag","state","ssrTag","stateElements","prev","previousElementSibling","toLowerCase","STRUCTURAL_TYPES","Set","collectBindingsFromLiveNodes","nodes","bindingSession","BindingSession","bindingInfos","subscriberNodes","parent","wrapper","allBindings","collectNodesAndBindingInfos","initialize","registerAddress","applyOnReconnect","firstChild","collectBlockBindings","out","bindings","loopDepth","binding","has","INDEX_BY_INDEX_NAME","getPathInfo","wildcardCount","findPlaceholderComment","type","keyword","for","commentForPrefix","if","commentIfPrefix","elseif","commentElseIfPrefix","else","commentElsePrefix","pattern","restoreFragments","rootNode","lastIfParseResult","parseBindTextsForElement","outFilters","createNotFilter","forPath","optimizeFragment","expandShorthandPaths","collectStructuralFragments","nodeInfos","getFragmentNodeInfos","setFragmentInfoByUUID","ssrFeatureHooks","hydrate","async","waitForStateInitialize","console","warn","blocks","startMatch","info","parts","index","parseInt","slice","join","collectSsrBlocks","blockBindings","listIndexesByUuid","block","createContentFromNodes","setBindingSessionByContent","ELEMENT_NODE","setBindingsByContent","setNodesByContent","indexBindings","setIndexBindingsByContent","placeholderComment","listIndex","createListIndex","hydrateSetContent","lastNode","hydrateSetLastNode","setContentByNode","pathInfo","WILDCARD","stateAddress","createStateAddress","bindLoopContextToContent","registerPathInfo","indexes","getRootNode","stateElement","getStateElement","createState","list","isArray","setListIndexesByList","hydrateBlocks","ssrParents","hasAttribute","setLoopContextByNode","normalBindings","structuralBindings","initializedBindings","getOrCreateBindingSession","includes","absAddr","getAbsoluteStateAddressByBinding","replaceNode","setLastListValueByAbsoluteStateAddress","setStateListBaseline","applyChangeFromBindings","restoredSsrElements","propMap","target","completedEls","loadState","element","emitSnapshot","getBindingsReady","installed","installSsr","setSsrHooks","registerComponentDefiner","registry","define","globals","globalThis","SSR_SNAPSHOT_BUILDER_KEY","registerSsrSnapshotBuilder","name","install"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ssr.js","sources":["../../../../../src/ssr/Ssr.ts","../../../../../src/ssr/buildSsrDocument.ts","../../../../../src/ssr/hydrateBindings.ts","../../../../../src/ssr/install.ts","../../../../../src/features/ssr.ts"],"sourcesContent":["import { config } from \"../config\";\nimport { IState } from \"../types\";\nimport { VERSION } from \"../version\";\nimport { getFragmentInfoByUUID } from \"../structural/fragmentInfoByUUID\";\nimport { getNormalizedTextCommentData } from \"../structural/getFragmentNodeInfos\";\nimport { resolveNodePath } from \"../structural/resolveNodePath\";\nimport { IFragmentInfo } from \"../structural/types\";\nimport { getAllSsrPropertyNodes, getSsrProperties, clearSsrPropertyStore } from \"../apply/ssrPropertyStore\";\nimport { HTMLElementBase } from \"../platform/HTMLElementBase\";\n\nexport interface ISsrElement {\n readonly version: string;\n readonly stateData: IState;\n readonly templates: Map<string, HTMLTemplateElement>;\n readonly hydrateProps: Record<string, Record<string, unknown>>;\n getTemplate(uuid: string): HTMLTemplateElement | null;\n verifyVersion(): boolean;\n}\n\n// SSR コメントパターン\nconst SSR_PLACEHOLDER_COMMENT = /^@@wcs-(?:for|if|elseif|else):[^-]/;\nconst SSR_BLOCK_START = /^@@wcs-(for|if|elseif|else)-start:(.+)$/;\nconst SSR_BLOCK_END = /^@@wcs-(for|if|elseif|else)-end:(.+)$/;\nconst SSR_TEXT_START = /^@@wcs-text-start:(.+)$/;\n\nexport {\n SSR_PLACEHOLDER_COMMENT,\n SSR_BLOCK_START,\n SSR_BLOCK_END,\n SSR_TEXT_START,\n};\n\n/**\n * script 要素へ埋め込む JSON を HTML パーサから保護する。\n * HTML 直列化時、script の中身は生のまま出力されるため、state 値に\n * \"</script>\" や \"<!--\" を含む文字列があると script を脱出できてしまう。\n * \"<\" \">\" \"&\" と U+2028/U+2029 を JSON の \\uXXXX エスケープへ置換する\n * (JSON.parse では元の文字列と等価に復元される)。\n */\nfunction escapeJsonForScript(json: string): string {\n return json\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n}\n\n/**\n * **このドキュメントから到達できる**構造テンプレートの uuid だけを、文書順・幅優先で集める。\n *\n * `fragmentInfoByUUID` はモジュール寿命の台帳で削除の口が無く、`getAllFragmentUUIDs()` を\n * そのまま直列化すると、同じプロセスで描いた**別のドキュメント**のテンプレートまで\n * `<wcs-ssr>` に載る(実測: 同じページを繰り返すと 2 → 12 テンプレートに増え、ページ A の\n * `for: secretItems` がページ B の HTML に混入した)。uuid は単調増加で再利用されないので、\n * 「この文書のプレースホルダから辿れるか」は正確な所属判定になる。\n *\n * 入れ子のテンプレートは親の fragment の中にプレースホルダコメントとして居る(`if` が偽で\n * 一度も描かれていない枝の中身も含む)ので、辿りは fragment へ再帰する。\n */\nfunction collectReachableFragments(scanRoot: Node): Map<string, IFragmentInfo> {\n const found = new Map<string, IFragmentInfo>();\n const pending: Node[] = [scanRoot];\n while (pending.length > 0) {\n for (const placeholder of collectComments(pending.shift() as Node, isPlaceholder)) {\n const data = placeholder.data;\n const uuid = data.slice(data.indexOf(':') + 1);\n // 入れ子のプレースホルダは行ごとに現れるので重複する。台帳に無い uuid(別の\n // レンダリングの痕跡・後始末の順序違い)はそのまま落とす\n if (found.has(uuid)) continue;\n const info = getFragmentInfoByUUID(uuid);\n if (info === null) continue;\n found.set(uuid, info);\n pending.push(info.fragment);\n }\n }\n return found;\n}\n\n/**\n * `root` 以下のコメントのうち `match` が真を返すものを文書順で集める。\n *\n * この 1 本に寄せているのは、走査の**途中で DOM を壊す**呼び手が多いからでもある\n * (境界コメントの除去・テキスト束縛の復元・プレースホルダの差し替え)。先に集めてから\n * 触るという規則をここに固定しておくと、各所で TreeWalker を書き写さずに済む。\n */\nexport function collectComments(root: Node, match: (data: string) => boolean): Comment[] {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);\n const found: Comment[] = [];\n while (walker.nextNode()) {\n const comment = walker.currentNode as Comment;\n if (match(comment.data)) {\n found.push(comment);\n }\n }\n return found;\n}\n\nconst isPlaceholder = (data: string): boolean => SSR_PLACEHOLDER_COMMENT.test(data);\nexport const isBlockStart = (data: string): boolean => SSR_BLOCK_START.test(data);\n\n/**\n * 直列化用のクローン。`getFragmentNodeInfos` がテンプレート登録時に空 Text へ潰した\n * テキスト束縛のコメントを、**クローンの上でだけ**原文へ戻す。\n *\n * 戻さないと `for` / `if` テンプレートの中の `{{ }}` がマークアップから消え、\n * ハイドレーションが束縛を復元できず、その行のテキストが**恒久的に空**になる。\n * 元の fragment は生きているページの正本なので絶対に壊さない。\n */\nfunction cloneFragmentForSnapshot(fragmentInfo: IFragmentInfo): DocumentFragment {\n const clone = fragmentInfo.fragment.cloneNode(true) as DocumentFragment;\n for (const nodeInfo of fragmentInfo.nodeInfos) {\n const commentData = getNormalizedTextCommentData(\n resolveNodePath(fragmentInfo.fragment, nodeInfo.nodePath));\n if (commentData === null) continue;\n // nodePath はこの fragment から採ったもので、クローンは同形。ここまで来た枝は\n // 必ず解決でき、正規化された Text は必ず親を持つ(fragment 直下でも親は fragment)。\n // 同じ位置への置換なので、以降の nodePath は変わらない(正規化と対称)\n const target = resolveNodePath(clone, nodeInfo.nodePath) as Node;\n (target.parentNode as Node).replaceChild(document.createComment(commentData), target);\n }\n return clone;\n}\n\nexport class Ssr extends HTMLElementBase implements ISsrElement {\n private _stateData: IState | null = null;\n private _templates: Map<string, HTMLTemplateElement> | null = null;\n private _hydrateProps: Record<string, Record<string, unknown>> | null = null;\n\n get version(): string {\n return this.getAttribute('version') || '';\n }\n\n get stateData(): IState {\n if (this._stateData === null) {\n this._stateData = this._loadStateData();\n }\n return this._stateData;\n }\n\n get templates(): Map<string, HTMLTemplateElement> {\n if (this._templates === null) {\n this._templates = this._loadTemplates();\n }\n return this._templates;\n }\n\n get hydrateProps(): Record<string, Record<string, unknown>> {\n if (this._hydrateProps === null) {\n this._hydrateProps = this._loadHydrateProps();\n }\n return this._hydrateProps;\n }\n\n getTemplate(uuid: string): HTMLTemplateElement | null {\n return this.templates.get(uuid) ?? null;\n }\n\n /**\n * サーバーの SSR バージョンとクライアントの state バージョンを検証する。\n * メジャー・マイナーバージョンが一致すればtrue。\n * version 属性がない場合は検証スキップ(true)。\n */\n verifyVersion(): boolean {\n const serverVersion = this.version;\n if (!serverVersion) return true;\n const serverParts = serverVersion.split('.');\n const clientParts = VERSION.split('.');\n // メジャー・マイナーが一致すれば互換\n return serverParts[0] === clientParts[0] && serverParts[1] === clientParts[1];\n }\n\n setStateData(data: IState): void {\n this._stateData = data;\n }\n\n setHydrateProps(props: Record<string, Record<string, unknown>>): void {\n this._hydrateProps = props;\n }\n\n private _loadStateData(): IState {\n const script = this.querySelector(\n `script[type=\"application/json\"]:not([data-wcs-ssr-props])`\n );\n if (!script) return {};\n try {\n return JSON.parse(script.textContent || '{}');\n } catch {\n return {};\n }\n }\n\n private _loadTemplates(): Map<string, HTMLTemplateElement> {\n const map = new Map<string, HTMLTemplateElement>();\n const templates = this.querySelectorAll<HTMLTemplateElement>('template[id]');\n for (const tpl of templates) {\n const id = tpl.getAttribute('id');\n if (id) {\n map.set(id, tpl);\n }\n }\n return map;\n }\n\n private _loadHydrateProps(): Record<string, Record<string, unknown>> {\n const script = this.querySelector('script[data-wcs-ssr-props]');\n if (!script) return {};\n try {\n return JSON.parse(script.textContent || '{}');\n } catch {\n return {};\n }\n }\n\n static find(root: Node): ISsrElement | null {\n const tagName = config.tagNames.ssr;\n const parentEl = root instanceof Element\n ? root\n : root instanceof Document\n ? root.documentElement\n : null;\n if (!parentEl) return null;\n const el = parentEl.querySelector(tagName);\n return el as ISsrElement | null;\n }\n\n /**\n * stateData と構造テンプレート・プロパティから <wcs-ssr> の中身を構築する。\n * server パッケージの renderToString から呼ばれる。\n */\n /**\n * wcs-state 要素から $ プレフィックスや関数を除いたデータを抽出する。\n */\n static extractStateData(stateEl: Element): Record<string, any> {\n const raw = (stateEl as any).__state;\n if (!raw || typeof raw !== 'object') return {};\n const data: Record<string, any> = {};\n for (const key of Object.keys(raw)) {\n if (key.startsWith('$')) continue;\n // **アクセサは評価しない。** スナップショットが運ぶのはデータで、派生値は\n // クライアントが同じ宣言から作り直す。ここは Object.entries で舐めていたので、\n // own かつ enumerable な getter を**生の state オブジェクト**を this にして\n // 評価していた — proxy の上でしか意味を持たない本体(`this[\"items.*.n\"]` や\n // `this.$getAll(...)`)が、パス getter なら NaN → JSON の null で静かに壊れ、\n // `$getAll` を呼ぶ getter なら TypeError でページ全体の SSR を落としていた\n // (docs/state-recursive-path-impl-plan.md §7)。\n const descriptor = Object.getOwnPropertyDescriptor(raw, key);\n if (descriptor !== undefined && typeof descriptor.get === 'function') continue;\n const value = (raw as Record<string, unknown>)[key];\n if (typeof value !== 'function') {\n data[key] = value;\n }\n }\n return data;\n }\n\n /**\n * @param scanRoot このスナップショットが属するツリー(既定は `ssrEl` の document)。\n * テンプレートと props はここから到達できるものだけを載せる — モジュール寿命の台帳に\n * 残った**別のレンダリング**の分を混ぜないため(リクエスト間のデータ漏れ)。\n */\n static buildContent(ssrEl: Element, stateData: Record<string, any>, scanRoot?: Node): void {\n const root: Node = scanRoot ?? (ssrEl.ownerDocument as Document);\n // 初期データ JSON\n const jsonScript = document.createElement('script');\n jsonScript.setAttribute('type', 'application/json');\n jsonScript.textContent = escapeJsonForScript(JSON.stringify(stateData));\n ssrEl.appendChild(jsonScript);\n\n // この文書のプレースホルダから辿れるテンプレートだけを復元して格納\n for (const [uuid, fragmentInfo] of collectReachableFragments(root)) {\n const tpl = document.createElement('template');\n tpl.setAttribute('id', uuid);\n\n const bindResult = fragmentInfo.parseBindTextResult;\n const bindText = bindResult.bindingType === 'else'\n ? 'else:'\n : `${bindResult.bindingType}: ${bindResult.statePathName}`;\n tpl.setAttribute(config.bindAttributeName, bindText);\n\n tpl.content.appendChild(cloneFragmentForSnapshot(fragmentInfo));\n\n ssrEl.appendChild(tpl);\n }\n\n // 属性で代替不可なプロパティをハイドレーション用に格納。\n // **この文書に今も繋がっているノードだけ**を載せる — props の台帳(apply/ssrPropertyStore)は\n // `buildContent` の末尾でしか空にならないので、`enable-ssr` の無いページを 1 枚描くと\n // 次のレンダリングの props JSON に前のページの値が載っていた(実測)\n const ownerDocument = ssrEl.ownerDocument;\n const ssrNodes = getAllSsrPropertyNodes();\n if (ssrNodes.length > 0) {\n const propsData: Record<string, Record<string, unknown>> = {};\n for (let i = 0; i < ssrNodes.length; i++) {\n const node = ssrNodes[i];\n if (!node.isConnected || node.ownerDocument !== ownerDocument) continue;\n const entries = getSsrProperties(node);\n if (entries.length === 0) continue;\n const id = `wcs-ssr-${i}`;\n (node as Element).setAttribute('data-wcs-ssr-id', id);\n const props: Record<string, unknown> = {};\n for (const entry of entries) {\n props[entry.propName] = entry.value;\n }\n propsData[id] = props;\n }\n if (Object.keys(propsData).length > 0) {\n const propsScript = document.createElement('script');\n propsScript.setAttribute('type', 'application/json');\n propsScript.setAttribute('data-wcs-ssr-props', '');\n propsScript.textContent = escapeJsonForScript(JSON.stringify(propsData));\n ssrEl.appendChild(propsScript);\n }\n }\n\n clearSsrPropertyStore();\n }\n\n /**\n * SSR ブロック境界コメント (@@wcs-*-start/end) を除去する\n */\n static removeBlockBoundaryComments(root: Node): void {\n for (const comment of collectComments(root, (d) => SSR_BLOCK_START.test(d) || SSR_BLOCK_END.test(d))) {\n comment.remove();\n }\n }\n\n /**\n * SSR の構造プレースホルダーコメント (@@wcs-for:uuid 等) を除去する\n */\n static removeStructuralComments(root: Node): void {\n for (const comment of collectComments(root, isPlaceholder)) {\n comment.remove();\n }\n }\n\n /**\n * SSR テキストバインディングコメントを復元する。\n * <!--@@wcs-text-start:path-->text<!--@@wcs-text-end:path-->\n * → <!--@@: path--> (バインディングシステムが認識する形式)\n */\n static restoreTextBindings(root: Node): void {\n for (const comment of collectComments(root, (d) => SSR_TEXT_START.test(d))) {\n const path = (SSR_TEXT_START.exec(comment.data) as RegExpExecArray)[1];\n const bindComment = document.createComment(`@@: ${path}`);\n comment.parentNode!.insertBefore(bindComment, comment);\n\n let sibling: Node | null = comment.nextSibling;\n comment.remove();\n\n const endPattern = `@@wcs-text-end:${path}`;\n while (sibling) {\n const next: Node | null = sibling.nextSibling;\n if (sibling.nodeType === Node.COMMENT_NODE && (sibling as Comment).data === endPattern) {\n sibling.parentNode!.removeChild(sibling);\n break;\n }\n sibling.parentNode!.removeChild(sibling);\n sibling = next;\n }\n }\n }\n\n /**\n * SSR DOM をクリーンアップし、buildBindings が動作できる状態に戻す。\n * バージョン不一致時のフォールバック用。\n *\n * 1. SSR ブロック境界コメント間のレンダリング済みノードを除去\n * 2. SSR テキストバインディングを @@: 形式に復元\n * 3. プレースホルダーコメントを <wcs-ssr> 内のテンプレートで差し替え\n * 4. data-wcs-ssr-id 属性を除去\n * 5. <wcs-ssr> を除去\n */\n static cleanupDom(root: Document): void {\n const body = document.body;\n\n // <wcs-ssr> からテンプレート UUID マップを構築(カスタム要素未定義でも動作するよう DOM 直接走査)\n const ssrElements = root.querySelectorAll(config.tagNames.ssr);\n const templateByUuid = new Map<string, HTMLTemplateElement>();\n for (const ssrNode of ssrElements) {\n const templates = ssrNode.querySelectorAll<HTMLTemplateElement>('template[id]');\n for (const tpl of templates) {\n const id = tpl.getAttribute('id');\n if (id) {\n templateByUuid.set(id, tpl);\n }\n }\n }\n\n // SSR ブロック境界コメント間のレンダリング済みノードと境界コメントを除去\n for (const startComment of collectComments(body, isBlockStart)) {\n const match = SSR_BLOCK_START.exec(startComment.data)!;\n const type = match[1];\n const info = match[2];\n const endPattern = `@@wcs-${type}-end:${info}`;\n let sibling = startComment.nextSibling;\n while (sibling) {\n const next = sibling.nextSibling;\n if (sibling.nodeType === Node.COMMENT_NODE && (sibling as Comment).data === endPattern) {\n sibling.remove();\n break;\n }\n sibling.remove();\n sibling = next;\n }\n startComment.remove();\n }\n\n // SSR テキストバインディングを @@: 形式に復元\n Ssr.restoreTextBindings(body);\n\n // プレースホルダーコメント (@@wcs-for:uuid 等) をテンプレートに差し替え\n for (const comment of collectComments(body, isPlaceholder)) {\n const tpl = templateByUuid.get(comment.data.split(':')[1]);\n if (tpl) {\n const restored = document.createElement('template') as HTMLTemplateElement;\n const bindAttr = tpl.getAttribute(config.bindAttributeName);\n if (bindAttr) restored.setAttribute(config.bindAttributeName, bindAttr);\n const imported = document.importNode(tpl.content, true);\n if (imported.childNodes.length > 0) {\n restored.content.appendChild(imported);\n } else {\n for (const child of Array.from(tpl.childNodes)) {\n restored.content.appendChild(document.importNode(child, true));\n }\n }\n comment.parentNode!.replaceChild(restored, comment);\n }\n }\n\n // data-wcs-ssr-id 属性を除去\n const ssrIdElements = root.querySelectorAll('[data-wcs-ssr-id]');\n for (const el of ssrIdElements) {\n el.removeAttribute('data-wcs-ssr-id');\n }\n\n // <wcs-ssr> を除去\n for (const el of ssrElements) {\n el.remove();\n }\n }\n}\n","import { config } from \"../config\";\nimport { Ssr } from \"./Ssr\";\nimport { VERSION } from \"../version\";\nimport { IWcsSsrSnapshotBuilder, SSR_SNAPSHOT_BUILDER_KEY } from \"../protocol/ssrSnapshot\";\nimport { clearSsrPropertyStore } from \"../apply/ssrPropertyStore\";\nimport { clearFragmentInfos } from \"../structural/fragmentInfoByUUID\";\n\n/**\n * ssr-snapshot プロトコルの提供側(docs/ssr-router-design.md §5)。\n *\n * `<wcs-ssr>` スナップショットを document 全体に対する最終パスとして生成する。\n * connectedCallback 内の inline 生成は「その時点の DOM」しか見えず、router が\n * 後から挿入するルート内容の構造テンプレートを取り逃がすレースがあった\n * (state のロード方式と文書順に依存)。renderToString が全要素の完了と\n * バインディング構築の後にこれを呼ぶことで、スナップショットは常に確定後の\n * DOM を見る。\n *\n * 複数 `enable-ssr` state の意味論は inline 生成と同一に保つ(文書順に生成・\n * fragment レジストリはモジュール共有・props store は生成ごとにクリア)。\n * その整理は本プロトコルの範囲外の既存挙動として引き継ぐ。\n */\nexport function buildSsrDocument(root: Document): void {\n const stateTag = config.tagNames.state;\n const ssrTag = config.tagNames.ssr;\n // スナップショットはルートツリーに 1 本(D14): ボリューム(mount=)と bind-component は\n // 独立ツリー(__state)を持たないため、含めると空の <wcs-ssr> を生成してしまう。\n // Ssr.find は文書先頭一致なので、ルートより前の空スナップショットをルートが掴み、\n // ハイドレーション全体が無言で CSR 退化する\n const stateElements = root.querySelectorAll(`${stateTag}[enable-ssr]:not([mount]):not([bind-component])`);\n for (const stateEl of stateElements) {\n // 既に直前へ生成済み(旧 server との組み合わせで inline 生成された等)なら\n // 何もしない — build() は冪等でなければならない(プロトコル契約)\n const prev = stateEl.previousElementSibling;\n if (prev !== null && prev.tagName.toLowerCase() === ssrTag) {\n continue;\n }\n const ssrEl = document.createElement(ssrTag);\n ssrEl.setAttribute(\"version\", VERSION);\n Ssr.buildContent(ssrEl, Ssr.extractStateData(stateEl), stateEl.getRootNode());\n stateEl.parentNode?.insertBefore(ssrEl, stateEl);\n }\n // props の台帳は `buildContent` の末尾でも空にするが、**`enable-ssr` が 1 件も無いページ**は\n // そこを通らない。通らないと強参照の `Set<Node>` が次のレンダリングまで残り、前のページの\n // 値が次のリクエストの props JSON に載る(実測: `{\"wcs-ssr-0\":{\"valueAsNumber\":\"PRIVATE-C\"}}`)。\n // ここはサーバーの最終パスなので、この文書の props をこの後で読む者は居ない。\n // 構造テンプレートの台帳(fragmentInfoByUUID)はここでは消さない — この後に走る\n // 進行中のバインディング構築がまだ読む。プロセスの後始末は `resetSsrRenderState()`\n clearSsrPropertyStore();\n}\n\n/**\n * サーバーのレンダリング 1 回分のモジュール大域を捨てる。**サーバー専用**。\n *\n * `@wcstack/server` の `renderToString` は 1 プロセスで何枚も描くが、state 側の台帳\n * (構造テンプレート・SSR props)はモジュール寿命で削除の口が無い。出力への混入は\n * 到達可能性フィルタ(`Ssr.buildContent`)で閉じてあるので、これは**メモリ**の口である。\n *\n * 呼ぶ位置: `renderToString`(正確には `renderInWindow`)の `finally` の最後 — その\n * レンダリングの DOM をもう誰も触らないことが確定してから。ブラウザでは呼ばない。\n */\nexport function resetSsrRenderState(): void {\n clearFragmentInfos();\n clearSsrPropertyStore();\n}\n\nconst builder: IWcsSsrSnapshotBuilder = {\n protocol: \"wcs-ssr-snapshot\",\n version: 1,\n build: buildSsrDocument,\n // レンダラ(@wcstack/server)が後始末の最後に呼ぶ。パッケージの import ではなく\n // プロトコル越しに渡すのは `build` と同じ理由 — グローバル symbol は「実際に動いた\n // state のコピー」を指すので、捨てるべき台帳を持っている当人に必ず届く。\n // 分割エントリを直接 import すると core がもう 1 つ読み込まれ、**別の**台帳を\n // 空にして終わる\n reset: resetSsrRenderState,\n};\n\n/**\n * グローバル symbol へ自分を載せる。`bootstrapState` から呼ぶ。\n * binder(registerBinder)と同じ規範 — 既に別のコピーが載っているなら譲る\n * (そのコピーのレジストリが、そのページの正本だからである)。\n */\nexport function registerSsrSnapshotBuilder(): void {\n const globals = globalThis as Record<symbol, unknown>;\n if (globals[SSR_SNAPSHOT_BUILDER_KEY] === undefined) {\n globals[SSR_SNAPSHOT_BUILDER_KEY] = builder;\n }\n}\n\n/** テスト用: 登録を外す */\nexport function _unregisterSsrSnapshotBuilder(): void {\n const globals = globalThis as Record<symbol, unknown>;\n if (globals[SSR_SNAPSHOT_BUILDER_KEY] === builder) {\n delete globals[SSR_SNAPSHOT_BUILDER_KEY];\n }\n}\n","import { getAbsoluteStateAddressByBinding } from \"../binding/getAbsoluteStateAddressByBinding\";\nimport { setLastListValueByAbsoluteStateAddress } from \"../list/lastListValueByAbsoluteStateAddress\";\nimport { setStateListBaseline } from \"../list/stateListBaseline\";\nimport { parseBindTextsForElement } from \"../bindTextParser/parseBindTextsForElement\";\nimport { ParseBindTextResult } from \"../bindTextParser/types\";\nimport { BindingSession, getOrCreateBindingSession } from \"../bindings/BindingSession\";\nimport { collectNodesAndBindingInfos, IDeferredSpreadEntry } from \"../bindings/collectNodesAndBindingInfos\";\nimport { scheduleDeferredSpreads } from \"../bindings/initializeBindings\";\nimport { setBindingsByContent } from \"../bindings/bindingsByContent\";\nimport { setBindingSessionByContent } from \"../bindings/bindingSessionByContent\";\nimport { setIndexBindingsByContent } from \"../bindings/indexBindingsByContent\";\nimport { setNodesByContent } from \"../bindings/nodesByContent\";\nimport { bindLoopContextToContent } from \"../bindings/bindLoopContextToContent\";\nimport { config } from \"../config\";\nimport { WILDCARD, INDEX_BY_INDEX_NAME } from \"../define\";\nimport { Ssr, SSR_BLOCK_START, collectComments, isBlockStart } from \"./Ssr\";\nimport { getStateElement } from \"../stateElementByName\";\nimport { setLoopContextByNode } from \"../list/loopContextByNode\";\nimport { applyChangeFromBindings } from \"../apply/applyChangeFromBindings\";\nimport { hydrateSetContent, hydrateSetLastNode } from \"../apply/applyChangeToFor\";\nimport { waitForStateInitialize } from \"../waitForStateInitialize\";\nimport { setFragmentInfoByUUID, getFragmentInfoByUUID } from \"../structural/fragmentInfoByUUID\";\nimport { setContentByNode } from \"../structural/contentsByNode\";\nimport { createContentFromNodes } from \"../structural/createContent\";\nimport { collectStructuralFragments } from \"../structural/collectStructuralFragments\";\nimport { createNotFilter } from \"../structural/createNotFilter\";\nimport { getFragmentNodeInfos } from \"../structural/getFragmentNodeInfos\";\nimport { optimizeFragment } from \"../structural/optimizeFragment\";\nimport { expandShorthandPaths } from \"../structural/expandShorthandPaths\";\nimport { createListIndex } from \"../list/createListIndex\";\nimport { IListIndex, ILoopContext } from \"../list/types\";\nimport { setListIndexesByList } from \"../list/listIndexesByList\";\nimport { getPathInfo } from \"../address/PathInfo\";\nimport { createStateAddress } from \"../address/StateAddress\";\nimport { IBindingInfo } from \"../types\";\nimport { VERSION } from \"../version\";\n\n// ハイドレーション時にスキップするバインディングタイプ\nconst STRUCTURAL_TYPES = new Set(['for', 'if', 'elseif', 'else']);\n\n\ninterface ISsrBlock {\n type: string; // for, if, elseif, else\n uuid: string;\n path: string;\n index: number | null; // for のみ\n nodes: Node[]; // start〜end 間のノード\n}\n\n/**\n * SSR ブロック境界コメントを走査して、start〜end 間のノードを収集する\n */\nfunction collectSsrBlocks(root: Node): ISsrBlock[] {\n const blocks: ISsrBlock[] = [];\n // 先に集めてから触る(この後で兄弟を引き剥がすため)\n for (const comment of collectComments(root, isBlockStart)) {\n const startMatch = SSR_BLOCK_START.exec(comment.data) as RegExpExecArray;\n const type = startMatch[1];\n const info = startMatch[2]; // \"uuid:path:index\" or \"uuid:path\"\n const parts = info.split(':');\n\n let uuid: string;\n let path: string;\n let index: number | null = null;\n\n if (type === 'for') {\n // uuid:path:index\n uuid = parts[0];\n path = parts[1];\n index = parseInt(parts[2], 10);\n } else {\n // uuid:path\n uuid = parts[0];\n path = parts.slice(1).join(':');\n }\n\n // start と end の間のノードを収集\n const nodes: Node[] = [];\n let sibling = comment.nextSibling;\n const endPattern = `@@wcs-${type}-end:${info}`;\n while (sibling) {\n if (sibling.nodeType === Node.COMMENT_NODE && (sibling as Comment).data === endPattern) {\n break;\n }\n nodes.push(sibling);\n sibling = sibling.nextSibling;\n }\n\n blocks.push({ type, uuid, path, index, nodes });\n }\n\n return blocks;\n}\n\n/**\n * live DOM ノード群からバインディングを収集する。\n * ノードを一時的に DocumentFragment に移動して collectNodesAndBindingInfos を実行し、\n * 元の位置に戻す。\n */\nfunction collectBindingsFromLiveNodes(\n nodes: Node[],\n): { bindingInfos: IBindingInfo[], subscriberNodes: Node[], bindingSession: BindingSession, deferredSpreads: IDeferredSpreadEntry[] } {\n const bindingSession = new BindingSession();\n if (nodes.length === 0) {\n return { bindingInfos: [], subscriberNodes: [], bindingSession, deferredSpreads: [] };\n }\n\n // ノードの元の位置を記録\n const parent = nodes[0].parentNode;\n const nextSibling = nodes[nodes.length - 1].nextSibling;\n\n // 一時的に wrapper 要素に移動(collectNodesAndBindingInfos は Element を受け付ける)\n const wrapper = document.createElement('div');\n for (const node of nodes) {\n wrapper.appendChild(node);\n }\n\n // バインディング収集。3 要素目(未定義カスタム要素への `...: path`)は呼び出し側が\n // ループ文脈を確定させてから予約する(`scheduleDeferredSpreads`)\n const [subscriberNodes, allBindings, deferredSpreads] = collectNodesAndBindingInfos(wrapper);\n\n const bindingInfos = bindingSession.initialize(allBindings, {\n registerAddress: false,\n applyOnReconnect: false,\n });\n\n // 元の位置に戻す\n if (parent) {\n while (wrapper.firstChild) {\n parent.insertBefore(wrapper.firstChild, nextSibling);\n }\n }\n\n return {\n bindingInfos,\n subscriberNodes,\n bindingSession,\n deferredSpreads,\n };\n}\n\n/**\n * ブロックの中で初回値を適用するバインディングを集める(#258 X6 — hydrateBindings の注記)。\n *\n * 除くもの:\n * - 構造バインディング(SSR が描画済み)とイベント。\n * - コメントに乗ったバインディング。ブロックを集める時点のコメントのバインディングは構造の置き場\n * (`<!--@@wcs-for:uuid-->`)だけで、テキストの `@@:` はこの後で `Ssr.restoreTextBindings` が戻す。\n * テンプレートを復帰できなかった入れ子の置き場は text として解釈される(`text: uuid`)ので、\n * 構造の種別だけでは除けない。\n * - 添字のバインディング(`$1` / `$2` …)。state に依存しないので依存辺のための適用が要らず、SSR が\n * 描いた添字のままでよい(入れ子の内側の `$2` は、外側のループ文脈しか持たないので読めもしない)。\n * - ループの深さより多いワイルドカードを持つバインディング。入れ子のブロックの行は外側のブロックの\n * Content に吸収され、外側のループ文脈しか持たない(入れ子ハイドレーションの既知の制限・\n * integration.listLedgerParentKey.test.ts)。\n * コメントと段数の足りないバインディングは、適用しても失敗の報告をハイドレーションのたびに増やすだけになる。\n */\n/**\n * コメントに乗った**本物のテキスト束縛**の形(`<!--@@: path-->` / `<!--@@wcs-text:path-->`)。\n *\n * コメントの束縛を一律に除いていたのは、テンプレートを復帰できなかった入れ子の構造プレースホルダが\n * `text: uuid` として解釈され、適用すると `binding-path-missing` を量産するからである。それは\n * `@@wcs-for:` のような**構造のキーワード付き**コメントなので、キーワードの有無で precise に分けられる。\n * 分けないと、`{{ }}` から復元したテキスト束縛まで初回適用から落ち、`restoreTextBindings` が\n * 捨てたサーバー側のテキストが戻らない。\n */\nconst TEXT_BINDING_COMMENT = /^\\s*@@\\s*(?:wcs-text)?\\s*:/;\n\nfunction collectBlockBindings(out: IBindingInfo[], bindings: readonly IBindingInfo[], loopDepth: number): void {\n for (const binding of bindings) {\n if (binding.bindingType === \"event\" || STRUCTURAL_TYPES.has(binding.bindingType)) {\n continue;\n }\n if (binding.node.nodeType === Node.COMMENT_NODE\n && !(binding.bindingType === \"text\" && TEXT_BINDING_COMMENT.test((binding.node as Comment).data))) {\n continue;\n }\n if (binding.statePathName in INDEX_BY_INDEX_NAME) {\n continue;\n }\n if (getPathInfo(binding.statePathName).wildcardCount > loopDepth) {\n continue;\n }\n out.push(binding);\n }\n}\n\n/**\n * SSR ブロックの DOM ノードを Content 化し、バインディングを登録する。\n * 戻り値はブロックの中で初回値を適用するバインディング(`collectBlockBindings`)。\n */\nfunction hydrateBlocks(root: Node, blocks: ISsrBlock[]): IBindingInfo[] {\n // for ブロックの listIndex を UUID ごとに収集\n const listIndexesByUuid: Map<string, IListIndex[]> = new Map();\n const blockBindings: IBindingInfo[] = [];\n\n for (const block of blocks) {\n if (block.nodes.length === 0) continue;\n\n const content = createContentFromNodes(block.nodes);\n\n // Content のバインディングを収集\n const { bindingInfos, subscriberNodes, bindingSession, deferredSpreads } = collectBindingsFromLiveNodes(block.nodes);\n setBindingSessionByContent(content, bindingSession);\n\n // Content 内のノードに data-wcs-completed を付与\n // (メインの collectNodesAndBindingInfos で重複登録されないようにする)\n for (const node of subscriberNodes) {\n if (node.nodeType === Node.ELEMENT_NODE) {\n (node as Element).setAttribute('data-wcs-completed', '');\n }\n }\n setBindingsByContent(content, bindingInfos);\n setNodesByContent(content, subscriberNodes);\n\n const indexBindings: IBindingInfo[] = [];\n for (const binding of bindingInfos) {\n if (binding.statePathName in INDEX_BY_INDEX_NAME) {\n indexBindings.push(binding);\n }\n }\n setIndexBindingsByContent(content, indexBindings);\n\n if (block.type === 'for' && block.index !== null) {\n const placeholderComment = findPlaceholderComment(root, 'for', block.uuid);\n if (placeholderComment) {\n const listIndex = createListIndex(null, block.index);\n hydrateSetContent(placeholderComment, listIndex, content);\n const lastNode = block.nodes[block.nodes.length - 1];\n hydrateSetLastNode(placeholderComment, lastNode);\n setContentByNode(placeholderComment, content);\n\n // ループコンテキストをバインドし、バインディングをアドレスに登録\n const pathInfo = getPathInfo(block.path + '.' + WILDCARD);\n const stateAddress = createStateAddress(pathInfo, listIndex);\n // ILoopContext は IStateAddress + listIndex なので、stateAddress をそのまま使う\n bindLoopContextToContent(content, stateAddress as any);\n\n bindingSession.initialize(bindingInfos, {\n registerAddress: true,\n registerPathInfo: false,\n applyOnReconnect: false,\n });\n collectBlockBindings(blockBindings, bindingInfos, pathInfo.wildcardCount);\n // 未定義カスタム要素への `...: path` は行のループ文脈が確定してから予約する\n scheduleDeferredSpreads(deferredSpreads, stateAddress as unknown as ILoopContext, bindingSession);\n\n // listIndex を UUID ごとに収集(後で setListIndexesByList に渡す)\n let indexes = listIndexesByUuid.get(block.uuid);\n if (!indexes) {\n indexes = [];\n listIndexesByUuid.set(block.uuid, indexes);\n }\n indexes.push(listIndex);\n }\n } else {\n const placeholderComment = findPlaceholderComment(root, block.type, block.uuid);\n if (placeholderComment) {\n setContentByNode(placeholderComment, content);\n\n bindingSession.initialize(bindingInfos, {\n registerAddress: true,\n registerPathInfo: false,\n applyOnReconnect: false,\n });\n // if / elseif / else の中身はループの外(入れ子の行はここに吸収されても段数で除かれる)\n collectBlockBindings(blockBindings, bindingInfos, 0);\n scheduleDeferredSpreads(deferredSpreads, null, bindingSession);\n }\n }\n }\n\n // for ブロックの listIndex を state のリスト値に紐づける\n for (const [uuid, indexes] of listIndexesByUuid) {\n const placeholderComment = findPlaceholderComment(root, 'for', uuid);\n if (!placeholderComment) continue;\n // state から現在のリスト値を取得して listIndexes を設定\n const rootNode = placeholderComment.getRootNode() as Node;\n // structuralBindings はまだ登録前なので、getParseBindTextResults を直接使う\n const fragmentInfo = getFragmentInfoByUUID(uuid);\n if (!fragmentInfo) continue;\n const statePathName = fragmentInfo.parseBindTextResult.statePathName;\n const stateElement = getStateElement(rootNode);\n if (!stateElement) continue;\n stateElement.createState(\"readonly\", (state) => {\n const list = state[statePathName];\n if (Array.isArray(list)) {\n setListIndexesByList(list, indexes);\n }\n });\n }\n return blockBindings;\n}\n\nfunction findPlaceholderComment(root: Node, type: string, uuid: string): Comment | null {\n const keywordMap: Record<string, string> = {\n 'for': config.commentForPrefix,\n 'if': config.commentIfPrefix,\n 'elseif': config.commentElseIfPrefix,\n 'else': config.commentElsePrefix,\n };\n const keyword = keywordMap[type];\n if (!keyword) return null;\n\n const pattern = `@@${keyword}:${uuid}`;\n return collectComments(root, (d) => d === pattern)[0] ?? null;\n}\n\n/**\n * <wcs-ssr> 内のテンプレートを fragmentInfoByUUID に復帰させる。\n */\nfunction restoreFragments(root: Document, ssrEl: Ssr): void {\n const rootNode = root as Node;\n let lastIfParseResult: ParseBindTextResult | null = null;\n\n for (const [uuid, tpl] of ssrEl.templates) {\n const bindText = tpl.getAttribute(config.bindAttributeName) || '';\n const parseBindTextResults = parseBindTextsForElement(bindText);\n let parseBindTextResult = parseBindTextResults[0];\n const bindingType = parseBindTextResult.bindingType;\n\n // else: 直前の if 条件の not → 条件反転(elseif は独自条件のままでよい)\n if (bindingType === 'else' && lastIfParseResult) {\n parseBindTextResult = {\n ...lastIfParseResult,\n outFilters: [...lastIfParseResult.outFilters, createNotFilter()],\n bindingType: 'else',\n };\n }\n\n // if chain の追跡\n if (bindingType === 'if') {\n lastIfParseResult = parseBindTextResult;\n } else if (bindingType === 'elseif') {\n lastIfParseResult = parseBindTextResult;\n } else if (bindingType === 'else') {\n lastIfParseResult = null;\n }\n\n const fragment = document.importNode(tpl.content, true);\n const forPath = bindingType === \"for\" ? parseBindTextResult.statePathName : undefined;\n optimizeFragment(fragment);\n if (typeof forPath === \"string\") {\n expandShorthandPaths(fragment, forPath);\n }\n collectStructuralFragments(rootNode, fragment, forPath);\n\n const fragmentInfo = {\n fragment,\n parseBindTextResult,\n nodeInfos: getFragmentNodeInfos(fragment),\n };\n setFragmentInfoByUUID(uuid, rootNode, fragmentInfo);\n }\n}\n\n/**\n * SSR ハイドレーション用バインディング初期化。\n * バージョン不一致時は DOM をクリーンアップして false を返す\n * (呼び出し元で buildBindings にフォールバック)。\n */\nexport async function hydrateBindings(root: Document): Promise<boolean> {\n await waitForStateInitialize(root);\n\n // バージョン検証\n const ssrElements = root.querySelectorAll(config.tagNames.ssr);\n for (const ssrNode of ssrElements) {\n const ssrEl = ssrNode as Ssr;\n if (!ssrEl.verifyVersion()) {\n console.warn(\n `[@wcstack/state] SSR version mismatch: server=\"${ssrEl.version}\", client=\"${VERSION}\". Falling back to full render.`\n );\n Ssr.cleanupDom(root);\n return false;\n }\n }\n\n // <wcs-ssr> からテンプレートを fragmentInfoByUUID に復帰\n for (const ssrNode of ssrElements) {\n restoreFragments(root, ssrNode as Ssr);\n }\n\n // SSR テキストバインディングを @@: 形式に復元。\n // **ブロックを集める前に**行う。後回しにしていたので、`for` / `if` の中の `{{ }}` は\n // `collectBindingsFromLiveNodes` の時点でまだ `@@wcs-text-start/end` のコメント対であり、\n // 行のバインディングとして登録されなかった。そのうえで `restoreTextBindings` は start/end の\n // 間(=サーバーが描いたテキスト)を捨てるので、**行の `{{ }}` が空になったまま二度と戻らない**\n // (`data-wcs=\"textContent: …\"` は属性なのでこの穴に落ちない)。\n Ssr.restoreTextBindings(document.body);\n\n // SSR ブロック境界コメントから既存 DOM を Content 化\n const blocks = collectSsrBlocks(document.body);\n const blockBindings = hydrateBlocks(document.body, blocks);\n\n // ブロック境界コメント (start/end) を除去\n Ssr.removeBlockBoundaryComments(document.body);\n\n // <wcs-ssr> を一時除去(バインディング走査に含めない)\n const ssrParents: { el: Element, parent: Node, next: Node | null }[] = [];\n for (const el of ssrElements) {\n if (el.parentNode) {\n ssrParents.push({ el, parent: el.parentNode, next: el.nextSibling });\n el.remove();\n }\n }\n\n // 構造プレースホルダーコメント (@@wcs-for:uuid 等) は残す\n // → バインディング走査で拾われ、状態変化時の再レンダリングに使われる\n\n // ノードとバインディングを収集\n const [subscriberNodes, allBindings, deferredSpreads] = collectNodesAndBindingInfos(document.body);\n\n // 収集完了したノードに data-wcs-completed 属性を付与\n // for ブロック内ノード(hydrateBlocks で登録済み)にはループコンテキストをリセットしない\n for (const node of subscriberNodes) {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n if (!el.hasAttribute('data-wcs-completed')) {\n setLoopContextByNode(node, null);\n el.setAttribute('data-wcs-completed', '');\n }\n } else {\n // コメントノード等\n setLoopContextByNode(node, null);\n }\n }\n\n // バインディングを構造系とそれ以外に分離\n const normalBindings: IBindingInfo[] = [];\n const structuralBindings: IBindingInfo[] = [];\n const bindingSession = getOrCreateBindingSession(document.body);\n const initializedBindings = bindingSession.initialize(allBindings);\n // 未定義カスタム要素への `...: path` を `whenDefined` 後の配線として予約する。\n // 通常経路(`bindings/initializeBindings.ts`)はこれを行うが、ハイドレーション経路は\n // `collectNodesAndBindingInfos` の 3 要素目を捨てていたので、SSR したページでだけ\n // spread が**永久に配線されなかった**(実測: 通常経路は whenDefined を予約、\n // ハイドレーション経路は 1 件も予約しない)\n scheduleDeferredSpreads(deferredSpreads, null, bindingSession);\n\n for (const binding of initializedBindings) {\n if (binding.bindingType === \"event\") continue;\n if (STRUCTURAL_TYPES.has(binding.bindingType)) {\n structuralBindings.push(binding);\n } else if (binding.statePathName.includes(WILDCARD)) {\n // for ブロック内のバインディング → Content のバインディングとして登録済み\n continue;\n } else {\n normalBindings.push(binding);\n }\n }\n\n // for バインディングの lastListValue を初期値として設定\n // (次回の状態変化時に差分計算の基準になる)\n for (const binding of structuralBindings) {\n if (binding.bindingType === 'for') {\n const absAddr = getAbsoluteStateAddressByBinding(binding);\n const rootNode = binding.replaceNode.getRootNode() as Node;\n const stateElement = getStateElement(rootNode);\n if (stateElement) {\n stateElement.createState(\"readonly\", (state) => {\n const value = state[binding.statePathName];\n if (Array.isArray(value)) {\n setLastListValueByAbsoluteStateAddress(absAddr, value);\n // 描画の基準と同時に state 側の基準も進める(E1。applyChangeFromBindings と対称)\n setStateListBaseline(absAddr, value);\n }\n });\n }\n }\n }\n\n // 初回値の適用(構造バインディングは SSR 描画済みなので除く)。SSR ブロック(for の行・if の中身)の\n // 中のバインディングも適用する(#258 X6): getter の依存辺は getter を評価したときにしか張られない\n // ので、適用しないと行の `items.*.double` は `items.*.n` を書いても一度も再評価されず、サーバーが\n // 書いたテキストのまま固まる。値は SSR と同じ state から読むのでテキストは変わらない\n applyChangeFromBindings([...normalBindings, ...blockBindings]);\n\n // <wcs-ssr> を元に戻す\n for (const { el, parent, next } of ssrParents) {\n parent.insertBefore(el, next);\n }\n\n // hydrateProps 復元。\n // 索引は **1 回だけ** 作る。props 1 件ごとに `querySelector('[data-wcs-ssr-id=\"…\"]')` を\n // 回していたので、props に載る束縛(textContent 等)の行数に対して二次だった\n // (実測 800 / 1600 / 3200 行で 109 / 297 / 1207 ms。props が 0 件の `value` 行は線形)\n const targetBySsrId = new Map<string, Element>();\n for (const el of root.querySelectorAll('[data-wcs-ssr-id]')) {\n targetBySsrId.set(el.getAttribute('data-wcs-ssr-id') as string, el);\n }\n for (const ssrNode of root.querySelectorAll(config.tagNames.ssr)) {\n for (const [id, propMap] of Object.entries((ssrNode as Ssr).hydrateProps)) {\n const target = targetBySsrId.get(id);\n if (!target) continue;\n for (const [propName, value] of Object.entries(propMap)) {\n (target as any)[propName] = value;\n }\n }\n }\n\n // ハイドレーション中の重複登録防止用属性と、SSR の対応付け用属性を除去する。\n // 後者は成功経路だけ残していたので、バージョン不一致のフォールバック(`Ssr.cleanupDom`)と\n // 非対称だった — ハイドレーション済みの DOM にサーバー側の帳簿が残り、\n // `[data-wcs-ssr-id]` を見る CSS / 再ハイドレーションに漏れる\n for (const el of root.querySelectorAll('[data-wcs-completed],[data-wcs-ssr-id]')) {\n el.removeAttribute('data-wcs-completed');\n el.removeAttribute('data-wcs-ssr-id');\n }\n\n return true;\n}\n\n/** @internal テスト用 */\nexport const __test = {\n collectSsrBlocks,\n collectBindingsFromLiveNodes,\n hydrateBlocks,\n findPlaceholderComment,\n restoreFragments,\n};\n","/**\n * ssr/install.ts — SSR 機能の install(設計案 H8、S5)。core の受け口(core/ssrHooks.ts と\n * registerComponents の definer)へこの機能の実装を置き、ssr-snapshot プロトコルを提供する。\n * 従来 `State` と `stateElementByName` と `registerComponents` に直書きされていた分岐で、\n * 理由のコメントは元の位置から移してある。\n */\nimport { config } from \"../config\";\nimport type { IStateElement } from \"../components/types\";\nimport { ISsrHooks, setSsrHooks } from \"../core/ssrHooks\";\nimport { registerComponentDefiner } from \"../registerComponents\";\nimport { getBindingsReady } from \"../stateElementByName\";\nimport { VERSION } from \"../version\";\nimport { registerSsrSnapshotBuilder } from \"./buildSsrDocument\";\nimport { hydrateBindings } from \"./hydrateBindings\";\nimport { Ssr } from \"./Ssr\";\n\nexport const ssrFeatureHooks: ISsrHooks = {\n hydrate: hydrateBindings,\n loadState(element) {\n const root = element.parentNode;\n if (!root) return null;\n const ssrEl = Ssr.find(root);\n if (!ssrEl) return null;\n const data = ssrEl.stateData;\n return Object.keys(data).length > 0 ? data : null;\n },\n async emitSnapshot(element) {\n await getBindingsReady((element as unknown as IStateElement).rootNode);\n const stateData = Ssr.extractStateData(element);\n const ssrEl = document.createElement(config.tagNames.ssr);\n ssrEl.setAttribute(\"version\", VERSION);\n // スナップショットの範囲はこの state のツリー — モジュール寿命の台帳に残った\n // 別のレンダリングのテンプレートを載せない(リクエスト間のデータ漏れ)\n Ssr.buildContent(ssrEl, stateData, (element as unknown as IStateElement).rootNode);\n element.parentNode?.insertBefore(ssrEl, element);\n },\n};\n\nlet installed = false;\n/** 冪等。full / auto では `bootstrapState()` が呼ぶ */\nexport function installSsr(): void {\n if (installed) return;\n installed = true;\n setSsrHooks(ssrFeatureHooks);\n registerComponentDefiner((registry) => {\n if (!registry.get(config.tagNames.ssr)) {\n registry.define(config.tagNames.ssr, Ssr);\n }\n });\n // ssr-snapshot プロトコルの提供(docs/ssr-router-design.md §5)。renderToString が\n // `<wcs-ssr>` 生成をサーバー主導の最終パスへ回せるようにする。登録は冪等\n registerSsrSnapshotBuilder();\n}\n","/**\n * features/ssr.ts — サーバー描画とハイドレーション(@wcstack/state/features/ssr)。\n */\nimport type { IStateFeature } from \"../core/features\";\nimport { installSsr } from \"../ssr/install\";\n\nexport const ssr: IStateFeature = {\n name: \"ssr\",\n install(): void {\n installSsr();\n },\n};\nexport default ssr;\n"],"names":["SSR_PLACEHOLDER_COMMENT","SSR_BLOCK_START","SSR_BLOCK_END","SSR_TEXT_START","escapeJsonForScript","json","replace","collectComments","root","match","walker","document","createTreeWalker","NodeFilter","SHOW_COMMENT","found","nextNode","comment","currentNode","data","push","isPlaceholder","test","isBlockStart","cloneFragmentForSnapshot","fragmentInfo","clone","fragment","cloneNode","nodeInfo","nodeInfos","commentData","getNormalizedTextCommentData","resolveNodePath","nodePath","target","parentNode","replaceChild","createComment","Ssr","HTMLElementBase","_stateData","_templates","_hydrateProps","version","this","getAttribute","stateData","_loadStateData","templates","_loadTemplates","hydrateProps","_loadHydrateProps","getTemplate","uuid","get","verifyVersion","serverVersion","serverParts","split","clientParts","VERSION","setStateData","setHydrateProps","props","script","querySelector","JSON","parse","textContent","map","Map","querySelectorAll","tpl","id","set","find","tagName","config","tagNames","ssr","parentEl","Element","Document","documentElement","extractStateData","stateEl","raw","__state","key","Object","keys","startsWith","descriptor","getOwnPropertyDescriptor","undefined","value","buildContent","ssrEl","scanRoot","ownerDocument","jsonScript","createElement","setAttribute","stringify","appendChild","pending","length","placeholder","shift","slice","indexOf","has","info","getFragmentInfoByUUID","collectReachableFragments","bindResult","parseBindTextResult","bindText","bindingType","statePathName","bindAttributeName","content","ssrNodes","getAllSsrPropertyNodes","propsData","i","node","isConnected","entries","getSsrProperties","entry","propName","propsScript","clearSsrPropertyStore","removeBlockBoundaryComments","d","remove","removeStructuralComments","restoreTextBindings","path","exec","bindComment","insertBefore","sibling","nextSibling","endPattern","next","nodeType","Node","COMMENT_NODE","removeChild","cleanupDom","body","ssrElements","templateByUuid","ssrNode","startComment","restored","bindAttr","imported","importNode","childNodes","child","Array","from","ssrIdElements","el","removeAttribute","builder","protocol","build","stateTag","state","ssrTag","stateElements","prev","previousElementSibling","toLowerCase","getRootNode","reset","clearFragmentInfos","STRUCTURAL_TYPES","Set","collectBindingsFromLiveNodes","nodes","bindingSession","BindingSession","bindingInfos","subscriberNodes","deferredSpreads","parent","wrapper","allBindings","collectNodesAndBindingInfos","initialize","registerAddress","applyOnReconnect","firstChild","TEXT_BINDING_COMMENT","collectBlockBindings","out","bindings","loopDepth","binding","INDEX_BY_INDEX_NAME","getPathInfo","wildcardCount","findPlaceholderComment","type","keyword","for","commentForPrefix","if","commentIfPrefix","elseif","commentElseIfPrefix","else","commentElsePrefix","pattern","restoreFragments","rootNode","lastIfParseResult","parseBindTextsForElement","outFilters","createNotFilter","forPath","optimizeFragment","expandShorthandPaths","collectStructuralFragments","getFragmentNodeInfos","setFragmentInfoByUUID","ssrFeatureHooks","hydrate","async","waitForStateInitialize","console","warn","blocks","startMatch","parts","index","parseInt","join","collectSsrBlocks","blockBindings","listIndexesByUuid","block","createContentFromNodes","setBindingSessionByContent","ELEMENT_NODE","setBindingsByContent","setNodesByContent","indexBindings","setIndexBindingsByContent","placeholderComment","listIndex","createListIndex","hydrateSetContent","lastNode","hydrateSetLastNode","setContentByNode","pathInfo","WILDCARD","stateAddress","createStateAddress","bindLoopContextToContent","registerPathInfo","scheduleDeferredSpreads","indexes","stateElement","getStateElement","createState","list","isArray","setListIndexesByList","hydrateBlocks","ssrParents","hasAttribute","setLoopContextByNode","normalBindings","structuralBindings","getOrCreateBindingSession","initializedBindings","includes","absAddr","getAbsoluteStateAddressByBinding","replaceNode","setLastListValueByAbsoluteStateAddress","setStateListBaseline","applyChangeFromBindings","targetBySsrId","propMap","loadState","element","emitSnapshot","getBindingsReady","installed","installSsr","setSsrHooks","registerComponentDefiner","registry","define","globals","globalThis","SSR_SNAPSHOT_BUILDER_KEY","registerSsrSnapshotBuilder","name","install"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{g as e,r as t,N as n,a as o}from"../chunks/getAllPropertyDescriptors.js";import{S as s,a as r,D as a,W as i,b as c,g as u,M as l,c as f,d,e as $}from"../chunks/pathDiagnostics.js";import{r as m,L as p,d as h}from"../chunks/errorGuidance.js";import{c as y,d as g,e as w,l as b,f as v}from"../chunks/binder.js";import{g as k,c as O,a as j,s as S,m as A,b as P,r as I,h as E,i as _,d as R,e as x,f as z,w as W,n as M,p as D,j as L,k as N,l as C,o as F,q as G,t as T,u as q,v as U,x as H}from"../chunks/watchRuntime.js";import{i as K}from"../chunks/filterRegistry.js";const Q="[wcs/scan-declaration-invalid]",B=Object.freeze([]);function J(e){return`${s} entry "${e}"`}function V(e,t){for(const n of e.cumulativePaths)if(t.has(n))return n;return null}function X(e,t,n,o){return`[wcs/scan-source-computed] ${e} ${t} "${n}" ${o===n?"is a getter":o.includes(`${i}${i}`)?`is computed by the recursive getter "${o}"`:`is under the getter "${o}"`}. A getter re-evaluates whenever its inputs change, so folding it would count re-evaluations, not events. Fold the plain value the getter reads, or use "on" with an event token.${p}`}function Y(e,t,n){0===n.length&&m(`${Q} ${e} "${t}" must be a non-empty state path.${p}`),n.startsWith("$")&&m(`${Q} ${e} ${t} "${n}" must not start with "$" (reserved namespace).${p}`),n.includes("@")&&m(`${Q} ${e} ${t} "${n}" must not contain "@" — there is a single state tree per root.${p}`),n in Object.prototype&&m(`${Q} ${e} ${t} "${n}" must not be a property name inherited from Object.prototype (e.g. "__proto__", "constructor").${p}`);const o=u(n);for(const s of o.segments)0===s.length&&m(`${Q} ${e} ${t} "${n}" has an empty path segment.${p}`);return o.wildcardCount>l&&m(`${Q} ${e} ${t} "${n}" exceeds the maximum wildcard depth (${l}).`),o}function Z(e,t,n,o){const c=J(e);0===e.length&&m(`${Q} ${s} entry name must be a non-empty string.${p}`),(e.includes(a)||e.includes(i))&&m(`${Q} ${c} must be a flat property name ("${a}" and "${i}" are not allowed). The output is a property the runtime owns.${p}`),e.startsWith("$")&&m(`${Q} ${c} must not start with "$" (reserved namespace).${p}`),e in Object.prototype&&m(`${Q} ${c} must not be a property name inherited from Object.prototype (e.g. "__proto__", "constructor").${p}`),t.has(e)&&m(`${Q} ${c} conflicts with a getter declared on the state.${p}`),n.has(e)&&m(`${Q} ${c} conflicts with a setter declared on the state.${p}`),o.has(e)&&m(`${Q} ${c} conflicts with the ${r} entry of the same name — each output has exactly one owner.${p}`)}function ee(e,t,n){const o=t.get(e);t.has(e)&&o!==n.initial&&!W(e,o)&&m(`${Q} ${J(e)} conflicts with a method (a function-valued property) declared on the state. The output is a property the runtime owns.${p}`)}function te(e,t,n,o,s,r){const i=J(e),u=void 0!==t.from,l=void 0!==t.on;if(u===l&&m(`${Q} ${i} must declare exactly one of "from" (a state path) or "on" (an event-token name).${p}`),l){const e=t.on;return"string"==typeof e&&0!==e.length||m(`${Q} ${i} "on" must be a non-empty event-token name.${p}`),n.has(e)||m(`${Q} ${i} on "${e}" is not declared in ${c}.${h(e,n)}${p}`),{kind:"event",tokenName:e}}const f=t.from;"string"!=typeof f&&m(`${Q} ${i} "from" must be a state path string.${p}`);const d=Y(i,"from",f),$=V(d,o)??r?.recursiveGetterOwning(f)??null;null!==$&&m(X(i,"from",f,$));const y=V(d,s);return null!==y&&m(function(e,t,n){return`${Q} ${e} from "${t}" ${n===t?"is a setter without a getter":`is under the setter without a getter "${n}"`}, so it always reads undefined and every fold would receive undefined. Fold the plain value the setter writes, or use "on" with an event token.${p}`}(i,f,y)),(f===e||f.startsWith(e+a))&&m(`${Q} ${i} from "${f}" reads the entry's own output — the scan would fold its own writes forever.${p}`),{kind:"path",path:f,pathInfo:d}}function ne(e,t,n,o){if(void 0===t)return B;const s=J(e);Array.isArray(t)||m(`${Q} ${s} "resetOn" must be an array of state paths.${p}`);const r=[];for(const e of t){"string"!=typeof e&&m(`${Q} ${s} "resetOn" must contain only state path strings.${p}`);const t=Y(s,"resetOn",e);t.wildcardCount>0&&m(`${Q} ${s} resetOn "${e}" must not contain "${i}" — a reset returns the whole output to "initial".${p}`);const c=V(t,o);null!==c&&m(X(s,"resetOn",e,c)),"path"===n.kind&&e===n.path&&m(`${Q} ${s} resetOn "${e}" is the entry's own "from" — every change would reset instead of fold.${p}`),"path"===n.kind&&e.startsWith(n.path+a)&&m(`${Q} ${s} resetOn "${e}" sits under the entry's own "from" "${n.path}" — every write of "from" also lands it, so the reset would win every time and nothing would fold.${p}`),r.push(e)}return r}function oe(t,n,o=null){const a=t[s];if(void 0===a)return null;("object"!=typeof a||null===a||Array.isArray(a))&&m(`${Q} ${s} must be an object mapping output names to scan definitions ({ from | on, initial, fold, resetOn? }).${p}`);const i=new Set,c=new Set,l=new Set,f=new Map;for(const[n,o]of Object.entries(e(t)))"function"==typeof o.get?i.add(n):"function"==typeof o.set&&l.add(n),"function"==typeof o.set&&c.add(n),"function"==typeof o.value&&f.set(n,o.value);const d=function(e){const t=e[r];return"object"!=typeof t||null===t?new Set:new Set(Object.keys(t))}(t),$=[];let h=0;for(const[e,t]of Object.entries(a)){Z(e,i,c,d),("object"!=typeof t||null===t||Array.isArray(t))&&m(`${Q} ${J(e)} must be an object ({ from | on, initial, fold, resetOn? }).${p}`);const s=t;ee(e,f,s);const r=te(e,s,n,i,l,o);"initial"in s||m(`${Q} ${J(e)} requires "initial" — the seed of the accumulator and the value "resetOn" returns to.${p}`),"function"!=typeof s.fold&&m(`${Q} ${J(e)} fold must be a function.${p}`);const a=ne(e,s.resetOn,r,i);$.push({name:e,source:r,fold:s.fold,initial:s.initial,resetOn:a,order:h++})}return function(e){const t=new Set(e.map(e=>e.name)),n=new Map;for(const o of e){for(const e of o.resetOn){const n=u(e).segments[0];t.has(n)&&m(`${Q} ${J(o.name)} resetOn "${e}" reads the ${s} output "${n}" — a reset driven by an accumulator is a feedback loop. Reset on the plain inputs instead.${p}`)}if("path"===o.source.kind){const e=o.source.pathInfo.segments[0];t.has(e)&&n.set(o.name,e)}}for(const e of n.keys()){const o=[e];let r=n.get(e);for(;void 0!==r&&o.length<=t.size;)r===e&&m(`${Q} ${s} entries ${[...o,e].map(e=>`"${e}"`).join(" → ")} feed each other through "from" — each fold would re-trigger the next forever.${p}`),o.push(r),r=n.get(r)}}($),$.length>0?$:null}function se(e){const t=e.fold;return(n,o,...s)=>{const r=n;let a="read-output";try{const n=r[e.name];a="threw";const i=E(e),c=i&&!_(n,e.initial)?P(e.initial):n,u=t(c,o,...s);if(R(u))return void x(e.name,u);Object.is(u,n)||(a="write",r[e.name]=u,I(e.name,u)),i&&O(e)}catch(t){z(e.name,t,a)}}}const re="scanEntries",ae={order:8,validate(e,t,n){const o=oe(t,n.get("eventTokenNames")??new Set,n.get("recursionRegistry")??null);n.set(re,o)},applyEarly(e,t,n){const o=n.get(re)??null;null!==o&&(function(e,t){for(const n of t)if(!(n.name in e)){const t=P(n.initial);e[n.name]=t,I(n.name,t)}}(t,o),function(e,t){for(const n of t)"event"===n.source.kind&&g(e,n.source.tokenName).subscribe(se(n))}(e,o))},register(e,t,n){const o=n.get(re)??null,s=function(e){const t=new Map,n=k(e);if(void 0!==n)for(const e of n.entries)O(e)&&t.set(e.name,e.resetOn);return j(e),t}(e);e.setScanPaths?.(null===o?null:function(e,t,n){const o=S(e,t);for(const o of t){const t=n.get(o.name);"event"===o.source.kind&&void 0!==t&&o.resetOn.some(n=>t.includes(n)&&y().hasQueuedPath(e,n))&&A(o)}const s=new Set;for(const t of o.byFromPath.keys())s.add(t),e.setPathInfo(t,"prop","scan");for(const t of o.byResetPath.keys())e.setPathInfo(t,"prop","scan");return s.size>0?s:null}(e,o,s))}};let ie=!1;const ce=Object.freeze([]),ue=Object.freeze([]);function le(e,t,n){const o=new Set([n]),s=[n];for(;s.length>0;){const n=s.pop(),r=e.staticDependency.get(n)??ce,a=e.dynamicDependency.get(n)??ce,i=t.byFromPath.get(n)??ue;for(const e of[...r,...a,...i.map(e=>e.name)])o.has(e)||(o.add(e),s.push(e))}return o}const fe=(e,t)=>t,de=new Set;const $e=new WeakMap,me=new WeakMap;function pe(e,t,n){return new Proxy(Object.create(null),{get(t,o){if("string"!=typeof o)return;const s=N(e).get(o);return void 0!==s?n(s):void 0},has:(t,n)=>"string"==typeof n&&N(e).has(n),ownKeys:()=>Array.from(N(e).keys()),getOwnPropertyDescriptor(t,o){if("string"!=typeof o)return;const s=N(e).get(o);return void 0!==s?{configurable:!0,enumerable:!0,value:n(s)}:void 0},set(){m(`${t} namespace is read-only; assigning to it is not allowed.`)},deleteProperty(){m(`${t} namespace is read-only; deleting from it is not allowed.`)}})}function he(e){const t=$e.get(e);if(void 0!==t)return t;const n=pe(e,f,e=>e.status);return $e.set(e,n),n}function ye(e){const t=me.get(e);if(void 0!==t)return t;const n=pe(e,d,e=>e.error);return me.set(e,n),n}function ge(e){$e.delete(e),me.delete(e)}let we=null;function be(e,t){let n=e;for(let e=1;e<t.length;e++){if(Object(n)!==n)return;n=Reflect.get(n,t[e])}return n}const ve={read(e,t){!function(e,t){null!==we&&we.add(b(e,t))}(e,t);const o=t.pathInfo.segments[0];return o===f?be(he(e),t.pathInfo.segments):o===d?be(ye(e),t.pathInfo.segments):n},get:(e,t)=>t===f?he(e.stateElement):t===d?ye(e.stateElement):n};async function ke(e,t,n,o){let s=null,r=null;const a=()=>{if(s&&s!==r){r=s;try{s.return?.()?.then?.(void 0,()=>{})}catch{}}};n.addEventListener("abort",a,{once:!0});try{const r=await e(t,n);if(s=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("[@wcstack/state] $streams: source must return an AsyncIterable or a ReadableStream (got neither).")}(r,n)[Symbol.asyncIterator](),n.aborted)return void a();for(;;){const e=await s.next();if(e.done)break;if(n.aborted)return;o.fold(e.value)}if(n.aborted)return;o.done()}catch(e){if(n.aborted)return;o.fail(e)}finally{n.removeEventListener("abort",a)}}function Oe(e){_e();const t=N(e);if(0!==t.size){G(e);for(const n of t.values())je(e,n)}}function je(e,t){t.controller?.abort();const n=new AbortController;t.controller=n;const o=function(e,t){const n=t.definition.args;if(null===n)return void t.depAddresses.clear();const o=we,s=new Set;let i;we=s;try{e.createState("readonly",e=>{i=n(e)})}finally{we=o}i instanceof Promise&&m(`${r} entry "${t.name}" args must be synchronous (it returned a Promise).`);const c=`${f}${a}${t.name}`,u=`${d}${a}${t.name}`;for(const n of s){const o=n.absolutePathInfo.pathInfo;n.absolutePathInfo.stateElement!==e||o.path!==t.name&&o.path!==c&&o.path!==u||m(`${r} entry "${t.name}" args must not read the stream itself ("${o.path}"): a self-dependency would restart the stream on its own writes (infinite loop).`),o.wildcardCount>0&&m(`${r} entry "${t.name}" args must not read wildcard paths ("${o.path}"): wildcard dependencies are out of scope.`)}return t.depAddresses=s,i}(e,t);!function(e,t){const n=k(e);if(void 0!==n&&0!==t.depAddresses.size)for(const o of n.entries){if("path"!==o.source.kind||o.source.pathInfo.segments[0]!==t.name)continue;const a=le(e,n,o.name);for(const e of t.depAddresses){const n=e.absolutePathInfo.pathInfo.path;a.has(n)&&m(`[wcs/scan-feedback-loop] ${r} entry "${t.name}" args read "${n}", which is derived from the ${s} output "${o.name}" that this stream feeds. Every landing would restart the stream on its own result. Advance the cursor from an event ($on) and keep it a plain property instead of deriving it from the accumulator.`)}}}(e,t),e.createState("writable",e=>{e[t.name]=t.definition.initial}),Se(e,t,"active",null);const i=t.definition,c={fold(n){e.createState("writable",e=>{e[t.name]=i.fold(e[t.name],n)})},done(){Se(e,t,"done",null)},fail(o){Se(e,t,"error",o),n.abort()}};ke(i.source,o,n.signal,c)}function Se(e,t,n,o){t.status=n,t.error=o;const s=q(e,t.name),r=s.status!==n,i=!Object.is(s.error,o);(r||i)&&(U(e,t.name,n,o),e.createState("writable",e=>{r&&e.$postUpdate(`${f}${a}${t.name}`),i&&e.$postUpdate(`${d}${a}${t.name}`)}))}function Ae(e){const t=T();if(0===t.size)return;const n=[];for(const o of t)for(const t of N(o).values())for(const s of t.depAddresses)if(e.has(s)){n.push({stateElement:o,entry:t});break}for(const{stateElement:e,entry:o}of n)if(t.has(e)&&N(e).get(o.name)===o)try{je(e,o)}catch(n){o.controller?.abort(),t.has(e)&&N(e).get(o.name)===o&&Se(e,o,"error",n)}}let Pe=!1;const Ie=new WeakMap,Ee={order:20,apply(e,t){ge(e),F(e),function(e,t){const n=t[r];if(void 0===n)return void D(e,de);"object"==typeof n&&null!==n||m(`${r} must be an object mapping stream names to stream definitions.`);const o=new Map;for(const[s,c]of Object.entries(n)){0===s.length&&m(`${r} entry name must be a non-empty string.`),s.includes(a)&&m(`${r} entry "${s}" must be a flat property name ("${a}" is not allowed).`),s.includes(i)&&m(`${r} entry "${s}" must be a flat property name ("${i}" is not allowed).`),s.startsWith("$")&&m(`${r} entry "${s}" must not start with "$" (reserved namespace).`),s in Object.prototype&&m(`${r} entry "${s}" must not be a property name inherited from Object.prototype (e.g. "__proto__", "constructor").`),e.getterPaths.has(s)&&m(`${r} entry "${s}" conflicts with a getter declared on the state.`),e.setterPaths.has(s)&&m(`${r} entry "${s}" conflicts with a setter declared on the state.`),"object"==typeof c&&null!==c||m(`${r} entry "${s}" must be an object ({ args?, source, fold?, initial? }).`);const n=c;"function"!=typeof n.source&&m(`${r} entry "${s}" source must be a function.`);const u=void 0!==n.fold;u&&"function"!=typeof n.fold&&m(`${r} entry "${s}" fold must be a function.`),u&&!("initial"in n)&&m(`${r} entry "${s}" requires "initial" when fold is specified (reduce needs a seed value).`),void 0!==n.args&&"function"!=typeof n.args&&m(`${r} entry "${s}" args must be a function.`);const l={name:s,definition:{args:n.args??null,source:n.source,fold:n.fold??fe,initial:n.initial},status:"idle",error:null,controller:null,depAddresses:new Set};s in t||(t[s]=l.definition.initial),o.set(s,l)}L(e,o),D(e,new Set(o.keys()))}(e,t),void 0!==t[r]&&e.attachAddressHooks?.("streams",r)},activate(e,t){if(!K()&&null!=e.connectedRootNode){if(null===t){if(!0!==e.initialized)return;return Oe(e),void Ie.set(e,e.connectGeneration??0)}t===e.connectGeneration&&Ie.get(e)!==t&&Oe(e)}},deactivate(e){C(e),ge(e)}};function _e(){Pe||(Pe=!0,o("streams",ve),t("streams",Ee),v(Ae,$))}const Re={name:"temporal",install(){H(),ie||(ie=!0,t("scan",ae),w(M)),_e()}};export{Re as default,Re as temporal};
|
|
1
|
+
import{r as t}from"../chunks/declarationHooks.js";import{S as e,d as n,a as o,D as r,W as s,b as a,g as i,M as c,c as u,e as l,f,h as d,i as h,j as p}from"../chunks/pathDiagnostics.js";import{n as m}from"../chunks/declarationAliases.js";import{r as $,L as y,d as w}from"../chunks/errorGuidance.js";import{c as g,d as v,e as b,l as k,f as j,i as O,h as P,j as I,k as x,m as S,n as A,o as E,p as M}from"../chunks/binder.js";import{g as z,N as W,r as R}from"../chunks/getAllPropertyDescriptors.js";import{i as C}from"../chunks/filterRegistry.js";import{s as _}from"../chunks/featureBridge.js";const D=new WeakMap,F=new Set,N=new Set;function T(t){return t.byFromPath.size>0||t.byResetPath.size>0}function L(t,e,n){const o=t.get(e);void 0===o?t.set(e,[n]):o.push(n)}function G(t){return D.get(t)}function B(t){H(t),D.delete(t)}function H(t){F.delete(t),N.delete(t)}const K=new WeakSet;let q=0;function Q(t){return G(t.absolutePathInfo.stateElement)?.eventResetByPath.get(t.absolutePathInfo.pathInfo.path)}function U(t){if(0===N.size)return;if(e=t.absolutePathInfo.stateElement,!N.has(e))return;var e;const n=Q(t);if(void 0!==n)for(const t of n)V(t)}function V(t){K.has(t)||(K.add(t),q++)}function J(t){return!!K.delete(t)&&(q--,!0)}const X=/^(?:0|[1-9]\d*)$/;function Y(t,e){const n=Object.getOwnPropertyDescriptor(t,e);return!0===n.enumerable&&"value"in n}function Z(t){if("object"!=typeof t||null===t||Object.isFrozen(t))return null;const e=Object.getPrototypeOf(t);if(Array.isArray(t)){return e===Array.prototype&&Reflect.ownKeys(t).every(e=>"length"===e||"string"==typeof e&&X.test(e)&&Y(t,e))?"array":null}return(e===Object.prototype||null===e)&&Reflect.ownKeys(t).every(e=>"string"==typeof e&&Y(t,e))?"object":null}function tt(t,e){const n=Z(t);if(null===n)return t;const o=t,r=e.get(o);if(void 0!==r)return r;let s;s="array"===n?new Array(t.length):null===Object.getPrototypeOf(t)?Object.create(null):{},e.set(o,s);for(const t of Object.keys(o))s[t]=tt(o[t],e);return s}function et(t){return tt(t,new Map)}function nt(t,e,n){if(Object.is(t,e))return!0;const o=Z(e);if(null===o||"object"!=typeof t||null===t)return!1;const r=t,s=e;if("array"===o){if(!Array.isArray(t)||t.length!==e.length)return!1}else if(Array.isArray(t)||Object.keys(r).length!==Object.keys(s).length)return!1;if(Z(t)!==o)return!1;let a=n.get(r);if(void 0===a)a=new Set,n.set(r,a);else if(a.has(s))return!0;if(a.add(s),"array"===o){for(let e=0;e<t.length;e++)if(!nt(r[e],s[e],n))return!1;return!0}return Object.keys(s).every(t=>Object.prototype.hasOwnProperty.call(r,t)&&nt(r[t],s[t],n))}function ot(t,e){return nt(t,e,new Map)}const rt=new WeakMap;function st(t,e){if("function"!=typeof e)return;const n=rt.get(e);void 0===n?rt.set(e,new Set([t])):n.add(t)}function at(t,e){return"function"==typeof e&&!0===rt.get(e)?.has(t)}const it={"read-source":t=>`${e} could not read a "from" value for "${t}". That landing was not folded; the other landings of the batch still were.`,"read-rows":t=>`${e} could not read the rows of "from" for "${t}". Nothing was folded or written.`,"read-output":t=>`${e} could not read the output "${t}". Nothing was folded or written.`,threw:t=>`${e} fold for "${t}" threw. The output was not written.`,"returned-promise":t=>`${e} fold for "${t}" returned a Promise. fold must be synchronous and return the next value; the output was not written.`,write:t=>`${e} could not write the output "${t}".`},ct={"read-source":"evaluate","read-rows":"evaluate","read-output":"evaluate",threw:"fold","returned-promise":"fold",write:"write"};function ut(t,o,r){null!==n&&n({type:"state:watch-error",phase:o,path:`${e}.${t}`,error:r})}function lt(t,e,n){console.error(`[@wcstack/state] ${it[n](t)}`,e),ut(t,ct[n],e)}function ft(t){return null!==t&&("object"==typeof t||"function"==typeof t)&&"function"==typeof t.then}function dt(t,n){n.then(void 0,()=>{}),lt(t,new TypeError(`${e} fold for "${t}" returned a Promise. fold must be synchronous and return the next value.`),"returned-promise")}const ht=new WeakSet;const pt="[wcs/scan-declaration-invalid]",mt=Object.freeze([]);function $t(t){return`${e} entry "${t}"`}function yt(t,e){for(const n of t.cumulativePaths)if(e.has(n))return n;return null}function wt(t,e,n,o){return`[wcs/scan-source-computed] ${t} ${e} "${n}" ${o===n?"is a getter":o.includes(`${s}${s}`)?`is computed by the recursive getter "${o}"`:`is under the getter "${o}"`}. A getter re-evaluates whenever its inputs change, so folding it would count re-evaluations, not events. Fold the plain value the getter reads, or use "on" with an event token.${y}`}function gt(t,e,n){0===n.length&&$(`${pt} ${t} "${e}" must be a non-empty state path.${y}`),n.startsWith("$")&&$(`${pt} ${t} ${e} "${n}" must not start with "$" (reserved namespace).${y}`),n.includes("@")&&$(`${pt} ${t} ${e} "${n}" must not contain "@" — there is a single state tree per root.${y}`),n in Object.prototype&&$(`${pt} ${t} ${e} "${n}" must not be a property name inherited from Object.prototype (e.g. "__proto__", "constructor").${y}`);const o=i(n);for(const r of o.segments)0===r.length&&$(`${pt} ${t} ${e} "${n}" has an empty path segment.${y}`);return o.wildcardCount>c&&$(`${pt} ${t} ${e} "${n}" exceeds the maximum wildcard depth (${c}).`),o}function vt(t,n,a,i){const c=$t(t);0===t.length&&$(`${pt} ${e} entry name must be a non-empty string.${y}`),(t.includes(r)||t.includes(s))&&$(`${pt} ${c} must be a flat property name ("${r}" and "${s}" are not allowed). The output is a property the runtime owns.${y}`),t.startsWith("$")&&$(`${pt} ${c} must not start with "$" (reserved namespace).${y}`),t in Object.prototype&&$(`${pt} ${c} must not be a property name inherited from Object.prototype (e.g. "__proto__", "constructor").${y}`),n.has(t)&&$(`${pt} ${c} conflicts with a getter declared on the state.${y}`),a.has(t)&&$(`${pt} ${c} conflicts with a setter declared on the state.${y}`),i.has(t)&&$(`${pt} ${c} conflicts with the ${o} entry of the same name — each output has exactly one owner.${y}`)}function bt(t,e,n){const o=e.get(t);e.has(t)&&o!==n.initial&&!at(t,o)&&$(`${pt} ${$t(t)} conflicts with a method (a function-valued property) declared on the state. The output is a property the runtime owns.${y}`)}function kt(t,e,n,o,s,i){const c=$t(t),u=void 0!==e.from,l=void 0!==e.on;if(u===l&&$(`${pt} ${c} must declare exactly one of "from" (a state path) or "on" (an event-token name).${y}`),l){const t=e.on;return"string"==typeof t&&0!==t.length||$(`${pt} ${c} "on" must be a non-empty event-token name.${y}`),n.has(t)||$(`${pt} ${c} on "${t}" is not declared in ${a}.${w(t,n)}${y}`),{kind:"event",tokenName:t}}const f=e.from;"string"!=typeof f&&$(`${pt} ${c} "from" must be a state path string.${y}`);const d=gt(c,"from",f),h=yt(d,o)??i?.recursiveGetterOwning(f)??null;null!==h&&$(wt(c,"from",f,h));const p=yt(d,s);return null!==p&&$(function(t,e,n){return`${pt} ${t} from "${e}" ${n===e?"is a setter without a getter":`is under the setter without a getter "${n}"`}, so it always reads undefined and every fold would receive undefined. Fold the plain value the setter writes, or use "on" with an event token.${y}`}(c,f,p)),(f===t||f.startsWith(t+r))&&$(`${pt} ${c} from "${f}" reads the entry's own output — the scan would fold its own writes forever.${y}`),{kind:"path",path:f,pathInfo:d}}function jt(t,e,n,o){if(void 0===e)return mt;const a=$t(t);Array.isArray(e)||$(`${pt} ${a} "resetOn" must be an array of state paths.${y}`);const i=[];for(const t of e){"string"!=typeof t&&$(`${pt} ${a} "resetOn" must contain only state path strings.${y}`);const e=gt(a,"resetOn",t);e.wildcardCount>0&&$(`${pt} ${a} resetOn "${t}" must not contain "${s}" — a reset returns the whole output to "initial".${y}`);const c=yt(e,o);null!==c&&$(wt(a,"resetOn",t,c)),"path"===n.kind&&t===n.path&&$(`${pt} ${a} resetOn "${t}" is the entry's own "from" — every change would reset instead of fold.${y}`),"path"===n.kind&&t.startsWith(n.path+r)&&$(`${pt} ${a} resetOn "${t}" sits under the entry's own "from" "${n.path}" — every write of "from" also lands it, so the reset would win every time and nothing would fold.${y}`),i.push(t)}return i}function Ot(t,n,r=null){const s=t[e];if(void 0===s)return null;("object"!=typeof s||null===s||Array.isArray(s))&&$(`${pt} ${e} must be an object mapping output names to scan definitions ({ from | on, initial, fold, resetOn? }).${y}`);const a=new Set,c=new Set,u=new Set,l=new Map;for(const[e,n]of Object.entries(z(t)))"function"==typeof n.get?a.add(e):"function"==typeof n.set&&u.add(e),"function"==typeof n.set&&c.add(e),"function"==typeof n.value&&l.set(e,n.value);const f=function(t){m(t);const e=t[o];return"object"!=typeof e||null===e?new Set:new Set(Object.keys(e))}(t),d=[];let h=0;for(const[t,e]of Object.entries(s)){vt(t,a,c,f),("object"!=typeof e||null===e||Array.isArray(e))&&$(`${pt} ${$t(t)} must be an object ({ from | on, initial, fold, resetOn? }).${y}`);const o=e;bt(t,l,o);const s=kt(t,o,n,a,u,r);"initial"in o||$(`${pt} ${$t(t)} requires "initial" — the seed of the accumulator and the value "resetOn" returns to.${y}`),"function"!=typeof o.fold&&$(`${pt} ${$t(t)} fold must be a function.${y}`);const i=jt(t,o.resetOn,s,a);d.push({name:t,source:s,fold:o.fold,initial:o.initial,resetOn:i,order:h++})}return function(t){const n=new Set(t.map(t=>t.name)),o=new Map;for(const r of t){for(const t of r.resetOn){const o=i(t).segments[0];n.has(o)&&$(`${pt} ${$t(r.name)} resetOn "${t}" reads the ${e} output "${o}" — a reset driven by an accumulator is a feedback loop. Reset on the plain inputs instead.${y}`)}if("path"===r.source.kind){const t=r.source.pathInfo.segments[0];n.has(t)&&o.set(r.name,t)}}for(const t of o.keys()){const r=[t];let s=o.get(t);for(;void 0!==s&&r.length<=n.size;)s===t&&$(`${pt} ${e} entries ${[...r,t].map(t=>`"${t}"`).join(" → ")} feed each other through "from" — each fold would re-trigger the next forever.${y}`),r.push(s),s=o.get(s)}}(d),d.length>0?d:null}function Pt(t){const e=t.fold;return(n,o,...r)=>{const s=n;let a="read-output";try{const n=s[t.name];a="threw";const i=function(t){return K.has(t)}(t),c=i&&!ot(n,t.initial)?et(t.initial):n,u=e(c,o,...r);if(ft(u))return void dt(t.name,u);Object.is(u,n)||(a="write",s[t.name]=u,st(t.name,u)),i&&J(t)}catch(e){lt(t.name,e,a)}}}function It(t,e,n){const o=function(t,e){B(t);const n=new Map,o=new Map,r=new Map;for(const t of e){"path"===t.source.kind&&L(n,t.source.path,t);for(const e of t.resetOn)L(o,e,t),"event"===t.source.kind&&L(r,e,t)}const s={entries:new Set(e),byFromPath:n,byResetPath:o,eventResetByPath:r};return D.set(t,s),s}(t,e);for(const o of e){const e=n.get(o.name);"event"===o.source.kind&&void 0!==e&&(o.resetOn.some(n=>e.includes(n)&&g().hasQueuedPath(t,n))&&V(o))}const r=new Set;for(const e of o.byFromPath.keys())r.add(e),t.setPathInfo(e,"prop","scan");for(const e of o.byResetPath.keys())t.setPathInfo(e,"prop","scan");return r.size>0?r:null}const xt="scanEntries",St={order:8,validate(t,e,n){const o=Ot(e,n.get("eventTokenNames")??new Set,n.get("recursionRegistry")??null);n.set(xt,o)},applyEarly(t,e,n){const o=n.get(xt)??null;null!==o&&(function(t,e){for(const n of e)if(!(n.name in t)){const e=et(n.initial);t[n.name]=e,st(n.name,e)}}(e,o),function(t,e){for(const n of e)"event"===n.source.kind&&v(t,n.source.tokenName).subscribe(Pt(n))}(t,o))},register(t,e,n){const o=n.get(xt)??null,r=function(t){const e=new Map,n=G(t);if(void 0!==n)for(const t of n.entries)J(t)&&e.set(t.name,t.resetOn);return B(t),e}(t);t.setScanPaths?.(null===o?null:It(t,o,r))}};let At=!1;const Et=Object.freeze([]),Mt=Object.freeze([]);function zt(t,e,n){const o=new Set([n]),r=[n];for(;r.length>0;){const n=r.pop(),s=t.staticDependency.get(n)??Et,a=t.dynamicDependency.get(n)??Et,i=e.byFromPath.get(n)??Mt;for(const t of[...s,...a,...i.map(t=>t.name)])o.has(t)||(o.add(t),r.push(t))}return o}const Wt=Symbol("wcs-stream-last-notified-uncertain"),Rt=new WeakMap;function Ct(t,e){const n=Rt.get(t);if(void 0!==n)for(const t of n.keys())e.has(t)||n.delete(t)}function _t(t,e){const n=Rt.get(t);if(void 0===n)return;const o=n.get(e);void 0!==o&&n.set(e,{status:"idle"===o.status?o.status:Wt,error:Object.is(o.error,null)?null:Wt})}const Dt=new Set;function Ft(t){Dt.delete(t)}const Nt=new WeakMap;function Tt(t){return Nt.get(t)??new Map}function Lt(t){Ft(t);const e=Nt.get(t);if(void 0!==e)for(const n of e.values())n.controller?.abort(),n.controller=null,n.status="idle",n.error=null,_t(t,n.name)}const Gt=(t,e)=>e,Bt=new Set;function Ht(t,e){m(e);const n=e[o];if(void 0===n)return void Ct(t,Bt);"object"==typeof n&&null!==n||$(`${o} must be an object mapping stream names to stream definitions.`);const a=new Map,i=z(e);for(const[c,u]of Object.entries(n)){0===c.length&&$(`${o} entry name must be a non-empty string.`),c.includes(r)&&$(`${o} entry "${c}" must be a flat property name ("${r}" is not allowed).`),c.includes(s)&&$(`${o} entry "${c}" must be a flat property name ("${s}" is not allowed).`),c.startsWith("$")&&$(`${o} entry "${c}" must not start with "$" (reserved namespace).`),c in Object.prototype&&$(`${o} entry "${c}" must not be a property name inherited from Object.prototype (e.g. "__proto__", "constructor").`),t.getterPaths.has(c)&&$(`${o} entry "${c}" conflicts with a getter declared on the state.`),t.setterPaths.has(c)&&$(`${o} entry "${c}" conflicts with a setter declared on the state.`),"object"==typeof u&&null!==u||$(`${o} entry "${c}" must be an object ({ args?, source, fold?, initial? }).`);const n=u,l=i[c]?.value;"function"!=typeof l||l===n.initial||at(c,l)||$(`${o} entry "${c}" conflicts with a method (a function-valued property) declared on the state. The value is a property the runtime owns.`),"function"!=typeof n.source&&$(`${o} entry "${c}" source must be a function.`);const f=void 0!==n.fold;f&&"function"!=typeof n.fold&&$(`${o} entry "${c}" fold must be a function.`),f&&!("initial"in n)&&$(`${o} entry "${c}" requires "initial" when fold is specified (reduce needs a seed value).`);void 0!==n.args&&"function"!=typeof n.args&&$(`${o} entry "${c}" args must be a function.`);const d={name:c,definition:{args:n.args??null,source:n.source,fold:n.fold??Gt,initial:n.initial},status:"idle",error:null,controller:null,depAddresses:new Set};c in e||(e[c]=d.definition.initial,st(c,d.definition.initial)),a.set(c,d)}!function(t,e){Nt.set(t,e)}(t,a),Ct(t,new Set(a.keys()))}const Kt=new WeakMap,qt=new WeakMap;function Qt(t,e,n){return new Proxy(Object.create(null),{get(e,o){if("string"!=typeof o)return;const r=Tt(t).get(o);return void 0!==r?n(r):void 0},has:(e,n)=>"string"==typeof n&&Tt(t).has(n),ownKeys:()=>Array.from(Tt(t).keys()),getOwnPropertyDescriptor(e,o){if("string"!=typeof o)return;const r=Tt(t).get(o);return void 0!==r?{configurable:!0,enumerable:!0,value:n(r)}:void 0},set(){$(`${e} namespace is read-only; assigning to it is not allowed.`)},deleteProperty(){$(`${e} namespace is read-only; deleting from it is not allowed.`)}})}function Ut(t){const e=Kt.get(t);if(void 0!==e)return e;const n=Qt(t,u,t=>t.status);return Kt.set(t,n),n}function Vt(t){const e=qt.get(t);if(void 0!==e)return e;const n=Qt(t,l,t=>t.error);return qt.set(t,n),n}function Jt(t){Kt.delete(t),qt.delete(t)}let Xt=null;function Yt(t,e){let n=t;for(let t=1;t<e.length;t++){if(Object(n)!==n)return;n=Reflect.get(n,e[t])}return n}const Zt={read(t,e){!function(t,e){null!==Xt&&Xt.add(k(t,e))}(t,e);const n=e.pathInfo.segments[0];return n===u?Yt(Ut(t),e.pathInfo.segments):n===l?Yt(Vt(t),e.pathInfo.segments):W},get:(t,e)=>e===u?Ut(t.stateElement):e===l?Vt(t.stateElement):W};async function te(t,e,n,r){let s=null;const a=()=>{if(null!==s)try{s.return?.()?.then?.(void 0,()=>{})}catch{}};n.addEventListener("abort",a,{once:!0});try{const i=await t(e,n);if(s=function(t,e){if("function"==typeof t?.getReader)return async function*(t,e){const n=t.getReader(),o=()=>{n.cancel().catch(()=>{})};e.addEventListener("abort",o,{once:!0});try{for(;;){const{done:t,value:e}=await n.read();if(t)return;yield e}}finally{e.removeEventListener("abort",o),n.releaseLock()}}(t,e);if("function"==typeof t?.[Symbol.asyncIterator])return t;throw new TypeError(`[@wcstack/state] ${o}: source must return an AsyncIterable or a ReadableStream (got neither).`)}(i,n)[Symbol.asyncIterator](),n.aborted)return void a();for(;;){const t=await s.next();if(t.done)break;if(n.aborted)return;r.fold(t.value)}if(n.aborted)return;r.done()}catch(t){if(n.aborted)return;r.fail(t)}finally{n.removeEventListener("abort",a)}}function ee(t){ce();const e=Tt(t);if(0!==e.size){!function(t){Dt.add(t)}(t);for(const n of e.values())ne(t,n)}}function ne(t,n){n.controller?.abort();const s=new AbortController;n.controller=s;const a=function(t,e){const n=e.definition.args;if(null===n)return void e.depAddresses.clear();const s=Xt,a=new Set;let i;Xt=a;try{t.createState("readonly",t=>{i=n(t)})}finally{Xt=s}i instanceof Promise&&$(`${o} entry "${e.name}" args must be synchronous (it returned a Promise).`);const c=`${u}${r}${e.name}`,f=`${l}${r}${e.name}`;for(const n of a){const r=n.absolutePathInfo.pathInfo;n.absolutePathInfo.stateElement!==t||r.path!==e.name&&r.path!==c&&r.path!==f||$(`${o} entry "${e.name}" args must not read the stream itself ("${r.path}"): a self-dependency would restart the stream on its own writes (infinite loop).`),r.wildcardCount>0&&$(`${o} entry "${e.name}" args must not read wildcard paths ("${r.path}"): wildcard dependencies are out of scope.`)}return e.depAddresses=a,i}(t,n);!function(t,n){const r=G(t);if(void 0!==r&&0!==n.depAddresses.size)for(const s of r.entries){if("path"!==s.source.kind||s.source.pathInfo.segments[0]!==n.name)continue;const a=zt(t,r,s.name);for(const t of n.depAddresses){const r=t.absolutePathInfo.pathInfo.path;a.has(r)&&$(`[wcs/scan-feedback-loop] ${o} entry "${n.name}" args read "${r}", which is derived from the ${e} output "${s.name}" that this stream feeds. Every landing would restart the stream on its own result. Advance the cursor from an event ($on) and keep it a plain property instead of deriving it from the accumulator.`)}}}(t,n),t.createState("writable",t=>{t[n.name]=n.definition.initial}),st(n.name,n.definition.initial),oe(t,n,"active",null);const i=n.definition,c={fold(e){t.createState("writable",t=>{const o=i.fold(t[n.name],e);t[n.name]=o,st(n.name,o)})},done(){oe(t,n,"done",null)},fail(e){oe(t,n,"error",e),s.abort()}};te(i.source,a,s.signal,c)}function oe(t,e,n,o){e.status=n,e.error=o;const s=function(t,e){return Rt.get(t)?.get(e)??{status:"idle",error:null}}(t,e.name),a=s.status!==n,i=!Object.is(s.error,o);(a||i)&&(!function(t,e,n,o){let r=Rt.get(t);void 0===r&&(r=new Map,Rt.set(t,r)),r.set(e,{status:n,error:o})}(t,e.name,n,o),t.createState("writable",t=>{a&&t.$postUpdate(`${u}${r}${e.name}`),i&&t.$postUpdate(`${l}${r}${e.name}`)}))}function re(t){const e=Dt;if(0===e.size)return;const n=[];for(const o of e)for(const e of Tt(o).values())for(const r of e.depAddresses)if(t.has(r)){n.push({stateElement:o,entry:e});break}for(const{stateElement:t,entry:o}of n)if(e.has(t)&&Tt(t).get(o.name)===o)try{ne(t,o)}catch(n){o.controller?.abort(),e.has(t)&&Tt(t).get(o.name)===o&&oe(t,o,"error",n)}}let se=!1;const ae=new WeakMap,ie={order:20,apply(t,e){var n;Jt(t),Lt(n=t),Ft(n),Nt.delete(n),Ht(t,e),void 0!==e[o]&&t.attachAddressHooks?.("streams",o)},activate(t,e){if(!C()&&null!=t.connectedRootNode){if(null===e){if(!0!==t.initialized)return;return ee(t),void ae.set(t,t.connectGeneration??0)}e===t.connectGeneration&&ae.get(t)!==e&&ee(t)}},deactivate(t){Lt(t),Jt(t)}};function ce(){se||(se=!0,R("streams",Zt),t("streams",ie),j(re,f))}let ue=0,le=0;function fe(t){ue=t+1}function de(){ue=0}function he(){ue>le&&(le=ue)}const pe=new WeakMap;function me(t,e,n){let o=pe.get(t);void 0===o&&(o=new Map,pe.set(t,o)),o.set(e,n)}const $e=new Map;function ye(t,e){$e.has(t)||$e.set(t,e)}function we(t){return $e.get(t)}const ge=new WeakMap,ve=new WeakMap,be=new Set,ke=new Map;function je(t){return ge.get(t)??ke}function Oe(t){be.add(t),function(t){const e=D.get(t);void 0!==e&&(T(e)&&F.add(t),function(t){return t.eventResetByPath.size>0}(e)&&N.add(t))}(t)}function Pe(){return be}function Ie(t,e){let n=ve.get(t);void 0===n&&(n=new Map,ve.set(t,n));for(const t of e){const e=n.get(t.path);void 0===e?n.set(t.path,[t]):e.push(t)}}const xe=new Map;function Se(t){return ve.get(t)??xe}function Ae(t){0===t.length&&$(`[wcs/watch-declaration-invalid] ${d} entry name must be a non-empty state path.`),t.startsWith("$")&&$(`[wcs/watch-declaration-invalid] ${d} entry "${t}" must not start with "$" (reserved namespace).${y}`),t.includes("@")&&$(`[wcs/watch-declaration-invalid] ${d} entry "${t}" must not contain "@" — the name selector was removed in v2; watch only paths of the state tree.${y}`),t in Object.prototype&&$(`[wcs/watch-declaration-invalid] ${d} entry "${t}" must not be a property name inherited from Object.prototype (e.g. "__proto__", "constructor").`);const e=i(t);for(const n of e.segments)0===n.length&&$(`[wcs/watch-declaration-invalid] ${d} entry "${t}" has an empty path segment.${y}`);return e.wildcardCount>c&&$(`[wcs/watch-declaration-invalid] ${d} entry "${t}" exceeds the maximum wildcard depth (${c}).`),e}function Ee(t,e){const n=e[d];if(void 0===n)return null;"object"==typeof n&&null!==n||$(`[wcs/watch-declaration-invalid] ${d} must be an object mapping state paths to handler functions.${y}`);const o=new Map,r=new Set;let s=0;for(const[e,a]of Object.entries(n)){"function"!=typeof a&&$(`[wcs/watch-declaration-invalid] ${d} entry "${e}" must be a function.${y}`);const n=Ae(e);o.set(e,{path:e,pathInfo:n,handler:a,order:s++}),r.add(e),t.setPathInfo(e,"prop","watch")}return function(t,e){ge.set(t,e)}(t,o),r.size>0?r:null}const Me={writeObserve(t,e,n,o,r){if(!r)return;const s=t.watchPaths,a=t.scanPaths;!0!==s?.has(e)&&!0!==a?.has(e)||ye(n,o)},swapped(t,e,n){var o;o=n,$e.has(o)&&ye(e,we(n))}};function ze(t){for(let e=t;null!==e;e=e.parentListIndex)if(O(e))return!0;return!1}function We(t,e,n){const o=n.absAddress.listIndex;let r="current",s=null;for(let a=0;a<e.wildcardCount;a++){const i=t[P](I(e.wildcardParentPathInfos[a],s)),c=x(i)?.[n.indexes[a]];if(void 0===c)return"gone";const u=S(o,a,e.wildcardCount);if(c!==u&&!A(c,u)){if(O(u))return"gone";r="replaced"}s=c}return r}function Re(t,e,n){const o=new Map;for(const r of n){const n=We(t,e,r);if("gone"===n)continue;const s="current"===n?1:0,a=r.indexes.join(","),i=o.get(a);(void 0===i||s>i.rank)&&o.set(a,{row:r,rank:s})}return Array.from(o.values(),t=>t.row)}const Ce=Object.freeze([]);function _e(t,e,n){let o=t.get(n);return void 0===o&&(o={stateElement:e,entry:n,rows:[],reset:!1},t.set(n,o)),o}function De(t,e){let n=0;for(;n<t.indexes.length-1&&t.indexes[n]===e.indexes[n];)n++;return t.indexes[n]-e.indexes[n]}function Fe(t,e){return e.has(t.stateElement)&&!0===G(t.stateElement)?.entries.has(t.entry)}function Ne(t){const{entry:e,stateElement:n}=t;"event"!==e.source.kind||e.resetOn.some(t=>Te(n,t))||J(e)}function Te(t,e){return g().hasQueuedPath(t,e)}function Le(t,e){!function(t,e,n){if(0!==q)for(const o of t){const t=o.absolutePathInfo.stateElement;if(!0===e?.has(t))continue;const r=Q(o);if(void 0!==r)for(const e of r)e.resetOn.some(e=>n(t,e))||J(e)}}(t,e,Te)}function Ge(t,e,n){return 0===e.pathInfo.wildcardCount?t[e.path]:t.$resolve(e.path,n.indexes)}function Be(t,n,o){if(!Fe(t,o))return Ne(t),null;const{stateElement:r,entry:s}=t;if(t.reset)return{group:t,next:s.initial};const a=s.source;if(function(t,e){for(const n of e.pathInfo.cumulativePaths)if(t.getterPaths.has(n))return!0;return!1}(r,a))return function(t,n,o){if(ht.has(t))return;ht.add(t);const r=`[wcs/scan-source-computed] ${e} entry "${n}" from "${o}" is now backed by a getter (registered after the declaration, e.g. by a volume). A getter re-evaluates whenever its inputs change, so this scan stopped folding. Fold the plain value the getter reads instead.`;console.error(`[@wcstack/state] ${r}`),ut(n,"fold",new Error(r))}(s,s.name,a.path),null;if(function(t,e,n){const o=Tt(t).get(e);if(void 0===o)return!1;for(const t of o.depAddresses)if(n.has(t))return!0;return!1}(r,a.pathInfo.segments[0],n))return null;const i={plan:null,failure:"read-output"};try{r.createState("readonly",e=>{const n=e[s.name];i.failure="read-rows";const o=0===a.pathInfo.wildcardCount?t.rows:Re(e,a.pathInfo,t.rows);i.failure="threw";const r=s.fold;let c=n;for(const t of o.sort(De)){let n;try{n=Ge(e,a,t)}catch(t){lt(s.name,t,"read-source");continue}const o=r(c,n,we(t.absAddress),...t.indexes);if(ft(o))return void dt(s.name,o);c=o}Object.is(c,n)||(i.plan={group:t,next:c})})}catch(t){lt(s.name,t,i.failure)}return i.plan}function He(t,e){const{group:n}=t;if(!Fe(n,e))return void Ne(n);const{stateElement:o,entry:r}=n;if(!n.reset||"event"!==r.source.kind||J(r))try{o.createState("writable",e=>{const o=e[r.name];if(n.reset?!ot(o,r.initial):!Object.is(o,t.next)){const o=n.reset?et(r.initial):t.next;e[r.name]=o,st(r.name,o)}})}catch(t){lt(r.name,t,"write")}}function Ke(t,e,n){if(Le(t,e),0===F.size)return Ce;const o=new Map;for(const n of t){const t=n.absolutePathInfo.stateElement;if(!e.has(t))continue;const r=G(t);if(void 0===r)continue;const s=n.absolutePathInfo.pathInfo,a=r.byFromPath.get(s.path);if(void 0!==a){let e=[];if(s.wildcardCount>0){if(null===n.listIndex)continue;e=E(n.listIndex,s.wildcardCount)}for(const r of a)_e(o,t,r).rows.push({absAddress:n,indexes:e})}const i=r.byResetPath.get(s.path);if(void 0!==i)for(const e of i)_e(o,t,e).reset=!0}if(0===o.size)return Ce;const r=Array.from(o.values()).sort((t,e)=>t.entry.order-e.entry.order),s=[];fe(n);try{for(const n of r){const o=Be(n,t,e);null!==o&&s.push(o)}}finally{de()}return s}function qe(t){en(),(0!==je(t).size||0!==Se(t).size||function(t){const e=D.get(t);return void 0!==e&&T(e)}(t))&&(Oe(t),function(t){const e=[];for(const n of je(t).values())Qe(t,n)&&e.push(n);for(const n of Se(t).values())for(const o of n)Qe(t,o)&&e.push(o);if(0===e.length)return;t.createState("readonly",n=>{for(const o of e)try{me(t,M(t,o.pathInfo,null),n[o.path])}catch(e){Ve(t,o.path,"prime",e)}})}(t))}function Qe(t,e){return 0===e.pathInfo.wildcardCount&&t.getterPaths.has(e.path)}const Ue={prime:"initial evaluation of",evaluate:"evaluation of",handler:"handler for"};function Ve(t,e,o,r){console.error(`[@wcstack/state] $watch ${Ue[o]} "${e}" threw.`,r),null!==n&&n({type:"state:watch-error",phase:o,path:e,error:r})}function Je(t){const e=Pe();try{if(0===e.size)return void Le(t,e);const o=function(){const t=le;return le=0,t}();if(o>p){Le(t,null);const e=Array.from(t,t=>t.absolutePathInfo.pathInfo.path);return console.error("[@wcstack/state] $watch chain depth limit exceeded; $scan folds and $watch handlers for this batch were skipped.",{maxDepth:p,paths:e}),void(null!==n&&n({type:"state:watch-chain-limit",maxDepth:p,paths:e}))}!function(t,e,n){if(0!==t.length){fe(n);try{for(const n of t)He(n,e)}finally{de()}}}(Ke(t,e,o),e,o),function(t,e,n){const o=[];for(const n of t){const t=n.absolutePathInfo.stateElement;if(!e.has(t))continue;const r=n.absolutePathInfo.pathInfo.path,s=je(t).get(r),a=Se(t).get(r);if(void 0===s&&void 0===a)continue;const i=void 0===s?[]:[s];void 0!==a&&i.push(...a);for(const e of i){let r=[];if(e.pathInfo.wildcardCount>0){if(null===n.listIndex)continue;r=E(n.listIndex,e.pathInfo.wildcardCount)}o.push({stateElement:t,entry:e,absAddress:n,indexes:r})}}if(0===o.length)return;o.sort(Ye);const r=function(t){let e=null;for(let n=0;n<t.length;n++){const o=t[n];if(0===o.entry.pathInfo.wildcardCount)continue;const r=t[n-1];(ze(o.absAddress.listIndex)||r?.entry===o.entry&&Xe(r.indexes,o.indexes))&&(e??=new Set).add(o.entry)}if(null===e)return t;const n=new Set;for(const o of e){const e=t.filter(t=>t.entry===o),r=e[0].stateElement;try{r.createState("readonly",t=>{const r=new Set(Re(t,o.pathInfo,e));for(const t of e)r.has(t)||n.add(t)})}catch(t){Ve(0,o.path,"evaluate",t);for(const t of e)n.add(t)}}return t.filter(t=>!n.has(t))}(o);fe(n);try{for(const t of r){if(!e.has(t.stateElement))continue;const n=je(t.stateElement).get(t.entry.path)===t.entry,o=!0===Se(t.stateElement).get(t.entry.path)?.includes(t.entry);(n||o)&&Ze(t)}}finally{de()}}(t,e,o)}finally{$e.clear()}}function Xe(t,e){for(let n=0;n<t.length;n++)if(t[n]!==e[n])return!1;return!0}function Ye(t,e){if(t.entry.order!==e.entry.order)return t.entry.order-e.entry.order;const n=Math.min(t.indexes.length,e.indexes.length);for(let o=0;o<n;o++)if(t.indexes[o]!==e.indexes[o])return t.indexes[o]-e.indexes[o];return t.indexes.length-e.indexes.length}function Ze(t){const{stateElement:e,entry:o,absAddress:r,indexes:s}=t,a=Qe(e,o);try{e.createState("writable",t=>{let i;try{i=function(t,e,n){if(0===e.pathInfo.wildcardCount)return t[e.path];return t.$resolve(e.path,n)}(t,o,s)}catch(t){return void Ve(0,o.path,"evaluate",t)}const c=a?function(t,e){return pe.get(t)?.get(e)}(e,r):we(r);a&&me(e,r,i),null!==n&&n({type:"state:watch-fired",path:o.path,stateElement:e}),o.handler.call(t,i,c,...s)})}catch(t){Ve(0,o.path,"handler",t)}}let tn=!1;function en(){tn||(tn=!0,R("watch",Me),t("watch",nn),j(Je,h),b(he),_({assertValidPath:Ae,addEntries:Ie,start:qe}))}const nn={order:10,register(t,n){var o;o=t,be.delete(o),H(o),ge.delete(o),function(t){pe.delete(t)}(t);const r=Ee(t,n);t.setWatchPaths?.(r),null===r&&null==t.scanPaths||t.attachAddressHooks?.("watch",null!==r?d:e)},activate(t,e){C()||null==t.connectedRootNode||(null===e?!0===t.initialized:e===t.connectGeneration)&&qe(t)},deactivate(t){var e;e=t,be.delete(e),H(e)}},on={name:"temporal",install(){en(),At||(At=!0,t("scan",St),b(U)),ce()}};export{on as default,on as temporal};
|
|
2
2
|
//# sourceMappingURL=temporal.js.map
|