@vcp-dev/site-edit-bridge 0.10.14 → 0.10.16

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.
@@ -1,6 +1,6 @@
1
- import ue from"node:http";import{existsSync as _,readFileSync as b}from"node:fs";import{randomUUID as Bt}from"node:crypto";import{createRequire as Be}from"node:module";import{Socket as Pe}from"node:net";import H from"node:path";import{basename as De,dirname as A,join as B}from"node:path";import{fileURLToPath as Oe}from"node:url";import et from"node:http";import P from"node:fs";import{createHash as Ee,randomBytes as tt,timingSafeEqual as nt}from"node:crypto";import g from"node:path";import{WebSocket as ve,WebSocketServer as st}from"ws";import{createPreviewSiteEditEngineRuntime as it,openFileHistoryStore as ot,resolveManagedPageTemplateComposition as rt,scanSiteEditCompositionsFromFiles as at}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as ct,scanNextAppRouterRoutesFromFiles as ut}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as dt}from"@vcp-dev/site-edit-engine/source-watcher";function ge(e){let t=new Map,n=[],s=new Map,i=[],o=e.pageComposition.status==="supported"?e.pageComposition.placementUnitKeys:[],r=new Map(o.map((u,p)=>[u,p])),c=e.pageComposition.status==="supported"?{status:"supported",containerKey:e.pageComposition.containerKey,hasBusinessMembers:e.pageComposition.hasBusinessMembers,placementUnitKeys:e.pageComposition.placementUnitKeys,placements:e.pageComposition.placements.map(u=>({placementKey:u.placementKey,placementUnitKey:u.placementUnitKey,relation:u.relation}))}:{status:"unsupported",reason:e.pageComposition.reason,diagnostics:e.pageComposition.diagnostics},d=e.entries.flatMap((u,p)=>u.dynamicDataSummary?[[p,u.dynamicDataSummary]]:[]),a=u=>{let p=t.get(u);if(typeof p=="number")return p;let l=n.length;return n.push(u),t.set(u,l),l},h=u=>{let p=null;for(let l of We(u)){let S=`${p??"root"}\0${l}`,I=s.get(S);if(typeof I=="number"){p=I;continue}let w=i.length;i.push([p,a(l)]),s.set(S,w),p=w}if(p===null)throw new Error("Document key must not be empty");return p};return{encoding:"segment-trie-v1",siteId:e.siteId,snapshotId:e.snapshotId,composition:{...e.composition},routeId:e.composition.routeId,version:e.version,pageComposition:c,tokens:n,nodes:i,entries:e.entries.map(u=>{let p=u.pageCompositionUnitKey?r.get(u.pageCompositionUnitKey)??-1:-1;if(u.pageCompositionUnitKey&&p<0)throw new Error(`Render entry ${u.key} references unknown page composition unit ${u.pageCompositionUnitKey}`);return[h(u.key),$e(u.operationSummary),...p>=0?[p]:[]]}),...d.length>0?{dynamicDataSummaries:d}:{}}}function $e(e){return(e.canUpdateText?1:0)|(e.canInsertChild?2:0)|(e.canMove?4:0)|(e.canRemove?8:0)}function We(e){if(!e)return[];let t=[],n=0,s=me(e,n);if(s<0)return t.push(e),t;for(t.push(e.slice(0,s)),n=s;n<e.length;){if(s=me(e,n+(e.startsWith("::",n)?2:1)),s<0){t.push(e.slice(n));break}t.push(e.slice(n,s)),n=s}return t}function me(e,t){let n=e.indexOf("/",t),s=e.indexOf("::",t);return n<0?s:s<0?n:Math.min(n,s)}var ye="SITE_EDIT_COMPOSITION_SESSION_MISMATCH",Z="SITE_EDIT_COMPOSITION_SESSION_REQUIRED";var m=class e{constructor(){}static parseMessage(t,n={}){let s=JSON.parse(t);if(!e.isMessage(s,n))throw new Error("Invalid site edit bridge message");return s}static stringifyMessage(t){return JSON.stringify(t)}static extractRequestInfo(t){if(f(t)&&f(t.payload)){let n=typeof t.payload.id=="string"?t.payload.id:"unknown",s=this.isRequestKind(t.payload.kind)?t.payload.kind:"get-document";return{id:n,kind:s}}return{id:"unknown",kind:"get-document"}}static isRequestKind(t){return t==="initialize-session"||t==="get-document"||t==="get-history-status"||t==="get-style-context"||t==="get-object-edit-context"||t==="get-class-name-source"||t==="get-source-tree"||t==="get-source-file"||t==="resolve-source-location"||t==="execute"||t==="undo"||t==="redo"}static isMessage(t,n={}){return!f(t)||typeof t.type!="string"?!1:t.type==="request"?Le(t.payload,n):t.type==="response"||t.type==="error"?f(t.payload)&&typeof t.payload.id=="string":t.type==="event"?f(t.payload)&&f(t.payload.event):t.type==="auth"?f(t.payload)&&typeof t.payload.key=="string":t.type==="auth-ok"&&f(t.payload)&&t.payload.ok===!0}},j=class{constructor(t){this.options=t,this.session={subscribedCompositionKeys:new Set},this.messageQueue=new Y,this.eventDispatcher=new G(n=>{this.sendJson(n)}),this.unsubscribe=t.engine.subscribe(n=>{this.handleEngineEvent(n)}),t.socket.onmessage=n=>{try{this.messageQueue.enqueue(()=>this.handleRawMessage(n.data))}catch(s){this.sendJson({type:"error",payload:{id:"unknown",kind:"get-document",message:s instanceof Error?s.message:"Bridge queue overflow"}}),t.socket.close(1013,"bridge busy")}},t.socket.onclose=()=>{this.unsubscribe(),this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.clear()}}async handleMessage(t){try{let n=t;if(n.type!=="request")return;let s=n.payload,{id:i}=s;switch(s.kind){case"initialize-session":{let o=L(s),r=await this.resolve(o),c=await this.options.engine.initializeSession({composition:r.context,...s.mutationContext?{mutationContext:s.mutationContext}:{}});this.activateSession(r.context),this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:x({...c,data:{...c.data,document:Ie(c.data.document)}},r.context)}});return}case"get-document":{let o=await this.resolveWireContext(s),r=await this.options.engine.getDocument(o);this.sendResult(i,s.kind,Ze(r,o));return}case"get-history-status":{let o=await this.resolveWireContext(s),r=await this.options.engine.getHistoryStatus(o);this.sendResult(i,s.kind,x(r,o));return}case"get-style-context":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getStyleContext(o),o));return}case"get-object-edit-context":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getObjectEditContext(s.key,o,s.selectionRef),o));return}case"get-class-name-source":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getClassNameSource(s.key,o),o));return}case"get-source-tree":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getSourceTree(o),o));return}case"get-source-file":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getSourceFile({path:s.path,composition:o}),o));return}case"resolve-source-location":{let o=await this.resolveWireContext(s),r=x(await this.options.engine.resolveSourceLocation({key:s.key,composition:o}),o);this.sendResult(i,s.kind,r);return}case"execute":{let o=await this.resolveOperationWireContext(s.request),r=await this.resolveWireContext(s);if(C(o)!==C(r))throw new R(ye,"The execute request composition does not match its operation composition.");let c=await this.normalizeOperationRequest(s.request,o);await this.handleBufferedOperation(i,s.kind,()=>this.options.engine.execute(c));return}case"undo":case"redo":{let o=await this.resolveWireContext(s);await this.handleBufferedOperation(i,s.kind,()=>s.kind==="undo"?this.options.engine.undo(o):this.options.engine.redo(o));return}}}catch(n){let s=m.extractRequestInfo(t);this.sendJson({type:"error",payload:{...s,message:Q(n),code:J(n)}})}}async resolve(t){return this.options.engine.resolveComposition(t)}async resolveAndAssert(t){let n=await this.resolve(t),s=this.session.activeComposition;if(!s)throw new R(Z,"Initialize a composition session before issuing this request.");if(C(s)!==C(n.context))throw new R(ye,"The request composition does not match the active socket session.");return n.context}async resolveWireContext(t){let n=L(t);return this.resolveAndAssert(n)}async resolveOperationWireContext(t){let n=Ge(t);return this.resolveAndAssert(n)}activateSession(t){this.session.activeComposition=t,this.session.subscribedCompositionKeys.clear(),this.session.subscribedCompositionKeys.add(C(t))}async normalizeOperationRequest(t,n){let s={...t,composition:Se(n)};if(s.target.kind==="route"){let i=s.target,o=L(i),r=await this.resolveAndAssert(o);s.target={...i,composition:Se(r)}}return s}handleEngineEvent(t){let n=t?.composition;if(!Qe(n))return;let s=C(n);this.session.subscribedCompositionKeys.has(s)&&(this.eventDispatcher.push(t),t.type==="site-edit.graph.invalidated"&&s===Ye(this.session)&&(this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.delete(s)))}sendResult(t,n,s){this.sendJson({type:"response",payload:{id:t,kind:n,result:s}})}async handleRawMessage(t){try{await this.handleMessage(m.parseMessage(t,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let s=Xe(t);this.sendJson({type:"error",payload:{...s,message:Q(n),code:J(n)}})}}sendJson(t){this.options.socket.send(m.stringifyMessage(t))}async handleBufferedOperation(t,n,s){this.eventDispatcher.beginBuffering();try{let i=await s();this.sendResult(t,n,i)}catch(i){this.sendJson({type:"error",payload:{id:t,kind:n,message:Q(i),code:J(i)}})}finally{this.eventDispatcher.flush()}}},R=class extends Error{constructor(t,n){super(n),this.code=t,this.name="BridgeProtocolError"}},G=class{constructor(t){this.send=t,this.bufferedEvents=null}push(t){let n={type:"event",payload:{event:t}};if(this.bufferedEvents){this.bufferedEvents.push(n);return}this.send(n)}beginBuffering(){this.bufferedEvents=[]}flush(){let t=this.bufferedEvents??[];this.bufferedEvents=null,t.forEach(n=>this.send(n))}},Y=class{constructor(t=64){this.maxQueued=t,this.queue=Promise.resolve(),this.queued=0}enqueue(t){if(this.queued>=this.maxQueued)throw new Error("Bridge request queue is full");this.queued+=1;let n=async()=>{this.queued-=1,await t()},s=this.queue.then(n,n);return this.queue=s.then(()=>{},()=>{}),s}};function Le(e,t){if(!f(e)||typeof e.id!="string"||!m.isRequestKind(e.kind))return!1;let n=k(e.composition);switch(e.kind){case"initialize-session":return n&&(e.mutationContext===void 0||z(e.mutationContext));case"get-document":case"get-style-context":return n;case"get-object-edit-context":return typeof e.key=="string"&&n&&ze(e.selectionRef);case"get-class-name-source":case"resolve-source-location":return typeof e.key=="string"&&n;case"get-history-status":return n;case"get-source-tree":return n;case"get-source-file":return typeof e.path=="string"&&n;case"execute":return n&&Je(e.request)&&(e.request.mutationContext===void 0||z(e.request.mutationContext))&&(t.validateOperationRequest?.(e.request)??!0);case"undo":case"redo":return n&&e.mutationContext===void 0||n&&z(e.mutationContext)}}function ze(e){if(e===void 0)return!0;if(!f(e)||typeof e.key!="string"||e.key.length===0)return!1;let t=e.instanceIndex,n=e.instanceCount;return t!==void 0&&(typeof t!="number"||!Number.isInteger(t)||t<0)||n!==void 0&&(typeof n!="number"||!Number.isInteger(n)||n<=0)||typeof t=="number"&&typeof n=="number"&&t>=n?!1:Object.keys(e).every(s=>s==="key"||s==="instanceIndex"||s==="instanceCount")}function Je(e){return f(e)&&typeof e.id=="string"&&typeof e.siteId=="string"&&typeof e.baseSnapshotId=="string"&&k(e.composition)&&typeof e.kind=="string"&&f(e.target)&&(e.target.kind!=="route"||k(e.target.composition))&&typeof e.documentVersion=="number"&&Number.isInteger(e.documentVersion)&&e.documentVersion>=0&&f(e.params)}function k(e){return f(e)&&typeof e.routeId=="string"&&e.routeId.length>0&&(e.templateId===void 0||typeof e.templateId=="string"&&e.templateId.length>0)&&Object.keys(e).every(t=>t==="routeId"||t==="templateId")}function Qe(e){return f(e)&&typeof e.routeId=="string"&&(e.effectiveTemplateId===void 0||typeof e.effectiveTemplateId=="string")}function L(e){if(k(e.composition))return e.composition;throw new R(Z,"A composition selector is required for this request.")}function Ge(e){if(f(e)&&k(e.composition))return e.composition;throw new R(Z,"A composition selector is required for execute.")}function Se(e){return{routeId:e.routeId,...e.effectiveTemplateId?{templateId:e.effectiveTemplateId}:{}}}function Ye(e){return e.activeComposition?C(e.activeComposition):null}function C(e){return JSON.stringify([e.routeId,e.effectiveTemplateId??null])}function x(e,t){return f(e)&&e.ok===!0?{...e,composition:t}:e}function Ze(e,t){return!f(e)||e.ok!==!0?e:{...e,...t?{composition:t}:{},data:Ie(e.data)}}function Ie(e){let t=e;return ge(t.routeId?t:{...t,routeId:t.composition.routeId})}function f(e){return typeof e=="object"&&e!==null}function z(e){return f(e)&&typeof e.editingSessionId=="string"&&e.editingSessionId.length>0&&typeof e.lockId=="string"&&e.lockId.length>0&&typeof e.ownerId=="string"&&e.ownerId.length>0&&typeof e.leaseAssertion=="string"&&e.leaseAssertion.length>0}function J(e){if(!(!f(e)||typeof e.code!="string"||e.code.length===0))return e.code}function Q(e){return e instanceof Error||f(e)&&typeof e.message=="string"&&e.message.length>0?e.message:"Unknown websocket operation failure"}function Xe(e){try{return m.extractRequestInfo(JSON.parse(e))}catch{return{id:"unknown",kind:"get-document"}}}var F=class{constructor(){this.tail=Promise.resolve()}run(t){let n=this.tail.then(t,t);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as ht,computeSiteEditCompositionKey as E}from"@vcp-dev/contracts";var pt="127.0.0.1",ft=30218,lt="/__vcp/site-edit/bridge",be=6*1024*1024,mt=be*2,gt=3e3,we=50,te=".vcp/current-snapshot.json",xe="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function _e(e,t,n={}){let s=new ne(yt(t),e,!1,n);return s.start({listen:!1}),s}var ne=class{constructor(t,n=et.createServer((o,r)=>{r.writeHead(404),r.end("VCP site edit bridge server")}),s=!0,i={}){this.options=t,this.server=n,this.closeHttpServerOnStop=s,this.dependencies=i,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new F,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()}start(t={}){if(this.isStarted)return;this.stopping=!1;try{P.mkdirSync(g.join(this.options.projectRoot,".vcp"),{recursive:!0})}catch(s){console.warn(`[vcp-site-edit] failed to ensure .vcp directory: ${s instanceof Error?s.message:String(s)}`)}this.refreshCompositions(!0),this.isStarted=!0,this.createRuntime();let n=Et({server:this.server,path:this.options.path,sessionKey:this.options.sessionKey,host:this.options.host,allowedOrigins:this.options.allowedOrigins,engine:this.createBridgeEngine()});this.stopWebSocketServer=n.stop,this.stopWatching=new dt({projectRoot:this.options.projectRoot,watch_dirs:St(this.options.runtime.watch_dirs),interval_ms:this.options.runtime.watch_interval_ms,debounce_ms:this.options.runtime.watch_debounce_ms??we,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:s=>this.handleFileChanged(s),on_batch_change:s=>this.handleFilesChanged(s)}).start(),t.listen!==!1&&this.server.listen(this.options.port,this.options.host)}getHealth(){let t=!!this.runtime&&!this.compositionResolutionError;return{ok:this.isStarted&&t,bridgePath:this.options.path,runtimeReady:t,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 t=Ct(),n=Rt(this.options);t.get(n)===this&&t.delete(n),this.isStarted=!1}createRuntime(t,n=null){let s=this.runtime;this.activeHistoryStore=t??null;let i=(this.dependencies.createRuntime??it)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,compositions:this.compositions,onEvent:o=>{"version"in o&&typeof o.version=="number"&&this.recordRuntimeVersion(o.version,"composition"in o?o.composition:void 0);for(let r of this.eventListeners)r(o)},...t?{historyStore:t}:{},...this.getInitialDocumentVersions().length>0?{initialDocumentVersions:this.getInitialDocumentVersions()}:{}});this.runtime=i,this.activeHistorySessionId=n,s?.dispose()}createBridgeEngine(){return{resolveComposition:t=>this.resolveComposition(t),initializeSession:t=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)throw new Error(`${n.code}: ${n.message}`);let s=wt(t.composition),i=this.getRuntime(),o=y(s);await i.initializeComposition(o);let c=i.getDocument(o).data;await this.recordRuntimeVersion(c.version,s);let d=(await i.getHistoryStatus()).data;return{ok:!0,composition:s,data:{document:c,historyStatus:d}}}),getDocument:async t=>{let n=this.getRuntime(),s=y(t);await n.initializeComposition(s);let i=n.getDocument(s);return await this.recordRuntimeVersion(i.data.version,t),{...i,composition:t}},getHistoryStatus:async t=>({...await this.getRuntime().getHistoryStatus(),composition:t}),getStyleContext:async t=>this.getRuntime().getStyleContext(y(t)),getObjectEditContext:async(t,n,s)=>this.getRuntime().getObjectEditContext(t,y(n),s),getClassNameSource:async(t,n)=>this.getRuntime().getClassNameSource(t,y(n)),getSourceTree:async t=>this.getRuntime().getSourceTree(y(t)),getSourceFile:async t=>this.getRuntime().getSourceFile({path:t.path,composition:y(t.composition)}),resolveSourceLocation:async t=>this.getRuntime().resolveSourceLocation({key:t.key,composition:y(t.composition)}),execute:(t,n)=>this.mutationQueue.run(async()=>{let s=await this.bindHistorySession(this.options.snapshotId);if(!s.ok)return X(s,t);let i=await this.getRuntime().executeOperation(t);return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion,i.composition),i}),undo:t=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return X(n,void 0,t);let s=await this.getRuntime().undo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion,s.composition??t),s.composition?s:{...s,composition:t}}),redo:t=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return X(n,void 0,t);let s=await this.getRuntime().redo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion,s.composition??t),s.composition?s:{...s,composition:t}}),subscribe:t=>(this.eventListeners.add(t),()=>this.eventListeners.delete(t))}}async bindHistorySession(t){if(!this.options.historyRoot)return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history is not configured."};if(this.activeHistorySessionId===t)return{ok:!0};let n=this.historySessionTransition.then(async()=>{if(this.activeHistorySessionId===t)return;let s=await(this.dependencies.openHistoryStore??ot)(xt({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:t}));if(!this.isStarted)return;let i=s.readDocumentVersion?await s.readDocumentVersion():0,o=this.compositions[0]?.context;if(o){let r=E(o);this.runtimeVersionFloors.set(r,Math.max(this.runtimeVersionFloors.get(r)??0,i))}this.createRuntime(s,t)});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(t,n){let s=n?E(n):null;s&&this.runtimeVersionFloors.set(s,Math.max(this.runtimeVersionFloors.get(s)??0,t));let i=this.activeHistoryStore;if(!i?.writeDocumentVersion)return Promise.resolve();let o=s?this.runtimeVersionFloors.get(s)??t:t;return this.versionPersistence=this.versionPersistence.then(()=>i.writeDocumentVersion(o)).catch(()=>{}),this.versionPersistence}handleFileChanged(t){this.handleFilesChanged([t])}handleFilesChanged(t){for(let s of t)this.pendingChangedFiles.add(s);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let n=this.options.runtime.watch_debounce_ms??we;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 t=[...this.pendingChangedFiles].sort((n,s)=>n.localeCompare(s));if(this.pendingChangedFiles.clear(),t.some(At)&&this.scheduleSnapshotIdentityAdvance(),!!this.refreshCompositions(!1))for(let n of t)this.runtime?.notifyFileChanged(n)}scheduleSnapshotIdentityAdvance(){if(this.stopping||!this.isStarted)return;let t=this.readCurrentSnapshotMarker();!t||t===this.options.snapshotId||this.mutationQueue.run(()=>this.advanceSnapshotId(t)).catch(n=>{console.warn(`[vcp-site-edit] snapshot identity advance failed: ${n instanceof Error?n.message:String(n)}`)})}readCurrentSnapshotMarker(){try{let t=P.readFileSync(g.join(this.options.projectRoot,te),"utf8"),n=JSON.parse(t),s=typeof n.snapshot_id=="string"?n.snapshot_id:"";return!s||se(s)!==s?(console.warn("[vcp-site-edit] current-snapshot marker has no valid snapshot_id"),null):s}catch(t){return console.warn(`[vcp-site-edit] current-snapshot marker unreadable: ${t instanceof Error?t.message:String(t)}`),null}}async advanceSnapshotId(t){if(this.stopping||!this.isStarted||t===this.options.snapshotId)return;let n=this.options.snapshotId;this.options.snapshotId=t;try{if(this.options.historyRoot){let i=await this.bindHistorySession(t);if(!i.ok)throw new Error(`${i.code}: ${i.message}`)}else this.createRuntime()}catch(i){this.options.snapshotId=n,console.warn(`[vcp-site-edit] snapshot identity advance rolled back to ${n}: ${i instanceof Error?i.message:String(i)}`);return}let s=new Date().toISOString();for(let i of this.compositions){let o={type:"site-edit.snapshot.changed",siteId:this.options.siteId,composition:i.context,snapshotId:t,previousSnapshotId:n,at:s};for(let r of this.eventListeners)try{r(o)}catch(c){console.warn(`[vcp-site-edit] snapshot.changed listener failed: ${c instanceof Error?c.message:String(c)}`)}}try{this.options.onSnapshotIdChanged?.(t,n)}catch(i){console.warn(`[vcp-site-edit] onSnapshotIdChanged callback failed: ${i instanceof Error?i.message:String(i)}`)}}refreshCompositions(t=!1){try{let n=It(this.options.projectRoot),s=ut(n),i=s.length>0?s:(this.options.runtime.routes??[]).map(a=>({...a})),o=at({files:n});if(o.length===0)throw new Error("No canonical App Router composition is indexed by the preview bridge.");let r=JSON.stringify(o.filter(a=>a.context.effectiveTemplateId!==void 0).map(a=>a.entryFile).sort());if(t&&(this.launchCandidateEntries=r),this.launchCandidateEntries!==r)throw new Error("SITE_EDIT_RUNTIME_RELOAD_REQUIRED: Candidate route collection changed since preview launch");let c=new Map(this.compositions.map(a=>[E(a.context),a])),d=new Map(o.map(a=>[E(a.context),a]));if(this.runtime){for(let[a,h]of c){let u=d.get(a);(!u||u.entryFile!==h.entryFile)&&this.runtime.unregisterComposition(y(h.context))}for(let[a,h]of d){let u=c.get(a);(!u||u.entryFile!==h.entryFile)&&this.runtime.registerComposition(h.context,h.entryFile)}}return this.routes=i,this.compositions=o,this.compositionFiles=n,this.compositionResolutionError=null,!0}catch(n){let s=n instanceof Error?n:new Error(String(n));if(this.compositionResolutionError=s,t)throw s;return!1}}resolveComposition(t){if(this.compositionResolutionError)throw this.compositionResolutionError;let n=ct(t.routeId,this.routes,this.options.basePath);if(!n)throw new Error(`Route is not indexed by the preview bridge: ${t.routeId}`);let s=this.routes.find(r=>r.routeId===n);if(!s)throw new Error(`Route is not indexed by the preview bridge: ${t.routeId}`);let i=rt({selector:{routeId:n,...t.templateId!==void 0?{templateId:t.templateId}:{}},files:this.compositionFiles,canonicalEntryFile:s.entryFile}),o=E(i.context);if(!this.compositions.some(r=>E(r.context)===o))throw new Error(`Composition is not registered by the preview bridge: ${o}`);return i}getInitialDocumentVersions(){return this.compositions.flatMap(t=>{let n=this.runtimeVersionFloors.get(E(t.context));return n===void 0?[]:[{composition:t.context,version:n}]})}getRuntime(){if(!this.runtime)throw new Error("VCP site edit preview bridge runtime is not ready");return this.runtime}};function yt(e){return{tenantId:e.tenantId,siteId:e.siteId,snapshotId:e.snapshotId,projectRoot:g.resolve(e.projectRoot??process.cwd()),basePath:e.basePath??"",host:e.host??pt,port:e.port??ft,path:e.path??lt,sessionKey:e.sessionKey??Ht(),historyRoot:e.historyRoot,allowedOrigins:e.allowedOrigins??[],runtime:e.runtime??{},onSnapshotIdChanged:e.onSnapshotIdChanged}}function St(e){let t=["app","src/app","components/site","src/components/site","lib/generated","src/lib/generated","public"];return[...new Set([...e?.length?e:t,".vcp"])]}function It(e){let t=[],n=["app","src/app",".vcp"],s=new Set([".git",".next",".turbo","coverage","dist","node_modules","output","snapshot-store"]),i=o=>{if(!P.existsSync(o))return;let r;try{r=P.readdirSync(o,{withFileTypes:!0})}catch{return}for(let c of r){if(c.name.startsWith(".")&&c.name!==".vcp")continue;if(c.isDirectory()){if(s.has(c.name))continue;i(g.join(o,c.name));continue}if(!c.isFile())continue;let d=g.join(o,c.name),a="";try{a=P.readFileSync(d,"utf8")}catch{}t.push({path:g.relative(e,d).replaceAll(g.sep,"/"),content:a})}};for(let o of n)i(g.join(e,o));return t.sort((o,r)=>o.path.localeCompare(r.path))}function wt(e){if(!e||typeof e!="object"||typeof e.routeId!="string")throw new Error("Normalized composition context is required.");if("effectiveTemplateId"in e)return e;let t=e;return{routeId:t.routeId,...t.templateId?{effectiveTemplateId:t.templateId}:{}}}function y(e){return{routeId:e.routeId,...e.effectiveTemplateId?{templateId:e.effectiveTemplateId}:{}}}function xt(e){return g.join(g.resolve(e.root),se(e.siteId),se(e.snapshotId),Ee("sha256").update(e.editingSessionId).digest("hex").slice(0,32))}function se(e){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(e)&&e!=="."&&e!==".."?e:Ee("sha256").update(e).digest("hex").slice(0,32)}function Ct(){let e=globalThis;return e[xe]??=new Map,e[xe]}function Rt(e){return`${e.projectRoot}:${e.host}:${e.port}:${e.path}:${e.historyRoot??""}`}function X(e,t,n){let s=n??(t?.composition?{routeId:t.composition.routeId,...t.composition.templateId?{effectiveTemplateId:t.composition.templateId}:{}}:void 0);if(!s)throw new Error("A normalized composition is required to construct a mutation result.");return{ok:!1,composition:s,error:{code:e.code,message:e.message},data:{requestId:t?.id??"mutation-context",kind:t?.kind??"update-text",composition:s,target:t?.target??{kind:"node",key:""},resultVersion:t?.documentVersion??0}}}function Et(e){let t=new st({maxPayload:be,noServer:!0}),n=new WeakSet,s=o=>{new j({socket:new ie(o),engine:e.engine,validateOperationRequest:r=>ht.safeParse(r).success})},i=(o,r,c)=>{if(!vt(o,e))return;let d=bt(o,e);if(!d){r.write(`HTTP/1.1 401 Unauthorized\r
1
+ import ue from"node:http";import{existsSync as _,readFileSync as b}from"node:fs";import{randomUUID as Nt}from"node:crypto";import{createRequire as Ne}from"node:module";import{Socket as Pe}from"node:net";import H from"node:path";import{basename as Be,dirname as A,join as N}from"node:path";import{fileURLToPath as De}from"node:url";import et from"node:http";import P from"node:fs";import{createHash as Ee,randomBytes as tt,timingSafeEqual as nt}from"node:crypto";import g from"node:path";import{WebSocket as ve,WebSocketServer as st}from"ws";import{createPreviewSiteEditEngineRuntime as it,openFileHistoryStore as ot,resolveManagedPageTemplateComposition as rt,scanSiteEditCompositionsFromFiles as at}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as ct,scanNextAppRouterRoutesFromFiles as ut}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as dt}from"@vcp-dev/site-edit-engine/source-watcher";function ge(e){let t=new Map,n=[],s=new Map,i=[],o=e.pageComposition.status==="supported"?e.pageComposition.placementUnitKeys:[],r=new Map(o.map((c,p)=>[c,p])),u=e.pageComposition.status==="supported"?{status:"supported",containerKey:e.pageComposition.containerKey,hasBusinessMembers:e.pageComposition.hasBusinessMembers,placementUnitKeys:e.pageComposition.placementUnitKeys,placements:e.pageComposition.placements.map(c=>({placementKey:c.placementKey,placementUnitKey:c.placementUnitKey,relation:c.relation}))}:{status:"unsupported",reason:e.pageComposition.reason,diagnostics:e.pageComposition.diagnostics},d=e.entries.flatMap((c,p)=>c.dynamicDataSummary?[[p,c.dynamicDataSummary]]:[]),a=c=>{let p=t.get(c);if(typeof p=="number")return p;let f=n.length;return n.push(c),t.set(c,f),f},h=c=>{let p=null;for(let f of We(c)){let S=`${p??"root"}\0${f}`,I=s.get(S);if(typeof I=="number"){p=I;continue}let w=i.length;i.push([p,a(f)]),s.set(S,w),p=w}if(p===null)throw new Error("Document key must not be empty");return p};return{encoding:"segment-trie-v1",siteId:e.siteId,snapshotId:e.snapshotId,composition:{...e.composition},routeId:e.composition.routeId,version:e.version,pageComposition:u,tokens:n,nodes:i,entries:e.entries.map(c=>{let p=c.pageCompositionUnitKey?r.get(c.pageCompositionUnitKey)??-1:-1;if(c.pageCompositionUnitKey&&p<0)throw new Error(`Render entry ${c.key} references unknown page composition unit ${c.pageCompositionUnitKey}`);return[h(c.key),$e(c.operationSummary),...p>=0?[p]:[]]}),...e.entries.every(c=>c.parentKey!==void 0)?{entryParentNodeIds:e.entries.map(c=>c.parentKey===null?null:h(c.parentKey))}:{},...d.length>0?{dynamicDataSummaries:d}:{}}}function $e(e){return(e.canUpdateText?1:0)|(e.canInsertChild?2:0)|(e.canMove?4:0)|(e.canRemove?8:0)}function We(e){if(!e)return[];let t=[],n=0,s=me(e,n);if(s<0)return t.push(e),t;for(t.push(e.slice(0,s)),n=s;n<e.length;){if(s=me(e,n+(e.startsWith("::",n)?2:1)),s<0){t.push(e.slice(n));break}t.push(e.slice(n,s)),n=s}return t}function me(e,t){let n=e.indexOf("/",t),s=e.indexOf("::",t);return n<0?s:s<0?n:Math.min(n,s)}var ye="SITE_EDIT_COMPOSITION_SESSION_MISMATCH",Z="SITE_EDIT_COMPOSITION_SESSION_REQUIRED";var m=class e{constructor(){}static parseMessage(t,n={}){let s=JSON.parse(t);if(!e.isMessage(s,n))throw new Error("Invalid site edit bridge message");return s}static stringifyMessage(t){return JSON.stringify(t)}static extractRequestInfo(t){if(l(t)&&l(t.payload)){let n=typeof t.payload.id=="string"?t.payload.id:"unknown",s=this.isRequestKind(t.payload.kind)?t.payload.kind:"get-document";return{id:n,kind:s}}return{id:"unknown",kind:"get-document"}}static isRequestKind(t){return t==="initialize-session"||t==="get-document"||t==="get-history-status"||t==="get-style-context"||t==="get-object-edit-context"||t==="get-class-name-source"||t==="get-source-tree"||t==="get-source-file"||t==="resolve-source-location"||t==="execute"||t==="undo"||t==="redo"}static isMessage(t,n={}){return!l(t)||typeof t.type!="string"?!1:t.type==="request"?Le(t.payload,n):t.type==="response"||t.type==="error"?l(t.payload)&&typeof t.payload.id=="string":t.type==="event"?l(t.payload)&&l(t.payload.event):t.type==="auth"?l(t.payload)&&typeof t.payload.key=="string":t.type==="auth-ok"&&l(t.payload)&&t.payload.ok===!0}},j=class{constructor(t){this.options=t,this.session={subscribedCompositionKeys:new Set},this.messageQueue=new Y,this.eventDispatcher=new G(n=>{this.sendJson(n)}),this.unsubscribe=t.engine.subscribe(n=>{this.handleEngineEvent(n)}),t.socket.onmessage=n=>{try{this.messageQueue.enqueue(()=>this.handleRawMessage(n.data))}catch(s){this.sendJson({type:"error",payload:{id:"unknown",kind:"get-document",message:s instanceof Error?s.message:"Bridge queue overflow"}}),t.socket.close(1013,"bridge busy")}},t.socket.onclose=()=>{this.unsubscribe(),this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.clear()}}async handleMessage(t){try{let n=t;if(n.type!=="request")return;let s=n.payload,{id:i}=s;switch(s.kind){case"initialize-session":{let o=L(s),r=await this.resolve(o),u=await this.options.engine.initializeSession({composition:r.context,...s.mutationContext?{mutationContext:s.mutationContext}:{}});this.activateSession(r.context),this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:x({...u,data:{...u.data,document:Ie(u.data.document)}},r.context)}});return}case"get-document":{let o=await this.resolveWireContext(s),r=await this.options.engine.getDocument(o);this.sendResult(i,s.kind,Ze(r,o));return}case"get-history-status":{let o=await this.resolveWireContext(s),r=await this.options.engine.getHistoryStatus(o);this.sendResult(i,s.kind,x(r,o));return}case"get-style-context":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getStyleContext(o),o));return}case"get-object-edit-context":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getObjectEditContext(s.key,o,s.selectionRef),o));return}case"get-class-name-source":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getClassNameSource(s.key,o),o));return}case"get-source-tree":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getSourceTree(o),o));return}case"get-source-file":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getSourceFile({path:s.path,composition:o}),o));return}case"resolve-source-location":{let o=await this.resolveWireContext(s),r=x(await this.options.engine.resolveSourceLocation({key:s.key,composition:o}),o);this.sendResult(i,s.kind,r);return}case"execute":{let o=await this.resolveOperationWireContext(s.request),r=await this.resolveWireContext(s);if(C(o)!==C(r))throw new R(ye,"The execute request composition does not match its operation composition.");let u=await this.normalizeOperationRequest(s.request,o);await this.handleBufferedOperation(i,s.kind,()=>this.options.engine.execute(u));return}case"undo":case"redo":{let o=await this.resolveWireContext(s);await this.handleBufferedOperation(i,s.kind,()=>s.kind==="undo"?this.options.engine.undo(o):this.options.engine.redo(o));return}}}catch(n){let s=m.extractRequestInfo(t);this.sendJson({type:"error",payload:{...s,message:Q(n),code:J(n)}})}}async resolve(t){return this.options.engine.resolveComposition(t)}async resolveAndAssert(t){let n=await this.resolve(t),s=this.session.activeComposition;if(!s)throw new R(Z,"Initialize a composition session before issuing this request.");if(C(s)!==C(n.context))throw new R(ye,"The request composition does not match the active socket session.");return n.context}async resolveWireContext(t){let n=L(t);return this.resolveAndAssert(n)}async resolveOperationWireContext(t){let n=Ge(t);return this.resolveAndAssert(n)}activateSession(t){this.session.activeComposition=t,this.session.subscribedCompositionKeys.clear(),this.session.subscribedCompositionKeys.add(C(t))}async normalizeOperationRequest(t,n){let s={...t,composition:Se(n)};if(s.target.kind==="route"){let i=s.target,o=L(i),r=await this.resolveAndAssert(o);s.target={...i,composition:Se(r)}}return s}handleEngineEvent(t){let n=t?.composition;if(!Qe(n))return;let s=C(n);this.session.subscribedCompositionKeys.has(s)&&(this.eventDispatcher.push(t),t.type==="site-edit.graph.invalidated"&&s===Ye(this.session)&&(this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.delete(s)))}sendResult(t,n,s){this.sendJson({type:"response",payload:{id:t,kind:n,result:s}})}async handleRawMessage(t){try{await this.handleMessage(m.parseMessage(t,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let s=Xe(t);this.sendJson({type:"error",payload:{...s,message:Q(n),code:J(n)}})}}sendJson(t){this.options.socket.send(m.stringifyMessage(t))}async handleBufferedOperation(t,n,s){this.eventDispatcher.beginBuffering();try{let i=await s();this.sendResult(t,n,i)}catch(i){this.sendJson({type:"error",payload:{id:t,kind:n,message:Q(i),code:J(i)}})}finally{this.eventDispatcher.flush()}}},R=class extends Error{constructor(t,n){super(n),this.code=t,this.name="BridgeProtocolError"}},G=class{constructor(t){this.send=t,this.bufferedEvents=null}push(t){let n={type:"event",payload:{event:t}};if(this.bufferedEvents){this.bufferedEvents.push(n);return}this.send(n)}beginBuffering(){this.bufferedEvents=[]}flush(){let t=this.bufferedEvents??[];this.bufferedEvents=null,t.forEach(n=>this.send(n))}},Y=class{constructor(t=64){this.maxQueued=t,this.queue=Promise.resolve(),this.queued=0}enqueue(t){if(this.queued>=this.maxQueued)throw new Error("Bridge request queue is full");this.queued+=1;let n=async()=>{this.queued-=1,await t()},s=this.queue.then(n,n);return this.queue=s.then(()=>{},()=>{}),s}};function Le(e,t){if(!l(e)||typeof e.id!="string"||!m.isRequestKind(e.kind))return!1;let n=k(e.composition);switch(e.kind){case"initialize-session":return n&&(e.mutationContext===void 0||z(e.mutationContext));case"get-document":case"get-style-context":return n;case"get-object-edit-context":return typeof e.key=="string"&&n&&ze(e.selectionRef);case"get-class-name-source":case"resolve-source-location":return typeof e.key=="string"&&n;case"get-history-status":return n;case"get-source-tree":return n;case"get-source-file":return typeof e.path=="string"&&n;case"execute":return n&&Je(e.request)&&(e.request.mutationContext===void 0||z(e.request.mutationContext))&&(t.validateOperationRequest?.(e.request)??!0);case"undo":case"redo":return n&&e.mutationContext===void 0||n&&z(e.mutationContext)}}function ze(e){if(e===void 0)return!0;if(!l(e)||typeof e.key!="string"||e.key.length===0)return!1;let t=e.instanceIndex,n=e.instanceCount;return t!==void 0&&(typeof t!="number"||!Number.isInteger(t)||t<0)||n!==void 0&&(typeof n!="number"||!Number.isInteger(n)||n<=0)||typeof t=="number"&&typeof n=="number"&&t>=n?!1:Object.keys(e).every(s=>s==="key"||s==="instanceIndex"||s==="instanceCount")}function Je(e){return l(e)&&typeof e.id=="string"&&typeof e.siteId=="string"&&typeof e.baseSnapshotId=="string"&&k(e.composition)&&typeof e.kind=="string"&&l(e.target)&&(e.target.kind!=="route"||k(e.target.composition))&&typeof e.documentVersion=="number"&&Number.isInteger(e.documentVersion)&&e.documentVersion>=0&&l(e.params)}function k(e){return l(e)&&typeof e.routeId=="string"&&e.routeId.length>0&&(e.templateId===void 0||typeof e.templateId=="string"&&e.templateId.length>0)&&Object.keys(e).every(t=>t==="routeId"||t==="templateId")}function Qe(e){return l(e)&&typeof e.routeId=="string"&&(e.effectiveTemplateId===void 0||typeof e.effectiveTemplateId=="string")}function L(e){if(k(e.composition))return e.composition;throw new R(Z,"A composition selector is required for this request.")}function Ge(e){if(l(e)&&k(e.composition))return e.composition;throw new R(Z,"A composition selector is required for execute.")}function Se(e){return{routeId:e.routeId,...e.effectiveTemplateId?{templateId:e.effectiveTemplateId}:{}}}function Ye(e){return e.activeComposition?C(e.activeComposition):null}function C(e){return JSON.stringify([e.routeId,e.effectiveTemplateId??null])}function x(e,t){return l(e)&&e.ok===!0?{...e,composition:t}:e}function Ze(e,t){return!l(e)||e.ok!==!0?e:{...e,...t?{composition:t}:{},data:Ie(e.data)}}function Ie(e){let t=e;return ge(t.routeId?t:{...t,routeId:t.composition.routeId})}function l(e){return typeof e=="object"&&e!==null}function z(e){return l(e)&&typeof e.editingSessionId=="string"&&e.editingSessionId.length>0&&typeof e.lockId=="string"&&e.lockId.length>0&&typeof e.ownerId=="string"&&e.ownerId.length>0&&typeof e.leaseAssertion=="string"&&e.leaseAssertion.length>0}function J(e){if(!(!l(e)||typeof e.code!="string"||e.code.length===0))return e.code}function Q(e){return e instanceof Error||l(e)&&typeof e.message=="string"&&e.message.length>0?e.message:"Unknown websocket operation failure"}function Xe(e){try{return m.extractRequestInfo(JSON.parse(e))}catch{return{id:"unknown",kind:"get-document"}}}var F=class{constructor(){this.tail=Promise.resolve()}run(t){let n=this.tail.then(t,t);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as ht,computeSiteEditCompositionKey as E}from"@vcp-dev/contracts";var pt="127.0.0.1",lt=30218,ft="/__vcp/site-edit/bridge",be=6*1024*1024,mt=be*2,gt=3e3,we=50,te=".vcp/current-snapshot.json",xe="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function _e(e,t,n={}){let s=new ne(yt(t),e,!1,n);return s.start({listen:!1}),s}var ne=class{constructor(t,n=et.createServer((o,r)=>{r.writeHead(404),r.end("VCP site edit bridge server")}),s=!0,i={}){this.options=t,this.server=n,this.closeHttpServerOnStop=s,this.dependencies=i,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new F,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()}start(t={}){if(this.isStarted)return;this.stopping=!1;try{P.mkdirSync(g.join(this.options.projectRoot,".vcp"),{recursive:!0})}catch(s){console.warn(`[vcp-site-edit] failed to ensure .vcp directory: ${s instanceof Error?s.message:String(s)}`)}this.refreshCompositions(!0),this.isStarted=!0,this.createRuntime();let n=Et({server:this.server,path:this.options.path,sessionKey:this.options.sessionKey,host:this.options.host,allowedOrigins:this.options.allowedOrigins,engine:this.createBridgeEngine()});this.stopWebSocketServer=n.stop,this.stopWatching=new dt({projectRoot:this.options.projectRoot,watch_dirs:St(this.options.runtime.watch_dirs),interval_ms:this.options.runtime.watch_interval_ms,debounce_ms:this.options.runtime.watch_debounce_ms??we,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:s=>this.handleFileChanged(s),on_batch_change:s=>this.handleFilesChanged(s)}).start(),t.listen!==!1&&this.server.listen(this.options.port,this.options.host)}getHealth(){let t=!!this.runtime&&!this.compositionResolutionError;return{ok:this.isStarted&&t,bridgePath:this.options.path,runtimeReady:t,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 t=Ct(),n=Rt(this.options);t.get(n)===this&&t.delete(n),this.isStarted=!1}createRuntime(t,n=null){let s=this.runtime;this.activeHistoryStore=t??null;let i=(this.dependencies.createRuntime??it)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,compositions:this.compositions,onEvent:o=>{"version"in o&&typeof o.version=="number"&&this.recordRuntimeVersion(o.version,"composition"in o?o.composition:void 0);for(let r of this.eventListeners)r(o)},...t?{historyStore:t}:{},...this.getInitialDocumentVersions().length>0?{initialDocumentVersions:this.getInitialDocumentVersions()}:{}});this.runtime=i,this.activeHistorySessionId=n,s?.dispose()}createBridgeEngine(){return{resolveComposition:t=>this.resolveComposition(t),initializeSession:t=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)throw new Error(`${n.code}: ${n.message}`);let s=wt(t.composition),i=this.getRuntime(),o=y(s);await i.initializeComposition(o);let u=i.getDocument(o).data;await this.recordRuntimeVersion(u.version,s);let d=(await i.getHistoryStatus()).data;return{ok:!0,composition:s,data:{document:u,historyStatus:d}}}),getDocument:async t=>{let n=this.getRuntime(),s=y(t);await n.initializeComposition(s);let i=n.getDocument(s);return await this.recordRuntimeVersion(i.data.version,t),{...i,composition:t}},getHistoryStatus:async t=>({...await this.getRuntime().getHistoryStatus(),composition:t}),getStyleContext:async t=>this.getRuntime().getStyleContext(y(t)),getObjectEditContext:async(t,n,s)=>this.getRuntime().getObjectEditContext(t,y(n),s),getClassNameSource:async(t,n)=>this.getRuntime().getClassNameSource(t,y(n)),getSourceTree:async t=>this.getRuntime().getSourceTree(y(t)),getSourceFile:async t=>this.getRuntime().getSourceFile({path:t.path,composition:y(t.composition)}),resolveSourceLocation:async t=>this.getRuntime().resolveSourceLocation({key:t.key,composition:y(t.composition)}),execute:(t,n)=>this.mutationQueue.run(async()=>{let s=await this.bindHistorySession(this.options.snapshotId);if(!s.ok)return X(s,t);let i=await this.getRuntime().executeOperation(t);return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion,i.composition),i}),undo:t=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return X(n,void 0,t);let s=await this.getRuntime().undo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion,s.composition??t),s.composition?s:{...s,composition:t}}),redo:t=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return X(n,void 0,t);let s=await this.getRuntime().redo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion,s.composition??t),s.composition?s:{...s,composition:t}}),subscribe:t=>(this.eventListeners.add(t),()=>this.eventListeners.delete(t))}}async bindHistorySession(t){if(!this.options.historyRoot)return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history is not configured."};if(this.activeHistorySessionId===t)return{ok:!0};let n=this.historySessionTransition.then(async()=>{if(this.activeHistorySessionId===t)return;let s=await(this.dependencies.openHistoryStore??ot)(xt({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:t}));if(!this.isStarted)return;let i=s.readDocumentVersion?await s.readDocumentVersion():0,o=this.compositions[0]?.context;if(o){let r=E(o);this.runtimeVersionFloors.set(r,Math.max(this.runtimeVersionFloors.get(r)??0,i))}this.createRuntime(s,t)});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(t,n){let s=n?E(n):null;s&&this.runtimeVersionFloors.set(s,Math.max(this.runtimeVersionFloors.get(s)??0,t));let i=this.activeHistoryStore;if(!i?.writeDocumentVersion)return Promise.resolve();let o=s?this.runtimeVersionFloors.get(s)??t:t;return this.versionPersistence=this.versionPersistence.then(()=>i.writeDocumentVersion(o)).catch(()=>{}),this.versionPersistence}handleFileChanged(t){this.handleFilesChanged([t])}handleFilesChanged(t){for(let s of t)this.pendingChangedFiles.add(s);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let n=this.options.runtime.watch_debounce_ms??we;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 t=[...this.pendingChangedFiles].sort((n,s)=>n.localeCompare(s));if(this.pendingChangedFiles.clear(),t.some(At)&&this.scheduleSnapshotIdentityAdvance(),!!this.refreshCompositions(!1))for(let n of t)this.runtime?.notifyFileChanged(n)}scheduleSnapshotIdentityAdvance(){if(this.stopping||!this.isStarted)return;let t=this.readCurrentSnapshotMarker();!t||t===this.options.snapshotId||this.mutationQueue.run(()=>this.advanceSnapshotId(t)).catch(n=>{console.warn(`[vcp-site-edit] snapshot identity advance failed: ${n instanceof Error?n.message:String(n)}`)})}readCurrentSnapshotMarker(){try{let t=P.readFileSync(g.join(this.options.projectRoot,te),"utf8"),n=JSON.parse(t),s=typeof n.snapshot_id=="string"?n.snapshot_id:"";return!s||se(s)!==s?(console.warn("[vcp-site-edit] current-snapshot marker has no valid snapshot_id"),null):s}catch(t){return console.warn(`[vcp-site-edit] current-snapshot marker unreadable: ${t instanceof Error?t.message:String(t)}`),null}}async advanceSnapshotId(t){if(this.stopping||!this.isStarted||t===this.options.snapshotId)return;let n=this.options.snapshotId;this.options.snapshotId=t;try{if(this.options.historyRoot){let i=await this.bindHistorySession(t);if(!i.ok)throw new Error(`${i.code}: ${i.message}`)}else this.createRuntime()}catch(i){this.options.snapshotId=n,console.warn(`[vcp-site-edit] snapshot identity advance rolled back to ${n}: ${i instanceof Error?i.message:String(i)}`);return}let s=new Date().toISOString();for(let i of this.compositions){let o={type:"site-edit.snapshot.changed",siteId:this.options.siteId,composition:i.context,snapshotId:t,previousSnapshotId:n,at:s};for(let r of this.eventListeners)try{r(o)}catch(u){console.warn(`[vcp-site-edit] snapshot.changed listener failed: ${u instanceof Error?u.message:String(u)}`)}}try{this.options.onSnapshotIdChanged?.(t,n)}catch(i){console.warn(`[vcp-site-edit] onSnapshotIdChanged callback failed: ${i instanceof Error?i.message:String(i)}`)}}refreshCompositions(t=!1){try{let n=It(this.options.projectRoot),s=ut(n),i=s.length>0?s:(this.options.runtime.routes??[]).map(a=>({...a})),o=at({files:n});if(o.length===0)throw new Error("No canonical App Router composition is indexed by the preview bridge.");let r=JSON.stringify(o.filter(a=>a.context.effectiveTemplateId!==void 0).map(a=>a.entryFile).sort());if(t&&(this.launchCandidateEntries=r),this.launchCandidateEntries!==r)throw new Error("SITE_EDIT_RUNTIME_RELOAD_REQUIRED: Candidate route collection changed since preview launch");let u=new Map(this.compositions.map(a=>[E(a.context),a])),d=new Map(o.map(a=>[E(a.context),a]));if(this.runtime){for(let[a,h]of u){let c=d.get(a);(!c||c.entryFile!==h.entryFile)&&this.runtime.unregisterComposition(y(h.context))}for(let[a,h]of d){let c=u.get(a);(!c||c.entryFile!==h.entryFile)&&this.runtime.registerComposition(h.context,h.entryFile)}}return this.routes=i,this.compositions=o,this.compositionFiles=n,this.compositionResolutionError=null,!0}catch(n){let s=n instanceof Error?n:new Error(String(n));if(this.compositionResolutionError=s,t)throw s;return!1}}resolveComposition(t){if(this.compositionResolutionError)throw this.compositionResolutionError;let n=ct(t.routeId,this.routes,this.options.basePath);if(!n)throw new Error(`Route is not indexed by the preview bridge: ${t.routeId}`);let s=this.routes.find(r=>r.routeId===n);if(!s)throw new Error(`Route is not indexed by the preview bridge: ${t.routeId}`);let i=rt({selector:{routeId:n,...t.templateId!==void 0?{templateId:t.templateId}:{}},files:this.compositionFiles,canonicalEntryFile:s.entryFile}),o=E(i.context);if(!this.compositions.some(r=>E(r.context)===o))throw new Error(`Composition is not registered by the preview bridge: ${o}`);return i}getInitialDocumentVersions(){return this.compositions.flatMap(t=>{let n=this.runtimeVersionFloors.get(E(t.context));return n===void 0?[]:[{composition:t.context,version:n}]})}getRuntime(){if(!this.runtime)throw new Error("VCP site edit preview bridge runtime is not ready");return this.runtime}};function yt(e){return{tenantId:e.tenantId,siteId:e.siteId,snapshotId:e.snapshotId,projectRoot:g.resolve(e.projectRoot??process.cwd()),basePath:e.basePath??"",host:e.host??pt,port:e.port??lt,path:e.path??ft,sessionKey:e.sessionKey??Ht(),historyRoot:e.historyRoot,allowedOrigins:e.allowedOrigins??[],runtime:e.runtime??{},onSnapshotIdChanged:e.onSnapshotIdChanged}}function St(e){let t=["app","src/app","components/site","src/components/site","lib/generated","src/lib/generated","public"];return[...new Set([...e?.length?e:t,".vcp"])]}function It(e){let t=[],n=["app","src/app",".vcp"],s=new Set([".git",".next",".turbo","coverage","dist","node_modules","output","snapshot-store"]),i=o=>{if(!P.existsSync(o))return;let r;try{r=P.readdirSync(o,{withFileTypes:!0})}catch{return}for(let u of r){if(u.name.startsWith(".")&&u.name!==".vcp")continue;if(u.isDirectory()){if(s.has(u.name))continue;i(g.join(o,u.name));continue}if(!u.isFile())continue;let d=g.join(o,u.name),a="";try{a=P.readFileSync(d,"utf8")}catch{}t.push({path:g.relative(e,d).replaceAll(g.sep,"/"),content:a})}};for(let o of n)i(g.join(e,o));return t.sort((o,r)=>o.path.localeCompare(r.path))}function wt(e){if(!e||typeof e!="object"||typeof e.routeId!="string")throw new Error("Normalized composition context is required.");if("effectiveTemplateId"in e)return e;let t=e;return{routeId:t.routeId,...t.templateId?{effectiveTemplateId:t.templateId}:{}}}function y(e){return{routeId:e.routeId,...e.effectiveTemplateId?{templateId:e.effectiveTemplateId}:{}}}function xt(e){return g.join(g.resolve(e.root),se(e.siteId),se(e.snapshotId),Ee("sha256").update(e.editingSessionId).digest("hex").slice(0,32))}function se(e){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(e)&&e!=="."&&e!==".."?e:Ee("sha256").update(e).digest("hex").slice(0,32)}function Ct(){let e=globalThis;return e[xe]??=new Map,e[xe]}function Rt(e){return`${e.projectRoot}:${e.host}:${e.port}:${e.path}:${e.historyRoot??""}`}function X(e,t,n){let s=n??(t?.composition?{routeId:t.composition.routeId,...t.composition.templateId?{effectiveTemplateId:t.composition.templateId}:{}}:void 0);if(!s)throw new Error("A normalized composition is required to construct a mutation result.");return{ok:!1,composition:s,error:{code:e.code,message:e.message},data:{requestId:t?.id??"mutation-context",kind:t?.kind??"update-text",composition:s,target:t?.target??{kind:"node",key:""},resultVersion:t?.documentVersion??0}}}function Et(e){let t=new st({maxPayload:be,noServer:!0}),n=new WeakSet,s=o=>{new j({socket:new ie(o),engine:e.engine,validateOperationRequest:r=>ht.safeParse(r).success})},i=(o,r,u)=>{if(!vt(o,e))return;let d=bt(o,e);if(!d){r.write(`HTTP/1.1 401 Unauthorized\r
2
2
  Connection: close\r
3
3
  \r
4
- `),r.destroy();return}d.authenticatedByHeader&&n.add(o),t.handleUpgrade(o,r,c,a=>{t.emit("connection",a,o)})};return e.server.on("upgrade",i),t.on("connection",(o,r)=>{if(n.has(r)){s(o);return}_t(o,e.sessionKey,()=>{s(o)})}),t.stop=()=>{e.server.off("upgrade",i);for(let o of t.clients)o.close(1001,"bridge stopped");t.close()},t}var ie=class{constructor(t){this.socket=t,this.onmessage=null,this.onclose=null,t.on("error",()=>{}),t.on("message",n=>{this.onmessage?.({data:n.toString()})}),t.on("close",()=>{this.onclose?.()})}send(t){if(this.socket.readyState===ve.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(t,"utf8")>mt){this.socket.close(1013,"bridge backpressure");return}this.socket.send(t)}}close(t,n){this.socket.close(t,n)}};function vt(e,t){return new URL(e.url??"/","http://localhost").pathname===t.path}function bt(e,t){if(new URL(e.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let s=ee(e.headers["x-vcp-site-edit-key"]),i=Te(s,t.sessionKey),o=ee(e.headers.host),r=ee(e.headers.origin);return o&&!Tt(o,t.host,r)?null:r?Pt(r,o,t.allowedOrigins)?{authenticatedByHeader:i}:null:i?{authenticatedByHeader:!0}:null}function _t(e,t,n){let s=setTimeout(()=>{e.close(1008,"bridge authentication timeout")},gt),i,o=()=>{clearTimeout(s),e.off("message",i),e.off("close",o)},r=()=>{o(),e.close(1008,"bridge authentication failed")};i=c=>{try{let d=m.parseMessage(c.toString());if(d.type!=="auth"||!Te(d.payload.key,t)){r();return}o(),e.readyState===ve.OPEN&&e.send(m.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),n()}catch{r()}},e.on("message",i),e.on("close",o)}function Te(e,t){if(!e)return!1;let n=Buffer.from(e),s=Buffer.from(t);return n.length===s.length&&nt(n,s)}function Tt(e,t,n){let s=Re(e),i=Re(t);return s===i||Ce(i)&&Ce(s)&&(!n||oe(n,e))||kt(i)&&(!n||oe(n,e))}function kt(e){return e==="0.0.0.0"||e==="::"}function Ce(e){return e==="localhost"||e==="127.0.0.1"||e==="::1"}function Pt(e,t,n){return n.includes(e)||(t?oe(e,t):!1)}function oe(e,t){try{return new URL(e).host===ke(t)}catch{return!1}}function Re(e){let t=ke(e);return t.startsWith("[")?t.slice(1,t.indexOf("]")):t.split(":").length-1===1?t.split(":",1)[0]??"":t}function ke(e){return e.trim().toLowerCase()}function ee(e){return Array.isArray(e)?e[0]:e}function Ht(){return tt(32).toString("base64url")}function At(e){let t=e.replaceAll("\\","/");return t===te||t.endsWith(`/${te}`)}var Dt="next",Ne="127.0.0.1",Ot=3e3,V="/__vcp/site-edit/bridge",Me="/__vcp/site-edit/health",Nt="probe",Mt="render",He="/_error",q="/__vcp/site-edit/client/",jt=`${q}auto-bootstrap.js`,je='data-vcp-site-edit-bootstrap="true"',Ft=["next.config.ts","next.config.mts","next.config.cts","next.config.js","next.config.mjs","next.config.cjs"],Ut="@vcp-dev/site-edit-engine/webpack-loader",Kt=/@vcp\/site-edit-engine\/(?:webpack-loader|site-edit-instrumentation-loader)/,Vt=[/\bconfig\.webpack\s*=/,/\bwebpack\s*\([^)]*\)\s*\{/,/\bwebpack\s*:\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)?\s*=>/,/\bwebpack\s*:\s*(?:async\s+)?function\b/],qt=new Set(["auto-bootstrap.js","client.js","composition-key.js","designer-trace.js","document-codec.js","property-preview.js","protocol.js","window-message.js"]);async function ts(e={},t={}){let n=t.process??process,s=t.createServer??ue.createServer,i=t.attachBridgeServer??_e,o=t.loadNext??xn,r=H.resolve(e.projectRoot??process.cwd()),c=e.host??Ne,d=e.port??vn(n.env.PORT)??Ot,a=e.bridgePath??V,h=Gt(a),u=gn(),p=e.basePath??n.env.VCP_SITE_PREVIEW_BASE_PATH??"/",l=(e.tenantId??n.env.VCP_TENANT_ID)?.trim()||void 0,S=await o(r),I=$t(r),w=S({dev:!0,dir:r,hostname:c,port:d,...Wt(I)}),Ke=w.getRequestHandler(),Ve=e.siteId??n.env.VCP_SITE_ID??"018f6f4d-a9b8-7120-8000-000000000120",he=e.snapshotId??n.env.VCP_SNAPSHOT_ID??En(r),pe=e.historyRoot??n.env.VCP_SITE_EDIT_HISTORY_ROOT,fe=pe?.trim()?H.resolve(pe):void 0,D=null,le={launchId:Bt(),pid:process.pid,snapshotId:he},v=s((T,M)=>{sn({basePath:p,bridgePath:a,bridgeHealthPath:h,bridgeSessionKey:u,getBridgeHealth:()=>D?.getHealth()??dn(a),launch:le,runRenderProbe:()=>an(w),handle:Ke,request:T,response:M}).catch(qe=>{console.error(qe),M.headersSent||(M.statusCode=500),M.end("site edit preview request failed")})}),$=null,O=null,W=()=>O||(K(()=>n.off("SIGINT",N)),K(()=>n.off("SIGTERM",N)),K(()=>$?.()),$=null,K(()=>D?.stop()),D=null,O=Promise.allSettled([_n(v),Tn(()=>w.close())]).then(()=>{}),O),N=()=>{W()};try{return await w.prepare(),D=i(v,{...l?{tenantId:l}:{},siteId:Ve,snapshotId:he,projectRoot:r,basePath:p,host:c,port:d,path:a,sessionKey:u,...fe?{historyRoot:fe}:{},onSnapshotIdChanged:T=>{le.snapshotId=T}}),$=Jt({server:v,bridgePath:a,handleUpgrade:w.getUpgradeHandler()}),n.on("SIGINT",N),n.on("SIGTERM",N),await bn(v,d,c),{bundler:I,close:W,host:c,port:kn(v,d),server:v}}catch(T){throw await W(),T}}function $t(e){let t=Lt(e);return t&&zt(t)?"webpack":"turbopack"}function Wt(e){return e==="webpack"?{webpack:!0}:{turbopack:!0}}function Lt(e){for(let t of Ft){let n=B(e,t);if(_(n))return b(n,"utf8")}return null}function zt(e){return e.includes(Ut)?!0:Kt.test(e)&&Vt.some(t=>t.test(e))}function Jt(e){let t=(n,s,i)=>{Qt(n.url,e.bridgePath)||e.handleUpgrade(n,s,i).catch(o=>{console.error(o),s.destroyed||s.destroy()})};return e.server.on("upgrade",t),()=>e.server.off("upgrade",t)}function Qt(e,t){return new URL(e??"/","http://localhost").pathname===t}function Gt(e=V){if(e===V)return Me;let t=(e.startsWith("/")?e:`/${e}`).replace(/\/+$/,"");return t.endsWith("/bridge")?`${t.slice(0,-7)}/health`:t?`${t}/health`:"/health"}function Yt(e){let t=JSON.stringify({basePath:e.basePath,launch_id:e.launch?.launchId,snapshot_id:e.launch?.snapshotId,wsPath:e.bridgePath,bridge_session_key:e.bridgeSessionKey,defer_initial_sync:!0,runtime_versions:e.runtimeVersions??Zt()});return`<script type="module" ${je}>import { startSiteEditAutoBootstrap } from ${JSON.stringify(jt)};void startSiteEditAutoBootstrap(window, ${t}).catch(() => undefined);</script>`}function Zt(){return{bridge:re("@vcp-dev/site-edit-bridge"),contracts:re("@vcp-dev/contracts"),engine:re("@vcp-dev/site-edit-engine")}}function re(e){let t=Be(import.meta.url);try{let n=t.resolve(e),s=Xt(n,e);if(!s)return;let i=JSON.parse(b(s,"utf8"));return typeof i.version=="string"?i.version:void 0}catch{return}}function Xt(e,t){let n=H.parse(e).root,s=A(e);for(;s&&s!==n;){let i=B(s,"package.json");if(_(i))try{if(JSON.parse(b(i,"utf8")).name===t)return i}catch{return}s=A(s)}}function en(e,t){if(e.includes(je))return e;let n=Yt(t),s=/<\/body\s*>/i.exec(e);return s?`${e.slice(0,s.index)}${n}${e.slice(s.index)}`:`${e}${n}`}function ns(e,t=A(Oe(import.meta.url)),n=_){return Fe(e,t,n)?.path??null}function Fe(e,t=A(Oe(import.meta.url)),n=_){let s=new URL(e??"/","http://localhost").pathname;if(!s.startsWith(q))return null;let i=s.slice(q.length);if(!qt.has(i))return null;let o=de(Cn(t),i);if(De(t)!=="src")return{path:o,requiresTranspile:!1};let r=de(t,i.replace(/\.js$/,".ts"));return n(r)?{path:r,requiresTranspile:!0}:{path:o,requiresTranspile:!1}}async function tn(e,t){let n=Fe(e.url);if(!n)return!1;if(!_(n.path))return t.writeHead(404),t.end("site edit client asset not found"),!0;let s=n.requiresTranspile?await nn(b(n.path,"utf8")):b(n.path);return t.writeHead(200,{"cache-control":"no-store","content-type":"text/javascript; charset=utf-8"}),t.end(s),!0}async function nn(e){let t=await import("typescript");return t.transpileModule(e,{compilerOptions:{module:t.ModuleKind.ES2022,target:t.ScriptTarget.ES2022}}).outputText}async function sn({basePath:e,bridgePath:t,bridgeHealthPath:n,bridgeSessionKey:s,getBridgeHealth:i,launch:o,runRenderProbe:r,handle:c,request:d,response:a}){if(on(d.url,e)){a.statusCode=404,a.end();return}if(await un(d,a,n,i,o,r)||await tn(d,a))return;let h=hn(d),u=pn(d),p=h||u?mn(a,{basePath:e,bridgePath:t,bridgeSessionKey:s,patchNextDevChunk:u,launch:{...o}}):()=>{};try{await c(d,a)}catch(l){throw p(),l}}function on(e,t){let n=(e??"/").split(/[?#]/,1)[0]??"/";if(!/(\\|\/\/)/.test(n))return!1;let s;try{s=decodeURIComponent(n).replace(/\\/g,"/").replace(/\/{2,}/g,"/")}catch{return!1}let i=new URL(s,"http://namespace.invalid").pathname,o=t.replace(/\/+$/,"");return(o&&(i===o||i.startsWith(o+"/"))?i.slice(o.length)||"/":i).startsWith("/.vcp")}function rn(e){let t=e instanceof Error?e.message:String(e),n=e&&typeof e=="object"&&"code"in e?e.code:void 0;return/\.next[/\\]/.test(t)||/chunks[/\\]ssr[/\\]/.test(t)?n==="ENOENT"||n==="MODULE_NOT_FOUND"?{verdict:"artifact_broken",detail:t}:t.includes("Cannot find module")?{verdict:"artifact_broken",detail:t}:{verdict:"inconclusive",detail:t}:{verdict:"inconclusive",detail:t}}async function an(e){let t=new ue.IncomingMessage(new Pe);t.method="GET",t.url=He,t.headers={host:Ne};let n=new ue.ServerResponse(t);n.assignSocket(new Pe);try{return await e.renderToHTML(t,n,He,{}),{verdict:"ok"}}catch(s){return rn(s)}finally{try{let s=n.socket;s&&(n.detachSocket(s),s.destroy())}catch{}}}function cn(e){return new URL(e??"/","http://localhost").searchParams.get(Nt)===Mt}async function un(e,t,n,s,i,o){if(e.method!=="GET"||new URL(e.url??"/","http://localhost").pathname!==n)return!1;let c=s(),d=cn(e.url)?await o():void 0,a={...c,launch:i,...d?{render:d}:{}};return t.writeHead(c.ok?200:503,{"cache-control":"no-store","content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify(a)),!0}function dn(e){return{ok:!1,bridgePath:e,runtimeReady:!1,routesIndexed:0,sessionRequired:!0}}function hn(e){return fn(e)?(e.headers["accept-encoding"]="identity",!0):!1}function pn(e){return ln(e)?(e.headers["accept-encoding"]="identity",!0):!1}function fn(e){if(e.method!=="GET")return!1;let t=new URL(e.url??"/","http://localhost");if(e.headers["x-template-id"]!==void 0&&!t.searchParams.has("template_id"))return!1;let n=t.pathname;if(n.startsWith("/_next/")||n.startsWith(q)||n===V||n===Me)return!1;let s=e.headers.accept??"";return s===""||s.includes("text/html")}function ln(e){if(e.method!=="GET"||!String(e.headers["user-agent"]??"").includes("Firefox"))return!1;let n=new URL(e.url??"/","http://localhost").pathname;return n.startsWith("/_next/static/chunks/")&&n.endsWith(".js")}function mn(e,t){let n=[],s=e,i=s.flushHeaders.bind(e),o=s.write.bind(e),r=s.end.bind(e),c=!1;s.flushHeaders=()=>{};let d=()=>{if(t.patchNextDevChunk||c)return!1;let a=Buffer.concat(n);if(!/<body\b[^>]*>/iu.test(a.toString("utf8")))return!1;let h=Ae(e,a,t);return h!==a&&!e.headersSent&&e.removeHeader("content-length"),o(h),n.length=0,c=!0,!0};return s.write=(a,h,u)=>c?(a!==void 0&&o(ae(a,ce(h))),U(h),U(u),!0):(a!==void 0&&(n.push(ae(a,ce(h))),d()),U(h),U(u),!0),s.end=(a,h,u)=>{let p=typeof a=="function"?void 0:a;if(c)return s.write=o,s.end=r,s.flushHeaders=i,p===void 0?typeof a=="function"?r(a):typeof h=="function"?r(h):typeof u=="function"?r(u):r():typeof h=="function"?r(p,h):typeof u=="function"?r(p,h,u):r(p,h);p!==void 0&&n.push(ae(p,ce(h))),s.write=o,s.end=r,s.flushHeaders=i;let l=Buffer.concat(n),S=Ae(e,l,t);S!==l&&!e.headersSent&&e.removeHeader("content-length");let I=typeof a=="function"?a:typeof h=="function"?h:typeof u=="function"?u:void 0;return I?r(S,I):r(S)},()=>{s.write=o,s.end=r,s.flushHeaders=i}}function Ae(e,t,n){if(yn(e,t))return Buffer.from(en(t.toString("utf8"),n));if(n.patchNextDevChunk&&Sn(e)){let s=t.toString("utf8"),i=In(s);if(i!==s)return Buffer.from(i)}return t}function gn(){let e=new Uint8Array(32);return globalThis.crypto.getRandomValues(e),Buffer.from(e).toString("base64url")}function yn(e,t){if(e.statusCode<200||e.statusCode>=300)return!1;let n=e.getHeader("content-type"),s=Array.isArray(n)?n.join(";"):String(n??"");return t.toString("utf8",0,256).includes("<html")&&(s===""||s.includes("text/html"))}function Sn(e){if(e.statusCode<200||e.statusCode>=300)return!1;let t=e.getHeader("content-type"),n=Array.isArray(t)?t.join(";"):String(t??"");return n===""||n.includes("javascript")||n.includes("ecmascript")}function In(e){return[["debugChannel = createDebugChannel(undefined);","debugChannel = undefined;"],["if (isStreamErrorOrUnfinished(ctr)) {","if (false) {"],["return ctr.desiredSize === null || ctr.desiredSize < 0;","return false;"],[["findSourceMapURL: _appfindsourcemapurl.findSourceMapURL,"," debugChannel,"," startTime: 0"].join(`
4
+ `),r.destroy();return}d.authenticatedByHeader&&n.add(o),t.handleUpgrade(o,r,u,a=>{t.emit("connection",a,o)})};return e.server.on("upgrade",i),t.on("connection",(o,r)=>{if(n.has(r)){s(o);return}_t(o,e.sessionKey,()=>{s(o)})}),t.stop=()=>{e.server.off("upgrade",i);for(let o of t.clients)o.close(1001,"bridge stopped");t.close()},t}var ie=class{constructor(t){this.socket=t,this.onmessage=null,this.onclose=null,t.on("error",()=>{}),t.on("message",n=>{this.onmessage?.({data:n.toString()})}),t.on("close",()=>{this.onclose?.()})}send(t){if(this.socket.readyState===ve.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(t,"utf8")>mt){this.socket.close(1013,"bridge backpressure");return}this.socket.send(t)}}close(t,n){this.socket.close(t,n)}};function vt(e,t){return new URL(e.url??"/","http://localhost").pathname===t.path}function bt(e,t){if(new URL(e.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let s=ee(e.headers["x-vcp-site-edit-key"]),i=Te(s,t.sessionKey),o=ee(e.headers.host),r=ee(e.headers.origin);return o&&!Tt(o,t.host,r)?null:r?Pt(r,o,t.allowedOrigins)?{authenticatedByHeader:i}:null:i?{authenticatedByHeader:!0}:null}function _t(e,t,n){let s=setTimeout(()=>{e.close(1008,"bridge authentication timeout")},gt),i,o=()=>{clearTimeout(s),e.off("message",i),e.off("close",o)},r=()=>{o(),e.close(1008,"bridge authentication failed")};i=u=>{try{let d=m.parseMessage(u.toString());if(d.type!=="auth"||!Te(d.payload.key,t)){r();return}o(),e.readyState===ve.OPEN&&e.send(m.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),n()}catch{r()}},e.on("message",i),e.on("close",o)}function Te(e,t){if(!e)return!1;let n=Buffer.from(e),s=Buffer.from(t);return n.length===s.length&&nt(n,s)}function Tt(e,t,n){let s=Re(e),i=Re(t);return s===i||Ce(i)&&Ce(s)&&(!n||oe(n,e))||kt(i)&&(!n||oe(n,e))}function kt(e){return e==="0.0.0.0"||e==="::"}function Ce(e){return e==="localhost"||e==="127.0.0.1"||e==="::1"}function Pt(e,t,n){return n.includes(e)||(t?oe(e,t):!1)}function oe(e,t){try{return new URL(e).host===ke(t)}catch{return!1}}function Re(e){let t=ke(e);return t.startsWith("[")?t.slice(1,t.indexOf("]")):t.split(":").length-1===1?t.split(":",1)[0]??"":t}function ke(e){return e.trim().toLowerCase()}function ee(e){return Array.isArray(e)?e[0]:e}function Ht(){return tt(32).toString("base64url")}function At(e){let t=e.replaceAll("\\","/");return t===te||t.endsWith(`/${te}`)}var Bt="next",Oe="127.0.0.1",Dt=3e3,V="/__vcp/site-edit/bridge",Me="/__vcp/site-edit/health",Ot="probe",Mt="render",He="/_error",q="/__vcp/site-edit/client/",jt=`${q}auto-bootstrap.js`,je='data-vcp-site-edit-bootstrap="true"',Ft=["next.config.ts","next.config.mts","next.config.cts","next.config.js","next.config.mjs","next.config.cjs"],Ut="@vcp-dev/site-edit-engine/webpack-loader",Kt=/@vcp\/site-edit-engine\/(?:webpack-loader|site-edit-instrumentation-loader)/,Vt=[/\bconfig\.webpack\s*=/,/\bwebpack\s*\([^)]*\)\s*\{/,/\bwebpack\s*:\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)?\s*=>/,/\bwebpack\s*:\s*(?:async\s+)?function\b/],qt=new Set(["auto-bootstrap.js","client.js","composition-key.js","designer-trace.js","document-codec.js","property-preview.js","protocol.js","window-message.js"]);async function ts(e={},t={}){let n=t.process??process,s=t.createServer??ue.createServer,i=t.attachBridgeServer??_e,o=t.loadNext??xn,r=H.resolve(e.projectRoot??process.cwd()),u=e.host??Oe,d=e.port??vn(n.env.PORT)??Dt,a=e.bridgePath??V,h=Gt(a),c=gn(),p=e.basePath??n.env.VCP_SITE_PREVIEW_BASE_PATH??"/",f=(e.tenantId??n.env.VCP_TENANT_ID)?.trim()||void 0,S=await o(r),I=$t(r),w=S({dev:!0,dir:r,hostname:u,port:d,...Wt(I)}),Ke=w.getRequestHandler(),Ve=e.siteId??n.env.VCP_SITE_ID??"018f6f4d-a9b8-7120-8000-000000000120",he=e.snapshotId??n.env.VCP_SNAPSHOT_ID??En(r),pe=e.historyRoot??n.env.VCP_SITE_EDIT_HISTORY_ROOT,le=pe?.trim()?H.resolve(pe):void 0,B=null,fe={launchId:Nt(),pid:process.pid,snapshotId:he},v=s((T,M)=>{sn({basePath:p,bridgePath:a,bridgeHealthPath:h,bridgeSessionKey:c,getBridgeHealth:()=>B?.getHealth()??dn(a),launch:fe,runRenderProbe:()=>an(w),handle:Ke,request:T,response:M}).catch(qe=>{console.error(qe),M.headersSent||(M.statusCode=500),M.end("site edit preview request failed")})}),$=null,D=null,W=()=>D||(K(()=>n.off("SIGINT",O)),K(()=>n.off("SIGTERM",O)),K(()=>$?.()),$=null,K(()=>B?.stop()),B=null,D=Promise.allSettled([_n(v),Tn(()=>w.close())]).then(()=>{}),D),O=()=>{W()};try{return await w.prepare(),B=i(v,{...f?{tenantId:f}:{},siteId:Ve,snapshotId:he,projectRoot:r,basePath:p,host:u,port:d,path:a,sessionKey:c,...le?{historyRoot:le}:{},onSnapshotIdChanged:T=>{fe.snapshotId=T}}),$=Jt({server:v,bridgePath:a,handleUpgrade:w.getUpgradeHandler()}),n.on("SIGINT",O),n.on("SIGTERM",O),await bn(v,d,u),{bundler:I,close:W,host:u,port:kn(v,d),server:v}}catch(T){throw await W(),T}}function $t(e){let t=Lt(e);return t&&zt(t)?"webpack":"turbopack"}function Wt(e){return e==="webpack"?{webpack:!0}:{turbopack:!0}}function Lt(e){for(let t of Ft){let n=N(e,t);if(_(n))return b(n,"utf8")}return null}function zt(e){return e.includes(Ut)?!0:Kt.test(e)&&Vt.some(t=>t.test(e))}function Jt(e){let t=(n,s,i)=>{Qt(n.url,e.bridgePath)||e.handleUpgrade(n,s,i).catch(o=>{console.error(o),s.destroyed||s.destroy()})};return e.server.on("upgrade",t),()=>e.server.off("upgrade",t)}function Qt(e,t){return new URL(e??"/","http://localhost").pathname===t}function Gt(e=V){if(e===V)return Me;let t=(e.startsWith("/")?e:`/${e}`).replace(/\/+$/,"");return t.endsWith("/bridge")?`${t.slice(0,-7)}/health`:t?`${t}/health`:"/health"}function Yt(e){let t=JSON.stringify({basePath:e.basePath,launch_id:e.launch?.launchId,snapshot_id:e.launch?.snapshotId,wsPath:e.bridgePath,bridge_session_key:e.bridgeSessionKey,defer_initial_sync:!0,runtime_versions:e.runtimeVersions??Zt()});return`<script type="module" ${je}>import { startSiteEditAutoBootstrap } from ${JSON.stringify(jt)};void startSiteEditAutoBootstrap(window, ${t}).catch(() => undefined);</script>`}function Zt(){return{bridge:re("@vcp-dev/site-edit-bridge"),contracts:re("@vcp-dev/contracts"),engine:re("@vcp-dev/site-edit-engine")}}function re(e){let t=Ne(import.meta.url);try{let n=t.resolve(e),s=Xt(n,e);if(!s)return;let i=JSON.parse(b(s,"utf8"));return typeof i.version=="string"?i.version:void 0}catch{return}}function Xt(e,t){let n=H.parse(e).root,s=A(e);for(;s&&s!==n;){let i=N(s,"package.json");if(_(i))try{if(JSON.parse(b(i,"utf8")).name===t)return i}catch{return}s=A(s)}}function en(e,t){if(e.includes(je))return e;let n=Yt(t),s=/<\/body\s*>/i.exec(e);return s?`${e.slice(0,s.index)}${n}${e.slice(s.index)}`:`${e}${n}`}function ns(e,t=A(De(import.meta.url)),n=_){return Fe(e,t,n)?.path??null}function Fe(e,t=A(De(import.meta.url)),n=_){let s=new URL(e??"/","http://localhost").pathname;if(!s.startsWith(q))return null;let i=s.slice(q.length);if(!qt.has(i))return null;let o=de(Cn(t),i);if(Be(t)!=="src")return{path:o,requiresTranspile:!1};let r=de(t,i.replace(/\.js$/,".ts"));return n(r)?{path:r,requiresTranspile:!0}:{path:o,requiresTranspile:!1}}async function tn(e,t){let n=Fe(e.url);if(!n)return!1;if(!_(n.path))return t.writeHead(404),t.end("site edit client asset not found"),!0;let s=n.requiresTranspile?await nn(b(n.path,"utf8")):b(n.path);return t.writeHead(200,{"cache-control":"no-store","content-type":"text/javascript; charset=utf-8"}),t.end(s),!0}async function nn(e){let t=await import("typescript");return t.transpileModule(e,{compilerOptions:{module:t.ModuleKind.ES2022,target:t.ScriptTarget.ES2022}}).outputText}async function sn({basePath:e,bridgePath:t,bridgeHealthPath:n,bridgeSessionKey:s,getBridgeHealth:i,launch:o,runRenderProbe:r,handle:u,request:d,response:a}){if(on(d.url,e)){a.statusCode=404,a.end();return}if(await un(d,a,n,i,o,r)||await tn(d,a))return;let h=hn(d),c=pn(d),p=h||c?mn(a,{basePath:e,bridgePath:t,bridgeSessionKey:s,patchNextDevChunk:c,launch:{...o}}):()=>{};try{await u(d,a)}catch(f){throw p(),f}}function on(e,t){let n=(e??"/").split(/[?#]/,1)[0]??"/";if(!/(\\|\/\/)/.test(n))return!1;let s;try{s=decodeURIComponent(n).replace(/\\/g,"/").replace(/\/{2,}/g,"/")}catch{return!1}let i=new URL(s,"http://namespace.invalid").pathname,o=t.replace(/\/+$/,"");return(o&&(i===o||i.startsWith(o+"/"))?i.slice(o.length)||"/":i).startsWith("/.vcp")}function rn(e){let t=e instanceof Error?e.message:String(e),n=e&&typeof e=="object"&&"code"in e?e.code:void 0;return/\.next[/\\]/.test(t)||/chunks[/\\]ssr[/\\]/.test(t)?n==="ENOENT"||n==="MODULE_NOT_FOUND"?{verdict:"artifact_broken",detail:t}:t.includes("Cannot find module")?{verdict:"artifact_broken",detail:t}:{verdict:"inconclusive",detail:t}:{verdict:"inconclusive",detail:t}}async function an(e){let t=new ue.IncomingMessage(new Pe);t.method="GET",t.url=He,t.headers={host:Oe};let n=new ue.ServerResponse(t);n.assignSocket(new Pe);try{return await e.renderToHTML(t,n,He,{}),{verdict:"ok"}}catch(s){return rn(s)}finally{try{let s=n.socket;s&&(n.detachSocket(s),s.destroy())}catch{}}}function cn(e){return new URL(e??"/","http://localhost").searchParams.get(Ot)===Mt}async function un(e,t,n,s,i,o){if(e.method!=="GET"||new URL(e.url??"/","http://localhost").pathname!==n)return!1;let u=s(),d=cn(e.url)?await o():void 0,a={...u,launch:i,...d?{render:d}:{}};return t.writeHead(u.ok?200:503,{"cache-control":"no-store","content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify(a)),!0}function dn(e){return{ok:!1,bridgePath:e,runtimeReady:!1,routesIndexed:0,sessionRequired:!0}}function hn(e){return ln(e)?(e.headers["accept-encoding"]="identity",!0):!1}function pn(e){return fn(e)?(e.headers["accept-encoding"]="identity",!0):!1}function ln(e){if(e.method!=="GET")return!1;let t=new URL(e.url??"/","http://localhost");if(e.headers["x-template-id"]!==void 0&&!t.searchParams.has("template_id"))return!1;let n=t.pathname;if(n.startsWith("/_next/")||n.startsWith(q)||n===V||n===Me)return!1;let s=e.headers.accept??"";return s===""||s.includes("text/html")}function fn(e){if(e.method!=="GET"||!String(e.headers["user-agent"]??"").includes("Firefox"))return!1;let n=new URL(e.url??"/","http://localhost").pathname;return n.startsWith("/_next/static/chunks/")&&n.endsWith(".js")}function mn(e,t){let n=[],s=e,i=s.flushHeaders.bind(e),o=s.write.bind(e),r=s.end.bind(e),u=!1;s.flushHeaders=()=>{};let d=()=>{if(t.patchNextDevChunk||u)return!1;let a=Buffer.concat(n);if(!/<body\b[^>]*>/iu.test(a.toString("utf8")))return!1;let h=Ae(e,a,t);return h!==a&&!e.headersSent&&e.removeHeader("content-length"),o(h),n.length=0,u=!0,!0};return s.write=(a,h,c)=>u?(a!==void 0&&o(ae(a,ce(h))),U(h),U(c),!0):(a!==void 0&&(n.push(ae(a,ce(h))),d()),U(h),U(c),!0),s.end=(a,h,c)=>{let p=typeof a=="function"?void 0:a;if(u)return s.write=o,s.end=r,s.flushHeaders=i,p===void 0?typeof a=="function"?r(a):typeof h=="function"?r(h):typeof c=="function"?r(c):r():typeof h=="function"?r(p,h):typeof c=="function"?r(p,h,c):r(p,h);p!==void 0&&n.push(ae(p,ce(h))),s.write=o,s.end=r,s.flushHeaders=i;let f=Buffer.concat(n),S=Ae(e,f,t);S!==f&&!e.headersSent&&e.removeHeader("content-length");let I=typeof a=="function"?a:typeof h=="function"?h:typeof c=="function"?c:void 0;return I?r(S,I):r(S)},()=>{s.write=o,s.end=r,s.flushHeaders=i}}function Ae(e,t,n){if(yn(e,t))return Buffer.from(en(t.toString("utf8"),n));if(n.patchNextDevChunk&&Sn(e)){let s=t.toString("utf8"),i=In(s);if(i!==s)return Buffer.from(i)}return t}function gn(){let e=new Uint8Array(32);return globalThis.crypto.getRandomValues(e),Buffer.from(e).toString("base64url")}function yn(e,t){if(e.statusCode<200||e.statusCode>=300)return!1;let n=e.getHeader("content-type"),s=Array.isArray(n)?n.join(";"):String(n??"");return t.toString("utf8",0,256).includes("<html")&&(s===""||s.includes("text/html"))}function Sn(e){if(e.statusCode<200||e.statusCode>=300)return!1;let t=e.getHeader("content-type"),n=Array.isArray(t)?t.join(";"):String(t??"");return n===""||n.includes("javascript")||n.includes("ecmascript")}function In(e){return[["debugChannel = createDebugChannel(undefined);","debugChannel = undefined;"],["if (isStreamErrorOrUnfinished(ctr)) {","if (false) {"],["return ctr.desiredSize === null || ctr.desiredSize < 0;","return false;"],[["findSourceMapURL: _appfindsourcemapurl.findSourceMapURL,"," debugChannel,"," startTime: 0"].join(`
5
5
  `),["unstable_allowPartialStream: true,"," debugChannel,"," startTime: 0"].join(`
6
- `)],[["findSourceMapURL: _appfindsourcemapurl.findSourceMapURL,"," debugChannel,"," startTime: 0"].join("\\n"),["unstable_allowPartialStream: true,"," debugChannel,"," startTime: 0"].join("\\n")],["debugChannel: createDebugChannel && createDebugChannel(requestHeaders)","debugChannel: undefined"],["debugChannel: createDebugChannel && createDebugChannel(headers)","debugChannel: undefined"]].reduce((n,[s,i])=>wn(n,s,i),e)}function wn(e,t,n){if(n.length>t.length)throw new Error("Replacement must not be longer than the search string.");return e.replaceAll(t,n.padEnd(t.length," "))}function ae(e,t){return Buffer.isBuffer(e)?e:e instanceof Uint8Array?Buffer.from(e):Buffer.from(String(e),t)}function ce(e){return typeof e=="string"?e:void 0}function U(e){typeof e=="function"&&e()}async function xn(e){let n=Be(B(e,"package.json"))(Dt);return n.default??n}function Cn(e){return De(e)!=="src"?e:de(Rn(e),"dist")}function Rn(e){return Ue(e)?H.posix.dirname(e):A(e)}function de(e,t){return Ue(e)?H.posix.join(e,t):B(e,t)}function Ue(e){return e.includes("/")&&!e.includes("\\")}function En(e){let t=B(e,".vcp/current-snapshot.json");if(!_(t))return"sandbox_worktree";try{let n=JSON.parse(b(t,"utf8"));return typeof n.snapshot_id=="string"&&n.snapshot_id?n.snapshot_id:typeof n.snapshotId=="string"&&n.snapshotId?n.snapshotId:"sandbox_worktree"}catch{return"sandbox_worktree"}}function vn(e){if(!e)return null;let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0&&t<=65535?t:null}function bn(e,t,n){return new Promise((s,i)=>{e.once("error",i),e.listen(t,n,()=>{e.off("error",i),s()})})}function _n(e){return new Promise(t=>{e.close(()=>t())})}function K(e){try{e()}catch{}}function Tn(e){try{return e()}catch(t){return Promise.reject(t)}}function kn(e,t){let n=e.address();return typeof n=="object"&&n?n.port:t}export{Jt as attachNextDevUpgradeHandler,rn as classifySiteEditRenderProbeError,Yt as createSiteEditAutoBootstrapHtmlSnippet,en as injectSiteEditAutoBootstrap,cn as isSiteEditRenderProbeRequested,In as patchNextFirefoxDevChunk,pn as prepareFirefoxNextDevChunkPatchRequest,hn as prepareSiteEditHtmlInjectionRequest,Gt as resolveSiteEditBridgeHealthPath,Fe as resolveSiteEditClientAsset,ns as resolveSiteEditClientAssetPath,$t as resolveSiteEditNextDevBundler,an as runSiteEditRenderProbe,ts as startSiteEditNextDevServer,nn as transpileSiteEditClientSource};
6
+ `)],[["findSourceMapURL: _appfindsourcemapurl.findSourceMapURL,"," debugChannel,"," startTime: 0"].join("\\n"),["unstable_allowPartialStream: true,"," debugChannel,"," startTime: 0"].join("\\n")],["debugChannel: createDebugChannel && createDebugChannel(requestHeaders)","debugChannel: undefined"],["debugChannel: createDebugChannel && createDebugChannel(headers)","debugChannel: undefined"]].reduce((n,[s,i])=>wn(n,s,i),e)}function wn(e,t,n){if(n.length>t.length)throw new Error("Replacement must not be longer than the search string.");return e.replaceAll(t,n.padEnd(t.length," "))}function ae(e,t){return Buffer.isBuffer(e)?e:e instanceof Uint8Array?Buffer.from(e):Buffer.from(String(e),t)}function ce(e){return typeof e=="string"?e:void 0}function U(e){typeof e=="function"&&e()}async function xn(e){let n=Ne(N(e,"package.json"))(Bt);return n.default??n}function Cn(e){return Be(e)!=="src"?e:de(Rn(e),"dist")}function Rn(e){return Ue(e)?H.posix.dirname(e):A(e)}function de(e,t){return Ue(e)?H.posix.join(e,t):N(e,t)}function Ue(e){return e.includes("/")&&!e.includes("\\")}function En(e){let t=N(e,".vcp/current-snapshot.json");if(!_(t))return"sandbox_worktree";try{let n=JSON.parse(b(t,"utf8"));return typeof n.snapshot_id=="string"&&n.snapshot_id?n.snapshot_id:typeof n.snapshotId=="string"&&n.snapshotId?n.snapshotId:"sandbox_worktree"}catch{return"sandbox_worktree"}}function vn(e){if(!e)return null;let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0&&t<=65535?t:null}function bn(e,t,n){return new Promise((s,i)=>{e.once("error",i),e.listen(t,n,()=>{e.off("error",i),s()})})}function _n(e){return new Promise(t=>{e.close(()=>t())})}function K(e){try{e()}catch{}}function Tn(e){try{return e()}catch(t){return Promise.reject(t)}}function kn(e,t){let n=e.address();return typeof n=="object"&&n?n.port:t}export{Jt as attachNextDevUpgradeHandler,rn as classifySiteEditRenderProbeError,Yt as createSiteEditAutoBootstrapHtmlSnippet,en as injectSiteEditAutoBootstrap,cn as isSiteEditRenderProbeRequested,In as patchNextFirefoxDevChunk,pn as prepareFirefoxNextDevChunkPatchRequest,hn as prepareSiteEditHtmlInjectionRequest,Gt as resolveSiteEditBridgeHealthPath,Fe as resolveSiteEditClientAsset,ns as resolveSiteEditClientAssetPath,$t as resolveSiteEditNextDevBundler,an as runSiteEditRenderProbe,ts as startSiteEditNextDevServer,nn as transpileSiteEditClientSource};
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
1
- import gt from"node:http";import E from"node:fs";import{createHash as Z,randomBytes as yt,timingSafeEqual as It}from"node:crypto";import m from"node:path";import{WebSocket as X,WebSocketServer as St}from"ws";import{createPreviewSiteEditEngineRuntime as wt,openFileHistoryStore as Ct,resolveManagedPageTemplateComposition as Rt,scanSiteEditCompositionsFromFiles as Et}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as xt,scanNextAppRouterRoutesFromFiles as vt}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as bt}from"@vcp-dev/site-edit-engine/source-watcher";function $(e){let t=new Map,s=[],n=new Map,o=[],i=e.pageComposition.status==="supported"?e.pageComposition.placementUnitKeys:[],r=new Map(i.map((c,h)=>[c,h])),a=e.pageComposition.status==="supported"?{status:"supported",containerKey:e.pageComposition.containerKey,hasBusinessMembers:e.pageComposition.hasBusinessMembers,placementUnitKeys:e.pageComposition.placementUnitKeys,placements:e.pageComposition.placements.map(c=>({placementKey:c.placementKey,placementUnitKey:c.placementUnitKey,relation:c.relation}))}:{status:"unsupported",reason:e.pageComposition.reason,diagnostics:e.pageComposition.diagnostics},p=e.entries.flatMap((c,h)=>c.dynamicDataSummary?[[h,c.dynamicDataSummary]]:[]),u=c=>{let h=t.get(c);if(typeof h=="number")return h;let C=s.length;return s.push(c),t.set(c,C),C},g=c=>{let h=null;for(let C of at(c)){let V=`${h??"root"}\0${C}`,P=n.get(V);if(typeof P=="number"){h=P;continue}let U=o.length;o.push([h,u(C)]),n.set(V,U),h=U}if(h===null)throw new Error("Document key must not be empty");return h};return{encoding:"segment-trie-v1",siteId:e.siteId,snapshotId:e.snapshotId,composition:{...e.composition},routeId:e.composition.routeId,version:e.version,pageComposition:a,tokens:s,nodes:o,entries:e.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[g(c.key),rt(c.operationSummary),...h>=0?[h]:[]]}),...p.length>0?{dynamicDataSummaries:p}:{}}}function rt(e){return(e.canUpdateText?1:0)|(e.canInsertChild?2:0)|(e.canMove?4:0)|(e.canRemove?8:0)}function at(e){if(!e)return[];let t=[],s=0,n=W(e,s);if(n<0)return t.push(e),t;for(t.push(e.slice(0,n)),s=n;s<e.length;){if(n=W(e,s+(e.startsWith("::",s)?2:1)),n<0){t.push(e.slice(s));break}t.push(e.slice(s,n)),s=n}return t}function W(e,t){let s=e.indexOf("/",t),n=e.indexOf("::",t);return s<0?n:n<0?s:Math.min(s,n)}var j="SITE_EDIT_COMPOSITION_SESSION_MISMATCH",M="SITE_EDIT_COMPOSITION_SESSION_REQUIRED";var l=class e{constructor(){}static parseMessage(t,s={}){let n=JSON.parse(t);if(!e.isMessage(n,s))throw new Error("Invalid site edit bridge message");return n}static stringifyMessage(t){return JSON.stringify(t)}static extractRequestInfo(t){if(d(t)&&d(t.payload)){let s=typeof t.payload.id=="string"?t.payload.id:"unknown",n=this.isRequestKind(t.payload.kind)?t.payload.kind:"get-document";return{id:s,kind:n}}return{id:"unknown",kind:"get-document"}}static isRequestKind(t){return t==="initialize-session"||t==="get-document"||t==="get-history-status"||t==="get-style-context"||t==="get-object-edit-context"||t==="get-class-name-source"||t==="get-source-tree"||t==="get-source-file"||t==="resolve-source-location"||t==="execute"||t==="undo"||t==="redo"}static isMessage(t,s={}){return!d(t)||typeof t.type!="string"?!1:t.type==="request"?ct(t.payload,s):t.type==="response"||t.type==="error"?d(t.payload)&&typeof t.payload.id=="string":t.type==="event"?d(t.payload)&&d(t.payload.event):t.type==="auth"?d(t.payload)&&typeof t.payload.key=="string":t.type==="auth-ok"&&d(t.payload)&&t.payload.ok===!0}},x=class{constructor(t){this.options=t,this.session={subscribedCompositionKeys:new Set},this.messageQueue=new A,this.eventDispatcher=new D(s=>{this.sendJson(s)}),this.unsubscribe=t.engine.subscribe(s=>{this.handleEngineEvent(s)}),t.socket.onmessage=s=>{try{this.messageQueue.enqueue(()=>this.handleRawMessage(s.data))}catch(n){this.sendJson({type:"error",payload:{id:"unknown",kind:"get-document",message:n instanceof Error?n.message:"Bridge queue overflow"}}),t.socket.close(1013,"bridge busy")}},t.socket.onclose=()=>{this.unsubscribe(),this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.clear()}}async handleMessage(t){try{let s=t;if(s.type!=="request")return;let n=s.payload,{id:o}=n;switch(n.kind){case"initialize-session":{let i=k(n),r=await this.resolve(i),a=await this.options.engine.initializeSession({composition:r.context,...n.mutationContext?{mutationContext:n.mutationContext}:{}});this.activateSession(r.context),this.sendJson({type:"response",payload:{id:o,kind:n.kind,result:y({...a,data:{...a.data,document:z(a.data.document)}},r.context)}});return}case"get-document":{let i=await this.resolveWireContext(n),r=await this.options.engine.getDocument(i);this.sendResult(o,n.kind,mt(r,i));return}case"get-history-status":{let i=await this.resolveWireContext(n),r=await this.options.engine.getHistoryStatus(i);this.sendResult(o,n.kind,y(r,i));return}case"get-style-context":{let i=await this.resolveWireContext(n);this.sendResult(o,n.kind,y(await this.options.engine.getStyleContext(i),i));return}case"get-object-edit-context":{let i=await this.resolveWireContext(n);this.sendResult(o,n.kind,y(await this.options.engine.getObjectEditContext(n.key,i,n.selectionRef),i));return}case"get-class-name-source":{let i=await this.resolveWireContext(n);this.sendResult(o,n.kind,y(await this.options.engine.getClassNameSource(n.key,i),i));return}case"get-source-tree":{let i=await this.resolveWireContext(n);this.sendResult(o,n.kind,y(await this.options.engine.getSourceTree(i),i));return}case"get-source-file":{let i=await this.resolveWireContext(n);this.sendResult(o,n.kind,y(await this.options.engine.getSourceFile({path:n.path,composition:i}),i));return}case"resolve-source-location":{let i=await this.resolveWireContext(n),r=y(await this.options.engine.resolveSourceLocation({key:n.key,composition:i}),i);this.sendResult(o,n.kind,r);return}case"execute":{let i=await this.resolveOperationWireContext(n.request),r=await this.resolveWireContext(n);if(I(i)!==I(r))throw new S(j,"The execute request composition does not match its operation composition.");let a=await this.normalizeOperationRequest(n.request,i);await this.handleBufferedOperation(o,n.kind,()=>this.options.engine.execute(a));return}case"undo":case"redo":{let i=await this.resolveWireContext(n);await this.handleBufferedOperation(o,n.kind,()=>n.kind==="undo"?this.options.engine.undo(i):this.options.engine.redo(i));return}}}catch(s){let n=l.extractRequestInfo(t);this.sendJson({type:"error",payload:{...n,message:O(s),code:T(s)}})}}async resolve(t){return this.options.engine.resolveComposition(t)}async resolveAndAssert(t){let s=await this.resolve(t),n=this.session.activeComposition;if(!n)throw new S(M,"Initialize a composition session before issuing this request.");if(I(n)!==I(s.context))throw new S(j,"The request composition does not match the active socket session.");return s.context}async resolveWireContext(t){let s=k(t);return this.resolveAndAssert(s)}async resolveOperationWireContext(t){let s=pt(t);return this.resolveAndAssert(s)}activateSession(t){this.session.activeComposition=t,this.session.subscribedCompositionKeys.clear(),this.session.subscribedCompositionKeys.add(I(t))}async normalizeOperationRequest(t,s){let n={...t,composition:L(s)};if(n.target.kind==="route"){let o=n.target,i=k(o),r=await this.resolveAndAssert(i);n.target={...o,composition:L(r)}}return n}handleEngineEvent(t){let s=t?.composition;if(!ht(s))return;let n=I(s);this.session.subscribedCompositionKeys.has(n)&&(this.eventDispatcher.push(t),t.type==="site-edit.graph.invalidated"&&n===lt(this.session)&&(this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.delete(n)))}sendResult(t,s,n){this.sendJson({type:"response",payload:{id:t,kind:s,result:n}})}async handleRawMessage(t){try{await this.handleMessage(l.parseMessage(t,{validateOperationRequest:this.options.validateOperationRequest}))}catch(s){let n=ft(t);this.sendJson({type:"error",payload:{...n,message:O(s),code:T(s)}})}}sendJson(t){this.options.socket.send(l.stringifyMessage(t))}async handleBufferedOperation(t,s,n){this.eventDispatcher.beginBuffering();try{let o=await n();this.sendResult(t,s,o)}catch(o){this.sendJson({type:"error",payload:{id:t,kind:s,message:O(o),code:T(o)}})}finally{this.eventDispatcher.flush()}}},S=class extends Error{constructor(t,s){super(s),this.code=t,this.name="BridgeProtocolError"}},D=class{constructor(t){this.send=t,this.bufferedEvents=null}push(t){let s={type:"event",payload:{event:t}};if(this.bufferedEvents){this.bufferedEvents.push(s);return}this.send(s)}beginBuffering(){this.bufferedEvents=[]}flush(){let t=this.bufferedEvents??[];this.bufferedEvents=null,t.forEach(s=>this.send(s))}},A=class{constructor(t=64){this.maxQueued=t,this.queue=Promise.resolve(),this.queued=0}enqueue(t){if(this.queued>=this.maxQueued)throw new Error("Bridge request queue is full");this.queued+=1;let s=async()=>{this.queued-=1,await t()},n=this.queue.then(s,s);return this.queue=n.then(()=>{},()=>{}),n}};function ct(e,t){if(!d(e)||typeof e.id!="string"||!l.isRequestKind(e.kind))return!1;let s=R(e.composition);switch(e.kind){case"initialize-session":return s&&(e.mutationContext===void 0||_(e.mutationContext));case"get-document":case"get-style-context":return s;case"get-object-edit-context":return typeof e.key=="string"&&s&&ut(e.selectionRef);case"get-class-name-source":case"resolve-source-location":return typeof e.key=="string"&&s;case"get-history-status":return s;case"get-source-tree":return s;case"get-source-file":return typeof e.path=="string"&&s;case"execute":return s&&dt(e.request)&&(e.request.mutationContext===void 0||_(e.request.mutationContext))&&(t.validateOperationRequest?.(e.request)??!0);case"undo":case"redo":return s&&e.mutationContext===void 0||s&&_(e.mutationContext)}}function ut(e){if(e===void 0)return!0;if(!d(e)||typeof e.key!="string"||e.key.length===0)return!1;let t=e.instanceIndex,s=e.instanceCount;return t!==void 0&&(typeof t!="number"||!Number.isInteger(t)||t<0)||s!==void 0&&(typeof s!="number"||!Number.isInteger(s)||s<=0)||typeof t=="number"&&typeof s=="number"&&t>=s?!1:Object.keys(e).every(n=>n==="key"||n==="instanceIndex"||n==="instanceCount")}function dt(e){return d(e)&&typeof e.id=="string"&&typeof e.siteId=="string"&&typeof e.baseSnapshotId=="string"&&R(e.composition)&&typeof e.kind=="string"&&d(e.target)&&(e.target.kind!=="route"||R(e.target.composition))&&typeof e.documentVersion=="number"&&Number.isInteger(e.documentVersion)&&e.documentVersion>=0&&d(e.params)}function R(e){return d(e)&&typeof e.routeId=="string"&&e.routeId.length>0&&(e.templateId===void 0||typeof e.templateId=="string"&&e.templateId.length>0)&&Object.keys(e).every(t=>t==="routeId"||t==="templateId")}function ht(e){return d(e)&&typeof e.routeId=="string"&&(e.effectiveTemplateId===void 0||typeof e.effectiveTemplateId=="string")}function k(e){if(R(e.composition))return e.composition;throw new S(M,"A composition selector is required for this request.")}function pt(e){if(d(e)&&R(e.composition))return e.composition;throw new S(M,"A composition selector is required for execute.")}function L(e){return{routeId:e.routeId,...e.effectiveTemplateId?{templateId:e.effectiveTemplateId}:{}}}function lt(e){return e.activeComposition?I(e.activeComposition):null}function I(e){return JSON.stringify([e.routeId,e.effectiveTemplateId??null])}function y(e,t){return d(e)&&e.ok===!0?{...e,composition:t}:e}function mt(e,t){return!d(e)||e.ok!==!0?e:{...e,...t?{composition:t}:{},data:z(e.data)}}function z(e){let t=e;return $(t.routeId?t:{...t,routeId:t.composition.routeId})}function d(e){return typeof e=="object"&&e!==null}function _(e){return d(e)&&typeof e.editingSessionId=="string"&&e.editingSessionId.length>0&&typeof e.lockId=="string"&&e.lockId.length>0&&typeof e.ownerId=="string"&&e.ownerId.length>0&&typeof e.leaseAssertion=="string"&&e.leaseAssertion.length>0}function T(e){if(!(!d(e)||typeof e.code!="string"||e.code.length===0))return e.code}function O(e){return e instanceof Error||d(e)&&typeof e.message=="string"&&e.message.length>0?e.message:"Unknown websocket operation failure"}function ft(e){try{return l.extractRequestInfo(JSON.parse(e))}catch{return{id:"unknown",kind:"get-document"}}}var v=class{constructor(){this.tail=Promise.resolve()}run(t){let s=this.tail.then(t,t);return this.tail=s.then(()=>{},()=>{}),s}};import{SiteEditOperationRequestSchema as kt,computeSiteEditCompositionKey as w}from"@vcp-dev/contracts";var _t="127.0.0.1",Tt=30218,Ot="/__vcp/site-edit/bridge",tt=6*1024*1024,Dt=tt*2,At=3e3,J=50,K=".vcp/current-snapshot.json",Q="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function ae(e){let t=et(e),s=nt(t),n=st(),o=n.get(s);if(o)return o;let i=new b(t);n.set(s,i);try{i.start()}catch(r){throw n.delete(s),i.stop(),r}return i}function ce(e,t,s={}){let n=new b(et(t),e,!1,s);return n.start({listen:!1}),n}var b=class{constructor(t,s=gt.createServer((i,r)=>{r.writeHead(404),r.end("VCP site edit bridge server")}),n=!0,o={}){this.options=t,this.server=s,this.closeHttpServerOnStop=n,this.dependencies=o,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new v,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()}start(t={}){if(this.isStarted)return;this.stopping=!1;try{E.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)}`)}this.refreshCompositions(!0),this.isStarted=!0,this.createRuntime();let s=Ht({server:this.server,path:this.options.path,sessionKey:this.options.sessionKey,host:this.options.host,allowedOrigins:this.options.allowedOrigins,engine:this.createBridgeEngine()});this.stopWebSocketServer=s.stop,this.stopWatching=new bt({projectRoot:this.options.projectRoot,watch_dirs:Mt(this.options.runtime.watch_dirs),interval_ms:this.options.runtime.watch_interval_ms,debounce_ms:this.options.runtime.watch_debounce_ms??J,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(),t.listen!==!1&&this.server.listen(this.options.port,this.options.host)}getHealth(){let t=!!this.runtime&&!this.compositionResolutionError;return{ok:this.isStarted&&t,bridgePath:this.options.path,runtimeReady:t,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 t=st(),s=nt(this.options);t.get(s)===this&&t.delete(s),this.isStarted=!1}createRuntime(t,s=null){let n=this.runtime;this.activeHistoryStore=t??null;let o=(this.dependencies.createRuntime??wt)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,compositions:this.compositions,onEvent:i=>{"version"in i&&typeof i.version=="number"&&this.recordRuntimeVersion(i.version,"composition"in i?i.composition:void 0);for(let r of this.eventListeners)r(i)},...t?{historyStore:t}:{},...this.getInitialDocumentVersions().length>0?{initialDocumentVersions:this.getInitialDocumentVersions()}:{}});this.runtime=o,this.activeHistorySessionId=s,n?.dispose()}createBridgeEngine(){return{resolveComposition:t=>this.resolveComposition(t),initializeSession:t=>this.mutationQueue.run(async()=>{let s=await this.bindHistorySession(this.options.snapshotId);if(!s.ok)throw new Error(`${s.code}: ${s.message}`);let n=Bt(t.composition),o=this.getRuntime(),i=f(n);await o.initializeComposition(i);let a=o.getDocument(i).data;await this.recordRuntimeVersion(a.version,n);let p=(await o.getHistoryStatus()).data;return{ok:!0,composition:n,data:{document:a,historyStatus:p}}}),getDocument:async t=>{let s=this.getRuntime(),n=f(t);await s.initializeComposition(n);let o=s.getDocument(n);return await this.recordRuntimeVersion(o.data.version,t),{...o,composition:t}},getHistoryStatus:async t=>({...await this.getRuntime().getHistoryStatus(),composition:t}),getStyleContext:async t=>this.getRuntime().getStyleContext(f(t)),getObjectEditContext:async(t,s,n)=>this.getRuntime().getObjectEditContext(t,f(s),n),getClassNameSource:async(t,s)=>this.getRuntime().getClassNameSource(t,f(s)),getSourceTree:async t=>this.getRuntime().getSourceTree(f(t)),getSourceFile:async t=>this.getRuntime().getSourceFile({path:t.path,composition:f(t.composition)}),resolveSourceLocation:async t=>this.getRuntime().resolveSourceLocation({key:t.key,composition:f(t.composition)}),execute:(t,s)=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return F(n,t);let o=await this.getRuntime().executeOperation(t);return o.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(o.data.resultVersion,o.composition),o}),undo:t=>this.mutationQueue.run(async()=>{let s=await this.bindHistorySession(this.options.snapshotId);if(!s.ok)return F(s,void 0,t);let n=await this.getRuntime().undo();return n.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(n.data.resultVersion,n.composition??t),n.composition?n:{...n,composition:t}}),redo:t=>this.mutationQueue.run(async()=>{let s=await this.bindHistorySession(this.options.snapshotId);if(!s.ok)return F(s,void 0,t);let n=await this.getRuntime().redo();return n.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(n.data.resultVersion,n.composition??t),n.composition?n:{...n,composition:t}}),subscribe:t=>(this.eventListeners.add(t),()=>this.eventListeners.delete(t))}}async bindHistorySession(t){if(!this.options.historyRoot)return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history is not configured."};if(this.activeHistorySessionId===t)return{ok:!0};let s=this.historySessionTransition.then(async()=>{if(this.activeHistorySessionId===t)return;let n=await(this.dependencies.openHistoryStore??Ct)(Kt({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:t}));if(!this.isStarted)return;let o=n.readDocumentVersion?await n.readDocumentVersion():0,i=this.compositions[0]?.context;if(i){let r=w(i);this.runtimeVersionFloors.set(r,Math.max(this.runtimeVersionFloors.get(r)??0,o))}this.createRuntime(n,t)});this.historySessionTransition=s.catch(()=>{});try{return await s,{ok:!0}}catch{return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history could not be opened."}}}recordRuntimeVersion(t,s){let n=s?w(s):null;n&&this.runtimeVersionFloors.set(n,Math.max(this.runtimeVersionFloors.get(n)??0,t));let o=this.activeHistoryStore;if(!o?.writeDocumentVersion)return Promise.resolve();let i=n?this.runtimeVersionFloors.get(n)??t:t;return this.versionPersistence=this.versionPersistence.then(()=>o.writeDocumentVersion(i)).catch(()=>{}),this.versionPersistence}handleFileChanged(t){this.handleFilesChanged([t])}handleFilesChanged(t){for(let n of t)this.pendingChangedFiles.add(n);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let s=this.options.runtime.watch_debounce_ms??J;this.fileChangeTimer=setTimeout(()=>{this.fileChangeTimer=null,this.flushChangedFiles()},s),this.fileChangeTimer?.unref?.()}flushChangedFiles(){if(this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.pendingChangedFiles.size===0)return;let t=[...this.pendingChangedFiles].sort((s,n)=>s.localeCompare(n));if(this.pendingChangedFiles.clear(),t.some(jt)&&this.scheduleSnapshotIdentityAdvance(),!!this.refreshCompositions(!1))for(let s of t)this.runtime?.notifyFileChanged(s)}scheduleSnapshotIdentityAdvance(){if(this.stopping||!this.isStarted)return;let t=this.readCurrentSnapshotMarker();!t||t===this.options.snapshotId||this.mutationQueue.run(()=>this.advanceSnapshotId(t)).catch(s=>{console.warn(`[vcp-site-edit] snapshot identity advance failed: ${s instanceof Error?s.message:String(s)}`)})}readCurrentSnapshotMarker(){try{let t=E.readFileSync(m.join(this.options.projectRoot,K),"utf8"),s=JSON.parse(t),n=typeof s.snapshot_id=="string"?s.snapshot_id:"";return!n||H(n)!==n?(console.warn("[vcp-site-edit] current-snapshot marker has no valid snapshot_id"),null):n}catch(t){return console.warn(`[vcp-site-edit] current-snapshot marker unreadable: ${t instanceof Error?t.message:String(t)}`),null}}async advanceSnapshotId(t){if(this.stopping||!this.isStarted||t===this.options.snapshotId)return;let s=this.options.snapshotId;this.options.snapshotId=t;try{if(this.options.historyRoot){let o=await this.bindHistorySession(t);if(!o.ok)throw new Error(`${o.code}: ${o.message}`)}else this.createRuntime()}catch(o){this.options.snapshotId=s,console.warn(`[vcp-site-edit] snapshot identity advance rolled back to ${s}: ${o instanceof Error?o.message:String(o)}`);return}let n=new Date().toISOString();for(let o of this.compositions){let i={type:"site-edit.snapshot.changed",siteId:this.options.siteId,composition:o.context,snapshotId:t,previousSnapshotId:s,at:n};for(let r of this.eventListeners)try{r(i)}catch(a){console.warn(`[vcp-site-edit] snapshot.changed listener failed: ${a instanceof Error?a.message:String(a)}`)}}try{this.options.onSnapshotIdChanged?.(t,s)}catch(o){console.warn(`[vcp-site-edit] onSnapshotIdChanged callback failed: ${o instanceof Error?o.message:String(o)}`)}}refreshCompositions(t=!1){try{let s=Ft(this.options.projectRoot),n=vt(s),o=n.length>0?n:(this.options.runtime.routes??[]).map(u=>({...u})),i=Et({files:s});if(i.length===0)throw new Error("No canonical App Router composition is indexed by the preview bridge.");let r=JSON.stringify(i.filter(u=>u.context.effectiveTemplateId!==void 0).map(u=>u.entryFile).sort());if(t&&(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=>[w(u.context),u])),p=new Map(i.map(u=>[w(u.context),u]));if(this.runtime){for(let[u,g]of a){let c=p.get(u);(!c||c.entryFile!==g.entryFile)&&this.runtime.unregisterComposition(f(g.context))}for(let[u,g]of p){let c=a.get(u);(!c||c.entryFile!==g.entryFile)&&this.runtime.registerComposition(g.context,g.entryFile)}}return this.routes=o,this.compositions=i,this.compositionFiles=s,this.compositionResolutionError=null,!0}catch(s){let n=s instanceof Error?s:new Error(String(s));if(this.compositionResolutionError=n,t)throw n;return!1}}resolveComposition(t){if(this.compositionResolutionError)throw this.compositionResolutionError;let s=xt(t.routeId,this.routes,this.options.basePath);if(!s)throw new Error(`Route is not indexed by the preview bridge: ${t.routeId}`);let n=this.routes.find(r=>r.routeId===s);if(!n)throw new Error(`Route is not indexed by the preview bridge: ${t.routeId}`);let o=Rt({selector:{routeId:s,...t.templateId!==void 0?{templateId:t.templateId}:{}},files:this.compositionFiles,canonicalEntryFile:n.entryFile}),i=w(o.context);if(!this.compositions.some(r=>w(r.context)===i))throw new Error(`Composition is not registered by the preview bridge: ${i}`);return o}getInitialDocumentVersions(){return this.compositions.flatMap(t=>{let s=this.runtimeVersionFloors.get(w(t.context));return s===void 0?[]:[{composition:t.context,version:s}]})}getRuntime(){if(!this.runtime)throw new Error("VCP site edit preview bridge runtime is not ready");return this.runtime}};function et(e){return{tenantId:e.tenantId,siteId:e.siteId,snapshotId:e.snapshotId,projectRoot:m.resolve(e.projectRoot??process.cwd()),basePath:e.basePath??"",host:e.host??_t,port:e.port??Tt,path:e.path??Ot,sessionKey:e.sessionKey??$t(),historyRoot:e.historyRoot,allowedOrigins:e.allowedOrigins??[],runtime:e.runtime??{},onSnapshotIdChanged:e.onSnapshotIdChanged}}function Mt(e){let t=["app","src/app","components/site","src/components/site","lib/generated","src/lib/generated","public"];return[...new Set([...e?.length?e:t,".vcp"])]}function Ft(e){let t=[],s=["app","src/app",".vcp"],n=new Set([".git",".next",".turbo","coverage","dist","node_modules","output","snapshot-store"]),o=i=>{if(!E.existsSync(i))return;let r;try{r=E.readdirSync(i,{withFileTypes:!0})}catch{return}for(let a of r){if(a.name.startsWith(".")&&a.name!==".vcp")continue;if(a.isDirectory()){if(n.has(a.name))continue;o(m.join(i,a.name));continue}if(!a.isFile())continue;let p=m.join(i,a.name),u="";try{u=E.readFileSync(p,"utf8")}catch{}t.push({path:m.relative(e,p).replaceAll(m.sep,"/"),content:u})}};for(let i of s)o(m.join(e,i));return t.sort((i,r)=>i.path.localeCompare(r.path))}function Bt(e){if(!e||typeof e!="object"||typeof e.routeId!="string")throw new Error("Normalized composition context is required.");if("effectiveTemplateId"in e)return e;let t=e;return{routeId:t.routeId,...t.templateId?{effectiveTemplateId:t.templateId}:{}}}function f(e){return{routeId:e.routeId,...e.effectiveTemplateId?{templateId:e.effectiveTemplateId}:{}}}function Kt(e){return m.join(m.resolve(e.root),H(e.siteId),H(e.snapshotId),Z("sha256").update(e.editingSessionId).digest("hex").slice(0,32))}function H(e){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(e)&&e!=="."&&e!==".."?e:Z("sha256").update(e).digest("hex").slice(0,32)}function st(){let e=globalThis;return e[Q]??=new Map,e[Q]}function nt(e){return`${e.projectRoot}:${e.host}:${e.port}:${e.path}:${e.historyRoot??""}`}function F(e,t,s){let n=s??(t?.composition?{routeId:t.composition.routeId,...t.composition.templateId?{effectiveTemplateId:t.composition.templateId}:{}}:void 0);if(!n)throw new Error("A normalized composition is required to construct a mutation result.");return{ok:!1,composition:n,error:{code:e.code,message:e.message},data:{requestId:t?.id??"mutation-context",kind:t?.kind??"update-text",composition:n,target:t?.target??{kind:"node",key:""},resultVersion:t?.documentVersion??0}}}function Ht(e){let t=new St({maxPayload:tt,noServer:!0}),s=new WeakSet,n=i=>{new x({socket:new q(i),engine:e.engine,validateOperationRequest:r=>kt.safeParse(r).success})},o=(i,r,a)=>{if(!qt(i,e))return;let p=Nt(i,e);if(!p){r.write(`HTTP/1.1 401 Unauthorized\r
1
+ import ge from"node:http";import E from"node:fs";import{createHash as Z,randomBytes as ye,timingSafeEqual as Ie}from"node:crypto";import m from"node:path";import{WebSocket as X,WebSocketServer as Se}from"ws";import{createPreviewSiteEditEngineRuntime as we,openFileHistoryStore as Ce,resolveManagedPageTemplateComposition as Re,scanSiteEditCompositionsFromFiles as Ee}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as xe,scanNextAppRouterRoutesFromFiles as ve}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as be}from"@vcp-dev/site-edit-engine/source-watcher";function $(t){let e=new Map,n=[],s=new Map,o=[],i=t.pageComposition.status==="supported"?t.pageComposition.placementUnitKeys:[],r=new Map(i.map((a,h)=>[a,h])),c=t.pageComposition.status==="supported"?{status:"supported",containerKey:t.pageComposition.containerKey,hasBusinessMembers:t.pageComposition.hasBusinessMembers,placementUnitKeys:t.pageComposition.placementUnitKeys,placements:t.pageComposition.placements.map(a=>({placementKey:a.placementKey,placementUnitKey:a.placementUnitKey,relation:a.relation}))}:{status:"unsupported",reason:t.pageComposition.reason,diagnostics:t.pageComposition.diagnostics},p=t.entries.flatMap((a,h)=>a.dynamicDataSummary?[[h,a.dynamicDataSummary]]:[]),u=a=>{let h=e.get(a);if(typeof h=="number")return h;let C=n.length;return n.push(a),e.set(a,C),C},f=a=>{let h=null;for(let C of ae(a)){let q=`${h??"root"}\0${C}`,V=s.get(q);if(typeof V=="number"){h=V;continue}let U=o.length;o.push([h,u(C)]),s.set(q,U),h=U}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:c,tokens:n,nodes:o,entries:t.entries.map(a=>{let h=a.pageCompositionUnitKey?r.get(a.pageCompositionUnitKey)??-1:-1;if(a.pageCompositionUnitKey&&h<0)throw new Error(`Render entry ${a.key} references unknown page composition unit ${a.pageCompositionUnitKey}`);return[f(a.key),re(a.operationSummary),...h>=0?[h]:[]]}),...t.entries.every(a=>a.parentKey!==void 0)?{entryParentNodeIds:t.entries.map(a=>a.parentKey===null?null:f(a.parentKey))}:{},...p.length>0?{dynamicDataSummaries:p}:{}}}function re(t){return(t.canUpdateText?1:0)|(t.canInsertChild?2:0)|(t.canMove?4:0)|(t.canRemove?8:0)}function ae(t){if(!t)return[];let e=[],n=0,s=W(t,n);if(s<0)return e.push(t),e;for(e.push(t.slice(0,s)),n=s;n<t.length;){if(s=W(t,n+(t.startsWith("::",n)?2:1)),s<0){e.push(t.slice(n));break}e.push(t.slice(n,s)),n=s}return e}function W(t,e){let n=t.indexOf("/",e),s=t.indexOf("::",e);return n<0?s:s<0?n:Math.min(n,s)}var j="SITE_EDIT_COMPOSITION_SESSION_MISMATCH",M="SITE_EDIT_COMPOSITION_SESSION_REQUIRED";var l=class t{constructor(){}static parseMessage(e,n={}){let s=JSON.parse(e);if(!t.isMessage(s,n))throw new Error("Invalid site edit bridge message");return s}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",s=this.isRequestKind(e.payload.kind)?e.payload.kind:"get-document";return{id:n,kind:s}}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==="undo"||e==="redo"}static isMessage(e,n={}){return!d(e)||typeof e.type!="string"?!1:e.type==="request"?ce(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==="auth-ok"&&d(e.payload)&&e.payload.ok===!0}},x=class{constructor(e){this.options=e,this.session={subscribedCompositionKeys:new Set},this.messageQueue=new D,this.eventDispatcher=new A(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(s){this.sendJson({type:"error",payload:{id:"unknown",kind:"get-document",message:s instanceof Error?s.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 s=n.payload,{id:o}=s;switch(s.kind){case"initialize-session":{let i=k(s),r=await this.resolve(i),c=await this.options.engine.initializeSession({composition:r.context,...s.mutationContext?{mutationContext:s.mutationContext}:{}});this.activateSession(r.context),this.sendJson({type:"response",payload:{id:o,kind:s.kind,result:y({...c,data:{...c.data,document:z(c.data.document)}},r.context)}});return}case"get-document":{let i=await this.resolveWireContext(s),r=await this.options.engine.getDocument(i);this.sendResult(o,s.kind,me(r,i));return}case"get-history-status":{let i=await this.resolveWireContext(s),r=await this.options.engine.getHistoryStatus(i);this.sendResult(o,s.kind,y(r,i));return}case"get-style-context":{let i=await this.resolveWireContext(s);this.sendResult(o,s.kind,y(await this.options.engine.getStyleContext(i),i));return}case"get-object-edit-context":{let i=await this.resolveWireContext(s);this.sendResult(o,s.kind,y(await this.options.engine.getObjectEditContext(s.key,i,s.selectionRef),i));return}case"get-class-name-source":{let i=await this.resolveWireContext(s);this.sendResult(o,s.kind,y(await this.options.engine.getClassNameSource(s.key,i),i));return}case"get-source-tree":{let i=await this.resolveWireContext(s);this.sendResult(o,s.kind,y(await this.options.engine.getSourceTree(i),i));return}case"get-source-file":{let i=await this.resolveWireContext(s);this.sendResult(o,s.kind,y(await this.options.engine.getSourceFile({path:s.path,composition:i}),i));return}case"resolve-source-location":{let i=await this.resolveWireContext(s),r=y(await this.options.engine.resolveSourceLocation({key:s.key,composition:i}),i);this.sendResult(o,s.kind,r);return}case"execute":{let i=await this.resolveOperationWireContext(s.request),r=await this.resolveWireContext(s);if(I(i)!==I(r))throw new S(j,"The execute request composition does not match its operation composition.");let c=await this.normalizeOperationRequest(s.request,i);await this.handleBufferedOperation(o,s.kind,()=>this.options.engine.execute(c));return}case"undo":case"redo":{let i=await this.resolveWireContext(s);await this.handleBufferedOperation(o,s.kind,()=>s.kind==="undo"?this.options.engine.undo(i):this.options.engine.redo(i));return}}}catch(n){let s=l.extractRequestInfo(e);this.sendJson({type:"error",payload:{...s,message:O(n),code:T(n)}})}}async resolve(e){return this.options.engine.resolveComposition(e)}async resolveAndAssert(e){let n=await this.resolve(e),s=this.session.activeComposition;if(!s)throw new S(M,"Initialize a composition session before issuing this request.");if(I(s)!==I(n.context))throw new S(j,"The request composition does not match the active socket session.");return n.context}async resolveWireContext(e){let n=k(e);return this.resolveAndAssert(n)}async resolveOperationWireContext(e){let n=pe(e);return this.resolveAndAssert(n)}activateSession(e){this.session.activeComposition=e,this.session.subscribedCompositionKeys.clear(),this.session.subscribedCompositionKeys.add(I(e))}async normalizeOperationRequest(e,n){let s={...e,composition:L(n)};if(s.target.kind==="route"){let o=s.target,i=k(o),r=await this.resolveAndAssert(i);s.target={...o,composition:L(r)}}return s}handleEngineEvent(e){let n=e?.composition;if(!he(n))return;let s=I(n);this.session.subscribedCompositionKeys.has(s)&&(this.eventDispatcher.push(e),e.type==="site-edit.graph.invalidated"&&s===le(this.session)&&(this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.delete(s)))}sendResult(e,n,s){this.sendJson({type:"response",payload:{id:e,kind:n,result:s}})}async handleRawMessage(e){try{await this.handleMessage(l.parseMessage(e,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let s=fe(e);this.sendJson({type:"error",payload:{...s,message:O(n),code:T(n)}})}}sendJson(e){this.options.socket.send(l.stringifyMessage(e))}async handleBufferedOperation(e,n,s){this.eventDispatcher.beginBuffering();try{let o=await s();this.sendResult(e,n,o)}catch(o){this.sendJson({type:"error",payload:{id:e,kind:n,message:O(o),code:T(o)}})}finally{this.eventDispatcher.flush()}}},S=class extends Error{constructor(e,n){super(n),this.code=e,this.name="BridgeProtocolError"}},A=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))}},D=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()},s=this.queue.then(n,n);return this.queue=s.then(()=>{},()=>{}),s}};function ce(t,e){if(!d(t)||typeof t.id!="string"||!l.isRequestKind(t.kind))return!1;let n=R(t.composition);switch(t.kind){case"initialize-session":return n&&(t.mutationContext===void 0||_(t.mutationContext));case"get-document":case"get-style-context":return n;case"get-object-edit-context":return typeof t.key=="string"&&n&&ue(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":return n&&de(t.request)&&(t.request.mutationContext===void 0||_(t.request.mutationContext))&&(e.validateOperationRequest?.(t.request)??!0);case"undo":case"redo":return n&&t.mutationContext===void 0||n&&_(t.mutationContext)}}function ue(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(s=>s==="key"||s==="instanceIndex"||s==="instanceCount")}function de(t){return d(t)&&typeof t.id=="string"&&typeof t.siteId=="string"&&typeof t.baseSnapshotId=="string"&&R(t.composition)&&typeof t.kind=="string"&&d(t.target)&&(t.target.kind!=="route"||R(t.target.composition))&&typeof t.documentVersion=="number"&&Number.isInteger(t.documentVersion)&&t.documentVersion>=0&&d(t.params)}function R(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 he(t){return d(t)&&typeof t.routeId=="string"&&(t.effectiveTemplateId===void 0||typeof t.effectiveTemplateId=="string")}function k(t){if(R(t.composition))return t.composition;throw new S(M,"A composition selector is required for this request.")}function pe(t){if(d(t)&&R(t.composition))return t.composition;throw new S(M,"A composition selector is required for execute.")}function L(t){return{routeId:t.routeId,...t.effectiveTemplateId?{templateId:t.effectiveTemplateId}:{}}}function le(t){return t.activeComposition?I(t.activeComposition):null}function I(t){return JSON.stringify([t.routeId,t.effectiveTemplateId??null])}function y(t,e){return d(t)&&t.ok===!0?{...t,composition:e}:t}function me(t,e){return!d(t)||t.ok!==!0?t:{...t,...e?{composition:e}:{},data:z(t.data)}}function z(t){let e=t;return $(e.routeId?e:{...e,routeId:e.composition.routeId})}function d(t){return typeof t=="object"&&t!==null}function _(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 T(t){if(!(!d(t)||typeof t.code!="string"||t.code.length===0))return t.code}function O(t){return t instanceof Error||d(t)&&typeof t.message=="string"&&t.message.length>0?t.message:"Unknown websocket operation failure"}function fe(t){try{return l.extractRequestInfo(JSON.parse(t))}catch{return{id:"unknown",kind:"get-document"}}}var v=class{constructor(){this.tail=Promise.resolve()}run(e){let n=this.tail.then(e,e);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as ke,computeSiteEditCompositionKey as w}from"@vcp-dev/contracts";var _e="127.0.0.1",Te=30218,Oe="/__vcp/site-edit/bridge",ee=6*1024*1024,Ae=ee*2,De=3e3,J=50,B=".vcp/current-snapshot.json",Q="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function at(t){let e=te(t),n=se(e),s=ne(),o=s.get(n);if(o)return o;let i=new b(e);s.set(n,i);try{i.start()}catch(r){throw s.delete(n),i.stop(),r}return i}function ct(t,e,n={}){let s=new b(te(e),t,!1,n);return s.start({listen:!1}),s}var b=class{constructor(e,n=ge.createServer((i,r)=>{r.writeHead(404),r.end("VCP site edit bridge server")}),s=!0,o={}){this.options=e,this.server=n,this.closeHttpServerOnStop=s,this.dependencies=o,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new v,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()}start(e={}){if(this.isStarted)return;this.stopping=!1;try{E.mkdirSync(m.join(this.options.projectRoot,".vcp"),{recursive:!0})}catch(s){console.warn(`[vcp-site-edit] failed to ensure .vcp directory: ${s instanceof Error?s.message:String(s)}`)}this.refreshCompositions(!0),this.isStarted=!0,this.createRuntime();let n=Ne({server:this.server,path:this.options.path,sessionKey:this.options.sessionKey,host:this.options.host,allowedOrigins:this.options.allowedOrigins,engine:this.createBridgeEngine()});this.stopWebSocketServer=n.stop,this.stopWatching=new be({projectRoot:this.options.projectRoot,watch_dirs:Me(this.options.runtime.watch_dirs),interval_ms:this.options.runtime.watch_interval_ms,debounce_ms:this.options.runtime.watch_debounce_ms??J,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:s=>this.handleFileChanged(s),on_batch_change:s=>this.handleFilesChanged(s)}).start(),e.listen!==!1&&this.server.listen(this.options.port,this.options.host)}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=ne(),n=se(this.options);e.get(n)===this&&e.delete(n),this.isStarted=!1}createRuntime(e,n=null){let s=this.runtime;this.activeHistoryStore=e??null;let o=(this.dependencies.createRuntime??we)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,compositions:this.compositions,onEvent:i=>{"version"in i&&typeof i.version=="number"&&this.recordRuntimeVersion(i.version,"composition"in i?i.composition:void 0);for(let r of this.eventListeners)r(i)},...e?{historyStore:e}:{},...this.getInitialDocumentVersions().length>0?{initialDocumentVersions:this.getInitialDocumentVersions()}:{}});this.runtime=o,this.activeHistorySessionId=n,s?.dispose()}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 s=Ke(e.composition),o=this.getRuntime(),i=g(s);await o.initializeComposition(i);let c=o.getDocument(i).data;await this.recordRuntimeVersion(c.version,s);let p=(await o.getHistoryStatus()).data;return{ok:!0,composition:s,data:{document:c,historyStatus:p}}}),getDocument:async e=>{let n=this.getRuntime(),s=g(e);await n.initializeComposition(s);let o=n.getDocument(s);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(g(e)),getObjectEditContext:async(e,n,s)=>this.getRuntime().getObjectEditContext(e,g(n),s),getClassNameSource:async(e,n)=>this.getRuntime().getClassNameSource(e,g(n)),getSourceTree:async e=>this.getRuntime().getSourceTree(g(e)),getSourceFile:async e=>this.getRuntime().getSourceFile({path:e.path,composition:g(e.composition)}),resolveSourceLocation:async e=>this.getRuntime().resolveSourceLocation({key:e.key,composition:g(e.composition)}),execute:(e,n)=>this.mutationQueue.run(async()=>{let s=await this.bindHistorySession(this.options.snapshotId);if(!s.ok)return F(s,e);let o=await this.getRuntime().executeOperation(e);return o.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(o.data.resultVersion,o.composition),o}),undo:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return F(n,void 0,e);let s=await this.getRuntime().undo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion,s.composition??e),s.composition?s:{...s,composition:e}}),redo:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return F(n,void 0,e);let s=await this.getRuntime().redo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion,s.composition??e),s.composition?s:{...s,composition:e}}),subscribe:e=>(this.eventListeners.add(e),()=>this.eventListeners.delete(e))}}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 s=await(this.dependencies.openHistoryStore??Ce)(Be({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e}));if(!this.isStarted)return;let o=s.readDocumentVersion?await s.readDocumentVersion():0,i=this.compositions[0]?.context;if(i){let r=w(i);this.runtimeVersionFloors.set(r,Math.max(this.runtimeVersionFloors.get(r)??0,o))}this.createRuntime(s,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 s=n?w(n):null;s&&this.runtimeVersionFloors.set(s,Math.max(this.runtimeVersionFloors.get(s)??0,e));let o=this.activeHistoryStore;if(!o?.writeDocumentVersion)return Promise.resolve();let i=s?this.runtimeVersionFloors.get(s)??e:e;return this.versionPersistence=this.versionPersistence.then(()=>o.writeDocumentVersion(i)).catch(()=>{}),this.versionPersistence}handleFileChanged(e){this.handleFilesChanged([e])}handleFilesChanged(e){for(let s of e)this.pendingChangedFiles.add(s);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let n=this.options.runtime.watch_debounce_ms??J;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,s)=>n.localeCompare(s));if(this.pendingChangedFiles.clear(),e.some(je)&&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=E.readFileSync(m.join(this.options.projectRoot,B),"utf8"),n=JSON.parse(e),s=typeof n.snapshot_id=="string"?n.snapshot_id:"";return!s||N(s)!==s?(console.warn("[vcp-site-edit] current-snapshot marker has no valid snapshot_id"),null):s}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 s=new Date().toISOString();for(let o of this.compositions){let i={type:"site-edit.snapshot.changed",siteId:this.options.siteId,composition:o.context,snapshotId:e,previousSnapshotId:n,at:s};for(let r of this.eventListeners)try{r(i)}catch(c){console.warn(`[vcp-site-edit] snapshot.changed listener failed: ${c instanceof Error?c.message:String(c)}`)}}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=Fe(this.options.projectRoot),s=ve(n),o=s.length>0?s:(this.options.runtime.routes??[]).map(u=>({...u})),i=Ee({files:n});if(i.length===0)throw new Error("No canonical App Router composition is indexed by the preview bridge.");let r=JSON.stringify(i.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 c=new Map(this.compositions.map(u=>[w(u.context),u])),p=new Map(i.map(u=>[w(u.context),u]));if(this.runtime){for(let[u,f]of c){let a=p.get(u);(!a||a.entryFile!==f.entryFile)&&this.runtime.unregisterComposition(g(f.context))}for(let[u,f]of p){let a=c.get(u);(!a||a.entryFile!==f.entryFile)&&this.runtime.registerComposition(f.context,f.entryFile)}}return this.routes=o,this.compositions=i,this.compositionFiles=n,this.compositionResolutionError=null,!0}catch(n){let s=n instanceof Error?n:new Error(String(n));if(this.compositionResolutionError=s,e)throw s;return!1}}resolveComposition(e){if(this.compositionResolutionError)throw this.compositionResolutionError;let n=xe(e.routeId,this.routes,this.options.basePath);if(!n)throw new Error(`Route is not indexed by the preview bridge: ${e.routeId}`);let s=this.routes.find(r=>r.routeId===n);if(!s)throw new Error(`Route is not indexed by the preview bridge: ${e.routeId}`);let o=Re({selector:{routeId:n,...e.templateId!==void 0?{templateId:e.templateId}:{}},files:this.compositionFiles,canonicalEntryFile:s.entryFile}),i=w(o.context);if(!this.compositions.some(r=>w(r.context)===i))throw new Error(`Composition is not registered by the preview bridge: ${i}`);return o}getInitialDocumentVersions(){return this.compositions.flatMap(e=>{let n=this.runtimeVersionFloors.get(w(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 te(t){return{tenantId:t.tenantId,siteId:t.siteId,snapshotId:t.snapshotId,projectRoot:m.resolve(t.projectRoot??process.cwd()),basePath:t.basePath??"",host:t.host??_e,port:t.port??Te,path:t.path??Oe,sessionKey:t.sessionKey??$e(),historyRoot:t.historyRoot,allowedOrigins:t.allowedOrigins??[],runtime:t.runtime??{},onSnapshotIdChanged:t.onSnapshotIdChanged}}function Me(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 Fe(t){let e=[],n=["app","src/app",".vcp"],s=new Set([".git",".next",".turbo","coverage","dist","node_modules","output","snapshot-store"]),o=i=>{if(!E.existsSync(i))return;let r;try{r=E.readdirSync(i,{withFileTypes:!0})}catch{return}for(let c of r){if(c.name.startsWith(".")&&c.name!==".vcp")continue;if(c.isDirectory()){if(s.has(c.name))continue;o(m.join(i,c.name));continue}if(!c.isFile())continue;let p=m.join(i,c.name),u="";try{u=E.readFileSync(p,"utf8")}catch{}e.push({path:m.relative(t,p).replaceAll(m.sep,"/"),content:u})}};for(let i of n)o(m.join(t,i));return e.sort((i,r)=>i.path.localeCompare(r.path))}function Ke(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 g(t){return{routeId:t.routeId,...t.effectiveTemplateId?{templateId:t.effectiveTemplateId}:{}}}function Be(t){return m.join(m.resolve(t.root),N(t.siteId),N(t.snapshotId),Z("sha256").update(t.editingSessionId).digest("hex").slice(0,32))}function N(t){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(t)&&t!=="."&&t!==".."?t:Z("sha256").update(t).digest("hex").slice(0,32)}function ne(){let t=globalThis;return t[Q]??=new Map,t[Q]}function se(t){return`${t.projectRoot}:${t.host}:${t.port}:${t.path}:${t.historyRoot??""}`}function F(t,e,n){let s=n??(e?.composition?{routeId:e.composition.routeId,...e.composition.templateId?{effectiveTemplateId:e.composition.templateId}:{}}:void 0);if(!s)throw new Error("A normalized composition is required to construct a mutation result.");return{ok:!1,composition:s,error:{code:t.code,message:t.message},data:{requestId:e?.id??"mutation-context",kind:e?.kind??"update-text",composition:s,target:e?.target??{kind:"node",key:""},resultVersion:e?.documentVersion??0}}}function Ne(t){let e=new Se({maxPayload:ee,noServer:!0}),n=new WeakSet,s=i=>{new x({socket:new H(i),engine:t.engine,validateOperationRequest:r=>ke.safeParse(r).success})},o=(i,r,c)=>{if(!He(i,t))return;let p=Pe(i,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&&s.add(i),t.handleUpgrade(i,r,a,u=>{t.emit("connection",u,i)})};return e.server.on("upgrade",o),t.on("connection",(i,r)=>{if(s.has(r)){n(i);return}Vt(i,e.sessionKey,()=>{n(i)})}),t.stop=()=>{e.server.off("upgrade",o);for(let i of t.clients)i.close(1001,"bridge stopped");t.close()},t}var q=class{constructor(t){this.socket=t,this.onmessage=null,this.onclose=null,t.on("error",()=>{}),t.on("message",s=>{this.onmessage?.({data:s.toString()})}),t.on("close",()=>{this.onclose?.()})}send(t){if(this.socket.readyState===X.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(t,"utf8")>Dt){this.socket.close(1013,"bridge backpressure");return}this.socket.send(t)}}close(t,s){this.socket.close(t,s)}};function qt(e,t){return new URL(e.url??"/","http://localhost").pathname===t.path}function Nt(e,t){if(new URL(e.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let n=B(e.headers["x-vcp-site-edit-key"]),o=it(n,t.sessionKey),i=B(e.headers.host),r=B(e.headers.origin);return i&&!Pt(i,t.host,r)?null:r?Wt(r,i,t.allowedOrigins)?{authenticatedByHeader:o}:null:o?{authenticatedByHeader:!0}:null}function Vt(e,t,s){let n=setTimeout(()=>{e.close(1008,"bridge authentication timeout")},At),o,i=()=>{clearTimeout(n),e.off("message",o),e.off("close",i)},r=()=>{i(),e.close(1008,"bridge authentication failed")};o=a=>{try{let p=l.parseMessage(a.toString());if(p.type!=="auth"||!it(p.payload.key,t)){r();return}i(),e.readyState===X.OPEN&&e.send(l.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),s()}catch{r()}},e.on("message",o),e.on("close",i)}function it(e,t){if(!e)return!1;let s=Buffer.from(e),n=Buffer.from(t);return s.length===n.length&&It(s,n)}function Pt(e,t,s){let n=Y(e),o=Y(t);return n===o||G(o)&&G(n)&&(!s||N(s,e))||Ut(o)&&(!s||N(s,e))}function Ut(e){return e==="0.0.0.0"||e==="::"}function G(e){return e==="localhost"||e==="127.0.0.1"||e==="::1"}function Wt(e,t,s){return s.includes(e)||(t?N(e,t):!1)}function N(e,t){try{return new URL(e).host===ot(t)}catch{return!1}}function Y(e){let t=ot(e);return t.startsWith("[")?t.slice(1,t.indexOf("]")):t.split(":").length-1===1?t.split(":",1)[0]??"":t}function ot(e){return e.trim().toLowerCase()}function B(e){return Array.isArray(e)?e[0]:e}function $t(){return yt(32).toString("base64url")}function jt(e){let t=e.replaceAll("\\","/");return t===K||t.endsWith(`/${K}`)}export{Dt as SITE_EDIT_BRIDGE_MAX_BUFFERED_BYTES,tt as SITE_EDIT_BRIDGE_MAX_PAYLOAD_BYTES,b as SiteEditBridgeServer,ce as attachSiteEditBridgeServer,Ht as attachSiteEditWebSocketServer,ae as ensureSiteEditBridgeServerStarted};
4
+ `),r.destroy();return}p.authenticatedByHeader&&n.add(i),e.handleUpgrade(i,r,c,u=>{e.emit("connection",u,i)})};return t.server.on("upgrade",o),e.on("connection",(i,r)=>{if(n.has(r)){s(i);return}qe(i,t.sessionKey,()=>{s(i)})}),e.stop=()=>{t.server.off("upgrade",o);for(let i of e.clients)i.close(1001,"bridge stopped");e.close()},e}var H=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===X.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(e,"utf8")>Ae){this.socket.close(1013,"bridge backpressure");return}this.socket.send(e)}}close(e,n){this.socket.close(e,n)}};function He(t,e){return new URL(t.url??"/","http://localhost").pathname===e.path}function Pe(t,e){if(new URL(t.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let s=K(t.headers["x-vcp-site-edit-key"]),o=ie(s,e.sessionKey),i=K(t.headers.host),r=K(t.headers.origin);return i&&!Ve(i,e.host,r)?null:r?We(r,i,e.allowedOrigins)?{authenticatedByHeader:o}:null:o?{authenticatedByHeader:!0}:null}function qe(t,e,n){let s=setTimeout(()=>{t.close(1008,"bridge authentication timeout")},De),o,i=()=>{clearTimeout(s),t.off("message",o),t.off("close",i)},r=()=>{i(),t.close(1008,"bridge authentication failed")};o=c=>{try{let p=l.parseMessage(c.toString());if(p.type!=="auth"||!ie(p.payload.key,e)){r();return}i(),t.readyState===X.OPEN&&t.send(l.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),n()}catch{r()}},t.on("message",o),t.on("close",i)}function ie(t,e){if(!t)return!1;let n=Buffer.from(t),s=Buffer.from(e);return n.length===s.length&&Ie(n,s)}function Ve(t,e,n){let s=Y(t),o=Y(e);return s===o||G(o)&&G(s)&&(!n||P(n,t))||Ue(o)&&(!n||P(n,t))}function Ue(t){return t==="0.0.0.0"||t==="::"}function G(t){return t==="localhost"||t==="127.0.0.1"||t==="::1"}function We(t,e,n){return n.includes(t)||(e?P(t,e):!1)}function P(t,e){try{return new URL(t).host===oe(e)}catch{return!1}}function Y(t){let e=oe(t);return e.startsWith("[")?e.slice(1,e.indexOf("]")):e.split(":").length-1===1?e.split(":",1)[0]??"":e}function oe(t){return t.trim().toLowerCase()}function K(t){return Array.isArray(t)?t[0]:t}function $e(){return ye(32).toString("base64url")}function je(t){let e=t.replaceAll("\\","/");return e===B||e.endsWith(`/${B}`)}export{Ae as SITE_EDIT_BRIDGE_MAX_BUFFERED_BYTES,ee as SITE_EDIT_BRIDGE_MAX_PAYLOAD_BYTES,b as SiteEditBridgeServer,ct as attachSiteEditBridgeServer,Ne as attachSiteEditWebSocketServer,at as ensureSiteEditBridgeServerStarted};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vcp-dev/site-edit-bridge",
3
- "version": "0.10.14",
3
+ "version": "0.10.16",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -74,8 +74,8 @@
74
74
  "typecheck": "tsc -p tsconfig.json --noEmit"
75
75
  },
76
76
  "dependencies": {
77
- "@vcp-dev/contracts": "0.10.14",
78
- "@vcp-dev/site-edit-engine": "0.10.14",
77
+ "@vcp-dev/contracts": "0.10.16",
78
+ "@vcp-dev/site-edit-engine": "0.10.16",
79
79
  "@types/ws": "^8.18.1",
80
80
  "ws": "^8.20.1"
81
81
  },