@vcp-dev/site-edit-bridge 0.10.31 → 0.10.32

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/dist/client.d.ts CHANGED
@@ -64,7 +64,16 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
64
64
  onConnectionStatus?: (status: SiteEditBridgeConnectionStatus) => void;
65
65
  requestTimeoutMs?: number;
66
66
  requestTimeoutMsByKind?: Partial<Record<SiteEditBridgeRequestKind, number>>;
67
+ /** 建连到鉴权完成的总时限;0 关闭。挂住的握手会转成可重试的失败。 */
68
+ connectTimeoutMs?: number;
69
+ /** 传输层心跳;false 关闭。页面不可见时暂停,避免后台节流误判断线。 */
70
+ heartbeat?: {
71
+ intervalMs: number;
72
+ timeoutMs: number;
73
+ } | false;
74
+ visibility?: SiteEditBridgeVisibilitySource | null;
67
75
  });
76
+ private heartbeatState;
68
77
  connect(): Promise<void>;
69
78
  disconnect(): void;
70
79
  getConnectionState(): SiteEditBridgeConnectionState;
@@ -373,9 +382,20 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
373
382
  private resolveRequestTimeoutMs;
374
383
  private readonly connectionStatusListeners;
375
384
  private cleanupSocket;
385
+ private startHeartbeat;
386
+ private stopHeartbeat;
387
+ /** 任何入站帧都证明连接仍通,直接撤销等待中的心跳期限。 */
388
+ private noteInboundMessage;
376
389
  private setConnectionState;
377
390
  private createSocket;
378
391
  }
392
+ export declare const SITE_EDIT_BRIDGE_CONNECT_TIMEOUT = "SITE_EDIT_BRIDGE_CONNECT_TIMEOUT";
393
+ export declare const SITE_EDIT_BRIDGE_HEARTBEAT_TIMEOUT = "SITE_EDIT_BRIDGE_HEARTBEAT_TIMEOUT";
394
+ export type SiteEditBridgeVisibilitySource = {
395
+ readonly visibilityState: string;
396
+ addEventListener(type: "visibilitychange", listener: () => void): void;
397
+ removeEventListener(type: "visibilitychange", listener: () => void): void;
398
+ };
379
399
  export type SiteEditBridge = SiteEditBridgeClient & {
380
400
  initializeSession(input: SiteEditBridgeInitializeInput): Promise<SiteEditCommandSuccess<{
381
401
  document: SiteEditRenderDocument;
package/dist/client.js CHANGED
@@ -1 +1 @@
1
- function _(t){if(!P(t)){if(t.encoding==="segment-trie-v1")throw new Error("Invalid compact render document");return t}let e=new Map,n=t.pageComposition.status==="supported"?t.pageComposition.placementUnitKeys:[],o=new Map(t.dynamicDataSummaries??[]),i=new Map(t.pageCompositionActionScopes??[]),r=c=>{let s=e.get(c);if(s)return s;let a=t.nodes[c];if(!a)throw new Error(`Missing compact document node ${c}`);let[u,d]=a,p=t.tokens[d];if(typeof p!="string")throw new Error(`Missing compact document token ${d}`);let y=`${u===null?"":r(u)}${p}`;return e.set(c,y),y};return{siteId:t.siteId,snapshotId:t.snapshotId,composition:{...t.composition},routeId:t.composition.routeId,version:t.version,pageComposition:t.pageComposition,entries:t.entries.map((c,s)=>{let[a,u,d]=c,p=d===void 0?void 0:n[d];if(d!==void 0&&!p)throw new Error(`Missing page composition unit ${d}`);let y=t.entryParentNodeIds?.[s],w=i.get(s),D=w?.kind==="source-siblings"?{kind:"source-siblings",siblingKeys:w.siblingEntryIndexes.map(A=>{let O=t.entries[A];if(!O)throw new Error(`Missing source sibling entry ${A}`);return r(O[0])})}:w;return{key:r(a),...t.entryParentNodeIds?{parentKey:y===null?null:r(y)}:{},...p?{pageCompositionUnitKey:p}:{},...D?{pageCompositionActionScope:D}:{},operationSummary:F(u),...o.get(s)?{dynamicDataSummary:o.get(s)}:{}}})}}function F(t){return{canUpdateText:(t&1)!==0,canInsertChild:(t&2)!==0,canMove:(t&4)!==0,canRemove:(t&8)!==0}}function P(t){if(typeof t!="object"||t===null)return!1;let e=t;if(e.encoding!=="segment-trie-v1"||typeof e.siteId!="string"||typeof e.snapshotId!="string"||!U(e.composition)||typeof e.routeId!="string"||e.routeId!==e.composition.routeId||!Number.isInteger(e.version)||e.version<0||!b(e.pageComposition)||!Array.isArray(e.tokens)||!e.tokens.every(s=>typeof s=="string")||!Array.isArray(e.nodes)||!Array.isArray(e.entries))return!1;let n=e.nodes;if(!n.every((s,a)=>Array.isArray(s)&&s.length===2&&(s[0]===null||l(s[0])&&s[0]<a)&&l(s[1])&&s[1]<e.tokens.length)||!e.entries.every(s=>Array.isArray(s)&&(s.length===2||s.length===3)&&l(s[0])&&s[0]<n.length&&l(s[1])&&s[1]<=15&&(s.length===2||l(s[2]))))return!1;let o=e.entryParentNodeIds;if(o!==void 0&&(!Array.isArray(o)||o.length!==e.entries.length||!o.every(s=>s===null||l(s)&&s<n.length)))return!1;let i=e.dynamicDataSummaries,r=e.pageCompositionActionScopes;return(r===void 0||Array.isArray(r)&&r.every(s=>{if(!Array.isArray(s)||s.length!==2||!l(s[0])||s[0]>=e.entries.length||!b(s[1])||!["route-unit","source-siblings","unavailable"].includes(String(s[1].kind)))return!1;let a=s[1];return a.kind!=="source-siblings"||Array.isArray(a.siblingEntryIndexes)&&a.siblingEntryIndexes.length>=2&&a.siblingEntryIndexes.every(u=>l(u)&&u<e.entries.length)&&a.siblingEntryIndexes.includes(s[0])}))&&(i===void 0||Array.isArray(i)&&i.every(s=>Array.isArray(s)&&s.length===2&&l(s[0])&&s[0]<e.entries.length&&b(s[1])))}function b(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function U(t){return!b(t)||typeof t.routeId!="string"?!1:t.effectiveTemplateId===void 0||typeof t.effectiveTemplateId=="string"}function l(t){return typeof t=="number"&&Number.isInteger(t)&&t>=0}var f=class t{constructor(){}static parseMessage(e,n={}){let o=JSON.parse(e);if(!t.isMessage(o,n))throw new Error("Invalid site edit bridge message");return o}static stringifyMessage(e){return JSON.stringify(e)}static extractRequestInfo(e){if(h(e)&&h(e.payload)){let n=typeof e.payload.id=="string"?e.payload.id:"unknown",o=this.isRequestKind(e.payload.kind)?e.payload.kind:"get-document";return{id:n,kind:o}}return{id:"unknown",kind:"get-document"}}static isRequestKind(e){return e==="initialize-session"||e==="get-document"||e==="get-history-status"||e==="get-style-context"||e==="get-object-edit-context"||e==="get-class-name-source"||e==="get-source-tree"||e==="get-source-file"||e==="resolve-source-location"||e==="execute"||e==="get-operation-result"||e==="undo"||e==="redo"}static isMessage(e,n={}){return!h(e)||typeof e.type!="string"?!1:e.type==="request"?W(e.payload,n):e.type==="response"||e.type==="error"?h(e.payload)&&typeof e.payload.id=="string":e.type==="event"?h(e.payload)&&h(e.payload.event):e.type==="auth"?h(e.payload)&&typeof e.payload.key=="string":e.type==="auth-ok"&&h(e.payload)&&e.payload.ok===!0}};function W(t,e){if(!h(t)||typeof t.id!="string"||!f.isRequestKind(t.kind))return!1;let n=E(t.composition);switch(t.kind){case"initialize-session":return n&&(t.mutationContext===void 0||k(t.mutationContext));case"get-document":case"get-style-context":return n;case"get-object-edit-context":return typeof t.key=="string"&&n&&$(t.selectionRef);case"get-class-name-source":case"resolve-source-location":return typeof t.key=="string"&&n;case"get-history-status":return n;case"get-source-tree":return n;case"get-source-file":return typeof t.path=="string"&&n;case"execute":case"get-operation-result":return n&&z(t.request)&&(t.request.mutationContext===void 0||k(t.request.mutationContext))&&(e.validateOperationRequest?.(t.request)??!0);case"undo":case"redo":return n&&t.mutationContext===void 0||n&&k(t.mutationContext)}}function $(t){if(t===void 0)return!0;if(!h(t)||typeof t.key!="string"||t.key.length===0)return!1;let e=t.instanceIndex,n=t.instanceCount;return e!==void 0&&(typeof e!="number"||!Number.isInteger(e)||e<0)||n!==void 0&&(typeof n!="number"||!Number.isInteger(n)||n<=0)||typeof e=="number"&&typeof n=="number"&&e>=n?!1:Object.keys(t).every(o=>o==="key"||o==="instanceIndex"||o==="instanceCount")}function z(t){return h(t)&&typeof t.id=="string"&&typeof t.siteId=="string"&&typeof t.baseSnapshotId=="string"&&E(t.composition)&&typeof t.kind=="string"&&h(t.target)&&(t.target.kind!=="route"||E(t.target.composition))&&typeof t.documentVersion=="number"&&Number.isInteger(t.documentVersion)&&t.documentVersion>=0&&h(t.params)}function E(t){return h(t)&&typeof t.routeId=="string"&&t.routeId.length>0&&(t.templateId===void 0||typeof t.templateId=="string"&&t.templateId.length>0)&&Object.keys(t).every(e=>e==="routeId"||e==="templateId")}function h(t){return typeof t=="object"&&t!==null}function k(t){return h(t)&&typeof t.editingSessionId=="string"&&t.editingSessionId.length>0&&typeof t.lockId=="string"&&t.lockId.length>0&&typeof t.ownerId=="string"&&t.ownerId.length>0&&typeof t.leaseAssertion=="string"&&t.leaseAssertion.length>0}var L="vcp:designer:trace",V="designer_trace",M="__VCP_DESIGNER_TRACE__";var J=/(authorization|cookie|token|secret|providerTrace|headers?|sourceCode|rawSource|fullSource)/i;function G(t=globalThis){let e=t;try{if(e.localStorage?.getItem(L)==="1")return!0}catch{}try{let n=e.location?.search??"";return new URLSearchParams(n).get(V)==="1"}catch{return!1}}function g(t,e={},n=globalThis){if(!G(n))return;let o={type:t,timestamp:Date.now(),payload:Q(e)},i=n,r=i[M]??[];r.push(o),r.length>1e3&&r.splice(0,r.length-1e3),i[M]=r;let c=n.console?.debug;o.payload.diagnosticOnly!==!0&&typeof c=="function"&&c.call(n.console,"[designer-trace]",o)}function Q(t){return q(t,0)}function q(t,e){if(e>4)return"[truncated]";if(Array.isArray(t))return t.map(o=>q(o,e+1));if(!t||typeof t!="object")return t;let n={};for(let[o,i]of Object.entries(t)){if(J.test(o)){n[o]="[redacted]";continue}n[o]=q(i,e+1)}return n}var v=class{constructor(e){this.options=e,this.socket=null,this.connectPromise=null,this.connectAttempt=null,this.connectionState="idle",this.intentionalClose=!1,this.sequence=0,this.pending=new Map,this.listeners=new Set,this.connectionStatusListeners=new Set}connect(){return this.socket?.readyState===R&&this.connectionState==="open"?(this.setConnectionState("open"),Promise.resolve()):this.connectPromise?this.connectPromise:(this.intentionalClose=!1,this.setConnectionState("connecting"),this.connectPromise=new Promise((e,n)=>{let o;try{o=this.createSocket(this.options.url)}catch(s){let a=I(s,"WebSocket failed");this.socket=null,this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("failed",a.message),n(a);return}let i={socket:o,settled:!1,reject:n};this.connectAttempt=i,this.socket=o;let r=()=>{this.socket!==o||this.connectAttempt!==i||(i.settled=!0,this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("open"),e())},c=(s,a)=>{this.cleanupSocket(o,s,a)};o.onopen=()=>{if(!(this.socket!==o||this.connectAttempt!==i)){if(this.options.authKey){try{o.send(f.stringifyMessage({type:"auth",payload:{key:this.options.authKey}}))}catch(s){c(I(s,"WebSocket auth failed"),"failed")}return}r()}},o.onerror=s=>{if(this.socket!==o)return;let a=I(s,"WebSocket failed");c(a,"failed")},o.onmessage=s=>{if(this.socket===o){if(this.options.authKey&&this.connectAttempt===i&&!i.settled){this.handleAuthHandshakeMessage(s.data,o,i,()=>{r()});return}this.handleMessage(s.data)}},o.onclose=s=>{if(this.socket!==o)return;let a=this.intentionalClose,u=new Error(X(s,{intentional:a}));c(u,a?"closed":"failed")}}),this.connectPromise)}disconnect(){this.intentionalClose=!0,this.setConnectionState("closing");let e=this.socket;if(!e){this.rejectAll(new Error("WebSocket disconnected")),this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("closed");return}this.cleanupSocket(e,new Error("WebSocket disconnected"),"closed")}getConnectionState(){return this.connectionState}subscribeConnectionStatus(e){return this.connectionStatusListeners.add(e),e({state:this.connectionState}),()=>{this.connectionStatusListeners.delete(e)}}getDocument(e){let n=this.selector(e);return this.request("get-document",{composition:n})}async initializeSession(e){let n=this.selector(e.composition);return await this.request("initialize-session",{composition:n,...e.mutationContext?{mutationContext:e.mutationContext}:{}})}getHistoryStatus(e){return this.request("get-history-status",{composition:this.selector(e)})}getStyleContext(e){let n=this.selector(e);return this.request("get-style-context",{composition:n})}getObjectEditContext(e,n,o){let i=this.selector(n);return this.request("get-object-edit-context",{key:e,composition:i,...o?{selectionRef:o}:{}})}getClassNameSource(e,n){let o=this.selector(n);return this.request("get-class-name-source",{key:e,composition:o})}getSourceTree(e){return this.request("get-source-tree",{composition:this.selector(e)})}getSourceFile(e){return this.request("get-source-file",{path:e.path,composition:this.selector(e.composition)})}resolveSourceLocation(e){return this.request("resolve-source-location",{key:e.key,composition:this.selector(e.composition)})}execute(e,n){let o=S(e),i={...e,composition:m(o)};return this.request("execute",{request:i})}getOperationResult(e){return this.request("get-operation-result",{request:e,composition:m(S(e))})}undo(e,n){return this.request("undo",{composition:this.selector(e)})}redo(e,n){return this.request("redo",{composition:this.selector(e)})}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}selector(e){return m(e)}request(e,n){let o=this.socket;if(!o||o.readyState!==R||this.connectionState!=="open")return Promise.reject(new Error(`WebSocket is not open (state: ${this.connectionState}, readyState: ${o?.readyState??"none"})`));let i=`${e}-${this.sequence+=1}`,r=oe(i,e,n);return g("bridge.request",{id:i,kind:e,routeId:typeof n.routeId=="string"?n.routeId:null,key:typeof n.key=="string"?n.key:null,operationKind:N(n.request,"kind"),documentVersion:N(n.request,"documentVersion")}),new Promise((c,s)=>{let a=this.resolveRequestTimeoutMs(e),u=a>0?setTimeout(()=>{let d=this.pending.get(i);d&&(this.pending.delete(i),g("bridge.response",{id:i,kind:e,ok:!1,timedOut:!0}),d.reject(new Error(`Bridge request timed out after ${a}ms: ${e}`)))},a):null;u?.unref?.(),this.pending.set(i,{kind:e,timeout:u,resolve:d=>c(d),reject:s});try{o.send(f.stringifyMessage(r))}catch(d){let p=I(d,"WebSocket send failed");this.clearPendingRequest(i),s(p),this.cleanupSocket(o,p,"failed")}})}handleAuthHandshakeMessage(e,n,o,i){try{if(f.parseMessage(e).type==="auth-ok"){i();return}throw new Error("Bridge authentication failed")}catch(r){let c=I(r,"Bridge authentication failed");this.connectAttempt===o&&this.cleanupSocket(n,c,"failed")}}handleMessage(e){let n=f.parseMessage(e);if(n.type==="event"){for(let i of this.listeners)i(n.payload.event);return}if(n.type!=="response"&&n.type!=="error")return;let o=this.pending.get(n.payload.id);if(o){if(this.clearPendingRequest(n.payload.id),n.type==="error"){g("bridge.response",{id:n.payload.id,kind:o.kind,ok:!1});let i=new Error(n.payload.message);n.payload.code&&(i.code=n.payload.code),o.reject(i);return}g("bridge.response",{id:n.payload.id,kind:o.kind,ok:!0,resultVersion:ee(n.payload.result)}),o.resolve(o.kind==="get-document"?{...n.payload.result,data:j(n.payload.result.data,n.payload.result.composition)}:o.kind==="initialize-session"?te(n.payload.result):n.payload.result)}}rejectAll(e){for(let[n,o]of[...this.pending])this.clearPendingRequest(n),o.reject(e)}clearPendingRequest(e){let n=this.pending.get(e);n&&(n.timeout&&clearTimeout(n.timeout),this.pending.delete(e))}resolveRequestTimeoutMs(e){let n=this.options.requestTimeoutMsByKind?.[e];return typeof n=="number"?Math.max(0,n):typeof this.options.requestTimeoutMs=="number"?Math.max(0,this.options.requestTimeoutMs):Z[e]??Y}cleanupSocket(e,n,o){if(this.socket===e&&(this.socket=null),this.connectAttempt?.socket===e){let i=this.connectAttempt;this.connectAttempt=null,i.settled||(i.settled=!0,i.reject(n))}this.connectPromise=null,this.rejectAll(n),this.setConnectionState(o,n.message),e.onopen=null,e.onmessage=null,e.onerror=null,e.onclose=null;try{(e.readyState===H||e.readyState===R)&&e.close()}catch{}}setConnectionState(e,n){if(this.connectionState===e&&!n)return;this.connectionState=e;let o={state:e,...n?{message:n}:{}};this.options.onConnectionStatus?.(o);for(let i of this.connectionStatusListeners)i(o)}createSocket(e){if(this.options.createSocket)return this.options.createSocket(e);if(typeof WebSocket>"u")throw new Error("Site edit preview bridge requires WebSocket");return new WebSocket(e)}},H=0,R=1,Y=15e3,Z={"get-source-tree":1e4,"get-source-file":1e4,"resolve-source-location":1e4,execute:6e4,"get-operation-result":6e4,undo:2e4,redo:2e4};function I(t,e){return t instanceof Error?t:new Error(e)}function X(t,e){return e.intentional?"WebSocket disconnected":t?.code===1008?t.reason?`Bridge session authentication failed: ${t.reason}`:"Bridge session authentication failed":t?.reason?`WebSocket closed: ${t.reason}`:typeof t?.code=="number"&&t.code!==1005?`WebSocket closed with code ${t.code}`:"WebSocket closed"}function N(t,e){if(!t||typeof t!="object")return null;let o=t[e];return typeof o=="string"||typeof o=="number"?o:null}function ee(t){if(!t||typeof t!="object")return null;let e=t,n=e.data?.resultVersion??e.data?.version??null;return typeof n=="number"?n:null}function pe(t){let e="composition"in t?m(t.composition):m({routeId:t.routeId});return new T({composition:e,initialDocument:t.initialDocument,client:t.client})}var T=class{constructor(e){this.params=e,this.serverConfirmedComposition=null,this.refreshInFlight=null,this.objectGeneration=0,this.styleGeneration=0,this.editContextCache=new Map,this.editContextInFlight=new Map,this.classNameSourceCache=new Map,this.classNameSourceInFlight=new Map,this.styleContextCache=new Map,this.styleContextInFlight=new Map,this.snapshotIdentityStale=!1,this.composition=m(e.composition);let n=e.initialDocument.composition.effectiveTemplateId;if(n!==void 0&&(this.composition.templateId===void 0||this.composition.templateId!==n))throw new Error(`Site edit bridge requires a matching templateId for initial document composition ${JSON.stringify(e.initialDocument.composition)}`);this.currentDocument=e.initialDocument,this.observedDocumentVersion=e.initialDocument.version,this.sessionInitialized=!K(e.initialDocument),this.activeCompositionContext=this.sessionInitialized?{...e.initialDocument.composition}:null}async getDocument(e){this.requireBoundComposition(e);let n=K(this.currentDocument)||this.snapshotIdentityStale;return(n||this.currentDocument.version<this.observedDocumentVersion)&&(this.currentDocument=await this.refreshSnapshot(n?void 0:this.observedDocumentVersion,"get-document"),this.snapshotIdentityStale=!1,this.sessionInitialized=!0),{ok:!0,data:this.currentDocument}}async initializeSession(e){let n=this.requireBoundComposition(e.composition);if(!this.params.client.initializeSession)throw new Error("Site edit bridge client does not support initialize-session");let o=await this.params.client.initializeSession({...e,composition:n});return this.currentDocument=o.data.document,this.observedDocumentVersion=o.data.document.version,this.activeCompositionContext={...o.composition},this.rememberServerConfirmedComposition(o.composition),this.sessionInitialized=!0,this.clearObjectCaches(),o}async getHistoryStatus(e){return this.params.client.getHistoryStatus(this.requireBoundComposition(e))}async getStyleContext(e){let n=this.requireBoundComposition(e),o=C(n),i=this.styleContextCache.get(o);if(i?.generation===this.styleGeneration)return{ok:!0,data:i.value};let r=this.styleContextInFlight.get(o);if(r?.generation===this.styleGeneration)return r.request;let c=this.styleGeneration,s=this.params.client.getStyleContext(n).then(a=>(this.styleGeneration===c&&this.styleContextCache.set(o,{generation:c,value:a.data}),a)).finally(()=>{this.styleContextInFlight.get(o)?.request===s&&this.styleContextInFlight.delete(o)});return this.styleContextInFlight.set(o,{generation:c,request:s}),s}async ensureDocumentVersion(e,n){this.requireBoundComposition(e);let o=await this.refreshSnapshot(n,"ensure-version");if(o.version<n)throw new Error(`Document refresh returned version ${o.version}, expected at least ${n}`);return{ok:!0,data:o}}async acknowledgeDocumentVersion(e,n){if(this.requireBoundComposition(e),n>this.currentDocument.version){if(this.currentDocument.pageComposition.status==="supported"){let o=await this.refreshSnapshot(n,"ensure-version");if(o.version<n)throw new Error(`Document refresh returned version ${o.version}, expected at least ${n}`);return{ok:!0,data:o}}this.currentDocument={...this.currentDocument,version:n},this.clearObjectCaches({preserveStyleContext:!0})}return this.observedDocumentVersion=Math.max(this.observedDocumentVersion,n),{ok:!0,data:this.currentDocument}}async getObjectEditContext(e,n,o){let i=this.requireBoundComposition(n),r=this.objectCacheKey(e,i,o),c=this.editContextCache.get(r);if(c)return{ok:!0,data:c};let s=this.editContextInFlight.get(r);if(s)return s;let a=this.objectGeneration,u=this.params.client.getObjectEditContext(e,i,...o?[o]:[]).then(d=>(this.objectGeneration===a&&this.editContextCache.set(r,d.data),d)).finally(()=>{this.editContextInFlight.get(r)===u&&this.editContextInFlight.delete(r)});return this.editContextInFlight.set(r,u),u}async getClassNameSource(e,n){let o=this.requireBoundComposition(n),i=this.objectCacheKey(e,o),r=this.classNameSourceCache.get(i);if(r)return r;let c=this.classNameSourceInFlight.get(i);if(c)return c;let s=this.objectGeneration,a=this.params.client.getClassNameSource(e,o).then(u=>(this.objectGeneration===s&&this.classNameSourceCache.set(i,u),u)).finally(()=>{this.classNameSourceInFlight.get(i)===a&&this.classNameSourceInFlight.delete(i)});return this.classNameSourceInFlight.set(i,a),a}async getSourceTree(e){return this.params.client.getSourceTree(this.requireBoundComposition(e))}async getSourceFile(e){return this.params.client.getSourceFile({path:e.path,composition:this.requireBoundComposition(e.composition)})}async resolveSourceLocation(e){return this.params.client.resolveSourceLocation({key:e.key,composition:this.requireBoundComposition(e.composition)})}async execute(e,n){let o=S(e);o&&this.composition.templateId!==void 0&&this.requireBoundComposition(o);let i={...e,composition:o??this.composition};return this.params.client.execute(i)}async getOperationResult(e){return this.requireBoundComposition(e.composition),this.params.client.getOperationResult?this.params.client.getOperationResult(e):null}async undo(e,n){return this.params.client.undo(this.requireBoundComposition(e))}async redo(e,n){return this.params.client.redo(this.requireBoundComposition(e))}subscribe(e){return this.params.client.subscribe(n=>{if(n.type==="site-edit.snapshot.changed"&&this.sessionInitialized&&this.isActiveComposition(n.composition)){this.snapshotIdentityStale=!0,this.clearObjectCaches(),e(n);return}if(n.type==="site-edit.document.changed"&&this.sessionInitialized&&this.isActiveComposition(n.composition)){this.observedDocumentVersion=Math.max(this.observedDocumentVersion,n.version),n.version>this.currentDocument.version&&this.clearObjectCaches(),e(n);return}e(n)})}async refreshSnapshot(e,n="ensure-version"){if(typeof e=="number"&&this.currentDocument.version>=e)return g("bridge.document-refresh",{composition:this.composition,routeId:this.composition.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!0,skipped:!0,source:n}),this.currentDocument;let o=C(this.composition),i=this.refreshInFlight;if(i?.compositionKey===o){g("bridge.document-refresh",{composition:this.composition,routeId:this.composition.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!0,source:n});let c=await i.request;if(typeof e!="number"||c.version>=e||this.currentDocument.version>=e)return this.currentDocument}g("bridge.document-refresh",{composition:this.composition,routeId:this.composition.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!1,source:n});let r=this.params.client.getDocument(this.composition).then(c=>(this.currentDocument=c.data,this.activeCompositionContext={...c.data.composition},this.rememberServerConfirmedComposition(c.data.composition),this.observedDocumentVersion=Math.max(this.observedDocumentVersion,c.data.version),this.clearObjectCaches(),this.currentDocument));this.refreshInFlight={compositionKey:o,request:r};try{return await r}finally{this.refreshInFlight?.request===r&&(this.refreshInFlight=null)}}clearObjectCaches(e){this.objectGeneration+=1,this.editContextCache.clear(),this.editContextInFlight.clear(),this.classNameSourceCache.clear(),this.classNameSourceInFlight.clear(),!e?.preserveStyleContext&&(this.styleGeneration+=1,this.styleContextCache.clear(),this.styleContextInFlight.clear())}objectCacheKey(e,n,o){return`${C(n)}\0${e}\0${ne(o)}`}rememberServerConfirmedComposition(e){!e||typeof e.routeId!="string"||(this.serverConfirmedComposition={...e})}isAcceptedComposition(e){if(C(e)===C(this.composition))return!0;let n=this.serverConfirmedComposition;return n!==null&&x(n)===x(B(e))}requireBoundComposition(e){let n=e===void 0?this.composition:m(e);if(!this.isAcceptedComposition(n))throw new Error(`Site edit bridge is bound to composition ${JSON.stringify(this.composition)}, received ${JSON.stringify(n)}`);return m(n)}isActiveComposition(e){let n=this.activeCompositionContext??B(this.composition);return x(e)===x(n)}};function K(t){return t.version===0&&t.entries.length===0}function te(t){let e=t;return{...e,data:{...e.data,document:j(e.data.document,e.composition)}}}function j(t,e){let n=t,o=_(n.routeId?n:{...n,routeId:e?.routeId??n.composition?.routeId});return e?{...o,composition:e}:o}function m(t){if(t&&typeof t=="object"&&typeof t.routeId=="string"){let e="effectiveTemplateId"in t?t.effectiveTemplateId:"templateId"in t?t.templateId:void 0;return{routeId:t.routeId,...typeof e=="string"?{templateId:e}:{}}}throw new Error("SITE_EDIT_COMPOSITION_SESSION_REQUIRED: a composition selector is required")}function C(t){return JSON.stringify([t.routeId,t.templateId??null])}function ne(t){return t?JSON.stringify([t.key,t.instanceIndex??null,t.instanceCount??null]):""}function x(t){return JSON.stringify([t.routeId,t.effectiveTemplateId??null])}function B(t){return{routeId:t.routeId,...t.templateId?{effectiveTemplateId:t.templateId}:{}}}function S(t){if(t&&typeof t=="object"){let e=t;if(e.composition&&typeof e.composition=="object"){let n=e.composition;if(typeof n.routeId=="string")return{routeId:n.routeId,...typeof n.templateId=="string"?{templateId:n.templateId}:{}}}}}function oe(t,e,n){let o=m(e==="execute"?S(n.request):n.composition),i={id:t,kind:e,composition:o};switch(e){case"initialize-session":case"get-document":case"get-history-status":case"get-style-context":case"get-source-tree":case"undo":case"redo":return{type:"request",payload:{...i,...n.mutationContext?{mutationContext:n.mutationContext}:{}}};case"get-object-edit-context":return{type:"request",payload:{...i,key:String(n.key),...n.selectionRef?{selectionRef:n.selectionRef}:{}}};case"get-class-name-source":case"resolve-source-location":return{type:"request",payload:{...i,key:String(n.key)}};case"get-source-file":return{type:"request",payload:{...i,path:String(n.path)}};case"execute":case"get-operation-result":return{type:"request",payload:{...i,request:{...n.request,composition:S(n.request)??o}}}}}export{v as SiteEditWebSocketClient,pe as createSiteEditBridge};
1
+ function A(n){if(!P(n)){if(n.encoding==="segment-trie-v1")throw new Error("Invalid compact render document");return n}let e=new Map,t=n.pageComposition.status==="supported"?n.pageComposition.placementUnitKeys:[],i=new Map(n.dynamicDataSummaries??[]),o=new Map(n.pageCompositionActionScopes??[]),r=a=>{let s=e.get(a);if(s)return s;let c=n.nodes[a];if(!c)throw new Error(`Missing compact document node ${a}`);let[u,d]=c,p=n.tokens[d];if(typeof p!="string")throw new Error(`Missing compact document token ${d}`);let I=`${u===null?"":r(u)}${p}`;return e.set(a,I),I};return{siteId:n.siteId,snapshotId:n.snapshotId,composition:{...n.composition},routeId:n.composition.routeId,version:n.version,pageComposition:n.pageComposition,entries:n.entries.map((a,s)=>{let[c,u,d]=a,p=d===void 0?void 0:t[d];if(d!==void 0&&!p)throw new Error(`Missing page composition unit ${d}`);let I=n.entryParentNodeIds?.[s],x=o.get(s),R=x?.kind==="source-siblings"?{kind:"source-siblings",siblingKeys:x.siblingEntryIndexes.map(D=>{let M=n.entries[D];if(!M)throw new Error(`Missing source sibling entry ${D}`);return r(M[0])})}:x;return{key:r(c),...n.entryParentNodeIds?{parentKey:I===null?null:r(I)}:{},...p?{pageCompositionUnitKey:p}:{},...R?{pageCompositionActionScope:R}:{},operationSummary:j(u),...i.get(s)?{dynamicDataSummary:i.get(s)}:{}}})}}function j(n){return{canUpdateText:(n&1)!==0,canInsertChild:(n&2)!==0,canMove:(n&4)!==0,canRemove:(n&8)!==0}}function P(n){if(typeof n!="object"||n===null)return!1;let e=n;if(e.encoding!=="segment-trie-v1"||typeof e.siteId!="string"||typeof e.snapshotId!="string"||!$(e.composition)||typeof e.routeId!="string"||e.routeId!==e.composition.routeId||!Number.isInteger(e.version)||e.version<0||!b(e.pageComposition)||!Array.isArray(e.tokens)||!e.tokens.every(s=>typeof s=="string")||!Array.isArray(e.nodes)||!Array.isArray(e.entries))return!1;let t=e.nodes;if(!t.every((s,c)=>Array.isArray(s)&&s.length===2&&(s[0]===null||m(s[0])&&s[0]<c)&&m(s[1])&&s[1]<e.tokens.length)||!e.entries.every(s=>Array.isArray(s)&&(s.length===2||s.length===3)&&m(s[0])&&s[0]<t.length&&m(s[1])&&s[1]<=15&&(s.length===2||m(s[2]))))return!1;let i=e.entryParentNodeIds;if(i!==void 0&&(!Array.isArray(i)||i.length!==e.entries.length||!i.every(s=>s===null||m(s)&&s<t.length)))return!1;let o=e.dynamicDataSummaries,r=e.pageCompositionActionScopes;return(r===void 0||Array.isArray(r)&&r.every(s=>{if(!Array.isArray(s)||s.length!==2||!m(s[0])||s[0]>=e.entries.length||!b(s[1])||!["route-unit","source-siblings","unavailable"].includes(String(s[1].kind)))return!1;let c=s[1];return c.kind!=="source-siblings"||Array.isArray(c.siblingEntryIndexes)&&c.siblingEntryIndexes.length>=2&&c.siblingEntryIndexes.every(u=>m(u)&&u<e.entries.length)&&c.siblingEntryIndexes.includes(s[0])}))&&(o===void 0||Array.isArray(o)&&o.every(s=>Array.isArray(s)&&s.length===2&&m(s[0])&&s[0]<e.entries.length&&b(s[1])))}function b(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function $(n){return!b(n)||typeof n.routeId!="string"?!1:n.effectiveTemplateId===void 0||typeof n.effectiveTemplateId=="string"}function m(n){return typeof n=="number"&&Number.isInteger(n)&&n>=0}var f=class n{constructor(){}static parseMessage(e,t={}){let i=JSON.parse(e);if(!n.isMessage(i,t))throw new Error("Invalid site edit bridge message");return i}static stringifyMessage(e){return JSON.stringify(e)}static extractRequestInfo(e){if(h(e)&&h(e.payload)){let t=typeof e.payload.id=="string"?e.payload.id:"unknown",i=this.isRequestKind(e.payload.kind)?e.payload.kind:"get-document";return{id:t,kind:i}}return{id:"unknown",kind:"get-document"}}static isRequestKind(e){return e==="initialize-session"||e==="get-document"||e==="get-history-status"||e==="get-style-context"||e==="get-object-edit-context"||e==="get-class-name-source"||e==="get-source-tree"||e==="get-source-file"||e==="resolve-source-location"||e==="execute"||e==="get-operation-result"||e==="undo"||e==="redo"}static isMessage(e,t={}){return!h(e)||typeof e.type!="string"?!1:e.type==="request"?W(e.payload,t):e.type==="response"||e.type==="error"?h(e.payload)&&typeof e.payload.id=="string":e.type==="event"?h(e.payload)&&h(e.payload.event):e.type==="auth"?h(e.payload)&&typeof e.payload.key=="string":e.type==="ping"||e.type==="pong"?h(e.payload)&&typeof e.payload.id=="string":e.type==="auth-ok"&&h(e.payload)&&e.payload.ok===!0}};function W(n,e){if(!h(n)||typeof n.id!="string"||!f.isRequestKind(n.kind))return!1;let t=T(n.composition);switch(n.kind){case"initialize-session":return t&&(n.mutationContext===void 0||w(n.mutationContext));case"get-document":case"get-style-context":return t;case"get-object-edit-context":return typeof n.key=="string"&&t&&L(n.selectionRef);case"get-class-name-source":case"resolve-source-location":return typeof n.key=="string"&&t;case"get-history-status":return t;case"get-source-tree":return t;case"get-source-file":return typeof n.path=="string"&&t;case"execute":case"get-operation-result":return t&&G(n.request)&&(n.request.mutationContext===void 0||w(n.request.mutationContext))&&(e.validateOperationRequest?.(n.request)??!0);case"undo":case"redo":return t&&n.mutationContext===void 0||t&&w(n.mutationContext)}}function L(n){if(n===void 0)return!0;if(!h(n)||typeof n.key!="string"||n.key.length===0)return!1;let e=n.instanceIndex,t=n.instanceCount;return e!==void 0&&(typeof e!="number"||!Number.isInteger(e)||e<0)||t!==void 0&&(typeof t!="number"||!Number.isInteger(t)||t<=0)||typeof e=="number"&&typeof t=="number"&&e>=t?!1:Object.keys(n).every(i=>i==="key"||i==="instanceIndex"||i==="instanceCount")}function G(n){return h(n)&&typeof n.id=="string"&&typeof n.siteId=="string"&&typeof n.baseSnapshotId=="string"&&T(n.composition)&&typeof n.kind=="string"&&h(n.target)&&(n.target.kind!=="route"||T(n.target.composition))&&typeof n.documentVersion=="number"&&Number.isInteger(n.documentVersion)&&n.documentVersion>=0&&h(n.params)}function T(n){return h(n)&&typeof n.routeId=="string"&&n.routeId.length>0&&(n.templateId===void 0||typeof n.templateId=="string"&&n.templateId.length>0)&&Object.keys(n).every(e=>e==="routeId"||e==="templateId")}function h(n){return typeof n=="object"&&n!==null}function w(n){return h(n)&&typeof n.editingSessionId=="string"&&n.editingSessionId.length>0&&typeof n.lockId=="string"&&n.lockId.length>0&&typeof n.ownerId=="string"&&n.ownerId.length>0&&typeof n.leaseAssertion=="string"&&n.leaseAssertion.length>0}var z="vcp:designer:trace",V="designer_trace",O="__VCP_DESIGNER_TRACE__";var J=/(authorization|cookie|token|secret|providerTrace|headers?|sourceCode|rawSource|fullSource)/i;function H(n=globalThis){let e=n;try{if(e.localStorage?.getItem(z)==="1")return!0}catch{}try{let t=e.location?.search??"";return new URLSearchParams(t).get(V)==="1"}catch{return!1}}function l(n,e={},t=globalThis){if(!H(t))return;let i={type:n,timestamp:Date.now(),payload:Q(e)},o=t,r=o[O]??[];r.push(i),r.length>1e3&&r.splice(0,r.length-1e3),o[O]=r;let a=t.console?.debug;i.payload.diagnosticOnly!==!0&&typeof a=="function"&&a.call(t.console,"[designer-trace]",i)}function Q(n){return k(n,0)}function k(n,e){if(e>4)return"[truncated]";if(Array.isArray(n))return n.map(i=>k(i,e+1));if(!n||typeof n!="object")return n;let t={};for(let[i,o]of Object.entries(n)){if(J.test(i)){t[i]="[redacted]";continue}t[i]=k(o,e+1)}return t}var v=class{constructor(e){this.options=e,this.socket=null,this.connectPromise=null,this.connectAttempt=null,this.connectionState="idle",this.intentionalClose=!1,this.sequence=0,this.pending=new Map,this.listeners=new Set,this.heartbeatState=null,this.connectionStatusListeners=new Set}connect(){return this.socket?.readyState===_&&this.connectionState==="open"?(this.setConnectionState("open"),Promise.resolve()):this.connectPromise?this.connectPromise:(this.intentionalClose=!1,this.setConnectionState("connecting"),this.connectPromise=new Promise((e,t)=>{let i;try{i=this.createSocket(this.options.url)}catch(c){let u=y(c,"WebSocket failed");this.socket=null,this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("failed",u.message),t(u);return}let o={socket:i,settled:!1,reject:t};this.connectAttempt=o,this.socket=i;let r=Math.max(0,this.options.connectTimeoutMs??Z);r>0&&(o.deadline=setTimeout(()=>{this.connectAttempt!==o||o.settled||(l("bridge.connect-timeout",{connectTimeoutMs:r}),this.cleanupSocket(i,N(ee,`Bridge connection did not complete within ${r}ms`),"failed"))},r));let a=()=>{this.socket!==i||this.connectAttempt!==o||(o.settled=!0,clearTimeout(o.deadline),this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("open"),this.startHeartbeat(i),e())},s=(c,u)=>{this.cleanupSocket(i,c,u)};i.onopen=()=>{if(!(this.socket!==i||this.connectAttempt!==o)){if(this.options.authKey){try{i.send(f.stringifyMessage({type:"auth",payload:{key:this.options.authKey}}))}catch(c){s(y(c,"WebSocket auth failed"),"failed")}return}a()}},i.onerror=c=>{if(this.socket!==i)return;let u=y(c,"WebSocket failed");s(u,"failed")},i.onmessage=c=>{if(this.socket===i){if(this.noteInboundMessage(i),this.options.authKey&&this.connectAttempt===o&&!o.settled){this.handleAuthHandshakeMessage(c.data,i,o,()=>{a()});return}this.handleMessage(c.data)}},i.onclose=c=>{if(this.socket!==i)return;let u=this.intentionalClose,d=new Error(se(c,{intentional:u}));s(d,u?"closed":"failed")}}),this.connectPromise)}disconnect(){this.intentionalClose=!0,this.setConnectionState("closing");let e=this.socket;if(!e){this.rejectAll(new Error("WebSocket disconnected")),this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("closed");return}this.cleanupSocket(e,new Error("WebSocket disconnected"),"closed")}getConnectionState(){return this.connectionState}subscribeConnectionStatus(e){return this.connectionStatusListeners.add(e),e({state:this.connectionState}),()=>{this.connectionStatusListeners.delete(e)}}getDocument(e){let t=this.selector(e);return this.request("get-document",{composition:t})}async initializeSession(e){let t=this.selector(e.composition);return await this.request("initialize-session",{composition:t,...e.mutationContext?{mutationContext:e.mutationContext}:{}})}getHistoryStatus(e){return this.request("get-history-status",{composition:this.selector(e)})}getStyleContext(e){let t=this.selector(e);return this.request("get-style-context",{composition:t})}getObjectEditContext(e,t,i){let o=this.selector(t);return this.request("get-object-edit-context",{key:e,composition:o,...i?{selectionRef:i}:{}})}getClassNameSource(e,t){let i=this.selector(t);return this.request("get-class-name-source",{key:e,composition:i})}getSourceTree(e){return this.request("get-source-tree",{composition:this.selector(e)})}getSourceFile(e){return this.request("get-source-file",{path:e.path,composition:this.selector(e.composition)})}resolveSourceLocation(e){return this.request("resolve-source-location",{key:e.key,composition:this.selector(e.composition)})}execute(e,t){let i=S(e),o={...e,composition:g(i)};return this.request("execute",{request:o})}getOperationResult(e){return this.request("get-operation-result",{request:e,composition:g(S(e))})}undo(e,t){return this.request("undo",{composition:this.selector(e)})}redo(e,t){return this.request("redo",{composition:this.selector(e)})}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}selector(e){return g(e)}request(e,t){let i=this.socket;if(!i||i.readyState!==_||this.connectionState!=="open")return Promise.reject(new Error(`WebSocket is not open (state: ${this.connectionState}, readyState: ${i?.readyState??"none"})`));let o=`${e}-${this.sequence+=1}`,r=ue(o,e,t);return l("bridge.request",{id:o,kind:e,routeId:typeof t.routeId=="string"?t.routeId:null,key:typeof t.key=="string"?t.key:null,operationKind:K(t.request,"kind"),documentVersion:K(t.request,"documentVersion")}),new Promise((a,s)=>{let c=this.resolveRequestTimeoutMs(e),u=c>0?setTimeout(()=>{let d=this.pending.get(o);d&&(this.pending.delete(o),l("bridge.response",{id:o,kind:e,ok:!1,timedOut:!0}),d.reject(new Error(`Bridge request timed out after ${c}ms: ${e}`)))},c):null;u?.unref?.(),this.pending.set(o,{kind:e,timeout:u,resolve:d=>a(d),reject:s});try{i.send(f.stringifyMessage(r))}catch(d){let p=y(d,"WebSocket send failed");this.clearPendingRequest(o),s(p),this.cleanupSocket(i,p,"failed")}})}handleAuthHandshakeMessage(e,t,i,o){try{if(f.parseMessage(e).type==="auth-ok"){o();return}throw new Error("Bridge authentication failed")}catch(r){let a=y(r,"Bridge authentication failed");this.connectAttempt===i&&this.cleanupSocket(t,a,"failed")}}handleMessage(e){let t=f.parseMessage(e);if(t.type==="event"){for(let o of this.listeners)o(t.payload.event);return}if(t.type!=="response"&&t.type!=="error")return;let i=this.pending.get(t.payload.id);if(i){if(this.clearPendingRequest(t.payload.id),t.type==="error"){l("bridge.response",{id:t.payload.id,kind:i.kind,ok:!1});let o=new Error(t.payload.message);t.payload.code&&(o.code=t.payload.code),i.reject(o);return}l("bridge.response",{id:t.payload.id,kind:i.kind,ok:!0,resultVersion:re(t.payload.result)}),i.resolve(i.kind==="get-document"?{...t.payload.result,data:U(t.payload.result.data,t.payload.result.composition)}:i.kind==="initialize-session"?ce(t.payload.result):t.payload.result)}}rejectAll(e){for(let[t,i]of[...this.pending])this.clearPendingRequest(t),i.reject(e)}clearPendingRequest(e){let t=this.pending.get(e);t&&(t.timeout&&clearTimeout(t.timeout),this.pending.delete(e))}resolveRequestTimeoutMs(e){let t=this.options.requestTimeoutMsByKind?.[e];return typeof t=="number"?Math.max(0,t):typeof this.options.requestTimeoutMs=="number"?Math.max(0,this.options.requestTimeoutMs):oe[e]??ie}cleanupSocket(e,t,i){if(this.socket===e&&(this.socket=null),this.stopHeartbeat(e),this.connectAttempt?.socket===e){let o=this.connectAttempt;this.connectAttempt=null,clearTimeout(o.deadline),o.settled||(o.settled=!0,o.reject(t))}this.connectPromise=null,this.rejectAll(t),this.setConnectionState(i,t.message),e.onopen=null,e.onmessage=null,e.onerror=null,e.onclose=null;try{(e.readyState===Y||e.readyState===_)&&e.close()}catch{}}startHeartbeat(e){let t=this.options.heartbeat===void 0?X:this.options.heartbeat;if(!t||t.intervalMs<=0||t.timeoutMs<=0)return;this.stopHeartbeat(this.heartbeatState?.socket??e);let i=this.options.visibility===void 0?ne():this.options.visibility,o=()=>i?.visibilityState!=="hidden",r=()=>{this.heartbeatState?.socket===e&&this.heartbeatState.deadline&&(clearTimeout(this.heartbeatState.deadline),this.heartbeatState.deadline=null)},a=()=>{let c=this.heartbeatState;if(!(c?.socket!==e||this.socket!==e||this.connectionState!=="open"||c.deadline||!o())){try{e.send(f.stringifyMessage({type:"ping",payload:{id:`ping-${this.sequence+=1}`}}))}catch(u){this.cleanupSocket(e,y(u,"WebSocket heartbeat send failed"),"failed");return}c.deadline=setTimeout(()=>{this.heartbeatState?.socket===e&&(this.heartbeatState.deadline=null,o()&&(l("bridge.heartbeat-timeout",{timeoutMs:t.timeoutMs}),this.cleanupSocket(e,N(te,`Bridge heartbeat was not answered within ${t.timeoutMs}ms`),"failed")))},t.timeoutMs)}},s=()=>{o()?a():r()};i?.addEventListener("visibilitychange",s),this.heartbeatState={socket:e,interval:setInterval(a,t.intervalMs),deadline:null,unbindVisibility:()=>i?.removeEventListener("visibilitychange",s)}}stopHeartbeat(e){let t=this.heartbeatState;!t||t.socket!==e||(clearInterval(t.interval),t.deadline&&clearTimeout(t.deadline),t.unbindVisibility(),this.heartbeatState=null)}noteInboundMessage(e){let t=this.heartbeatState;t?.socket===e&&t.deadline&&(clearTimeout(t.deadline),t.deadline=null)}setConnectionState(e,t){if(this.connectionState===e&&!t)return;this.connectionState=e;let i={state:e,...t?{message:t}:{}};this.options.onConnectionStatus?.(i);for(let o of this.connectionStatusListeners)o(i)}createSocket(e){if(this.options.createSocket)return this.options.createSocket(e);if(typeof WebSocket>"u")throw new Error("Site edit preview bridge requires WebSocket");return new WebSocket(e)}},Y=0,_=1,Z=1e4,X={intervalMs:2e4,timeoutMs:1e4},ee="SITE_EDIT_BRIDGE_CONNECT_TIMEOUT",te="SITE_EDIT_BRIDGE_HEARTBEAT_TIMEOUT";function ne(){let n=globalThis.document;return n&&typeof n.visibilityState=="string"&&typeof n.addEventListener=="function"&&typeof n.removeEventListener=="function"?n:null}function N(n,e){let t=new Error(`${n}: ${e}`);return t.code=n,t}var ie=15e3,oe={"get-source-tree":1e4,"get-source-file":1e4,"resolve-source-location":1e4,execute:6e4,"get-operation-result":6e4,undo:2e4,redo:2e4};function y(n,e){return n instanceof Error?n:new Error(e)}function se(n,e){return e.intentional?"WebSocket disconnected":n?.code===1008?n.reason?`Bridge session authentication failed: ${n.reason}`:"Bridge session authentication failed":n?.reason?`WebSocket closed: ${n.reason}`:typeof n?.code=="number"&&n.code!==1005?`WebSocket closed with code ${n.code}`:"WebSocket closed"}function K(n,e){if(!n||typeof n!="object")return null;let i=n[e];return typeof i=="string"||typeof i=="number"?i:null}function re(n){if(!n||typeof n!="object")return null;let e=n,t=e.data?.resultVersion??e.data?.version??null;return typeof t=="number"?t:null}function Ie(n){let e="composition"in n?g(n.composition):g({routeId:n.routeId});return new q({composition:e,initialDocument:n.initialDocument,client:n.client})}var q=class{constructor(e){this.params=e,this.serverConfirmedComposition=null,this.refreshInFlight=null,this.objectGeneration=0,this.styleGeneration=0,this.editContextCache=new Map,this.editContextInFlight=new Map,this.classNameSourceCache=new Map,this.classNameSourceInFlight=new Map,this.styleContextCache=new Map,this.styleContextInFlight=new Map,this.snapshotIdentityStale=!1,this.composition=g(e.composition);let t=e.initialDocument.composition.effectiveTemplateId;if(t!==void 0&&(this.composition.templateId===void 0||this.composition.templateId!==t))throw new Error(`Site edit bridge requires a matching templateId for initial document composition ${JSON.stringify(e.initialDocument.composition)}`);this.currentDocument=e.initialDocument,this.observedDocumentVersion=e.initialDocument.version,this.sessionInitialized=!B(e.initialDocument),this.activeCompositionContext=this.sessionInitialized?{...e.initialDocument.composition}:null}async getDocument(e){this.requireBoundComposition(e);let t=B(this.currentDocument)||this.snapshotIdentityStale;return(t||this.currentDocument.version<this.observedDocumentVersion)&&(this.currentDocument=await this.refreshSnapshot(t?void 0:this.observedDocumentVersion,"get-document"),this.snapshotIdentityStale=!1,this.sessionInitialized=!0),{ok:!0,data:this.currentDocument}}async initializeSession(e){let t=this.requireBoundComposition(e.composition);if(!this.params.client.initializeSession)throw new Error("Site edit bridge client does not support initialize-session");let i=await this.params.client.initializeSession({...e,composition:t});return this.currentDocument=i.data.document,this.observedDocumentVersion=i.data.document.version,this.activeCompositionContext={...i.composition},this.rememberServerConfirmedComposition(i.composition),this.sessionInitialized=!0,this.clearObjectCaches(),i}async getHistoryStatus(e){return this.params.client.getHistoryStatus(this.requireBoundComposition(e))}async getStyleContext(e){let t=this.requireBoundComposition(e),i=C(t),o=this.styleContextCache.get(i);if(o?.generation===this.styleGeneration)return{ok:!0,data:o.value};let r=this.styleContextInFlight.get(i);if(r?.generation===this.styleGeneration)return r.request;let a=this.styleGeneration,s=this.params.client.getStyleContext(t).then(c=>(this.styleGeneration===a&&this.styleContextCache.set(i,{generation:a,value:c.data}),c)).finally(()=>{this.styleContextInFlight.get(i)?.request===s&&this.styleContextInFlight.delete(i)});return this.styleContextInFlight.set(i,{generation:a,request:s}),s}async ensureDocumentVersion(e,t){this.requireBoundComposition(e);let i=await this.refreshSnapshot(t,"ensure-version");if(i.version<t)throw new Error(`Document refresh returned version ${i.version}, expected at least ${t}`);return{ok:!0,data:i}}async acknowledgeDocumentVersion(e,t){if(this.requireBoundComposition(e),t>this.currentDocument.version){if(this.currentDocument.pageComposition.status==="supported"){let i=await this.refreshSnapshot(t,"ensure-version");if(i.version<t)throw new Error(`Document refresh returned version ${i.version}, expected at least ${t}`);return{ok:!0,data:i}}this.currentDocument={...this.currentDocument,version:t},this.clearObjectCaches({preserveStyleContext:!0})}return this.observedDocumentVersion=Math.max(this.observedDocumentVersion,t),{ok:!0,data:this.currentDocument}}async getObjectEditContext(e,t,i){let o=this.requireBoundComposition(t),r=this.objectCacheKey(e,o,i),a=this.editContextCache.get(r);if(a)return{ok:!0,data:a};let s=this.editContextInFlight.get(r);if(s)return s;let c=this.objectGeneration,u=this.params.client.getObjectEditContext(e,o,...i?[i]:[]).then(d=>(this.objectGeneration===c&&this.editContextCache.set(r,d.data),d)).finally(()=>{this.editContextInFlight.get(r)===u&&this.editContextInFlight.delete(r)});return this.editContextInFlight.set(r,u),u}async getClassNameSource(e,t){let i=this.requireBoundComposition(t),o=this.objectCacheKey(e,i),r=this.classNameSourceCache.get(o);if(r)return r;let a=this.classNameSourceInFlight.get(o);if(a)return a;let s=this.objectGeneration,c=this.params.client.getClassNameSource(e,i).then(u=>(this.objectGeneration===s&&this.classNameSourceCache.set(o,u),u)).finally(()=>{this.classNameSourceInFlight.get(o)===c&&this.classNameSourceInFlight.delete(o)});return this.classNameSourceInFlight.set(o,c),c}async getSourceTree(e){return this.params.client.getSourceTree(this.requireBoundComposition(e))}async getSourceFile(e){return this.params.client.getSourceFile({path:e.path,composition:this.requireBoundComposition(e.composition)})}async resolveSourceLocation(e){return this.params.client.resolveSourceLocation({key:e.key,composition:this.requireBoundComposition(e.composition)})}async execute(e,t){let i=S(e);i&&this.composition.templateId!==void 0&&this.requireBoundComposition(i);let o={...e,composition:i??this.composition};return this.params.client.execute(o)}async getOperationResult(e){return this.requireBoundComposition(e.composition),this.params.client.getOperationResult?this.params.client.getOperationResult(e):null}async undo(e,t){return this.params.client.undo(this.requireBoundComposition(e))}async redo(e,t){return this.params.client.redo(this.requireBoundComposition(e))}subscribe(e){return this.params.client.subscribe(t=>{if(t.type==="site-edit.snapshot.changed"&&this.sessionInitialized&&this.isActiveComposition(t.composition)){this.snapshotIdentityStale=!0,this.clearObjectCaches(),e(t);return}if(t.type==="site-edit.document.changed"&&this.sessionInitialized&&this.isActiveComposition(t.composition)){this.observedDocumentVersion=Math.max(this.observedDocumentVersion,t.version),t.version>this.currentDocument.version&&this.clearObjectCaches(),e(t);return}e(t)})}async refreshSnapshot(e,t="ensure-version"){if(typeof e=="number"&&this.currentDocument.version>=e)return l("bridge.document-refresh",{composition:this.composition,routeId:this.composition.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!0,skipped:!0,source:t}),this.currentDocument;let i=C(this.composition),o=this.refreshInFlight;if(o?.compositionKey===i){l("bridge.document-refresh",{composition:this.composition,routeId:this.composition.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!0,source:t});let a=await o.request;if(typeof e!="number"||a.version>=e||this.currentDocument.version>=e)return this.currentDocument}l("bridge.document-refresh",{composition:this.composition,routeId:this.composition.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!1,source:t});let r=this.params.client.getDocument(this.composition).then(a=>(this.currentDocument=a.data,this.activeCompositionContext={...a.data.composition},this.rememberServerConfirmedComposition(a.data.composition),this.observedDocumentVersion=Math.max(this.observedDocumentVersion,a.data.version),this.clearObjectCaches(),this.currentDocument));this.refreshInFlight={compositionKey:i,request:r};try{return await r}finally{this.refreshInFlight?.request===r&&(this.refreshInFlight=null)}}clearObjectCaches(e){this.objectGeneration+=1,this.editContextCache.clear(),this.editContextInFlight.clear(),this.classNameSourceCache.clear(),this.classNameSourceInFlight.clear(),!e?.preserveStyleContext&&(this.styleGeneration+=1,this.styleContextCache.clear(),this.styleContextInFlight.clear())}objectCacheKey(e,t,i){return`${C(t)}\0${e}\0${ae(i)}`}rememberServerConfirmedComposition(e){!e||typeof e.routeId!="string"||(this.serverConfirmedComposition={...e})}isAcceptedComposition(e){if(C(e)===C(this.composition))return!0;let t=this.serverConfirmedComposition;return t!==null&&E(t)===E(F(e))}requireBoundComposition(e){let t=e===void 0?this.composition:g(e);if(!this.isAcceptedComposition(t))throw new Error(`Site edit bridge is bound to composition ${JSON.stringify(this.composition)}, received ${JSON.stringify(t)}`);return g(t)}isActiveComposition(e){let t=this.activeCompositionContext??F(this.composition);return E(e)===E(t)}};function B(n){return n.version===0&&n.entries.length===0}function ce(n){let e=n;return{...e,data:{...e.data,document:U(e.data.document,e.composition)}}}function U(n,e){let t=n,i=A(t.routeId?t:{...t,routeId:e?.routeId??t.composition?.routeId});return e?{...i,composition:e}:i}function g(n){if(n&&typeof n=="object"&&typeof n.routeId=="string"){let e="effectiveTemplateId"in n?n.effectiveTemplateId:"templateId"in n?n.templateId:void 0;return{routeId:n.routeId,...typeof e=="string"?{templateId:e}:{}}}throw new Error("SITE_EDIT_COMPOSITION_SESSION_REQUIRED: a composition selector is required")}function C(n){return JSON.stringify([n.routeId,n.templateId??null])}function ae(n){return n?JSON.stringify([n.key,n.instanceIndex??null,n.instanceCount??null]):""}function E(n){return JSON.stringify([n.routeId,n.effectiveTemplateId??null])}function F(n){return{routeId:n.routeId,...n.templateId?{effectiveTemplateId:n.templateId}:{}}}function S(n){if(n&&typeof n=="object"){let e=n;if(e.composition&&typeof e.composition=="object"){let t=e.composition;if(typeof t.routeId=="string")return{routeId:t.routeId,...typeof t.templateId=="string"?{templateId:t.templateId}:{}}}}}function ue(n,e,t){let i=g(e==="execute"?S(t.request):t.composition),o={id:n,kind:e,composition:i};switch(e){case"initialize-session":case"get-document":case"get-history-status":case"get-style-context":case"get-source-tree":case"undo":case"redo":return{type:"request",payload:{...o,...t.mutationContext?{mutationContext:t.mutationContext}:{}}};case"get-object-edit-context":return{type:"request",payload:{...o,key:String(t.key),...t.selectionRef?{selectionRef:t.selectionRef}:{}}};case"get-class-name-source":case"resolve-source-location":return{type:"request",payload:{...o,key:String(t.key)}};case"get-source-file":return{type:"request",payload:{...o,path:String(t.path)}};case"execute":case"get-operation-result":return{type:"request",payload:{...o,request:{...t.request,composition:S(t.request)??i}}}}}export{ee as SITE_EDIT_BRIDGE_CONNECT_TIMEOUT,te as SITE_EDIT_BRIDGE_HEARTBEAT_TIMEOUT,v as SiteEditWebSocketClient,Ie as createSiteEditBridge};
@@ -1,4 +1,4 @@
1
- import{parentPort as Xe,workerData as et}from"node:worker_threads";function M(t){let e=t&&typeof t=="object"?t:null,n=typeof e?.message=="string"&&e.message.length>0?e.message:String(t);return{name:typeof e?.name=="string"?e.name:"Error",message:n,...typeof e?.code=="string"&&e.code.length>0?{code:e.code}:{}}}import oe from"node:http";import _ from"node:fs";import{createHash as J,randomBytes as Ce,timingSafeEqual as Ee}from"node:crypto";import m from"node:path";import{WebSocket as re,WebSocketServer as Re}from"ws";import{createPreviewSiteEditEngineRuntime as xe,openFileHistoryStore as be,resolveManagedPageTemplateComposition as ve,scanSiteEditCompositionsFromFiles as ke}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as _e,scanNextAppRouterRoutesFromFiles as Te}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as Oe}from"@vcp-dev/site-edit-engine/source-watcher";function Y(t){let e=new Map,n=[],i=new Map,o=[],s=t.pageComposition.status==="supported"?t.pageComposition.placementUnitKeys:[],r=new Map(s.map((c,d)=>[c,d])),a=t.pageComposition.status==="supported"?{status:"supported",containerKey:t.pageComposition.containerKey,hasBusinessMembers:t.pageComposition.hasBusinessMembers,placementUnitKeys:t.pageComposition.placementUnitKeys,placements:t.pageComposition.placements.map(c=>({placementKey:c.placementKey,placementUnitKey:c.placementUnitKey,relation:c.relation}))}:{status:"unsupported",reason:t.pageComposition.reason,diagnostics:t.pageComposition.diagnostics},p=t.entries.flatMap((c,d)=>c.dynamicDataSummary?[[d,c.dynamicDataSummary]]:[]),u=new Map(t.entries.map((c,d)=>[c.key,d])),l=[];for(let[c,d]of t.entries.entries()){let f=d.pageCompositionActionScope;if(!f)continue;if(f.kind!=="source-siblings"){l.push([c,{kind:f.kind}]);continue}let O=f.siblingKeys.map(b=>{let v=u.get(b);if(v===void 0)throw new Error("Render entry "+d.key+" references missing source sibling "+b);return v});l.push([c,{kind:"source-siblings",siblingEntryIndexes:O}])}let S=c=>{let d=e.get(c);if(typeof d=="number")return d;let f=n.length;return n.push(c),e.set(c,f),f},Q=c=>{let d=null;for(let f of pe(c)){let O=`${d??"root"}\0${f}`,b=i.get(O);if(typeof b=="number"){d=b;continue}let v=o.length;o.push([d,S(f)]),i.set(O,v),d=v}if(d===null)throw new Error("Document key must not be empty");return d};return{encoding:"segment-trie-v1",siteId:t.siteId,snapshotId:t.snapshotId,composition:{...t.composition},routeId:t.composition.routeId,version:t.version,pageComposition:a,tokens:n,nodes:o,entries:t.entries.map(c=>{let d=c.pageCompositionUnitKey?r.get(c.pageCompositionUnitKey)??-1:-1;if(c.pageCompositionUnitKey&&d<0)throw new Error(`Render entry ${c.key} references unknown page composition unit ${c.pageCompositionUnitKey}`);return[Q(c.key),he(c.operationSummary),...d>=0?[d]:[]]}),...l.length>0?{pageCompositionActionScopes:l}:{},...t.entries.every(c=>c.parentKey!==void 0)?{entryParentNodeIds:t.entries.map(c=>c.parentKey===null?null:Q(c.parentKey))}:{},...p.length>0?{dynamicDataSummaries:p}:{}}}function he(t){return(t.canUpdateText?1:0)|(t.canInsertChild?2:0)|(t.canMove?4:0)|(t.canRemove?8:0)}function pe(t){if(!t)return[];let e=[],n=0,i=G(t,n);if(i<0)return e.push(t),e;for(e.push(t.slice(0,i)),n=i;n<t.length;){if(i=G(t,n+(t.startsWith("::",n)?2:1)),i<0){e.push(t.slice(n));break}e.push(t.slice(n,i)),n=i}return e}function G(t,e){let n=t.indexOf("/",e),i=t.indexOf("::",e);return n<0?i:i<0?n:Math.min(n,i)}var Z="SITE_EDIT_COMPOSITION_SESSION_MISMATCH",V="SITE_EDIT_COMPOSITION_SESSION_REQUIRED";var g=class t{constructor(){}static parseMessage(e,n={}){let i=JSON.parse(e);if(!t.isMessage(i,n))throw new Error("Invalid site edit bridge message");return i}static stringifyMessage(e){return JSON.stringify(e)}static extractRequestInfo(e){if(h(e)&&h(e.payload)){let n=typeof e.payload.id=="string"?e.payload.id:"unknown",i=this.isRequestKind(e.payload.kind)?e.payload.kind:"get-document";return{id:n,kind:i}}return{id:"unknown",kind:"get-document"}}static isRequestKind(e){return e==="initialize-session"||e==="get-document"||e==="get-history-status"||e==="get-style-context"||e==="get-object-edit-context"||e==="get-class-name-source"||e==="get-source-tree"||e==="get-source-file"||e==="resolve-source-location"||e==="execute"||e==="get-operation-result"||e==="undo"||e==="redo"}static isMessage(e,n={}){return!h(e)||typeof e.type!="string"?!1:e.type==="request"?le(e.payload,n):e.type==="response"||e.type==="error"?h(e.payload)&&typeof e.payload.id=="string":e.type==="event"?h(e.payload)&&h(e.payload.event):e.type==="auth"?h(e.payload)&&typeof e.payload.key=="string":e.type==="auth-ok"&&h(e.payload)&&e.payload.ok===!0}},A=class{constructor(e){this.options=e,this.session={subscribedCompositionKeys:new Set},this.messageQueue=new P,this.eventDispatcher=new B(n=>{this.sendJson(n)}),this.unsubscribe=e.engine.subscribe(n=>{this.handleEngineEvent(n)}),e.socket.onmessage=n=>{try{this.messageQueue.enqueue(()=>this.handleRawMessage(n.data))}catch(i){this.sendJson({type:"error",payload:{id:"unknown",kind:"get-document",message:i instanceof Error?i.message:"Bridge queue overflow"}}),e.socket.close(1013,"bridge busy")}},e.socket.onclose=()=>{this.unsubscribe(),this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.clear()}}async handleMessage(e){try{let n=e;if(n.type!=="request")return;let i=n.payload,{id:o}=i;switch(i.kind){case"initialize-session":{let s=D(i),r=await this.resolve(s),a=await this.options.engine.initializeSession({composition:r.context,...i.mutationContext?{mutationContext:i.mutationContext}:{}});this.activateSession(r.context),this.sendJson({type:"response",payload:{id:o,kind:i.kind,result:I({...a,data:{...a.data,document:ee(a.data.document)}},r.context)}});return}case"get-document":{let s=await this.resolveWireContext(i),r=await this.options.engine.getDocument(s);this.sendResult(o,i.kind,Se(r,s));return}case"get-history-status":{let s=await this.resolveWireContext(i),r=await this.options.engine.getHistoryStatus(s);this.sendResult(o,i.kind,I(r,s));return}case"get-style-context":{let s=await this.resolveWireContext(i);this.sendResult(o,i.kind,I(await this.options.engine.getStyleContext(s),s));return}case"get-object-edit-context":{let s=await this.resolveWireContext(i);this.sendResult(o,i.kind,I(await this.options.engine.getObjectEditContext(i.key,s,i.selectionRef),s));return}case"get-class-name-source":{let s=await this.resolveWireContext(i);this.sendResult(o,i.kind,I(await this.options.engine.getClassNameSource(i.key,s),s));return}case"get-source-tree":{let s=await this.resolveWireContext(i);this.sendResult(o,i.kind,I(await this.options.engine.getSourceTree(s),s));return}case"get-source-file":{let s=await this.resolveWireContext(i);this.sendResult(o,i.kind,I(await this.options.engine.getSourceFile({path:i.path,composition:s}),s));return}case"resolve-source-location":{let s=await this.resolveWireContext(i),r=I(await this.options.engine.resolveSourceLocation({key:i.key,composition:s}),s);this.sendResult(o,i.kind,r);return}case"execute":case"get-operation-result":{let s=await this.resolveOperationWireContext(i.request),r=await this.resolveWireContext(i);if(w(s)!==w(r))throw new C(Z,"The execute request composition does not match its operation composition.");let a=await this.normalizeOperationRequest(i.request,s);if(i.kind==="get-operation-result"){this.sendResult(o,i.kind,await this.options.engine.getOperationResult?.(a)??null);return}await this.handleBufferedOperation(o,i.kind,()=>this.options.engine.execute(a));return}case"undo":case"redo":{let s=await this.resolveWireContext(i);await this.handleBufferedOperation(o,i.kind,()=>i.kind==="undo"?this.options.engine.undo(s):this.options.engine.redo(s));return}}}catch(n){let i=g.extractRequestInfo(e);this.sendJson({type:"error",payload:{...i,message:N(n),code:F(n)}})}}async resolve(e){return this.options.engine.resolveComposition(e)}async resolveAndAssert(e){let n=await this.resolve(e),i=this.session.activeComposition;if(!i)throw new C(V,"Initialize a composition session before issuing this request.");if(w(i)!==w(n.context))throw new C(Z,"The request composition does not match the active socket session.");return n.context}async resolveWireContext(e){let n=D(e);return this.resolveAndAssert(n)}async resolveOperationWireContext(e){let n=ye(e);return this.resolveAndAssert(n)}activateSession(e){this.session.activeComposition=e,this.session.subscribedCompositionKeys.clear(),this.session.subscribedCompositionKeys.add(w(e))}async normalizeOperationRequest(e,n){let i={...e,composition:X(n)};if(i.target.kind==="route"){let o=i.target,s=D(o),r=await this.resolveAndAssert(s);i.target={...o,composition:X(r)}}return i}handleEngineEvent(e){let n=e?.composition;if(!me(n))return;let i=w(n);this.session.subscribedCompositionKeys.has(i)&&(this.eventDispatcher.push(e),e.type==="site-edit.graph.invalidated"&&i===Ie(this.session)&&(this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.delete(i)))}sendResult(e,n,i){this.sendJson({type:"response",payload:{id:e,kind:n,result:i}})}async handleRawMessage(e){try{await this.handleMessage(g.parseMessage(e,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let i=we(e);this.sendJson({type:"error",payload:{...i,message:N(n),code:F(n)}})}}sendJson(e){this.options.socket.send(g.stringifyMessage(e))}async handleBufferedOperation(e,n,i){this.eventDispatcher.beginBuffering();try{let o=await i();this.sendResult(e,n,o)}catch(o){this.sendJson({type:"error",payload:{id:e,kind:n,message:N(o),code:F(o)}})}finally{this.eventDispatcher.flush()}}},C=class extends Error{constructor(e,n){super(n),this.code=e,this.name="BridgeProtocolError"}},B=class{constructor(e){this.send=e,this.bufferedEvents=null}push(e){let n={type:"event",payload:{event:e}};if(this.bufferedEvents){this.bufferedEvents.push(n);return}this.send(n)}beginBuffering(){this.bufferedEvents=[]}flush(){let e=this.bufferedEvents??[];this.bufferedEvents=null,e.forEach(n=>this.send(n))}},P=class{constructor(e=64){this.maxQueued=e,this.queue=Promise.resolve(),this.queued=0}enqueue(e){if(this.queued>=this.maxQueued)throw new Error("Bridge request queue is full");this.queued+=1;let n=async()=>{this.queued-=1,await e()},i=this.queue.then(n,n);return this.queue=i.then(()=>{},()=>{}),i}};function le(t,e){if(!h(t)||typeof t.id!="string"||!g.isRequestKind(t.kind))return!1;let n=k(t.composition);switch(t.kind){case"initialize-session":return n&&(t.mutationContext===void 0||K(t.mutationContext));case"get-document":case"get-style-context":return n;case"get-object-edit-context":return typeof t.key=="string"&&n&&fe(t.selectionRef);case"get-class-name-source":case"resolve-source-location":return typeof t.key=="string"&&n;case"get-history-status":return n;case"get-source-tree":return n;case"get-source-file":return typeof t.path=="string"&&n;case"execute":case"get-operation-result":return n&&ge(t.request)&&(t.request.mutationContext===void 0||K(t.request.mutationContext))&&(e.validateOperationRequest?.(t.request)??!0);case"undo":case"redo":return n&&t.mutationContext===void 0||n&&K(t.mutationContext)}}function fe(t){if(t===void 0)return!0;if(!h(t)||typeof t.key!="string"||t.key.length===0)return!1;let e=t.instanceIndex,n=t.instanceCount;return e!==void 0&&(typeof e!="number"||!Number.isInteger(e)||e<0)||n!==void 0&&(typeof n!="number"||!Number.isInteger(n)||n<=0)||typeof e=="number"&&typeof n=="number"&&e>=n?!1:Object.keys(t).every(i=>i==="key"||i==="instanceIndex"||i==="instanceCount")}function ge(t){return h(t)&&typeof t.id=="string"&&typeof t.siteId=="string"&&typeof t.baseSnapshotId=="string"&&k(t.composition)&&typeof t.kind=="string"&&h(t.target)&&(t.target.kind!=="route"||k(t.target.composition))&&typeof t.documentVersion=="number"&&Number.isInteger(t.documentVersion)&&t.documentVersion>=0&&h(t.params)}function k(t){return h(t)&&typeof t.routeId=="string"&&t.routeId.length>0&&(t.templateId===void 0||typeof t.templateId=="string"&&t.templateId.length>0)&&Object.keys(t).every(e=>e==="routeId"||e==="templateId")}function me(t){return h(t)&&typeof t.routeId=="string"&&(t.effectiveTemplateId===void 0||typeof t.effectiveTemplateId=="string")}function D(t){if(k(t.composition))return t.composition;throw new C(V,"A composition selector is required for this request.")}function ye(t){if(h(t)&&k(t.composition))return t.composition;throw new C(V,"A composition selector is required for execute.")}function X(t){return{routeId:t.routeId,...t.effectiveTemplateId?{templateId:t.effectiveTemplateId}:{}}}function Ie(t){return t.activeComposition?w(t.activeComposition):null}function w(t){return JSON.stringify([t.routeId,t.effectiveTemplateId??null])}function I(t,e){return h(t)&&t.ok===!0?{...t,composition:e}:t}function Se(t,e){return!h(t)||t.ok!==!0?t:{...t,...e?{composition:e}:{},data:ee(t.data)}}function ee(t){let e=t;return Y(e.routeId?e:{...e,routeId:e.composition.routeId})}function h(t){return typeof t=="object"&&t!==null}function K(t){return h(t)&&typeof t.editingSessionId=="string"&&t.editingSessionId.length>0&&typeof t.lockId=="string"&&t.lockId.length>0&&typeof t.ownerId=="string"&&t.ownerId.length>0&&typeof t.leaseAssertion=="string"&&t.leaseAssertion.length>0}function F(t){if(!(!h(t)||typeof t.code!="string"||t.code.length===0))return t.code}function N(t){return t instanceof Error||h(t)&&typeof t.message=="string"&&t.message.length>0?t.message:"Unknown websocket operation failure"}function we(t){try{return g.extractRequestInfo(JSON.parse(t))}catch{return{id:"unknown",kind:"get-document"}}}var H=class{constructor(){this.tail=Promise.resolve()}run(e){let n=this.tail.then(e,e);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as Ae,computeSiteEditCompositionKey as E}from"@vcp-dev/contracts";var He="127.0.0.1",Me=30218,De="/__vcp/site-edit/bridge",ae=6*1024*1024,Ke=ae*2,Fe=3e3,te=50,j=".vcp/current-snapshot.json",ne="__VCP_SITE_EDIT_BRIDGE_SERVERS__";var $=class{constructor(e,n=oe.createServer((s,r)=>{r.writeHead(404),r.end("VCP site edit bridge server")}),i=!0,o={}){this.options=e,this.server=n,this.closeHttpServerOnStop=i,this.dependencies=o,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new H,this.operationReceipts=new Map,this.eventListeners=new Set,this.routes=[],this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.launchCandidateEntries=null,this.stopWatching=null,this.stopWebSocketServer=null,this.pendingChangedFiles=new Set,this.fileChangeTimer=null,this.isStarted=!1,this.stopping=!1,this.runtimeVersionFloors=new Map,this.activeHistoryStore=null,this.versionPersistence=Promise.resolve(),this.bridgeEngine=null,this.lastNotifiedHealth=null}start(e={}){if(!this.isStarted){this.stopping=!1;try{_.mkdirSync(m.join(this.options.projectRoot,".vcp"),{recursive:!0})}catch(n){console.warn(`[vcp-site-edit] failed to ensure .vcp directory: ${n instanceof Error?n.message:String(n)}`)}if(this.refreshCompositions(!0),this.isStarted=!0,this.createRuntime(),this.bridgeEngine=this.createBridgeEngine(),e.webSocket!==!1){let n=$e({server:this.server,path:this.options.path,sessionKey:this.options.sessionKey,host:this.options.host,allowedOrigins:this.options.allowedOrigins,engine:this.bridgeEngine});this.stopWebSocketServer=n.stop}this.stopWatching=new Oe({projectRoot:this.options.projectRoot,watch_dirs:Be(this.options.runtime.watch_dirs),interval_ms:this.options.runtime.watch_interval_ms,debounce_ms:this.options.runtime.watch_debounce_ms??te,max_dirs:this.options.runtime.watch_max_dirs,max_files:this.options.runtime.watch_max_files,max_depth:this.options.runtime.watch_max_depth,max_startup_ms:this.options.runtime.watch_max_startup_ms,on_change:n=>this.handleFileChanged(n),on_batch_change:n=>this.handleFilesChanged(n)}).start(),e.listen!==!1&&this.server.listen(this.options.port,this.options.host),this.notifyHealthChanged()}}getEngine(){if(!this.bridgeEngine)throw new Error("VCP site edit preview bridge runtime is not ready");return this.bridgeEngine}getHealth(){let e=!!this.runtime&&!this.compositionResolutionError;return{ok:this.isStarted&&e,bridgePath:this.options.path,runtimeReady:e,routesIndexed:this.routes.length,sessionRequired:!!this.options.sessionKey}}stop(){this.stopping=!0,this.stopWatching?.(),this.stopWatching=null,this.flushChangedFiles(),this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.stopWebSocketServer?.(),this.stopWebSocketServer=null,this.runtime?.dispose(),this.runtime=null,this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.closeHttpServerOnStop&&this.server.close();let e=We(),n=je(this.options);e.get(n)===this&&e.delete(n),this.isStarted=!1,this.notifyHealthChanged()}notifyHealthChanged(){let e=this.options.onHealthChanged;if(!e)return;let n=this.getHealth(),i=JSON.stringify(n);if(i!==this.lastNotifiedHealth){this.lastNotifiedHealth=i;try{e(n)}catch(o){console.warn(`[vcp-site-edit] onHealthChanged callback failed: ${o instanceof Error?o.message:String(o)}`)}}}createRuntime(e,n=null){let i=this.runtime;this.activeHistoryStore=e??null;let o=(this.dependencies.createRuntime??xe)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,compositions:this.compositions,onEvent:s=>{"version"in s&&typeof s.version=="number"&&this.recordRuntimeVersion(s.version,"composition"in s?s.composition:void 0);for(let r of this.eventListeners)r(s)},...e?{historyStore:e}:{},...this.getInitialDocumentVersions().length>0?{initialDocumentVersions:this.getInitialDocumentVersions()}:{}});this.runtime=o,this.activeHistorySessionId=n,i?.dispose(),this.notifyHealthChanged()}createBridgeEngine(){return{resolveComposition:e=>this.resolveComposition(e),initializeSession:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)throw new Error(`${n.code}: ${n.message}`);let i=Ve(e.composition),o=this.getRuntime(),s=y(i);await o.initializeComposition(s);let a=o.getDocument(s).data;await this.recordRuntimeVersion(a.version,i);let p=(await o.getHistoryStatus()).data;return{ok:!0,composition:i,data:{document:a,historyStatus:p}}}),getDocument:async e=>{let n=this.getRuntime(),i=y(e);await n.initializeComposition(i);let o=n.getDocument(i);return await this.recordRuntimeVersion(o.data.version,e),{...o,composition:e}},getHistoryStatus:async e=>({...await this.getRuntime().getHistoryStatus(),composition:e}),getStyleContext:async e=>this.getRuntime().getStyleContext(y(e)),getObjectEditContext:async(e,n,i)=>this.getRuntime().getObjectEditContext(e,y(n),i),getClassNameSource:async(e,n)=>this.getRuntime().getClassNameSource(e,y(n)),getSourceTree:async e=>this.getRuntime().getSourceTree(y(e)),getSourceFile:async e=>this.getRuntime().getSourceFile({path:e.path,composition:y(e.composition)}),resolveSourceLocation:async e=>this.getRuntime().resolveSourceLocation({key:e.key,composition:y(e.composition)}),execute:(e,n)=>this.executeWithReceipt(e,()=>this.mutationQueue.run(async()=>{let i=await this.bindHistorySession(this.options.snapshotId);if(!i.ok)return U(i,e);let o=await this.getRuntime().executeOperation(e);return o.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(o.data.resultVersion,o.composition),o})),getOperationResult:e=>{if(e.siteId!==this.options.siteId||e.baseSnapshotId!==this.options.snapshotId)return null;let n=this.operationReceipts.get(this.operationReceiptKey(e));return n?.fingerprint===this.operationFingerprint(e)?n.result:null},undo:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return U(n,void 0,e);let i=await this.getRuntime().undo();return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion,i.composition??e),i.composition?i:{...i,composition:e}}),redo:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return U(n,void 0,e);let i=await this.getRuntime().redo();return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion,i.composition??e),i.composition?i:{...i,composition:e}}),subscribe:e=>(this.eventListeners.add(e),()=>this.eventListeners.delete(e))}}operationReceiptKey(e){return JSON.stringify([e.siteId,e.baseSnapshotId,e.id])}operationFingerprint(e){return J("sha256").update(JSON.stringify(e)).digest("hex")}executeWithReceipt(e,n){if(e.siteId!==this.options.siteId||e.baseSnapshotId!==this.options.snapshotId)return n();let i=this.operationReceiptKey(e),o=this.operationFingerprint(e),s=this.operationReceipts.get(i);if(s)return s.fingerprint!==o?Promise.reject(new Error("Operation ID was reused with a different request.")):s.result;let r={fingerprint:o,result:n(),settled:!1};return this.operationReceipts.set(i,r),r.result.then(()=>{r.settled=!0;for(let[a,p]of this.operationReceipts){if(this.operationReceipts.size<=256)break;p.settled&&this.operationReceipts.delete(a)}},()=>{this.operationReceipts.delete(i)}),r.result}async bindHistorySession(e){if(!this.options.historyRoot)return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history is not configured."};if(this.activeHistorySessionId===e)return{ok:!0};let n=this.historySessionTransition.then(async()=>{if(this.activeHistorySessionId===e)return;let i=await(this.dependencies.openHistoryStore??be)(Ue({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e}));if(!this.isStarted)return;let o=i.readDocumentVersion?await i.readDocumentVersion():0,s=this.compositions[0]?.context;if(s){let r=E(s);this.runtimeVersionFloors.set(r,Math.max(this.runtimeVersionFloors.get(r)??0,o))}this.createRuntime(i,e)});this.historySessionTransition=n.catch(()=>{});try{return await n,{ok:!0}}catch{return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history could not be opened."}}}recordRuntimeVersion(e,n){let i=n?E(n):null;i&&this.runtimeVersionFloors.set(i,Math.max(this.runtimeVersionFloors.get(i)??0,e));let o=this.activeHistoryStore;if(!o?.writeDocumentVersion)return Promise.resolve();let s=i?this.runtimeVersionFloors.get(i)??e:e;return this.versionPersistence=this.versionPersistence.then(()=>o.writeDocumentVersion(s)).catch(()=>{}),this.versionPersistence}handleFileChanged(e){this.handleFilesChanged([e])}handleFilesChanged(e){for(let i of e)this.pendingChangedFiles.add(i);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let n=this.options.runtime.watch_debounce_ms??te;this.fileChangeTimer=setTimeout(()=>{this.fileChangeTimer=null,this.flushChangedFiles()},n),this.fileChangeTimer?.unref?.()}flushChangedFiles(){if(this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.pendingChangedFiles.size===0)return;let e=[...this.pendingChangedFiles].sort((n,i)=>n.localeCompare(i));if(this.pendingChangedFiles.clear(),e.some(Ze)&&this.scheduleSnapshotIdentityAdvance(),!!this.refreshCompositions(!1))for(let n of e)this.runtime?.notifyFileChanged(n)}scheduleSnapshotIdentityAdvance(){if(this.stopping||!this.isStarted)return;let e=this.readCurrentSnapshotMarker();!e||e===this.options.snapshotId||this.mutationQueue.run(()=>this.advanceSnapshotId(e)).catch(n=>{console.warn(`[vcp-site-edit] snapshot identity advance failed: ${n instanceof Error?n.message:String(n)}`)})}readCurrentSnapshotMarker(){try{let e=_.readFileSync(m.join(this.options.projectRoot,j),"utf8"),n=JSON.parse(e),i=typeof n.snapshot_id=="string"?n.snapshot_id:"";return!i||q(i)!==i?(console.warn("[vcp-site-edit] current-snapshot marker has no valid snapshot_id"),null):i}catch(e){return console.warn(`[vcp-site-edit] current-snapshot marker unreadable: ${e instanceof Error?e.message:String(e)}`),null}}async advanceSnapshotId(e){if(this.stopping||!this.isStarted||e===this.options.snapshotId)return;let n=this.options.snapshotId;this.options.snapshotId=e;try{if(this.options.historyRoot){let o=await this.bindHistorySession(e);if(!o.ok)throw new Error(`${o.code}: ${o.message}`)}else this.createRuntime()}catch(o){this.options.snapshotId=n,console.warn(`[vcp-site-edit] snapshot identity advance rolled back to ${n}: ${o instanceof Error?o.message:String(o)}`);return}let i=new Date().toISOString();for(let o of this.compositions){let s={type:"site-edit.snapshot.changed",siteId:this.options.siteId,composition:o.context,snapshotId:e,previousSnapshotId:n,at:i};for(let r of this.eventListeners)try{r(s)}catch(a){console.warn(`[vcp-site-edit] snapshot.changed listener failed: ${a instanceof Error?a.message:String(a)}`)}}try{this.options.onSnapshotIdChanged?.(e,n)}catch(o){console.warn(`[vcp-site-edit] onSnapshotIdChanged callback failed: ${o instanceof Error?o.message:String(o)}`)}}refreshCompositions(e=!1){try{let n=Pe(this.options.projectRoot),i=Te(n),o=i.length>0?i:(this.options.runtime.routes??[]).map(u=>({...u})),s=ke({files:n});if(s.length===0)throw new Error("No canonical App Router composition is indexed by the preview bridge.");let r=JSON.stringify(s.filter(u=>u.context.effectiveTemplateId!==void 0).map(u=>u.entryFile).sort());if(e&&(this.launchCandidateEntries=r),this.launchCandidateEntries!==r)throw new Error("SITE_EDIT_RUNTIME_RELOAD_REQUIRED: Candidate route collection changed since preview launch");let a=new Map(this.compositions.map(u=>[E(u.context),u])),p=new Map(s.map(u=>[E(u.context),u]));if(this.runtime){for(let[u,l]of a){let S=p.get(u);(!S||S.entryFile!==l.entryFile)&&this.runtime.unregisterComposition(y(l.context))}for(let[u,l]of p){let S=a.get(u);(!S||S.entryFile!==l.entryFile)&&this.runtime.registerComposition(l.context,l.entryFile)}}return this.routes=o,this.compositions=s,this.compositionFiles=n,this.compositionResolutionError=null,this.notifyHealthChanged(),!0}catch(n){let i=n instanceof Error?n:new Error(String(n));if(this.compositionResolutionError=i,e)throw i;return this.notifyHealthChanged(),!1}}resolveComposition(e){if(this.compositionResolutionError)throw this.compositionResolutionError;let n=_e(e.routeId,this.routes,this.options.basePath);if(!n)throw new Error(`Route is not indexed by the preview bridge: ${e.routeId}`);let i=this.routes.find(r=>r.routeId===n);if(!i)throw new Error(`Route is not indexed by the preview bridge: ${e.routeId}`);let o=ve({selector:{routeId:n,...e.templateId!==void 0?{templateId:e.templateId}:{}},files:this.compositionFiles,canonicalEntryFile:i.entryFile}),s=E(o.context);if(!this.compositions.some(r=>E(r.context)===s))throw new Error(`Composition is not registered by the preview bridge: ${s}`);return o}getInitialDocumentVersions(){return this.compositions.flatMap(e=>{let n=this.runtimeVersionFloors.get(E(e.context));return n===void 0?[]:[{composition:e.context,version:n}]})}getRuntime(){if(!this.runtime)throw new Error("VCP site edit preview bridge runtime is not ready");return this.runtime}};function ce(t){let e=new $(Ne(t),oe.createServer(),!1);return e.start({listen:!1,webSocket:!1}),{engine:e.getEngine(),getHealth:()=>e.getHealth(),stop:()=>e.stop()}}function Ne(t){return{tenantId:t.tenantId,siteId:t.siteId,snapshotId:t.snapshotId,projectRoot:m.resolve(t.projectRoot??process.cwd()),basePath:t.basePath??"",host:t.host??He,port:t.port??Me,path:t.path??De,sessionKey:t.sessionKey??Ye(),historyRoot:t.historyRoot,allowedOrigins:t.allowedOrigins??[],runtime:t.runtime??{},onSnapshotIdChanged:t.onSnapshotIdChanged,onHealthChanged:t.onHealthChanged}}function Be(t){let e=["app","src/app","components/site","src/components/site","lib/generated","src/lib/generated","public"];return[...new Set([...t?.length?t:e,".vcp"])]}function Pe(t){let e=[],n=["app","src/app",".vcp"],i=new Set([".git",".next",".turbo","coverage","dist","node_modules","output","snapshot-store"]),o=s=>{if(!_.existsSync(s))return;let r;try{r=_.readdirSync(s,{withFileTypes:!0})}catch{return}for(let a of r){if(a.name.startsWith(".")&&a.name!==".vcp")continue;if(a.isDirectory()){if(i.has(a.name))continue;o(m.join(s,a.name));continue}if(!a.isFile())continue;let p=m.join(s,a.name),u="";try{u=_.readFileSync(p,"utf8")}catch{}e.push({path:m.relative(t,p).replaceAll(m.sep,"/"),content:u})}};for(let s of n)o(m.join(t,s));return e.sort((s,r)=>s.path.localeCompare(r.path))}function Ve(t){if(!t||typeof t!="object"||typeof t.routeId!="string")throw new Error("Normalized composition context is required.");if("effectiveTemplateId"in t)return t;let e=t;return{routeId:e.routeId,...e.templateId?{effectiveTemplateId:e.templateId}:{}}}function y(t){return{routeId:t.routeId,...t.effectiveTemplateId?{templateId:t.effectiveTemplateId}:{}}}function Ue(t){return m.join(m.resolve(t.root),q(t.siteId),q(t.snapshotId),J("sha256").update(t.editingSessionId).digest("hex").slice(0,32))}function q(t){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(t)&&t!=="."&&t!==".."?t:J("sha256").update(t).digest("hex").slice(0,32)}function We(){let t=globalThis;return t[ne]??=new Map,t[ne]}function je(t){return`${t.projectRoot}:${t.host}:${t.port}:${t.path}:${t.historyRoot??""}`}function U(t,e,n){let i=n??(e?.composition?{routeId:e.composition.routeId,...e.composition.templateId?{effectiveTemplateId:e.composition.templateId}:{}}:void 0);if(!i)throw new Error("A normalized composition is required to construct a mutation result.");return{ok:!1,composition:i,error:{code:t.code,message:t.message},data:{requestId:e?.id??"mutation-context",kind:e?.kind??"update-text",composition:i,target:e?.target??{kind:"node",key:""},resultVersion:e?.documentVersion??0}}}function $e(t){let e=new Re({maxPayload:ae,noServer:!0}),n=new WeakSet,i=s=>{new A({socket:new z(s),engine:t.engine,validateOperationRequest:r=>Ae.safeParse(r).success})},o=(s,r,a)=>{if(!qe(s,t))return;let p=ze(s,t);if(!p){r.write(`HTTP/1.1 401 Unauthorized\r
1
+ import{parentPort as tt,workerData as nt}from"node:worker_threads";function M(t){let e=t&&typeof t=="object"?t:null,n=typeof e?.message=="string"&&e.message.length>0?e.message:String(t);return{name:typeof e?.name=="string"?e.name:"Error",message:n,...typeof e?.code=="string"&&e.code.length>0?{code:e.code}:{}}}import oe from"node:http";import _ from"node:fs";import{createHash as J,randomBytes as Re,timingSafeEqual as xe}from"node:crypto";import m from"node:path";import{WebSocket as re,WebSocketServer as be}from"ws";import{createPreviewSiteEditEngineRuntime as ke,openFileHistoryStore as ve,resolveManagedPageTemplateComposition as _e,scanSiteEditCompositionsFromFiles as Te}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as Oe,scanNextAppRouterRoutesFromFiles as Ae}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as He}from"@vcp-dev/site-edit-engine/source-watcher";function Y(t){let e=new Map,n=[],i=new Map,o=[],s=t.pageComposition.status==="supported"?t.pageComposition.placementUnitKeys:[],r=new Map(s.map((c,h)=>[c,h])),a=t.pageComposition.status==="supported"?{status:"supported",containerKey:t.pageComposition.containerKey,hasBusinessMembers:t.pageComposition.hasBusinessMembers,placementUnitKeys:t.pageComposition.placementUnitKeys,placements:t.pageComposition.placements.map(c=>({placementKey:c.placementKey,placementUnitKey:c.placementUnitKey,relation:c.relation}))}:{status:"unsupported",reason:t.pageComposition.reason,diagnostics:t.pageComposition.diagnostics},p=t.entries.flatMap((c,h)=>c.dynamicDataSummary?[[h,c.dynamicDataSummary]]:[]),u=new Map(t.entries.map((c,h)=>[c.key,h])),l=[];for(let[c,h]of t.entries.entries()){let g=h.pageCompositionActionScope;if(!g)continue;if(g.kind!=="source-siblings"){l.push([c,{kind:g.kind}]);continue}let O=g.siblingKeys.map(b=>{let k=u.get(b);if(k===void 0)throw new Error("Render entry "+h.key+" references missing source sibling "+b);return k});l.push([c,{kind:"source-siblings",siblingEntryIndexes:O}])}let S=c=>{let h=e.get(c);if(typeof h=="number")return h;let g=n.length;return n.push(c),e.set(c,g),g},Q=c=>{let h=null;for(let g of pe(c)){let O=`${h??"root"}\0${g}`,b=i.get(O);if(typeof b=="number"){h=b;continue}let k=o.length;o.push([h,S(g)]),i.set(O,k),h=k}if(h===null)throw new Error("Document key must not be empty");return h};return{encoding:"segment-trie-v1",siteId:t.siteId,snapshotId:t.snapshotId,composition:{...t.composition},routeId:t.composition.routeId,version:t.version,pageComposition:a,tokens:n,nodes:o,entries:t.entries.map(c=>{let h=c.pageCompositionUnitKey?r.get(c.pageCompositionUnitKey)??-1:-1;if(c.pageCompositionUnitKey&&h<0)throw new Error(`Render entry ${c.key} references unknown page composition unit ${c.pageCompositionUnitKey}`);return[Q(c.key),he(c.operationSummary),...h>=0?[h]:[]]}),...l.length>0?{pageCompositionActionScopes:l}:{},...t.entries.every(c=>c.parentKey!==void 0)?{entryParentNodeIds:t.entries.map(c=>c.parentKey===null?null:Q(c.parentKey))}:{},...p.length>0?{dynamicDataSummaries:p}:{}}}function he(t){return(t.canUpdateText?1:0)|(t.canInsertChild?2:0)|(t.canMove?4:0)|(t.canRemove?8:0)}function pe(t){if(!t)return[];let e=[],n=0,i=G(t,n);if(i<0)return e.push(t),e;for(e.push(t.slice(0,i)),n=i;n<t.length;){if(i=G(t,n+(t.startsWith("::",n)?2:1)),i<0){e.push(t.slice(n));break}e.push(t.slice(n,i)),n=i}return e}function G(t,e){let n=t.indexOf("/",e),i=t.indexOf("::",e);return n<0?i:i<0?n:Math.min(n,i)}var Z="SITE_EDIT_COMPOSITION_SESSION_MISMATCH",V="SITE_EDIT_COMPOSITION_SESSION_REQUIRED";var f=class t{constructor(){}static parseMessage(e,n={}){let i=JSON.parse(e);if(!t.isMessage(i,n))throw new Error("Invalid site edit bridge message");return i}static stringifyMessage(e){return JSON.stringify(e)}static extractRequestInfo(e){if(d(e)&&d(e.payload)){let n=typeof e.payload.id=="string"?e.payload.id:"unknown",i=this.isRequestKind(e.payload.kind)?e.payload.kind:"get-document";return{id:n,kind:i}}return{id:"unknown",kind:"get-document"}}static isRequestKind(e){return e==="initialize-session"||e==="get-document"||e==="get-history-status"||e==="get-style-context"||e==="get-object-edit-context"||e==="get-class-name-source"||e==="get-source-tree"||e==="get-source-file"||e==="resolve-source-location"||e==="execute"||e==="get-operation-result"||e==="undo"||e==="redo"}static isMessage(e,n={}){return!d(e)||typeof e.type!="string"?!1:e.type==="request"?fe(e.payload,n):e.type==="response"||e.type==="error"?d(e.payload)&&typeof e.payload.id=="string":e.type==="event"?d(e.payload)&&d(e.payload.event):e.type==="auth"?d(e.payload)&&typeof e.payload.key=="string":e.type==="ping"||e.type==="pong"?d(e.payload)&&typeof e.payload.id=="string":e.type==="auth-ok"&&d(e.payload)&&e.payload.ok===!0}},A=class{constructor(e){this.options=e,this.session={subscribedCompositionKeys:new Set},this.messageQueue=new P,this.eventDispatcher=new B(n=>{this.sendJson(n)}),this.unsubscribe=e.engine.subscribe(n=>{this.handleEngineEvent(n)}),e.socket.onmessage=n=>{let i=ge(n.data);if(i!==null){this.sendJson({type:"pong",payload:{id:i}});return}try{this.messageQueue.enqueue(()=>this.handleRawMessage(n.data))}catch(o){this.sendJson({type:"error",payload:{id:"unknown",kind:"get-document",message:o instanceof Error?o.message:"Bridge queue overflow"}}),e.socket.close(1013,"bridge busy")}},e.socket.onclose=()=>{this.unsubscribe(),this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.clear()}}async handleMessage(e){try{let n=e;if(n.type!=="request")return;let i=n.payload,{id:o}=i;switch(i.kind){case"initialize-session":{let s=D(i),r=await this.resolve(s),a=await this.options.engine.initializeSession({composition:r.context,...i.mutationContext?{mutationContext:i.mutationContext}:{}});this.activateSession(r.context),this.sendJson({type:"response",payload:{id:o,kind:i.kind,result:I({...a,data:{...a.data,document:ee(a.data.document)}},r.context)}});return}case"get-document":{let s=await this.resolveWireContext(i),r=await this.options.engine.getDocument(s);this.sendResult(o,i.kind,Ee(r,s));return}case"get-history-status":{let s=await this.resolveWireContext(i),r=await this.options.engine.getHistoryStatus(s);this.sendResult(o,i.kind,I(r,s));return}case"get-style-context":{let s=await this.resolveWireContext(i);this.sendResult(o,i.kind,I(await this.options.engine.getStyleContext(s),s));return}case"get-object-edit-context":{let s=await this.resolveWireContext(i);this.sendResult(o,i.kind,I(await this.options.engine.getObjectEditContext(i.key,s,i.selectionRef),s));return}case"get-class-name-source":{let s=await this.resolveWireContext(i);this.sendResult(o,i.kind,I(await this.options.engine.getClassNameSource(i.key,s),s));return}case"get-source-tree":{let s=await this.resolveWireContext(i);this.sendResult(o,i.kind,I(await this.options.engine.getSourceTree(s),s));return}case"get-source-file":{let s=await this.resolveWireContext(i);this.sendResult(o,i.kind,I(await this.options.engine.getSourceFile({path:i.path,composition:s}),s));return}case"resolve-source-location":{let s=await this.resolveWireContext(i),r=I(await this.options.engine.resolveSourceLocation({key:i.key,composition:s}),s);this.sendResult(o,i.kind,r);return}case"execute":case"get-operation-result":{let s=await this.resolveOperationWireContext(i.request),r=await this.resolveWireContext(i);if(w(s)!==w(r))throw new E(Z,"The execute request composition does not match its operation composition.");let a=await this.normalizeOperationRequest(i.request,s);if(i.kind==="get-operation-result"){this.sendResult(o,i.kind,await this.options.engine.getOperationResult?.(a)??null);return}await this.handleBufferedOperation(o,i.kind,()=>this.options.engine.execute(a));return}case"undo":case"redo":{let s=await this.resolveWireContext(i);await this.handleBufferedOperation(o,i.kind,()=>i.kind==="undo"?this.options.engine.undo(s):this.options.engine.redo(s));return}}}catch(n){let i=f.extractRequestInfo(e);this.sendJson({type:"error",payload:{...i,message:F(n),code:K(n)}})}}async resolve(e){return this.options.engine.resolveComposition(e)}async resolveAndAssert(e){let n=await this.resolve(e),i=this.session.activeComposition;if(!i)throw new E(V,"Initialize a composition session before issuing this request.");if(w(i)!==w(n.context))throw new E(Z,"The request composition does not match the active socket session.");return n.context}async resolveWireContext(e){let n=D(e);return this.resolveAndAssert(n)}async resolveOperationWireContext(e){let n=Se(e);return this.resolveAndAssert(n)}activateSession(e){this.session.activeComposition=e,this.session.subscribedCompositionKeys.clear(),this.session.subscribedCompositionKeys.add(w(e))}async normalizeOperationRequest(e,n){let i={...e,composition:X(n)};if(i.target.kind==="route"){let o=i.target,s=D(o),r=await this.resolveAndAssert(s);i.target={...o,composition:X(r)}}return i}handleEngineEvent(e){let n=e?.composition;if(!Ie(n))return;let i=w(n);this.session.subscribedCompositionKeys.has(i)&&(this.eventDispatcher.push(e),e.type==="site-edit.graph.invalidated"&&i===we(this.session)&&(this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.delete(i)))}sendResult(e,n,i){this.sendJson({type:"response",payload:{id:e,kind:n,result:i}})}async handleRawMessage(e){try{await this.handleMessage(f.parseMessage(e,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let i=Ce(e);this.sendJson({type:"error",payload:{...i,message:F(n),code:K(n)}})}}sendJson(e){this.options.socket.send(f.stringifyMessage(e))}async handleBufferedOperation(e,n,i){this.eventDispatcher.beginBuffering();try{let o=await i();this.sendResult(e,n,o)}catch(o){this.sendJson({type:"error",payload:{id:e,kind:n,message:F(o),code:K(o)}})}finally{this.eventDispatcher.flush()}}},le=256;function ge(t){if(t.length>le||!t.includes('"ping"'))return null;try{let e=JSON.parse(t);return d(e)&&e.type==="ping"&&d(e.payload)&&typeof e.payload.id=="string"?e.payload.id:null}catch{return null}}var E=class extends Error{constructor(e,n){super(n),this.code=e,this.name="BridgeProtocolError"}},B=class{constructor(e){this.send=e,this.bufferedEvents=null}push(e){let n={type:"event",payload:{event:e}};if(this.bufferedEvents){this.bufferedEvents.push(n);return}this.send(n)}beginBuffering(){this.bufferedEvents=[]}flush(){let e=this.bufferedEvents??[];this.bufferedEvents=null,e.forEach(n=>this.send(n))}},P=class{constructor(e=64){this.maxQueued=e,this.queue=Promise.resolve(),this.queued=0}enqueue(e){if(this.queued>=this.maxQueued)throw new Error("Bridge request queue is full");this.queued+=1;let n=async()=>{this.queued-=1,await e()},i=this.queue.then(n,n);return this.queue=i.then(()=>{},()=>{}),i}};function fe(t,e){if(!d(t)||typeof t.id!="string"||!f.isRequestKind(t.kind))return!1;let n=v(t.composition);switch(t.kind){case"initialize-session":return n&&(t.mutationContext===void 0||N(t.mutationContext));case"get-document":case"get-style-context":return n;case"get-object-edit-context":return typeof t.key=="string"&&n&&me(t.selectionRef);case"get-class-name-source":case"resolve-source-location":return typeof t.key=="string"&&n;case"get-history-status":return n;case"get-source-tree":return n;case"get-source-file":return typeof t.path=="string"&&n;case"execute":case"get-operation-result":return n&&ye(t.request)&&(t.request.mutationContext===void 0||N(t.request.mutationContext))&&(e.validateOperationRequest?.(t.request)??!0);case"undo":case"redo":return n&&t.mutationContext===void 0||n&&N(t.mutationContext)}}function me(t){if(t===void 0)return!0;if(!d(t)||typeof t.key!="string"||t.key.length===0)return!1;let e=t.instanceIndex,n=t.instanceCount;return e!==void 0&&(typeof e!="number"||!Number.isInteger(e)||e<0)||n!==void 0&&(typeof n!="number"||!Number.isInteger(n)||n<=0)||typeof e=="number"&&typeof n=="number"&&e>=n?!1:Object.keys(t).every(i=>i==="key"||i==="instanceIndex"||i==="instanceCount")}function ye(t){return d(t)&&typeof t.id=="string"&&typeof t.siteId=="string"&&typeof t.baseSnapshotId=="string"&&v(t.composition)&&typeof t.kind=="string"&&d(t.target)&&(t.target.kind!=="route"||v(t.target.composition))&&typeof t.documentVersion=="number"&&Number.isInteger(t.documentVersion)&&t.documentVersion>=0&&d(t.params)}function v(t){return d(t)&&typeof t.routeId=="string"&&t.routeId.length>0&&(t.templateId===void 0||typeof t.templateId=="string"&&t.templateId.length>0)&&Object.keys(t).every(e=>e==="routeId"||e==="templateId")}function Ie(t){return d(t)&&typeof t.routeId=="string"&&(t.effectiveTemplateId===void 0||typeof t.effectiveTemplateId=="string")}function D(t){if(v(t.composition))return t.composition;throw new E(V,"A composition selector is required for this request.")}function Se(t){if(d(t)&&v(t.composition))return t.composition;throw new E(V,"A composition selector is required for execute.")}function X(t){return{routeId:t.routeId,...t.effectiveTemplateId?{templateId:t.effectiveTemplateId}:{}}}function we(t){return t.activeComposition?w(t.activeComposition):null}function w(t){return JSON.stringify([t.routeId,t.effectiveTemplateId??null])}function I(t,e){return d(t)&&t.ok===!0?{...t,composition:e}:t}function Ee(t,e){return!d(t)||t.ok!==!0?t:{...t,...e?{composition:e}:{},data:ee(t.data)}}function ee(t){let e=t;return Y(e.routeId?e:{...e,routeId:e.composition.routeId})}function d(t){return typeof t=="object"&&t!==null}function N(t){return d(t)&&typeof t.editingSessionId=="string"&&t.editingSessionId.length>0&&typeof t.lockId=="string"&&t.lockId.length>0&&typeof t.ownerId=="string"&&t.ownerId.length>0&&typeof t.leaseAssertion=="string"&&t.leaseAssertion.length>0}function K(t){if(!(!d(t)||typeof t.code!="string"||t.code.length===0))return t.code}function F(t){return t instanceof Error||d(t)&&typeof t.message=="string"&&t.message.length>0?t.message:"Unknown websocket operation failure"}function Ce(t){try{return f.extractRequestInfo(JSON.parse(t))}catch{return{id:"unknown",kind:"get-document"}}}var H=class{constructor(){this.tail=Promise.resolve()}run(e){let n=this.tail.then(e,e);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as Me,computeSiteEditCompositionKey as C}from"@vcp-dev/contracts";var De="127.0.0.1",Ne=30218,Ke="/__vcp/site-edit/bridge",ae=6*1024*1024,Fe=ae*2,Be=3e3,te=50,j=".vcp/current-snapshot.json",ne="__VCP_SITE_EDIT_BRIDGE_SERVERS__";var $=class{constructor(e,n=oe.createServer((s,r)=>{r.writeHead(404),r.end("VCP site edit bridge server")}),i=!0,o={}){this.options=e,this.server=n,this.closeHttpServerOnStop=i,this.dependencies=o,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new H,this.operationReceipts=new Map,this.eventListeners=new Set,this.routes=[],this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.launchCandidateEntries=null,this.stopWatching=null,this.stopWebSocketServer=null,this.pendingChangedFiles=new Set,this.fileChangeTimer=null,this.isStarted=!1,this.stopping=!1,this.runtimeVersionFloors=new Map,this.activeHistoryStore=null,this.versionPersistence=Promise.resolve(),this.bridgeEngine=null,this.lastNotifiedHealth=null}start(e={}){if(!this.isStarted){this.stopping=!1;try{_.mkdirSync(m.join(this.options.projectRoot,".vcp"),{recursive:!0})}catch(n){console.warn(`[vcp-site-edit] failed to ensure .vcp directory: ${n instanceof Error?n.message:String(n)}`)}if(this.refreshCompositions(!0),this.isStarted=!0,this.createRuntime(),this.bridgeEngine=this.createBridgeEngine(),e.webSocket!==!1){let n=ze({server:this.server,path:this.options.path,sessionKey:this.options.sessionKey,host:this.options.host,allowedOrigins:this.options.allowedOrigins,engine:this.bridgeEngine});this.stopWebSocketServer=n.stop}this.stopWatching=new He({projectRoot:this.options.projectRoot,watch_dirs:Ve(this.options.runtime.watch_dirs),interval_ms:this.options.runtime.watch_interval_ms,debounce_ms:this.options.runtime.watch_debounce_ms??te,max_dirs:this.options.runtime.watch_max_dirs,max_files:this.options.runtime.watch_max_files,max_depth:this.options.runtime.watch_max_depth,max_startup_ms:this.options.runtime.watch_max_startup_ms,on_change:n=>this.handleFileChanged(n),on_batch_change:n=>this.handleFilesChanged(n)}).start(),e.listen!==!1&&this.server.listen(this.options.port,this.options.host),this.notifyHealthChanged()}}getEngine(){if(!this.bridgeEngine)throw new Error("VCP site edit preview bridge runtime is not ready");return this.bridgeEngine}getHealth(){let e=!!this.runtime&&!this.compositionResolutionError;return{ok:this.isStarted&&e,bridgePath:this.options.path,runtimeReady:e,routesIndexed:this.routes.length,sessionRequired:!!this.options.sessionKey}}stop(){this.stopping=!0,this.stopWatching?.(),this.stopWatching=null,this.flushChangedFiles(),this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.stopWebSocketServer?.(),this.stopWebSocketServer=null,this.runtime?.dispose(),this.runtime=null,this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.closeHttpServerOnStop&&this.server.close();let e=$e(),n=qe(this.options);e.get(n)===this&&e.delete(n),this.isStarted=!1,this.notifyHealthChanged()}notifyHealthChanged(){let e=this.options.onHealthChanged;if(!e)return;let n=this.getHealth(),i=JSON.stringify(n);if(i!==this.lastNotifiedHealth){this.lastNotifiedHealth=i;try{e(n)}catch(o){console.warn(`[vcp-site-edit] onHealthChanged callback failed: ${o instanceof Error?o.message:String(o)}`)}}}createRuntime(e,n=null){let i=this.runtime;this.activeHistoryStore=e??null;let o=(this.dependencies.createRuntime??ke)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,compositions:this.compositions,onEvent:s=>{"version"in s&&typeof s.version=="number"&&this.recordRuntimeVersion(s.version,"composition"in s?s.composition:void 0);for(let r of this.eventListeners)r(s)},...e?{historyStore:e}:{},...this.getInitialDocumentVersions().length>0?{initialDocumentVersions:this.getInitialDocumentVersions()}:{}});this.runtime=o,this.activeHistorySessionId=n,i?.dispose(),this.notifyHealthChanged()}createBridgeEngine(){return{resolveComposition:e=>this.resolveComposition(e),initializeSession:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)throw new Error(`${n.code}: ${n.message}`);let i=We(e.composition),o=this.getRuntime(),s=y(i);await o.initializeComposition(s);let a=o.getDocument(s).data;await this.recordRuntimeVersion(a.version,i);let p=(await o.getHistoryStatus()).data;return{ok:!0,composition:i,data:{document:a,historyStatus:p}}}),getDocument:async e=>{let n=this.getRuntime(),i=y(e);await n.initializeComposition(i);let o=n.getDocument(i);return await this.recordRuntimeVersion(o.data.version,e),{...o,composition:e}},getHistoryStatus:async e=>({...await this.getRuntime().getHistoryStatus(),composition:e}),getStyleContext:async e=>this.getRuntime().getStyleContext(y(e)),getObjectEditContext:async(e,n,i)=>this.getRuntime().getObjectEditContext(e,y(n),i),getClassNameSource:async(e,n)=>this.getRuntime().getClassNameSource(e,y(n)),getSourceTree:async e=>this.getRuntime().getSourceTree(y(e)),getSourceFile:async e=>this.getRuntime().getSourceFile({path:e.path,composition:y(e.composition)}),resolveSourceLocation:async e=>this.getRuntime().resolveSourceLocation({key:e.key,composition:y(e.composition)}),execute:(e,n)=>this.executeWithReceipt(e,()=>this.mutationQueue.run(async()=>{let i=await this.bindHistorySession(this.options.snapshotId);if(!i.ok)return U(i,e);let o=await this.getRuntime().executeOperation(e);return o.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(o.data.resultVersion,o.composition),o})),getOperationResult:e=>{if(e.siteId!==this.options.siteId||e.baseSnapshotId!==this.options.snapshotId)return null;let n=this.operationReceipts.get(this.operationReceiptKey(e));return n?.fingerprint===this.operationFingerprint(e)?n.result:null},undo:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return U(n,void 0,e);let i=await this.getRuntime().undo();return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion,i.composition??e),i.composition?i:{...i,composition:e}}),redo:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return U(n,void 0,e);let i=await this.getRuntime().redo();return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion,i.composition??e),i.composition?i:{...i,composition:e}}),subscribe:e=>(this.eventListeners.add(e),()=>this.eventListeners.delete(e))}}operationReceiptKey(e){return JSON.stringify([e.siteId,e.baseSnapshotId,e.id])}operationFingerprint(e){return J("sha256").update(JSON.stringify(e)).digest("hex")}executeWithReceipt(e,n){if(e.siteId!==this.options.siteId||e.baseSnapshotId!==this.options.snapshotId)return n();let i=this.operationReceiptKey(e),o=this.operationFingerprint(e),s=this.operationReceipts.get(i);if(s)return s.fingerprint!==o?Promise.reject(new Error("Operation ID was reused with a different request.")):s.result;let r={fingerprint:o,result:n(),settled:!1};return this.operationReceipts.set(i,r),r.result.then(()=>{r.settled=!0;for(let[a,p]of this.operationReceipts){if(this.operationReceipts.size<=256)break;p.settled&&this.operationReceipts.delete(a)}},()=>{this.operationReceipts.delete(i)}),r.result}async bindHistorySession(e){if(!this.options.historyRoot)return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history is not configured."};if(this.activeHistorySessionId===e)return{ok:!0};let n=this.historySessionTransition.then(async()=>{if(this.activeHistorySessionId===e)return;let i=await(this.dependencies.openHistoryStore??ve)(je({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e}));if(!this.isStarted)return;let o=i.readDocumentVersion?await i.readDocumentVersion():0,s=this.compositions[0]?.context;if(s){let r=C(s);this.runtimeVersionFloors.set(r,Math.max(this.runtimeVersionFloors.get(r)??0,o))}this.createRuntime(i,e)});this.historySessionTransition=n.catch(()=>{});try{return await n,{ok:!0}}catch{return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history could not be opened."}}}recordRuntimeVersion(e,n){let i=n?C(n):null;i&&this.runtimeVersionFloors.set(i,Math.max(this.runtimeVersionFloors.get(i)??0,e));let o=this.activeHistoryStore;if(!o?.writeDocumentVersion)return Promise.resolve();let s=i?this.runtimeVersionFloors.get(i)??e:e;return this.versionPersistence=this.versionPersistence.then(()=>o.writeDocumentVersion(s)).catch(()=>{}),this.versionPersistence}handleFileChanged(e){this.handleFilesChanged([e])}handleFilesChanged(e){for(let i of e)this.pendingChangedFiles.add(i);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let n=this.options.runtime.watch_debounce_ms??te;this.fileChangeTimer=setTimeout(()=>{this.fileChangeTimer=null,this.flushChangedFiles()},n),this.fileChangeTimer?.unref?.()}flushChangedFiles(){if(this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.pendingChangedFiles.size===0)return;let e=[...this.pendingChangedFiles].sort((n,i)=>n.localeCompare(i));if(this.pendingChangedFiles.clear(),e.some(et)&&this.scheduleSnapshotIdentityAdvance(),!!this.refreshCompositions(!1))for(let n of e)this.runtime?.notifyFileChanged(n)}scheduleSnapshotIdentityAdvance(){if(this.stopping||!this.isStarted)return;let e=this.readCurrentSnapshotMarker();!e||e===this.options.snapshotId||this.mutationQueue.run(()=>this.advanceSnapshotId(e)).catch(n=>{console.warn(`[vcp-site-edit] snapshot identity advance failed: ${n instanceof Error?n.message:String(n)}`)})}readCurrentSnapshotMarker(){try{let e=_.readFileSync(m.join(this.options.projectRoot,j),"utf8"),n=JSON.parse(e),i=typeof n.snapshot_id=="string"?n.snapshot_id:"";return!i||q(i)!==i?(console.warn("[vcp-site-edit] current-snapshot marker has no valid snapshot_id"),null):i}catch(e){return console.warn(`[vcp-site-edit] current-snapshot marker unreadable: ${e instanceof Error?e.message:String(e)}`),null}}async advanceSnapshotId(e){if(this.stopping||!this.isStarted||e===this.options.snapshotId)return;let n=this.options.snapshotId;this.options.snapshotId=e;try{if(this.options.historyRoot){let o=await this.bindHistorySession(e);if(!o.ok)throw new Error(`${o.code}: ${o.message}`)}else this.createRuntime()}catch(o){this.options.snapshotId=n,console.warn(`[vcp-site-edit] snapshot identity advance rolled back to ${n}: ${o instanceof Error?o.message:String(o)}`);return}let i=new Date().toISOString();for(let o of this.compositions){let s={type:"site-edit.snapshot.changed",siteId:this.options.siteId,composition:o.context,snapshotId:e,previousSnapshotId:n,at:i};for(let r of this.eventListeners)try{r(s)}catch(a){console.warn(`[vcp-site-edit] snapshot.changed listener failed: ${a instanceof Error?a.message:String(a)}`)}}try{this.options.onSnapshotIdChanged?.(e,n)}catch(o){console.warn(`[vcp-site-edit] onSnapshotIdChanged callback failed: ${o instanceof Error?o.message:String(o)}`)}}refreshCompositions(e=!1){try{let n=Ue(this.options.projectRoot),i=Ae(n),o=i.length>0?i:(this.options.runtime.routes??[]).map(u=>({...u})),s=Te({files:n});if(s.length===0)throw new Error("No canonical App Router composition is indexed by the preview bridge.");let r=JSON.stringify(s.filter(u=>u.context.effectiveTemplateId!==void 0).map(u=>u.entryFile).sort());if(e&&(this.launchCandidateEntries=r),this.launchCandidateEntries!==r)throw new Error("SITE_EDIT_RUNTIME_RELOAD_REQUIRED: Candidate route collection changed since preview launch");let a=new Map(this.compositions.map(u=>[C(u.context),u])),p=new Map(s.map(u=>[C(u.context),u]));if(this.runtime){for(let[u,l]of a){let S=p.get(u);(!S||S.entryFile!==l.entryFile)&&this.runtime.unregisterComposition(y(l.context))}for(let[u,l]of p){let S=a.get(u);(!S||S.entryFile!==l.entryFile)&&this.runtime.registerComposition(l.context,l.entryFile)}}return this.routes=o,this.compositions=s,this.compositionFiles=n,this.compositionResolutionError=null,this.notifyHealthChanged(),!0}catch(n){let i=n instanceof Error?n:new Error(String(n));if(this.compositionResolutionError=i,e)throw i;return this.notifyHealthChanged(),!1}}resolveComposition(e){if(this.compositionResolutionError)throw this.compositionResolutionError;let n=Oe(e.routeId,this.routes,this.options.basePath);if(!n)throw new Error(`Route is not indexed by the preview bridge: ${e.routeId}`);let i=this.routes.find(r=>r.routeId===n);if(!i)throw new Error(`Route is not indexed by the preview bridge: ${e.routeId}`);let o=_e({selector:{routeId:n,...e.templateId!==void 0?{templateId:e.templateId}:{}},files:this.compositionFiles,canonicalEntryFile:i.entryFile}),s=C(o.context);if(!this.compositions.some(r=>C(r.context)===s))throw new Error(`Composition is not registered by the preview bridge: ${s}`);return o}getInitialDocumentVersions(){return this.compositions.flatMap(e=>{let n=this.runtimeVersionFloors.get(C(e.context));return n===void 0?[]:[{composition:e.context,version:n}]})}getRuntime(){if(!this.runtime)throw new Error("VCP site edit preview bridge runtime is not ready");return this.runtime}};function ce(t){let e=new $(Pe(t),oe.createServer(),!1);return e.start({listen:!1,webSocket:!1}),{engine:e.getEngine(),getHealth:()=>e.getHealth(),stop:()=>e.stop()}}function Pe(t){return{tenantId:t.tenantId,siteId:t.siteId,snapshotId:t.snapshotId,projectRoot:m.resolve(t.projectRoot??process.cwd()),basePath:t.basePath??"",host:t.host??De,port:t.port??Ne,path:t.path??Ke,sessionKey:t.sessionKey??Xe(),historyRoot:t.historyRoot,allowedOrigins:t.allowedOrigins??[],runtime:t.runtime??{},onSnapshotIdChanged:t.onSnapshotIdChanged,onHealthChanged:t.onHealthChanged}}function Ve(t){let e=["app","src/app","components/site","src/components/site","lib/generated","src/lib/generated","public"];return[...new Set([...t?.length?t:e,".vcp"])]}function Ue(t){let e=[],n=["app","src/app",".vcp"],i=new Set([".git",".next",".turbo","coverage","dist","node_modules","output","snapshot-store"]),o=s=>{if(!_.existsSync(s))return;let r;try{r=_.readdirSync(s,{withFileTypes:!0})}catch{return}for(let a of r){if(a.name.startsWith(".")&&a.name!==".vcp")continue;if(a.isDirectory()){if(i.has(a.name))continue;o(m.join(s,a.name));continue}if(!a.isFile())continue;let p=m.join(s,a.name),u="";try{u=_.readFileSync(p,"utf8")}catch{}e.push({path:m.relative(t,p).replaceAll(m.sep,"/"),content:u})}};for(let s of n)o(m.join(t,s));return e.sort((s,r)=>s.path.localeCompare(r.path))}function We(t){if(!t||typeof t!="object"||typeof t.routeId!="string")throw new Error("Normalized composition context is required.");if("effectiveTemplateId"in t)return t;let e=t;return{routeId:e.routeId,...e.templateId?{effectiveTemplateId:e.templateId}:{}}}function y(t){return{routeId:t.routeId,...t.effectiveTemplateId?{templateId:t.effectiveTemplateId}:{}}}function je(t){return m.join(m.resolve(t.root),q(t.siteId),q(t.snapshotId),J("sha256").update(t.editingSessionId).digest("hex").slice(0,32))}function q(t){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(t)&&t!=="."&&t!==".."?t:J("sha256").update(t).digest("hex").slice(0,32)}function $e(){let t=globalThis;return t[ne]??=new Map,t[ne]}function qe(t){return`${t.projectRoot}:${t.host}:${t.port}:${t.path}:${t.historyRoot??""}`}function U(t,e,n){let i=n??(e?.composition?{routeId:e.composition.routeId,...e.composition.templateId?{effectiveTemplateId:e.composition.templateId}:{}}:void 0);if(!i)throw new Error("A normalized composition is required to construct a mutation result.");return{ok:!1,composition:i,error:{code:t.code,message:t.message},data:{requestId:e?.id??"mutation-context",kind:e?.kind??"update-text",composition:i,target:e?.target??{kind:"node",key:""},resultVersion:e?.documentVersion??0}}}function ze(t){let e=new be({maxPayload:ae,noServer:!0}),n=new WeakSet,i=s=>{new A({socket:new z(s),engine:t.engine,validateOperationRequest:r=>Me.safeParse(r).success})},o=(s,r,a)=>{if(!Le(s,t))return;let p=Je(s,t);if(!p){r.write(`HTTP/1.1 401 Unauthorized\r
2
2
  Connection: close\r
3
3
  \r
4
- `),r.destroy();return}p.authenticatedByHeader&&n.add(s),e.handleUpgrade(s,r,a,u=>{e.emit("connection",u,s)})};return t.server.on("upgrade",o),e.on("connection",(s,r)=>{if(n.has(r)){i(s);return}Le(s,t.sessionKey,()=>{i(s)})}),e.stop=()=>{t.server.off("upgrade",o);for(let s of e.clients)s.close(1001,"bridge stopped");e.close()},e}var z=class{constructor(e){this.socket=e,this.onmessage=null,this.onclose=null,e.on("error",()=>{}),e.on("message",n=>{this.onmessage?.({data:n.toString()})}),e.on("close",()=>{this.onclose?.()})}send(e){if(this.socket.readyState===re.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(e,"utf8")>Ke){this.socket.close(1013,"bridge backpressure");return}this.socket.send(e)}}close(e,n){this.socket.close(e,n)}};function qe(t,e){return new URL(t.url??"/","http://localhost").pathname===e.path}function ze(t,e){if(new URL(t.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let i=W(t.headers["x-vcp-site-edit-key"]),o=ue(i,e.sessionKey),s=W(t.headers.host),r=W(t.headers.origin);return s&&!Je(s,e.host,r)?null:r?Ge(r,s,e.allowedOrigins)?{authenticatedByHeader:o}:null:o?{authenticatedByHeader:!0}:null}function Le(t,e,n){let i=setTimeout(()=>{t.close(1008,"bridge authentication timeout")},Fe),o,s=()=>{clearTimeout(i),t.off("message",o),t.off("close",s)},r=()=>{s(),t.close(1008,"bridge authentication failed")};o=a=>{try{let p=g.parseMessage(a.toString());if(p.type!=="auth"||!ue(p.payload.key,e)){r();return}s(),t.readyState===re.OPEN&&t.send(g.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),n()}catch{r()}},t.on("message",o),t.on("close",s)}function ue(t,e){if(!t)return!1;let n=Buffer.from(t),i=Buffer.from(e);return n.length===i.length&&Ee(n,i)}function Je(t,e,n){let i=se(t),o=se(e);return i===o||ie(o)&&ie(i)&&(!n||L(n,t))||Qe(o)&&(!n||L(n,t))}function Qe(t){return t==="0.0.0.0"||t==="::"}function ie(t){return t==="localhost"||t==="127.0.0.1"||t==="::1"}function Ge(t,e,n){return n.includes(t)||(e?L(t,e):!1)}function L(t,e){try{return new URL(t).host===de(e)}catch{return!1}}function se(t){let e=de(t);return e.startsWith("[")?e.slice(1,e.indexOf("]")):e.split(":").length-1===1?e.split(":",1)[0]??"":e}function de(t){return t.trim().toLowerCase()}function W(t){return Array.isArray(t)?t[0]:t}function Ye(){return Ce(32).toString("base64url")}function Ze(t){let e=t.replaceAll("\\","/");return e===j||e.endsWith(`/${j}`)}var x=Xe;if(!x)throw new Error("site edit engine worker must run inside worker_threads");var R=t=>{x.postMessage(t)},tt=(t,e)=>{try{x.postMessage({type:"result",id:t,ok:!0,value:e})}catch(n){if(n instanceof Error&&n.name==="DataCloneError"){x.postMessage({type:"result",id:t,ok:!0,value:e===void 0?void 0:JSON.parse(JSON.stringify(e))});return}throw n}},{options:nt}=et,T;try{T=ce({...nt,onSnapshotIdChanged:(t,e)=>{R({type:"snapshot-id-changed",next:t,previous:e})},onHealthChanged:t=>{R({type:"health",health:t})}})}catch(t){R({type:"start-failed",error:M(t)}),process.exit(1)}T.engine.subscribe(t=>{R({type:"event",event:t})});var it=async t=>{try{let e=T.engine[t.method],n=await e(...t.args);tt(t.id,n)}catch(e){R({type:"result",id:t.id,ok:!1,error:M(e)})}};x.on("message",t=>{if(t.type==="call"){it(t);return}t.type==="stop"&&(T.stop(),x.close())});R({type:"ready",health:T.getHealth()});
4
+ `),r.destroy();return}p.authenticatedByHeader&&n.add(s),e.handleUpgrade(s,r,a,u=>{e.emit("connection",u,s)})};return t.server.on("upgrade",o),e.on("connection",(s,r)=>{if(n.has(r)){i(s);return}Qe(s,t.sessionKey,()=>{i(s)})}),e.stop=()=>{t.server.off("upgrade",o);for(let s of e.clients)s.close(1001,"bridge stopped");e.close()},e}var z=class{constructor(e){this.socket=e,this.onmessage=null,this.onclose=null,e.on("error",()=>{}),e.on("message",n=>{this.onmessage?.({data:n.toString()})}),e.on("close",()=>{this.onclose?.()})}send(e){if(this.socket.readyState===re.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(e,"utf8")>Fe){this.socket.close(1013,"bridge backpressure");return}this.socket.send(e)}}close(e,n){this.socket.close(e,n)}};function Le(t,e){return new URL(t.url??"/","http://localhost").pathname===e.path}function Je(t,e){if(new URL(t.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let i=W(t.headers["x-vcp-site-edit-key"]),o=de(i,e.sessionKey),s=W(t.headers.host),r=W(t.headers.origin);return s&&!Ge(s,e.host,r)?null:r?Ze(r,s,e.allowedOrigins)?{authenticatedByHeader:o}:null:o?{authenticatedByHeader:!0}:null}function Qe(t,e,n){let i=setTimeout(()=>{t.close(1008,"bridge authentication timeout")},Be),o,s=()=>{clearTimeout(i),t.off("message",o),t.off("close",s)},r=()=>{s(),t.close(1008,"bridge authentication failed")};o=a=>{try{let p=f.parseMessage(a.toString());if(p.type!=="auth"||!de(p.payload.key,e)){r();return}s(),t.readyState===re.OPEN&&t.send(f.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),n()}catch{r()}},t.on("message",o),t.on("close",s)}function de(t,e){if(!t)return!1;let n=Buffer.from(t),i=Buffer.from(e);return n.length===i.length&&xe(n,i)}function Ge(t,e,n){let i=se(t),o=se(e);return i===o||ie(o)&&ie(i)&&(!n||L(n,t))||Ye(o)&&(!n||L(n,t))}function Ye(t){return t==="0.0.0.0"||t==="::"}function ie(t){return t==="localhost"||t==="127.0.0.1"||t==="::1"}function Ze(t,e,n){return n.includes(t)||(e?L(t,e):!1)}function L(t,e){try{return new URL(t).host===ue(e)}catch{return!1}}function se(t){let e=ue(t);return e.startsWith("[")?e.slice(1,e.indexOf("]")):e.split(":").length-1===1?e.split(":",1)[0]??"":e}function ue(t){return t.trim().toLowerCase()}function W(t){return Array.isArray(t)?t[0]:t}function Xe(){return Re(32).toString("base64url")}function et(t){let e=t.replaceAll("\\","/");return e===j||e.endsWith(`/${j}`)}var x=tt;if(!x)throw new Error("site edit engine worker must run inside worker_threads");var R=t=>{x.postMessage(t)},it=(t,e)=>{try{x.postMessage({type:"result",id:t,ok:!0,value:e})}catch(n){if(n instanceof Error&&n.name==="DataCloneError"){x.postMessage({type:"result",id:t,ok:!0,value:e===void 0?void 0:JSON.parse(JSON.stringify(e))});return}throw n}},{options:st}=nt,T;try{T=ce({...st,onSnapshotIdChanged:(t,e)=>{R({type:"snapshot-id-changed",next:t,previous:e})},onHealthChanged:t=>{R({type:"health",health:t})}})}catch(t){R({type:"start-failed",error:M(t)}),process.exit(1)}T.engine.subscribe(t=>{R({type:"event",event:t})});var ot=async t=>{try{let e=T.engine[t.method],n=await e(...t.args);it(t.id,n)}catch(e){R({type:"result",id:t.id,ok:!1,error:M(e)})}};x.on("message",t=>{if(t.type==="call"){ot(t);return}t.type==="stop"&&(T.stop(),x.close())});R({type:"ready",health:T.getHealth()});