@vcp-dev/site-edit-bridge 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,6 @@
1
- import Ge from"node:http";import{existsSync as I,readFileSync as S}from"node:fs";import{createRequire as ue}from"node:module";import x from"node:path";import{basename as ce,dirname as R,join as E}from"node:path";import{fileURLToPath as de}from"node:url";import{createEd25519MutationContextVerifier as Qe}from"@vcp-dev/site-edit-engine";import Re from"node:http";import{createHash as X,randomBytes as Ee,timingSafeEqual as Ce}from"node:crypto";import O from"node:path";import{WebSocket as ee,WebSocketServer as be}from"ws";import{createPreviewSiteEditEngineRuntime as ve,openFileHistoryStore as ke}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as _e,scanNextAppRouterRoutes as Pe}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as Te}from"@vcp-dev/site-edit-engine/source-watcher";function V(t){let e=new Map,n=[],i=new Map,s=[],r=t.pageComposition.status==="supported"?t.pageComposition.placementUnitKeys:[],o=new Map(r.map((a,c)=>[a,c])),u=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},d=a=>{let c=e.get(a);if(typeof c=="number")return c;let f=n.length;return n.push(a),e.set(a,f),f},h=a=>{let c=null;for(let f of Se(a)){let g=`${c??"root"}\0${f}`,C=i.get(g);if(typeof C=="number"){c=C;continue}let b=s.length;s.push([c,d(f)]),i.set(g,b),c=b}if(c===null)throw new Error("Document key must not be empty");return c};return{encoding:"segment-trie-v1",siteId:t.siteId,snapshotId:t.snapshotId,routeId:t.routeId,version:t.version,pageComposition:u,tokens:n,nodes:s,entries:t.entries.map(a=>{let c=a.pageCompositionUnitKey?o.get(a.pageCompositionUnitKey)??-1:-1;if(a.pageCompositionUnitKey&&c<0)throw new Error(`Render entry ${a.key} references unknown page composition unit ${a.pageCompositionUnitKey}`);return[h(a.key),ye(a.operationSummary),...c>=0?[c]:[]]})}}function ye(t){return(t.canUpdateText?1:0)|(t.canInsertChild?2:0)|(t.canMove?4:0)|(t.canRemove?8:0)}function Se(t){if(!t)return[];let e=[],n=0,i=$(t,n);if(i<0)return e.push(t),e;for(e.push(t.slice(0,i)),n=i;n<t.length;){if(i=$(t,n+(t.startsWith("::",n)?2:1)),i<0){e.push(t.slice(n));break}e.push(t.slice(n,i)),n=i}return e}function $(t,e){let n=t.indexOf("/",e),i=t.indexOf("::",e);return n<0?i:i<0?n:Math.min(n,i)}var p=class t{constructor(){}static parseMessage(e,n={}){let i=JSON.parse(e);if(!t.isMessage(i,n))throw new Error("Invalid site edit bridge message");return i}static stringifyMessage(e){return JSON.stringify(e)}static extractRequestInfo(e){if(typeof e=="object"&&e!==null&&"payload"in e&&typeof e.payload=="object"&&e.payload!==null){let n=e.payload,i=typeof n.id=="string"?n.id:"unknown",s=t.isRequestKind(n.kind)?n.kind:"get-document";return{id:i,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!l(e)||typeof e.type!="string"?!1:e.type==="request"?t.isRequestPayload(e.payload,n):e.type==="response"||e.type==="error"?l(e.payload)&&typeof e.payload.id=="string":e.type==="event"?l(e.payload)&&l(e.payload.event):e.type==="auth"?l(e.payload)&&typeof e.payload.key=="string":e.type==="auth-ok"?l(e.payload)&&e.payload.ok===!0:!1}static isRequestPayload(e,n={}){if(!l(e)||typeof e.id!="string"||!t.isRequestKind(e.kind))return!1;switch(e.kind){case"initialize-session":return typeof e.routeId=="string"&&(e.mutationContext===void 0||m(e.mutationContext));case"get-document":case"get-style-context":return typeof e.routeId=="string";case"get-object-edit-context":case"get-class-name-source":case"resolve-source-location":return typeof e.key=="string"&&(e.routeId===void 0||typeof e.routeId=="string");case"get-history-status":return e.mutationContext===void 0;case"get-source-tree":return!0;case"get-source-file":return typeof e.path=="string";case"execute":return Ie(e.request)&&m(e.request.mutationContext)&&(n.validateOperationRequest?.(e.request)??!0);case"undo":case"redo":return m(e.mutationContext)}}},k=class{constructor(e){this.options=e,this.messageQueue=new M,this.eventDispatcher=new D(n=>{this.sendJson(n)}),this.unsubscribe=e.engine.subscribe(n=>{this.eventDispatcher.push(n)}),e.socket.onmessage=n=>{try{this.messageQueue.enqueue(()=>this.handleRawMessage(n.data))}catch(i){this.sendJson({type:"error",payload:{id:"unknown",kind:"get-document",message:i instanceof Error?i.message:"Bridge queue overflow"}}),e.socket.close(1013,"bridge busy")}},e.socket.onclose=()=>{this.unsubscribe()}}async handleMessage(e){try{let n=e;if(n.type!=="request")return;let i=n.payload,{id:s}=i;switch(i.kind){case"initialize-session":{let r=await this.options.engine.initializeSession({routeId:i.routeId,...i.mutationContext?{mutationContext:i.mutationContext}:{}});this.sendJson({type:"response",payload:{id:s,kind:i.kind,result:{ok:!0,data:{document:V(r.data.document),historyStatus:r.data.historyStatus}}}});return}case"get-document":this.sendJson({type:"response",payload:{id:s,kind:i.kind,result:{ok:!0,data:V((await this.options.engine.getDocument(i.routeId)).data)}}});return;case"get-history-status":this.sendJson({type:"response",payload:{id:s,kind:i.kind,result:await this.options.engine.getHistoryStatus()}});return;case"get-style-context":this.sendJson({type:"response",payload:{id:s,kind:i.kind,result:await this.options.engine.getStyleContext(i.routeId)}});return;case"get-object-edit-context":this.sendJson({type:"response",payload:{id:s,kind:i.kind,result:await this.options.engine.getObjectEditContext(i.key,i.routeId)}});return;case"get-class-name-source":this.sendJson({type:"response",payload:{id:s,kind:i.kind,result:await this.options.engine.getClassNameSource(i.key,i.routeId)}});return;case"get-source-tree":this.sendJson({type:"response",payload:{id:s,kind:i.kind,result:await this.options.engine.getSourceTree()}});return;case"get-source-file":this.sendJson({type:"response",payload:{id:s,kind:i.kind,result:await this.options.engine.getSourceFile({path:i.path})}});return;case"resolve-source-location":this.sendJson({type:"response",payload:{id:s,kind:i.kind,result:await this.options.engine.resolveSourceLocation({key:i.key,routeId:i.routeId})}});return;case"execute":{if(!m(i.request.mutationContext))throw new Error("Invalid site edit bridge message");let r=i.request.mutationContext;await this.handleBufferedOperation(s,i.kind,()=>this.options.engine.execute(i.request,r));return}case"undo":if(!m(i.mutationContext))throw new Error("Invalid site edit bridge message");await this.handleBufferedOperation(s,i.kind,()=>this.options.engine.undo(i.mutationContext));return;case"redo":if(!m(i.mutationContext))throw new Error("Invalid site edit bridge message");await this.handleBufferedOperation(s,i.kind,()=>this.options.engine.redo(i.mutationContext));return}}catch(n){let i=p.extractRequestInfo(e);this.sendJson({type:"error",payload:{...i,message:W(n),code:J(n)}})}}async handleRawMessage(e){try{await this.handleMessage(p.parseMessage(e,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let i=n instanceof Error?n.message:"Invalid websocket message",s=xe(e);this.sendJson({type:"error",payload:{...s,message:i}})}}sendJson(e){this.options.socket.send(p.stringifyMessage(e))}async handleBufferedOperation(e,n,i){this.eventDispatcher.beginBuffering();try{let s=await i();this.sendJson({type:"response",payload:{id:e,kind:n,result:s}}),this.eventDispatcher.flush()}catch(s){this.sendJson({type:"error",payload:{id:e,kind:n,message:W(s),code:J(s)}}),this.eventDispatcher.flush()}}},D=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)})}},M=class{constructor(e=64){this.maxQueued=e,this.queue=Promise.resolve(),this.queued=0}enqueue(e){if(this.queued>=this.maxQueued)throw new Error("Bridge request queue is full");this.queued+=1;let n=async()=>{this.queued-=1,await e()},i=this.queue.then(n,n);return this.queue=i.then(()=>{},()=>{}),i}};function l(t){return typeof t=="object"&&t!==null}function Ie(t){return l(t)&&typeof t.id=="string"&&typeof t.siteId=="string"&&typeof t.baseSnapshotId=="string"&&(t.routeId===void 0||typeof t.routeId=="string")&&typeof t.kind=="string"&&l(t.target)&&typeof t.documentVersion=="number"&&Number.isInteger(t.documentVersion)&&t.documentVersion>=0&&l(t.params)}function m(t){return we(t)}function J(t){if(!t||typeof t!="object")return;let e=t.code;return typeof e=="string"&&e.length>0?e:void 0}function W(t){if(t instanceof Error)return t.message;if(!t||typeof t!="object")return"Unknown websocket operation failure";let e=t.message;return typeof e=="string"&&e.length>0?e:"Unknown websocket operation failure"}function we(t){return l(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:!1}function xe(t){try{return p.extractRequestInfo(JSON.parse(t))}catch{return{id:"unknown",kind:"get-document"}}}var _=class{constructor(){this.tail=Promise.resolve()}run(e){let n=this.tail.then(e,e);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as He}from"@vcp-dev/contracts";var Be="127.0.0.1",Ve=30218,De="/__vcp/site-edit/bridge",te=6*1024*1024,Me=te*2,Ne=3e3,z=50,G="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function ne(t,e,n={}){let i=new j(Oe(e),t,!1,n);return i.start({listen:!1}),i}var j=class{constructor(e,n=Re.createServer((r,o)=>{o.writeHead(404),o.end("VCP site edit bridge server")}),i=!0,s={}){this.options=e,this.server=n,this.closeHttpServerOnStop=i,this.dependencies=s,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new _,this.eventListeners=new Set,this.routes=[],this.stopWatching=null,this.stopWebSocketServer=null,this.pendingChangedFiles=new Set,this.fileChangeTimer=null,this.isStarted=!1,this.runtimeVersionFloor=0,this.activeHistoryStore=null,this.versionPersistence=Promise.resolve()}start(e={}){if(this.isStarted)return;this.refreshRoutes(),this.isStarted=!0,this.createRuntime();let n=Ke({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 Te({projectRoot:this.options.projectRoot,watch_dirs:this.options.runtime.watch_dirs,debounce_ms:this.options.runtime.watch_debounce_ms??z,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:i=>this.handleFileChanged(i),on_batch_change:i=>this.handleFilesChanged(i)}).start(),e.listen!==!1&&this.server.listen(this.options.port,this.options.host)}getHealth(){let e=!!this.runtime;return{ok:this.isStarted&&e,bridgePath:this.options.path,runtimeReady:e,routesIndexed:this.routes.length,sessionRequired:!!this.options.sessionKey}}stop(){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.closeHttpServerOnStop&&this.server.close();let e=Ue(),n=Ae(this.options);e.get(n)===this&&e.delete(n),this.isStarted=!1}createRuntime(e,n=null){let i=this.runtime;this.activeHistoryStore=e??null;let s=(this.dependencies.createRuntime??ve)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,routes:this.routes,onEvent:r=>{"version"in r&&typeof r.version=="number"&&this.recordRuntimeVersion(r.version);for(let o of this.eventListeners)o(r)},...e?{historyStore:e}:{},initialDocumentVersion:this.runtimeVersionFloor,...this.options.mutationContextVerifier?{mutationContextVerifier:this.options.mutationContextVerifier}:{}});this.runtime=s,this.activeHistorySessionId=n,i?.dispose()}createBridgeEngine(){return{initializeSession:e=>this.mutationQueue.run(async()=>{if(e.mutationContext){let o=await this.verifyMutationContext(e.mutationContext);if(!o.ok)throw new Error(`${o.code}: ${o.message??"Mutation session verification failed"}`);let u=await this.bindHistorySession(e.mutationContext.editingSessionId);if(!u.ok)throw new Error(`${u.code}: ${u.message}`)}let{runtime:n,resolvedRouteId:i}=await this.activateRoute(e.routeId),s=n.getDocument(i).data;await this.recordRuntimeVersion(s.version);let r=(await n.getHistoryStatus()).data;return{ok:!0,data:{document:s,historyStatus:r}}}),getDocument:async e=>{let{runtime:n,resolvedRouteId:i}=await this.activateRoute(e),s=n.getDocument(i);return await this.recordRuntimeVersion(s.data.version),s},getHistoryStatus:async()=>this.getRuntime().getHistoryStatus(),getStyleContext:async e=>{let{runtime:n,resolvedRouteId:i}=await this.activateRoute(e);return n.getStyleContext(i)},getObjectEditContext:async(e,n)=>this.getRuntime().getObjectEditContext(e,n?this.resolveRouteId(n):void 0),getClassNameSource:async(e,n)=>{let i=this.getRuntime(),s=n?this.resolveRouteId(n):void 0;return i.getClassNameSource(e,s)},getSourceTree:async()=>this.getRuntime().getSourceTree(),getSourceFile:async e=>this.getRuntime().getSourceFile(e),resolveSourceLocation:async e=>this.getRuntime().resolveSourceLocation({key:e.key,routeId:e.routeId?this.resolveRouteId(e.routeId):void 0}),execute:(e,n)=>this.mutationQueue.run(async()=>{let i=n??e.mutationContext,s=await this.verifyMutationContext(i);if(!s.ok)return y(s,e);let r=await this.bindHistorySession(i.editingSessionId);if(!r.ok)return y(r,e);let o=await this.getRuntime().executeOperation(this.normalizeRequest(e),i);return o.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(o.data.resultVersion),o}),undo:e=>this.mutationQueue.run(async()=>{let n=await this.verifyMutationContext(e);if(!n.ok)return y(n);let i=await this.bindHistorySession(e.editingSessionId);if(!i.ok)return y(i);let s=await this.getRuntime().undo(e);return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion),s}),redo:e=>this.mutationQueue.run(async()=>{let n=await this.verifyMutationContext(e);if(!n.ok)return y(n);let i=await this.bindHistorySession(e.editingSessionId);if(!i.ok)return y(i);let s=await this.getRuntime().redo(e);return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion),s}),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 i=await(this.dependencies.openHistoryStore??ke)(je({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e}));if(!this.isStarted)return;let s=i.readDocumentVersion?await i.readDocumentVersion():0;this.runtimeVersionFloor=Math.max(this.runtimeVersionFloor,s),this.createRuntime(i,e)});this.historySessionTransition=n.catch(()=>{});try{return await n,{ok:!0}}catch{return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history could not be opened."}}}recordRuntimeVersion(e){this.runtimeVersionFloor=Math.max(this.runtimeVersionFloor,e);let n=this.activeHistoryStore;return n?.writeDocumentVersion?(this.versionPersistence=this.versionPersistence.then(()=>n.writeDocumentVersion(this.runtimeVersionFloor)).catch(()=>{}),this.versionPersistence):Promise.resolve()}async activateRoute(e){let n=this.getRuntime(),i=this.resolveRouteId(e);return await n.setActiveRoute(i),{runtime:n,resolvedRouteId:i}}normalizeRequest(e){return e.target.kind!=="route"?e.routeId?{...e,routeId:this.resolveRouteId(e.routeId)}:e:{...e,target:{...e.target,routeId:this.resolveRouteId(e.target.routeId)}}}handleFileChanged(e){this.handleFilesChanged([e])}handleFilesChanged(e){for(let i of e)this.pendingChangedFiles.add(i);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let n=this.options.runtime.watch_debounce_ms??z;this.fileChangeTimer=setTimeout(()=>{this.fileChangeTimer=null,this.flushChangedFiles()},n),this.fileChangeTimer?.unref?.()}flushChangedFiles(){if(this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.pendingChangedFiles.size===0)return;let e=[...this.pendingChangedFiles].sort((n,i)=>n.localeCompare(i));this.pendingChangedFiles.clear(),this.refreshRoutes();for(let n of e)this.runtime?.notifyFileChanged(n)}refreshRoutes(){let e=this.options.runtime.routes?.length?this.options.runtime.routes:Pe(this.options.projectRoot),n=new Map(this.routes.map(s=>[s.routeId,s.entryFile])),i=new Set(e.map(s=>s.routeId));this.routes=e;for(let s of e)n.get(s.routeId)!==s.entryFile&&this.runtime?.registerRoute(s.routeId,s.entryFile);for(let s of n.keys())i.has(s)||this.runtime?.unregisterRoute(s)}resolveRouteId(e){let n=_e(e,this.routes,this.options.basePath);if(!n)throw new Error(`Route is not indexed by the preview bridge: ${e}`);return n}getRuntime(){if(!this.runtime)throw new Error("VCP site edit preview bridge runtime is not ready");return this.runtime}async verifyMutationContext(e){if(!e)return{ok:!1,code:"MUTATION_SESSION_REQUIRED",message:"A mutation session is required for write operations."};if(!this.options.mutationContextVerifier||!this.options.tenantId)return{ok:!1,code:"LEASE_ASSERTION_INVALID",message:"Lease assertion verification is unavailable."};try{return await this.options.mutationContextVerifier.verify(e,{tenantId:this.options.tenantId,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e.editingSessionId,lockId:e.lockId,ownerId:e.ownerId})}catch{return{ok:!1,code:"LEASE_ASSERTION_INVALID",message:"Lease assertion verification failed."}}}};function Oe(t){return{tenantId:t.tenantId,siteId:t.siteId,snapshotId:t.snapshotId,projectRoot:O.resolve(t.projectRoot??process.cwd()),basePath:t.basePath??"",host:t.host??Be,port:t.port??Ve,path:t.path??De,sessionKey:t.sessionKey??ze(),historyRoot:t.historyRoot,mutationContextVerifier:t.mutationContextVerifier,allowedOrigins:t.allowedOrigins??[],runtime:t.runtime??{}}}function je(t){return O.join(O.resolve(t.root),Q(t.siteId),Q(t.snapshotId),X("sha256").update(t.editingSessionId).digest("hex").slice(0,32))}function Q(t){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(t)&&t!=="."&&t!==".."?t:X("sha256").update(t).digest("hex").slice(0,32)}function Ue(){let t=globalThis;return t[G]??=new Map,t[G]}function Ae(t){return`${t.projectRoot}:${t.host}:${t.port}:${t.path}:${t.historyRoot??""}`}function y(t,e){return{ok:!1,error:{code:t.code,message:t.message},data:{requestId:e?.id??"mutation-context",kind:e?.kind??"update-text",target:e?.target??{kind:"node",key:""},resultVersion:e?.documentVersion??0}}}function Ke(t){let e=new be({maxPayload:te,noServer:!0}),n=new WeakSet,i=r=>{new k({socket:new U(r),engine:t.engine,validateOperationRequest:o=>He.safeParse(o).success})},s=(r,o,u)=>{if(!Fe(r,t))return;let d=Le(r,t);if(!d){o.write(`HTTP/1.1 401 Unauthorized\r
1
+ import ze from"node:http";import{existsSync as y,readFileSync as m}from"node:fs";import{createRequire as ae}from"node:module";import I from"node:path";import{basename as ue,dirname as x,join as b}from"node:path";import{fileURLToPath as ce}from"node:url";import Ie from"node:http";import{createHash as Z,randomBytes as xe,timingSafeEqual as be}from"node:crypto";import V from"node:path";import{WebSocket as X,WebSocketServer as ke}from"ws";import{createPreviewSiteEditEngineRuntime as Ce,openFileHistoryStore as ve}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as Ee,scanNextAppRouterRoutes as _e}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as Pe}from"@vcp-dev/site-edit-engine/source-watcher";function B(t){let e=new Map,n=[],s=new Map,i=[],r=t.pageComposition.status==="supported"?t.pageComposition.placementUnitKeys:[],o=new Map(r.map((a,u)=>[a,u])),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},d=t.entries.flatMap((a,u)=>a.dynamicDataSummary?[[u,a.dynamicDataSummary]]:[]),h=a=>{let u=e.get(a);if(typeof u=="number")return u;let p=n.length;return n.push(a),e.set(a,p),p},f=a=>{let u=null;for(let p of ye(a)){let k=`${u??"root"}\0${p}`,S=s.get(k);if(typeof S=="number"){u=S;continue}let w=i.length;i.push([u,h(p)]),s.set(k,w),u=w}if(u===null)throw new Error("Document key must not be empty");return u};return{encoding:"segment-trie-v1",siteId:t.siteId,snapshotId:t.snapshotId,routeId:t.routeId,version:t.version,pageComposition:c,tokens:n,nodes:i,entries:t.entries.map(a=>{let u=a.pageCompositionUnitKey?o.get(a.pageCompositionUnitKey)??-1:-1;if(a.pageCompositionUnitKey&&u<0)throw new Error(`Render entry ${a.key} references unknown page composition unit ${a.pageCompositionUnitKey}`);return[f(a.key),me(a.operationSummary),...u>=0?[u]:[]]}),...d.length>0?{dynamicDataSummaries:d}:{}}}function me(t){return(t.canUpdateText?1:0)|(t.canInsertChild?2:0)|(t.canMove?4:0)|(t.canRemove?8:0)}function ye(t){if(!t)return[];let e=[],n=0,s=L(t,n);if(s<0)return e.push(t),e;for(e.push(t.slice(0,s)),n=s;n<t.length;){if(s=L(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 L(t,e){let n=t.indexOf("/",e),s=t.indexOf("::",e);return n<0?s:s<0?n:Math.min(n,s)}var g=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(typeof e=="object"&&e!==null&&"payload"in e&&typeof e.payload=="object"&&e.payload!==null){let n=e.payload,s=typeof n.id=="string"?n.id:"unknown",i=t.isRequestKind(n.kind)?n.kind:"get-document";return{id:s,kind:i}}return{id:"unknown",kind:"get-document"}}static isRequestKind(e){return e==="initialize-session"||e==="get-document"||e==="get-history-status"||e==="get-style-context"||e==="get-object-edit-context"||e==="get-class-name-source"||e==="get-source-tree"||e==="get-source-file"||e==="resolve-source-location"||e==="execute"||e==="undo"||e==="redo"}static isMessage(e,n={}){return!l(e)||typeof e.type!="string"?!1:e.type==="request"?t.isRequestPayload(e.payload,n):e.type==="response"||e.type==="error"?l(e.payload)&&typeof e.payload.id=="string":e.type==="event"?l(e.payload)&&l(e.payload.event):e.type==="auth"?l(e.payload)&&typeof e.payload.key=="string":e.type==="auth-ok"?l(e.payload)&&e.payload.ok===!0:!1}static isRequestPayload(e,n={}){if(!l(e)||typeof e.id!="string"||!t.isRequestKind(e.kind))return!1;switch(e.kind){case"initialize-session":return typeof e.routeId=="string"&&(e.mutationContext===void 0||j(e.mutationContext));case"get-document":case"get-style-context":return typeof e.routeId=="string";case"get-object-edit-context":case"get-class-name-source":case"resolve-source-location":return typeof e.key=="string"&&(e.routeId===void 0||typeof e.routeId=="string");case"get-history-status":return e.mutationContext===void 0;case"get-source-tree":return!0;case"get-source-file":return typeof e.path=="string";case"execute":return Se(e.request)&&(e.request.mutationContext===void 0||j(e.request.mutationContext))&&(n.validateOperationRequest?.(e.request)??!0);case"undo":case"redo":return e.mutationContext===void 0||j(e.mutationContext)}}},v=class{constructor(e){this.options=e,this.messageQueue=new M,this.eventDispatcher=new D(n=>{this.sendJson(n)}),this.unsubscribe=e.engine.subscribe(n=>{this.eventDispatcher.push(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()}}async handleMessage(e){try{let n=e;if(n.type!=="request")return;let s=n.payload,{id:i}=s;switch(s.kind){case"initialize-session":{let r=await this.options.engine.initializeSession({routeId:s.routeId,...s.mutationContext?{mutationContext:s.mutationContext}:{}});this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:{ok:!0,data:{document:B(r.data.document),historyStatus:r.data.historyStatus}}}});return}case"get-document":this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:{ok:!0,data:B((await this.options.engine.getDocument(s.routeId)).data)}}});return;case"get-history-status":this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:await this.options.engine.getHistoryStatus()}});return;case"get-style-context":this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:await this.options.engine.getStyleContext(s.routeId)}});return;case"get-object-edit-context":this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:await this.options.engine.getObjectEditContext(s.key,s.routeId)}});return;case"get-class-name-source":this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:await this.options.engine.getClassNameSource(s.key,s.routeId)}});return;case"get-source-tree":this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:await this.options.engine.getSourceTree()}});return;case"get-source-file":this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:await this.options.engine.getSourceFile({path:s.path})}});return;case"resolve-source-location":this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:await this.options.engine.resolveSourceLocation({key:s.key,routeId:s.routeId})}});return;case"execute":{await this.handleBufferedOperation(i,s.kind,()=>this.options.engine.execute(s.request));return}case"undo":await this.handleBufferedOperation(i,s.kind,()=>this.options.engine.undo());return;case"redo":await this.handleBufferedOperation(i,s.kind,()=>this.options.engine.redo());return}}catch(n){let s=g.extractRequestInfo(e);this.sendJson({type:"error",payload:{...s,message:J(n),code:$(n)}})}}async handleRawMessage(e){try{await this.handleMessage(g.parseMessage(e,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let s=n instanceof Error?n.message:"Invalid websocket message",i=Re(e);this.sendJson({type:"error",payload:{...i,message:s}})}}sendJson(e){this.options.socket.send(g.stringifyMessage(e))}async handleBufferedOperation(e,n,s){this.eventDispatcher.beginBuffering();try{let i=await s();this.sendJson({type:"response",payload:{id:e,kind:n,result:i}}),this.eventDispatcher.flush()}catch(i){this.sendJson({type:"error",payload:{id:e,kind:n,message:J(i),code:$(i)}}),this.eventDispatcher.flush()}}},D=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)})}},M=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 l(t){return typeof t=="object"&&t!==null}function Se(t){return l(t)&&typeof t.id=="string"&&typeof t.siteId=="string"&&typeof t.baseSnapshotId=="string"&&(t.routeId===void 0||typeof t.routeId=="string")&&typeof t.kind=="string"&&l(t.target)&&typeof t.documentVersion=="number"&&Number.isInteger(t.documentVersion)&&t.documentVersion>=0&&l(t.params)}function j(t){return we(t)}function $(t){if(!t||typeof t!="object")return;let e=t.code;return typeof e=="string"&&e.length>0?e:void 0}function J(t){if(t instanceof Error)return t.message;if(!t||typeof t!="object")return"Unknown websocket operation failure";let e=t.message;return typeof e=="string"&&e.length>0?e:"Unknown websocket operation failure"}function we(t){return l(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:!1}function Re(t){try{return g.extractRequestInfo(JSON.parse(t))}catch{return{id:"unknown",kind:"get-document"}}}var E=class{constructor(){this.tail=Promise.resolve()}run(e){let n=this.tail.then(e,e);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as Te}from"@vcp-dev/contracts";var He="127.0.0.1",Be=30218,je="/__vcp/site-edit/bridge",ee=6*1024*1024,De=ee*2,Me=3e3,W=50,z="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function te(t,e,n={}){let s=new A(Ue(e),t,!1,n);return s.start({listen:!1}),s}var A=class{constructor(e,n=Ie.createServer((r,o)=>{o.writeHead(404),o.end("VCP site edit bridge server")}),s=!0,i={}){this.options=e,this.server=n,this.closeHttpServerOnStop=s,this.dependencies=i,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new E,this.eventListeners=new Set,this.routes=[],this.stopWatching=null,this.stopWebSocketServer=null,this.pendingChangedFiles=new Set,this.fileChangeTimer=null,this.isStarted=!1,this.runtimeVersionFloor=0,this.activeHistoryStore=null,this.versionPersistence=Promise.resolve()}start(e={}){if(this.isStarted)return;this.refreshRoutes(),this.isStarted=!0,this.createRuntime();let n=Fe({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 Pe({projectRoot:this.options.projectRoot,watch_dirs:this.options.runtime.watch_dirs,debounce_ms:this.options.runtime.watch_debounce_ms??W,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;return{ok:this.isStarted&&e,bridgePath:this.options.path,runtimeReady:e,routesIndexed:this.routes.length,sessionRequired:!!this.options.sessionKey}}stop(){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.closeHttpServerOnStop&&this.server.close();let e=Ve(),n=Ae(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 i=(this.dependencies.createRuntime??Ce)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,routes:this.routes,onEvent:r=>{"version"in r&&typeof r.version=="number"&&this.recordRuntimeVersion(r.version);for(let o of this.eventListeners)o(r)},...e?{historyStore:e}:{},initialDocumentVersion:this.runtimeVersionFloor});this.runtime=i,this.activeHistorySessionId=n,s?.dispose()}createBridgeEngine(){return{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{runtime:s,resolvedRouteId:i}=await this.activateRoute(e.routeId),r=s.getDocument(i).data;await this.recordRuntimeVersion(r.version);let o=(await s.getHistoryStatus()).data;return{ok:!0,data:{document:r,historyStatus:o}}}),getDocument:async e=>{let{runtime:n,resolvedRouteId:s}=await this.activateRoute(e),i=n.getDocument(s);return await this.recordRuntimeVersion(i.data.version),i},getHistoryStatus:async()=>this.getRuntime().getHistoryStatus(),getStyleContext:async e=>{let{runtime:n,resolvedRouteId:s}=await this.activateRoute(e);return n.getStyleContext(s)},getObjectEditContext:async(e,n)=>this.getRuntime().getObjectEditContext(e,n?this.resolveRouteId(n):void 0),getClassNameSource:async(e,n)=>{let s=this.getRuntime(),i=n?this.resolveRouteId(n):void 0;return s.getClassNameSource(e,i)},getSourceTree:async()=>this.getRuntime().getSourceTree(),getSourceFile:async e=>this.getRuntime().getSourceFile(e),resolveSourceLocation:async e=>this.getRuntime().resolveSourceLocation({key:e.key,routeId:e.routeId?this.resolveRouteId(e.routeId):void 0}),execute:(e,n)=>this.mutationQueue.run(async()=>{let s=await this.bindHistorySession(this.options.snapshotId);if(!s.ok)return U(s,e);let i=await this.getRuntime().executeOperation(this.normalizeRequest(e));return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion),i}),undo:()=>this.mutationQueue.run(async()=>{let e=await this.bindHistorySession(this.options.snapshotId);if(!e.ok)return U(e);let n=await this.getRuntime().undo();return n.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(n.data.resultVersion),n}),redo:()=>this.mutationQueue.run(async()=>{let e=await this.bindHistorySession(this.options.snapshotId);if(!e.ok)return U(e);let n=await this.getRuntime().redo();return n.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(n.data.resultVersion),n}),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??ve)(Oe({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e}));if(!this.isStarted)return;let i=s.readDocumentVersion?await s.readDocumentVersion():0;this.runtimeVersionFloor=Math.max(this.runtimeVersionFloor,i),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){this.runtimeVersionFloor=Math.max(this.runtimeVersionFloor,e);let n=this.activeHistoryStore;return n?.writeDocumentVersion?(this.versionPersistence=this.versionPersistence.then(()=>n.writeDocumentVersion(this.runtimeVersionFloor)).catch(()=>{}),this.versionPersistence):Promise.resolve()}async activateRoute(e){let n=this.getRuntime(),s=this.resolveRouteId(e);return await n.setActiveRoute(s),{runtime:n,resolvedRouteId:s}}normalizeRequest(e){return e.target.kind!=="route"?e.routeId?{...e,routeId:this.resolveRouteId(e.routeId)}:e:{...e,target:{...e.target,routeId:this.resolveRouteId(e.target.routeId)}}}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??W;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));this.pendingChangedFiles.clear(),this.refreshRoutes();for(let n of e)this.runtime?.notifyFileChanged(n)}refreshRoutes(){let e=this.options.runtime.routes?.length?this.options.runtime.routes:_e(this.options.projectRoot),n=new Map(this.routes.map(i=>[i.routeId,i.entryFile])),s=new Set(e.map(i=>i.routeId));this.routes=e;for(let i of e)n.get(i.routeId)!==i.entryFile&&this.runtime?.registerRoute(i.routeId,i.entryFile);for(let i of n.keys())s.has(i)||this.runtime?.unregisterRoute(i)}resolveRouteId(e){let n=Ee(e,this.routes,this.options.basePath);if(!n)throw new Error(`Route is not indexed by the preview bridge: ${e}`);return n}getRuntime(){if(!this.runtime)throw new Error("VCP site edit preview bridge runtime is not ready");return this.runtime}};function Ue(t){return{tenantId:t.tenantId,siteId:t.siteId,snapshotId:t.snapshotId,projectRoot:V.resolve(t.projectRoot??process.cwd()),basePath:t.basePath??"",host:t.host??He,port:t.port??Be,path:t.path??je,sessionKey:t.sessionKey??We(),historyRoot:t.historyRoot,allowedOrigins:t.allowedOrigins??[],runtime:t.runtime??{}}}function Oe(t){return V.join(V.resolve(t.root),G(t.siteId),G(t.snapshotId),Z("sha256").update(t.editingSessionId).digest("hex").slice(0,32))}function G(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 Ve(){let t=globalThis;return t[z]??=new Map,t[z]}function Ae(t){return`${t.projectRoot}:${t.host}:${t.port}:${t.path}:${t.historyRoot??""}`}function U(t,e){return{ok:!1,error:{code:t.code,message:t.message},data:{requestId:e?.id??"mutation-context",kind:e?.kind??"update-text",target:e?.target??{kind:"node",key:""},resultVersion:e?.documentVersion??0}}}function Fe(t){let e=new ke({maxPayload:ee,noServer:!0}),n=new WeakSet,s=r=>{new v({socket:new F(r),engine:t.engine,validateOperationRequest:o=>Te.safeParse(o).success})},i=(r,o,c)=>{if(!Ne(r,t))return;let d=Ke(r,t);if(!d){o.write(`HTTP/1.1 401 Unauthorized\r
2
2
  Connection: close\r
3
3
  \r
4
- `),o.destroy();return}d.authenticatedByHeader&&n.add(r),e.handleUpgrade(r,o,u,h=>{e.emit("connection",h,r)})};return t.server.on("upgrade",s),e.on("connection",(r,o)=>{if(n.has(o)){i(r);return}qe(r,t.sessionKey,()=>{i(r)})}),e.stop=()=>{t.server.off("upgrade",s);for(let r of e.clients)r.close(1001,"bridge stopped");e.close()},e}var U=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===ee.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(e,"utf8")>Me){this.socket.close(1013,"bridge backpressure");return}this.socket.send(e)}}close(e,n){this.socket.close(e,n)}};function Fe(t,e){return new URL(t.url??"/","http://localhost").pathname===e.path}function Le(t,e){if(new URL(t.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let i=N(t.headers["x-vcp-site-edit-key"]),s=ie(i,e.sessionKey),r=N(t.headers.host),o=N(t.headers.origin);return r&&!$e(r,e.host,o)?null:o?We(o,r,e.allowedOrigins)?{authenticatedByHeader:s}:null:s?{authenticatedByHeader:!0}:null}function qe(t,e,n){let i=setTimeout(()=>{t.close(1008,"bridge authentication timeout")},Ne),s,r=()=>{clearTimeout(i),t.off("message",s),t.off("close",r)},o=()=>{r(),t.close(1008,"bridge authentication failed")};s=u=>{try{let d=p.parseMessage(u.toString());if(d.type!=="auth"||!ie(d.payload.key,e)){o();return}r(),t.readyState===ee.OPEN&&t.send(p.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),n()}catch{o()}},t.on("message",s),t.on("close",r)}function ie(t,e){if(!t)return!1;let n=Buffer.from(t),i=Buffer.from(e);return n.length===i.length&&Ce(n,i)}function $e(t,e,n){let i=Z(t),s=Z(e);return i===s||Y(s)&&Y(i)&&(!n||A(n,t))||Je(s)&&(!n||A(n,t))}function Je(t){return t==="0.0.0.0"||t==="::"}function Y(t){return t==="localhost"||t==="127.0.0.1"||t==="::1"}function We(t,e,n){return n.includes(t)||(e?A(t,e):!1)}function A(t,e){try{return new URL(t).host===se(e)}catch{return!1}}function Z(t){let e=se(t);return e.startsWith("[")?e.slice(1,e.indexOf("]")):e.split(":").length-1===1?e.split(":",1)[0]??"":e}function se(t){return t.trim().toLowerCase()}function N(t){return Array.isArray(t)?t[0]:t}function ze(){return Ee(32).toString("base64url")}var Ye="next",Ze="127.0.0.1",Xe=3e3,P="/__vcp/site-edit/bridge",he="/__vcp/site-edit/health",T="/__vcp/site-edit/client/",et=`${T}auto-bootstrap.js`,fe='data-vcp-site-edit-bootstrap="true"',tt=["next.config.ts","next.config.mts","next.config.cts","next.config.js","next.config.mjs","next.config.cjs"],nt="@vcp-dev/site-edit-engine/webpack-loader",it=/@vcp\/site-edit-engine\/(?:webpack-loader|site-edit-instrumentation-loader)/,st=[/\bconfig\.webpack\s*=/,/\bwebpack\s*\([^)]*\)\s*\{/,/\bwebpack\s*:\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)?\s*=>/,/\bwebpack\s*:\s*(?:async\s+)?function\b/],rt=new Set(["auto-bootstrap.js","client.js","designer-trace.js","document-codec.js","protocol.js","window-message.js"]);async function fn(t={}){let e=x.resolve(t.projectRoot??process.cwd()),n=t.host??Ze,i=t.port??At(process.env.PORT)??Xe,s=t.bridgePath??P,r=gt(s),o=Ht(),u=t.basePath??process.env.VCP_SITE_PREVIEW_BASE_PATH??"/",{tenantId:d,mutationContextVerifier:h}=ot(t,process.env),a=await Nt(e),c=ct(e),f=a({dev:!0,dir:e,hostname:n,port:i,...dt(c)}),g=f.getRequestHandler(),C=t.siteId??process.env.VCP_SITE_ID??"site_preview",b=t.snapshotId??process.env.VCP_SNAPSHOT_ID??Ut(e),L=t.historyRoot??process.env.VCP_SITE_EDIT_HISTORY_ROOT,q=L?.trim()?x.resolve(L):void 0;await f.prepare();let H=null,w=Ge.createServer((ge,v)=>{Rt({basePath:u,bridgePath:s,bridgeHealthPath:r,bridgeSessionKey:o,getBridgeHealth:()=>H?.getHealth()??Ct(s),handle:g,request:ge,response:v}).catch(me=>{console.error(me),v.headersSent||(v.statusCode=500),v.end("site edit preview request failed")})});H=ne(w,{...d?{tenantId:d}:{},siteId:C,snapshotId:b,projectRoot:e,basePath:u,host:n,port:i,path:s,sessionKey:o,...q?{historyRoot:q}:{},...h?{mutationContextVerifier:h}:{}}),lt({server:w,bridgePath:s,handleUpgrade:f.getUpgradeHandler()});let B=()=>{H?.stop(),f.close().catch(()=>{}),w.close()};return process.on("SIGINT",B),process.on("SIGTERM",B),await Kt(w,i,n),{bundler:c,close:B,server:w}}function ot(t,e){let n=(t.tenantId??e.VCP_TENANT_ID)?.trim()||void 0,i=t.requireMutationContextVerifier??ut(e.VCP_DESIGNER_SESSION_REQUIRE_VERIFIER),s=at(t,e);if(i&&!n)throw new Error("tenantId is required when mutation context verification is enabled.");if(i&&!s)throw new Error("A mutation context verifier is required when mutation context verification is enabled.");return{...n?{tenantId:n}:{},...s?{mutationContextVerifier:s}:{},requireMutationContextVerifier:i}}function at(t,e){if(t.mutationContextVerifier)return t.mutationContextVerifier;let n=(t.mutationContextPublicKeyPem??e.VCP_DESIGNER_SESSION_PUBLIC_KEY_PEM)?.trim();if(n)try{let i=e.VCP_DESIGNER_SESSION_KEY_ID?.trim();if(!i)throw new Error("VCP_DESIGNER_SESSION_KEY_ID is required.");return Qe({publicKey:n.replace(/\\n/g,`
5
- `),issuer:e.VCP_DESIGNER_SESSION_ISSUER??"vcp-api",audience:e.VCP_DESIGNER_SESSION_AUDIENCE??"site-edit-engine",keyId:i})}catch(i){throw new Error("VCP_DESIGNER_SESSION_PUBLIC_KEY_PEM is invalid.",{cause:i})}}function ut(t){return t==="1"||t==="true"}function ct(t){let e=ht(t);return e&&ft(e)?"webpack":"turbopack"}function dt(t){return t==="webpack"?{webpack:!0}:{turbopack:!0}}function ht(t){for(let e of tt){let n=E(t,e);if(I(n))return S(n,"utf8")}return null}function ft(t){return t.includes(nt)?!0:it.test(t)&&st.some(e=>e.test(t))}function lt(t){let e=(n,i,s)=>{pt(n.url,t.bridgePath)||t.handleUpgrade(n,i,s).catch(r=>{console.error(r),i.destroyed||i.destroy()})};return t.server.on("upgrade",e),()=>t.server.off("upgrade",e)}function pt(t,e){return new URL(t??"/","http://localhost").pathname===e}function gt(t=P){if(t===P)return he;let e=(t.startsWith("/")?t:`/${t}`).replace(/\/+$/,"");return e.endsWith("/bridge")?`${e.slice(0,-7)}/health`:e?`${e}/health`:"/health"}function mt(t){let e=JSON.stringify({basePath:t.basePath,wsPath:t.bridgePath,bridge_session_key:t.bridgeSessionKey,defer_initial_sync:!0,runtime_versions:t.runtimeVersions??yt()});return`<script type="module" ${fe}>import { startSiteEditAutoBootstrap } from ${JSON.stringify(et)};void startSiteEditAutoBootstrap(window, ${e}).catch(() => undefined);</script>`}function yt(){return{bridge:K("@vcp-dev/site-edit-bridge"),contracts:K("@vcp-dev/contracts"),engine:K("@vcp-dev/site-edit-engine")}}function K(t){let e=ue(import.meta.url);try{let n=e.resolve(t),i=St(n,t);if(!i)return;let s=JSON.parse(S(i,"utf8"));return typeof s.version=="string"?s.version:void 0}catch{return}}function St(t,e){let n=x.parse(t).root,i=R(t);for(;i&&i!==n;){let s=E(i,"package.json");if(I(s))try{if(JSON.parse(S(s,"utf8")).name===e)return s}catch{return}i=R(i)}}function It(t,e){if(t.includes(fe))return t;let n=mt(e),i=/<\/body\s*>/i.exec(t);return i?`${t.slice(0,i.index)}${n}${t.slice(i.index)}`:`${t}${n}`}function ln(t,e=R(de(import.meta.url)),n=I){return le(t,e,n)?.path??null}function le(t,e=R(de(import.meta.url)),n=I){let i=new URL(t??"/","http://localhost").pathname;if(!i.startsWith(T))return null;let s=i.slice(T.length);if(!rt.has(s))return null;let r=F(Ot(e),s);if(ce(e)!=="src")return{path:r,requiresTranspile:!1};let o=F(e,s.replace(/\.js$/,".ts"));return n(o)?{path:o,requiresTranspile:!0}:{path:r,requiresTranspile:!1}}async function wt(t,e){let n=le(t.url);if(!n)return!1;if(!I(n.path))return e.writeHead(404),e.end("site edit client asset not found"),!0;let i=n.requiresTranspile?await xt(S(n.path,"utf8")):S(n.path);return e.writeHead(200,{"cache-control":"no-store","content-type":"text/javascript; charset=utf-8"}),e.end(i),!0}async function xt(t){let e=await import("typescript");return e.transpileModule(t,{compilerOptions:{module:e.ModuleKind.ES2022,target:e.ScriptTarget.ES2022}}).outputText}async function Rt({basePath:t,bridgePath:e,bridgeHealthPath:n,bridgeSessionKey:i,getBridgeHealth:s,handle:r,request:o,response:u}){if(Et(o,u,n,s)||await wt(o,u))return;let d=bt(o),h=vt(o),a=d||h?Pt(u,{basePath:t,bridgePath:e,bridgeSessionKey:i,patchNextDevChunk:h}):()=>{};try{await r(o,u)}catch(c){throw a(),c}}function Et(t,e,n,i){if(t.method!=="GET"||new URL(t.url??"/","http://localhost").pathname!==n)return!1;let r=i();return e.writeHead(r.ok?200:503,{"cache-control":"no-store","content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify(r)),!0}function Ct(t){return{ok:!1,bridgePath:t,runtimeReady:!1,routesIndexed:0,sessionRequired:!0}}function bt(t){return kt(t)?(t.headers["accept-encoding"]="identity",!0):!1}function vt(t){return _t(t)?(t.headers["accept-encoding"]="identity",!0):!1}function kt(t){if(t.method!=="GET")return!1;let e=new URL(t.url??"/","http://localhost").pathname;if(e.startsWith("/_next/")||e.startsWith(T)||e===P||e===he)return!1;let n=t.headers.accept??"";return n===""||n.includes("text/html")}function _t(t){if(t.method!=="GET"||!String(t.headers["user-agent"]??"").includes("Firefox"))return!1;let n=new URL(t.url??"/","http://localhost").pathname;return n.startsWith("/_next/static/chunks/")&&n.endsWith(".js")}function Pt(t,e){let n=[],i=t,s=i.flushHeaders.bind(t),r=i.write.bind(t),o=i.end.bind(t);return i.flushHeaders=()=>{},i.write=(u,d,h)=>(u!==void 0&&n.push(re(u,oe(d))),ae(d),ae(h),!0),i.end=(u,d,h)=>{let a=typeof u=="function"?void 0:u;a!==void 0&&n.push(re(a,oe(d))),i.write=r,i.end=o,i.flushHeaders=s;let c=Buffer.concat(n),f=Tt(t,c,e);f!==c&&!t.headersSent&&t.removeHeader("content-length");let g=typeof u=="function"?u:typeof d=="function"?d:typeof h=="function"?h:void 0;return g?o(f,g):o(f)},()=>{i.write=r,i.end=o,i.flushHeaders=s}}function Tt(t,e,n){if(Bt(t,e))return Buffer.from(It(e.toString("utf8"),n));if(n.patchNextDevChunk&&Vt(t)){let i=e.toString("utf8"),s=Dt(i);if(s!==i)return Buffer.from(s)}return e}function Ht(){let t=new Uint8Array(32);return globalThis.crypto.getRandomValues(t),Buffer.from(t).toString("base64url")}function Bt(t,e){if(t.statusCode<200||t.statusCode>=300)return!1;let n=t.getHeader("content-type"),i=Array.isArray(n)?n.join(";"):String(n??"");return e.toString("utf8",0,256).includes("<html")&&(i===""||i.includes("text/html"))}function Vt(t){if(t.statusCode<200||t.statusCode>=300)return!1;let e=t.getHeader("content-type"),n=Array.isArray(e)?e.join(";"):String(e??"");return n===""||n.includes("javascript")||n.includes("ecmascript")}function Dt(t){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
+ `),o.destroy();return}d.authenticatedByHeader&&n.add(r),e.handleUpgrade(r,o,c,h=>{e.emit("connection",h,r)})};return t.server.on("upgrade",i),e.on("connection",(r,o)=>{if(n.has(o)){s(r);return}qe(r,t.sessionKey,()=>{s(r)})}),e.stop=()=>{t.server.off("upgrade",i);for(let r of e.clients)r.close(1001,"bridge stopped");e.close()},e}var F=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")>De){this.socket.close(1013,"bridge backpressure");return}this.socket.send(e)}}close(e,n){this.socket.close(e,n)}};function Ne(t,e){return new URL(t.url??"/","http://localhost").pathname===e.path}function Ke(t,e){if(new URL(t.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let s=O(t.headers["x-vcp-site-edit-key"]),i=ne(s,e.sessionKey),r=O(t.headers.host),o=O(t.headers.origin);return r&&!Le(r,e.host,o)?null:o?Je(o,r,e.allowedOrigins)?{authenticatedByHeader:i}:null:i?{authenticatedByHeader:!0}:null}function qe(t,e,n){let s=setTimeout(()=>{t.close(1008,"bridge authentication timeout")},Me),i,r=()=>{clearTimeout(s),t.off("message",i),t.off("close",r)},o=()=>{r(),t.close(1008,"bridge authentication failed")};i=c=>{try{let d=g.parseMessage(c.toString());if(d.type!=="auth"||!ne(d.payload.key,e)){o();return}r(),t.readyState===X.OPEN&&t.send(g.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),n()}catch{o()}},t.on("message",i),t.on("close",r)}function ne(t,e){if(!t)return!1;let n=Buffer.from(t),s=Buffer.from(e);return n.length===s.length&&be(n,s)}function Le(t,e,n){let s=Y(t),i=Y(e);return s===i||Q(i)&&Q(s)&&(!n||N(n,t))||$e(i)&&(!n||N(n,t))}function $e(t){return t==="0.0.0.0"||t==="::"}function Q(t){return t==="localhost"||t==="127.0.0.1"||t==="::1"}function Je(t,e,n){return n.includes(t)||(e?N(t,e):!1)}function N(t,e){try{return new URL(t).host===se(e)}catch{return!1}}function Y(t){let e=se(t);return e.startsWith("[")?e.slice(1,e.indexOf("]")):e.split(":").length-1===1?e.split(":",1)[0]??"":e}function se(t){return t.trim().toLowerCase()}function O(t){return Array.isArray(t)?t[0]:t}function We(){return xe(32).toString("base64url")}var Ge="next",Qe="127.0.0.1",Ye=3e3,_="/__vcp/site-edit/bridge",de="/__vcp/site-edit/health",P="/__vcp/site-edit/client/",Ze=`${P}auto-bootstrap.js`,he='data-vcp-site-edit-bootstrap="true"',Xe=["next.config.ts","next.config.mts","next.config.cts","next.config.js","next.config.mjs","next.config.cjs"],et="@vcp-dev/site-edit-engine/webpack-loader",tt=/@vcp\/site-edit-engine\/(?:webpack-loader|site-edit-instrumentation-loader)/,nt=[/\bconfig\.webpack\s*=/,/\bwebpack\s*\([^)]*\)\s*\{/,/\bwebpack\s*:\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)?\s*=>/,/\bwebpack\s*:\s*(?:async\s+)?function\b/],st=new Set(["auto-bootstrap.js","client.js","designer-trace.js","document-codec.js","protocol.js","window-message.js"]);async function on(t={}){let e=I.resolve(t.projectRoot??process.cwd()),n=t.host??Qe,s=t.port??Mt(process.env.PORT)??Ye,i=t.bridgePath??_,r=dt(i),o=vt(),c=t.basePath??process.env.VCP_SITE_PREVIEW_BASE_PATH??"/",d=(t.tenantId??process.env.VCP_TENANT_ID)?.trim()||void 0,h=await Ht(e),f=it(e),a=h({dev:!0,dir:e,hostname:n,port:s,...rt(f)}),u=a.getRequestHandler(),p=t.siteId??process.env.VCP_SITE_ID??"site_preview",k=t.snapshotId??process.env.VCP_SNAPSHOT_ID??Dt(e),S=t.historyRoot??process.env.VCP_SITE_EDIT_HISTORY_ROOT,w=S?.trim()?I.resolve(S):void 0;await a.prepare();let T=null,R=ze.createServer((fe,C)=>{yt({basePath:c,bridgePath:i,bridgeHealthPath:r,bridgeSessionKey:o,getBridgeHealth:()=>T?.getHealth()??wt(i),handle:u,request:fe,response:C}).catch(ge=>{console.error(ge),C.headersSent||(C.statusCode=500),C.end("site edit preview request failed")})});T=te(R,{...d?{tenantId:d}:{},siteId:p,snapshotId:k,projectRoot:e,basePath:c,host:n,port:s,path:i,sessionKey:o,...w?{historyRoot:w}:{}}),ut({server:R,bridgePath:i,handleUpgrade:a.getUpgradeHandler()});let H=()=>{T?.stop(),a.close().catch(()=>{}),R.close()};return process.on("SIGINT",H),process.on("SIGTERM",H),await Ut(R,s,n),{bundler:f,close:H,server:R}}function it(t){let e=ot(t);return e&&at(e)?"webpack":"turbopack"}function rt(t){return t==="webpack"?{webpack:!0}:{turbopack:!0}}function ot(t){for(let e of Xe){let n=b(t,e);if(y(n))return m(n,"utf8")}return null}function at(t){return t.includes(et)?!0:tt.test(t)&&nt.some(e=>e.test(t))}function ut(t){let e=(n,s,i)=>{ct(n.url,t.bridgePath)||t.handleUpgrade(n,s,i).catch(r=>{console.error(r),s.destroyed||s.destroy()})};return t.server.on("upgrade",e),()=>t.server.off("upgrade",e)}function ct(t,e){return new URL(t??"/","http://localhost").pathname===e}function dt(t=_){if(t===_)return de;let e=(t.startsWith("/")?t:`/${t}`).replace(/\/+$/,"");return e.endsWith("/bridge")?`${e.slice(0,-7)}/health`:e?`${e}/health`:"/health"}function ht(t){let e=JSON.stringify({basePath:t.basePath,wsPath:t.bridgePath,bridge_session_key:t.bridgeSessionKey,defer_initial_sync:!0,runtime_versions:t.runtimeVersions??lt()});return`<script type="module" ${he}>import { startSiteEditAutoBootstrap } from ${JSON.stringify(Ze)};void startSiteEditAutoBootstrap(window, ${e}).catch(() => undefined);</script>`}function lt(){return{bridge:K("@vcp-dev/site-edit-bridge"),contracts:K("@vcp-dev/contracts"),engine:K("@vcp-dev/site-edit-engine")}}function K(t){let e=ae(import.meta.url);try{let n=e.resolve(t),s=pt(n,t);if(!s)return;let i=JSON.parse(m(s,"utf8"));return typeof i.version=="string"?i.version:void 0}catch{return}}function pt(t,e){let n=I.parse(t).root,s=x(t);for(;s&&s!==n;){let i=b(s,"package.json");if(y(i))try{if(JSON.parse(m(i,"utf8")).name===e)return i}catch{return}s=x(s)}}function ft(t,e){if(t.includes(he))return t;let n=ht(e),s=/<\/body\s*>/i.exec(t);return s?`${t.slice(0,s.index)}${n}${t.slice(s.index)}`:`${t}${n}`}function an(t,e=x(ce(import.meta.url)),n=y){return le(t,e,n)?.path??null}function le(t,e=x(ce(import.meta.url)),n=y){let s=new URL(t??"/","http://localhost").pathname;if(!s.startsWith(P))return null;let i=s.slice(P.length);if(!st.has(i))return null;let r=q(Bt(e),i);if(ue(e)!=="src")return{path:r,requiresTranspile:!1};let o=q(e,i.replace(/\.js$/,".ts"));return n(o)?{path:o,requiresTranspile:!0}:{path:r,requiresTranspile:!1}}async function gt(t,e){let n=le(t.url);if(!n)return!1;if(!y(n.path))return e.writeHead(404),e.end("site edit client asset not found"),!0;let s=n.requiresTranspile?await mt(m(n.path,"utf8")):m(n.path);return e.writeHead(200,{"cache-control":"no-store","content-type":"text/javascript; charset=utf-8"}),e.end(s),!0}async function mt(t){let e=await import("typescript");return e.transpileModule(t,{compilerOptions:{module:e.ModuleKind.ES2022,target:e.ScriptTarget.ES2022}}).outputText}async function yt({basePath:t,bridgePath:e,bridgeHealthPath:n,bridgeSessionKey:s,getBridgeHealth:i,handle:r,request:o,response:c}){if(St(o,c,n,i)||await gt(o,c))return;let d=Rt(o),h=It(o),f=d||h?kt(c,{basePath:t,bridgePath:e,bridgeSessionKey:s,patchNextDevChunk:h}):()=>{};try{await r(o,c)}catch(a){throw f(),a}}function St(t,e,n,s){if(t.method!=="GET"||new URL(t.url??"/","http://localhost").pathname!==n)return!1;let r=s();return e.writeHead(r.ok?200:503,{"cache-control":"no-store","content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify(r)),!0}function wt(t){return{ok:!1,bridgePath:t,runtimeReady:!1,routesIndexed:0,sessionRequired:!0}}function Rt(t){return xt(t)?(t.headers["accept-encoding"]="identity",!0):!1}function It(t){return bt(t)?(t.headers["accept-encoding"]="identity",!0):!1}function xt(t){if(t.method!=="GET")return!1;let e=new URL(t.url??"/","http://localhost").pathname;if(e.startsWith("/_next/")||e.startsWith(P)||e===_||e===de)return!1;let n=t.headers.accept??"";return n===""||n.includes("text/html")}function bt(t){if(t.method!=="GET"||!String(t.headers["user-agent"]??"").includes("Firefox"))return!1;let n=new URL(t.url??"/","http://localhost").pathname;return n.startsWith("/_next/static/chunks/")&&n.endsWith(".js")}function kt(t,e){let n=[],s=t,i=s.flushHeaders.bind(t),r=s.write.bind(t),o=s.end.bind(t);return s.flushHeaders=()=>{},s.write=(c,d,h)=>(c!==void 0&&n.push(ie(c,re(d))),oe(d),oe(h),!0),s.end=(c,d,h)=>{let f=typeof c=="function"?void 0:c;f!==void 0&&n.push(ie(f,re(d))),s.write=r,s.end=o,s.flushHeaders=i;let a=Buffer.concat(n),u=Ct(t,a,e);u!==a&&!t.headersSent&&t.removeHeader("content-length");let p=typeof c=="function"?c:typeof d=="function"?d:typeof h=="function"?h:void 0;return p?o(u,p):o(u)},()=>{s.write=r,s.end=o,s.flushHeaders=i}}function Ct(t,e,n){if(Et(t,e))return Buffer.from(ft(e.toString("utf8"),n));if(n.patchNextDevChunk&&_t(t)){let s=e.toString("utf8"),i=Pt(s);if(i!==s)return Buffer.from(i)}return e}function vt(){let t=new Uint8Array(32);return globalThis.crypto.getRandomValues(t),Buffer.from(t).toString("base64url")}function Et(t,e){if(t.statusCode<200||t.statusCode>=300)return!1;let n=t.getHeader("content-type"),s=Array.isArray(n)?n.join(";"):String(n??"");return e.toString("utf8",0,256).includes("<html")&&(s===""||s.includes("text/html"))}function _t(t){if(t.statusCode<200||t.statusCode>=300)return!1;let e=t.getHeader("content-type"),n=Array.isArray(e)?e.join(";"):String(e??"");return n===""||n.includes("javascript")||n.includes("ecmascript")}function Pt(t){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(`
6
5
  `),["unstable_allowPartialStream: true,"," debugChannel,"," startTime: 0"].join(`
7
- `)],[["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,[i,s])=>Mt(n,i,s),t)}function Mt(t,e,n){if(n.length>e.length)throw new Error("Replacement must not be longer than the search string.");return t.replaceAll(e,n.padEnd(e.length," "))}function re(t,e){return Buffer.isBuffer(t)?t:t instanceof Uint8Array?Buffer.from(t):Buffer.from(String(t),e)}function oe(t){return typeof t=="string"?t:void 0}function ae(t){typeof t=="function"&&t()}async function Nt(t){let n=ue(E(t,"package.json"))(Ye);return n.default??n}function Ot(t){return ce(t)!=="src"?t:F(jt(t),"dist")}function jt(t){return pe(t)?x.posix.dirname(t):R(t)}function F(t,e){return pe(t)?x.posix.join(t,e):E(t,e)}function pe(t){return t.includes("/")&&!t.includes("\\")}function Ut(t){let e=E(t,".vcp/current-snapshot.json");if(!I(e))return"sandbox_worktree";try{let n=JSON.parse(S(e,"utf8"));return typeof n.snapshotId=="string"?n.snapshotId:"sandbox_worktree"}catch{return"sandbox_worktree"}}function At(t){if(!t)return null;let e=Number.parseInt(t,10);return Number.isInteger(e)&&e>0&&e<=65535?e:null}function Kt(t,e,n){return new Promise((i,s)=>{t.once("error",s),t.listen(e,n,()=>{t.off("error",s),i()})})}export{lt as attachNextDevUpgradeHandler,mt as createSiteEditAutoBootstrapHtmlSnippet,It as injectSiteEditAutoBootstrap,Dt as patchNextFirefoxDevChunk,vt as prepareFirefoxNextDevChunkPatchRequest,bt as prepareSiteEditHtmlInjectionRequest,gt as resolveSiteEditBridgeHealthPath,le as resolveSiteEditClientAsset,ln as resolveSiteEditClientAssetPath,ct as resolveSiteEditNextDevBundler,ot as resolveSiteEditNextDevServerMutationConfig,fn as startSiteEditNextDevServer,xt 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])=>Tt(n,s,i),t)}function Tt(t,e,n){if(n.length>e.length)throw new Error("Replacement must not be longer than the search string.");return t.replaceAll(e,n.padEnd(e.length," "))}function ie(t,e){return Buffer.isBuffer(t)?t:t instanceof Uint8Array?Buffer.from(t):Buffer.from(String(t),e)}function re(t){return typeof t=="string"?t:void 0}function oe(t){typeof t=="function"&&t()}async function Ht(t){let n=ae(b(t,"package.json"))(Ge);return n.default??n}function Bt(t){return ue(t)!=="src"?t:q(jt(t),"dist")}function jt(t){return pe(t)?I.posix.dirname(t):x(t)}function q(t,e){return pe(t)?I.posix.join(t,e):b(t,e)}function pe(t){return t.includes("/")&&!t.includes("\\")}function Dt(t){let e=b(t,".vcp/current-snapshot.json");if(!y(e))return"sandbox_worktree";try{let n=JSON.parse(m(e,"utf8"));return typeof n.snapshotId=="string"?n.snapshotId:"sandbox_worktree"}catch{return"sandbox_worktree"}}function Mt(t){if(!t)return null;let e=Number.parseInt(t,10);return Number.isInteger(e)&&e>0&&e<=65535?e:null}function Ut(t,e,n){return new Promise((s,i)=>{t.once("error",i),t.listen(e,n,()=>{t.off("error",i),s()})})}export{ut as attachNextDevUpgradeHandler,ht as createSiteEditAutoBootstrapHtmlSnippet,ft as injectSiteEditAutoBootstrap,Pt as patchNextFirefoxDevChunk,It as prepareFirefoxNextDevChunkPatchRequest,Rt as prepareSiteEditHtmlInjectionRequest,dt as resolveSiteEditBridgeHealthPath,le as resolveSiteEditClientAsset,an as resolveSiteEditClientAssetPath,it as resolveSiteEditNextDevBundler,on as startSiteEditNextDevServer,mt as transpileSiteEditClientSource};
@@ -86,7 +86,8 @@ export type SiteEditBridgeRequest = {
86
86
  payload: {
87
87
  id: string;
88
88
  kind: "undo" | "redo";
89
- mutationContext: SiteEditMutationContext;
89
+ /** @deprecated Ignored by the runtime. */
90
+ mutationContext?: SiteEditMutationContext;
90
91
  };
91
92
  };
92
93
  export type SiteEditBridgeResponse = {
@@ -206,9 +207,9 @@ export type SiteEditBridgeEngine = {
206
207
  path: string;
207
208
  }): Awaitable<SiteEditCommandSuccess<SiteEditSourceFileResponse>>;
208
209
  resolveSourceLocation(input: SiteEditResolveSourceLocationInput): Awaitable<SiteEditCommandSuccess<SiteEditSourceLocation>>;
209
- execute(request: SiteEditAuthenticatedOperationRequest, mutationContext: SiteEditMutationContext): Awaitable<SiteEditOperationCommandResult>;
210
- undo(mutationContext: SiteEditMutationContext): Awaitable<SiteEditOperationCommandResult>;
211
- redo(mutationContext: SiteEditMutationContext): Awaitable<SiteEditOperationCommandResult>;
210
+ execute(request: SiteEditAuthenticatedOperationRequest, mutationContext?: SiteEditMutationContext): Awaitable<SiteEditOperationCommandResult>;
211
+ undo(): Awaitable<SiteEditOperationCommandResult>;
212
+ redo(): Awaitable<SiteEditOperationCommandResult>;
212
213
  subscribe(listener: (event: SiteEditEvent) => void): () => void;
213
214
  };
214
215
  export type SiteEditMessageEventLike = {
package/dist/server.d.ts CHANGED
@@ -4,7 +4,6 @@ import { type PreviewSiteEditEngineRuntime, type PreviewSiteEditEngineRuntimeOpt
4
4
  import type { FileHistoryStore } from "@vcp-dev/site-edit-engine";
5
5
  import { type SiteEditNextRouteConfig } from "@vcp-dev/site-edit-engine/next-app-router";
6
6
  import { type SiteEditBridgeEngine } from "./protocol.js";
7
- import type { SiteEditMutationContextVerifier } from "@vcp-dev/site-edit-engine";
8
7
  export type SiteEditBridgeRuntimeOptions = {
9
8
  routes?: SiteEditNextRouteConfig[];
10
9
  watch_dirs?: string[];
@@ -26,7 +25,6 @@ export type SiteEditBridgeServerOptions = {
26
25
  sessionKey?: string;
27
26
  /** Private runtime directory for the durable, server-side undo/redo store. */
28
27
  historyRoot?: string;
29
- mutationContextVerifier?: SiteEditMutationContextVerifier;
30
28
  allowedOrigins?: string[];
31
29
  runtime?: SiteEditBridgeRuntimeOptions;
32
30
  };
@@ -83,7 +81,6 @@ export declare class SiteEditBridgeServer {
83
81
  private refreshRoutes;
84
82
  private resolveRouteId;
85
83
  private getRuntime;
86
- private verifyMutationContext;
87
84
  }
88
85
  declare function normalizeOptions(options: SiteEditBridgeServerOptions): {
89
86
  tenantId: string | undefined;
@@ -96,7 +93,6 @@ declare function normalizeOptions(options: SiteEditBridgeServerOptions): {
96
93
  path: string;
97
94
  sessionKey: string;
98
95
  historyRoot: string | undefined;
99
- mutationContextVerifier: SiteEditMutationContextVerifier | undefined;
100
96
  allowedOrigins: string[];
101
97
  runtime: SiteEditBridgeRuntimeOptions;
102
98
  };
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
1
- import Y from"node:http";import{createHash as U,randomBytes as X,timingSafeEqual as Z}from"node:crypto";import C from"node:path";import{WebSocket as P,WebSocketServer as ee}from"ws";import{createPreviewSiteEditEngineRuntime as te,openFileHistoryStore as se}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as ne,scanNextAppRouterRoutes as ie}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as oe}from"@vcp-dev/site-edit-engine/source-watcher";function S(t){let e=new Map,s=[],n=new Map,i=[],o=t.pageComposition.status==="supported"?t.pageComposition.placementUnitKeys:[],r=new Map(o.map((a,u)=>[a,u])),d=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=a=>{let u=e.get(a);if(typeof u=="number")return u;let g=s.length;return s.push(a),e.set(a,g),g},w=a=>{let u=null;for(let g of W(a)){let E=`${u??"root"}\0${g}`,_=n.get(E);if(typeof _=="number"){u=_;continue}let T=i.length;i.push([u,p(g)]),n.set(E,T),u=T}if(u===null)throw new Error("Document key must not be empty");return u};return{encoding:"segment-trie-v1",siteId:t.siteId,snapshotId:t.snapshotId,routeId:t.routeId,version:t.version,pageComposition:d,tokens:s,nodes:i,entries:t.entries.map(a=>{let u=a.pageCompositionUnitKey?r.get(a.pageCompositionUnitKey)??-1:-1;if(a.pageCompositionUnitKey&&u<0)throw new Error(`Render entry ${a.key} references unknown page composition unit ${a.pageCompositionUnitKey}`);return[w(a.key),J(a.operationSummary),...u>=0?[u]:[]]})}}function J(t){return(t.canUpdateText?1:0)|(t.canInsertChild?2:0)|(t.canMove?4:0)|(t.canRemove?8:0)}function W(t){if(!t)return[];let e=[],s=0,n=V(t,s);if(n<0)return e.push(t),e;for(e.push(t.slice(0,n)),s=n;s<t.length;){if(n=V(t,s+(t.startsWith("::",s)?2:1)),n<0){e.push(t.slice(s));break}e.push(t.slice(s,n)),s=n}return e}function V(t,e){let s=t.indexOf("/",e),n=t.indexOf("::",e);return s<0?n:n<0?s:Math.min(s,n)}var h=class t{constructor(){}static parseMessage(e,s={}){let n=JSON.parse(e);if(!t.isMessage(n,s))throw new Error("Invalid site edit bridge message");return n}static stringifyMessage(e){return JSON.stringify(e)}static extractRequestInfo(e){if(typeof e=="object"&&e!==null&&"payload"in e&&typeof e.payload=="object"&&e.payload!==null){let s=e.payload,n=typeof s.id=="string"?s.id:"unknown",i=t.isRequestKind(s.kind)?s.kind:"get-document";return{id:n,kind:i}}return{id:"unknown",kind:"get-document"}}static isRequestKind(e){return e==="initialize-session"||e==="get-document"||e==="get-history-status"||e==="get-style-context"||e==="get-object-edit-context"||e==="get-class-name-source"||e==="get-source-tree"||e==="get-source-file"||e==="resolve-source-location"||e==="execute"||e==="undo"||e==="redo"}static isMessage(e,s={}){return!c(e)||typeof e.type!="string"?!1:e.type==="request"?t.isRequestPayload(e.payload,s):e.type==="response"||e.type==="error"?c(e.payload)&&typeof e.payload.id=="string":e.type==="event"?c(e.payload)&&c(e.payload.event):e.type==="auth"?c(e.payload)&&typeof e.payload.key=="string":e.type==="auth-ok"?c(e.payload)&&e.payload.ok===!0:!1}static isRequestPayload(e,s={}){if(!c(e)||typeof e.id!="string"||!t.isRequestKind(e.kind))return!1;switch(e.kind){case"initialize-session":return typeof e.routeId=="string"&&(e.mutationContext===void 0||l(e.mutationContext));case"get-document":case"get-style-context":return typeof e.routeId=="string";case"get-object-edit-context":case"get-class-name-source":case"resolve-source-location":return typeof e.key=="string"&&(e.routeId===void 0||typeof e.routeId=="string");case"get-history-status":return e.mutationContext===void 0;case"get-source-tree":return!0;case"get-source-file":return typeof e.path=="string";case"execute":return z(e.request)&&l(e.request.mutationContext)&&(s.validateOperationRequest?.(e.request)??!0);case"undo":case"redo":return l(e.mutationContext)}}},m=class{constructor(e){this.options=e,this.messageQueue=new x,this.eventDispatcher=new R(s=>{this.sendJson(s)}),this.unsubscribe=e.engine.subscribe(s=>{this.eventDispatcher.push(s)}),e.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"}}),e.socket.close(1013,"bridge busy")}},e.socket.onclose=()=>{this.unsubscribe()}}async handleMessage(e){try{let s=e;if(s.type!=="request")return;let n=s.payload,{id:i}=n;switch(n.kind){case"initialize-session":{let o=await this.options.engine.initializeSession({routeId:n.routeId,...n.mutationContext?{mutationContext:n.mutationContext}:{}});this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:{ok:!0,data:{document:S(o.data.document),historyStatus:o.data.historyStatus}}}});return}case"get-document":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:{ok:!0,data:S((await this.options.engine.getDocument(n.routeId)).data)}}});return;case"get-history-status":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getHistoryStatus()}});return;case"get-style-context":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getStyleContext(n.routeId)}});return;case"get-object-edit-context":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getObjectEditContext(n.key,n.routeId)}});return;case"get-class-name-source":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getClassNameSource(n.key,n.routeId)}});return;case"get-source-tree":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getSourceTree()}});return;case"get-source-file":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getSourceFile({path:n.path})}});return;case"resolve-source-location":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.resolveSourceLocation({key:n.key,routeId:n.routeId})}});return;case"execute":{if(!l(n.request.mutationContext))throw new Error("Invalid site edit bridge message");let o=n.request.mutationContext;await this.handleBufferedOperation(i,n.kind,()=>this.options.engine.execute(n.request,o));return}case"undo":if(!l(n.mutationContext))throw new Error("Invalid site edit bridge message");await this.handleBufferedOperation(i,n.kind,()=>this.options.engine.undo(n.mutationContext));return;case"redo":if(!l(n.mutationContext))throw new Error("Invalid site edit bridge message");await this.handleBufferedOperation(i,n.kind,()=>this.options.engine.redo(n.mutationContext));return}}catch(s){let n=h.extractRequestInfo(e);this.sendJson({type:"error",payload:{...n,message:M(s),code:B(s)}})}}async handleRawMessage(e){try{await this.handleMessage(h.parseMessage(e,{validateOperationRequest:this.options.validateOperationRequest}))}catch(s){let n=s instanceof Error?s.message:"Invalid websocket message",i=G(e);this.sendJson({type:"error",payload:{...i,message:n}})}}sendJson(e){this.options.socket.send(h.stringifyMessage(e))}async handleBufferedOperation(e,s,n){this.eventDispatcher.beginBuffering();try{let i=await n();this.sendJson({type:"response",payload:{id:e,kind:s,result:i}}),this.eventDispatcher.flush()}catch(i){this.sendJson({type:"error",payload:{id:e,kind:s,message:M(i),code:B(i)}}),this.eventDispatcher.flush()}}},R=class{constructor(e){this.send=e,this.bufferedEvents=null}push(e){let s={type:"event",payload:{event:e}};if(this.bufferedEvents){this.bufferedEvents.push(s);return}this.send(s)}beginBuffering(){this.bufferedEvents=[]}flush(){let e=this.bufferedEvents??[];this.bufferedEvents=null,e.forEach(s=>{this.send(s)})}},x=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 s=async()=>{this.queued-=1,await e()},n=this.queue.then(s,s);return this.queue=n.then(()=>{},()=>{}),n}};function c(t){return typeof t=="object"&&t!==null}function z(t){return c(t)&&typeof t.id=="string"&&typeof t.siteId=="string"&&typeof t.baseSnapshotId=="string"&&(t.routeId===void 0||typeof t.routeId=="string")&&typeof t.kind=="string"&&c(t.target)&&typeof t.documentVersion=="number"&&Number.isInteger(t.documentVersion)&&t.documentVersion>=0&&c(t.params)}function l(t){return Q(t)}function B(t){if(!t||typeof t!="object")return;let e=t.code;return typeof e=="string"&&e.length>0?e:void 0}function M(t){if(t instanceof Error)return t.message;if(!t||typeof t!="object")return"Unknown websocket operation failure";let e=t.message;return typeof e=="string"&&e.length>0?e:"Unknown websocket operation failure"}function Q(t){return c(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:!1}function G(t){try{return h.extractRequestInfo(JSON.parse(t))}catch{return{id:"unknown",kind:"get-document"}}}var y=class{constructor(){this.tail=Promise.resolve()}run(e){let s=this.tail.then(e,e);return this.tail=s.then(()=>{},()=>{}),s}};import{SiteEditOperationRequestSchema as re}from"@vcp-dev/contracts";var ae="127.0.0.1",ue=30218,ce="/__vcp/site-edit/bridge",q=6*1024*1024,de=q*2,he=3e3,O=50,H="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function De(t){let e=A(t),s=j(e),n=L(),i=n.get(s);if(i)return i;let o=new I(e);n.set(s,o);try{o.start()}catch(r){throw n.delete(s),o.stop(),r}return o}function Ke(t,e,s={}){let n=new I(A(e),t,!1,s);return n.start({listen:!1}),n}var I=class{constructor(e,s=Y.createServer((o,r)=>{r.writeHead(404),r.end("VCP site edit bridge server")}),n=!0,i={}){this.options=e,this.server=s,this.closeHttpServerOnStop=n,this.dependencies=i,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new y,this.eventListeners=new Set,this.routes=[],this.stopWatching=null,this.stopWebSocketServer=null,this.pendingChangedFiles=new Set,this.fileChangeTimer=null,this.isStarted=!1,this.runtimeVersionFloor=0,this.activeHistoryStore=null,this.versionPersistence=Promise.resolve()}start(e={}){if(this.isStarted)return;this.refreshRoutes(),this.isStarted=!0,this.createRuntime();let s=le({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 oe({projectRoot:this.options.projectRoot,watch_dirs:this.options.runtime.watch_dirs,debounce_ms:this.options.runtime.watch_debounce_ms??O,max_dirs:this.options.runtime.watch_max_dirs,max_files:this.options.runtime.watch_max_files,max_depth:this.options.runtime.watch_max_depth,max_startup_ms:this.options.runtime.watch_max_startup_ms,on_change:n=>this.handleFileChanged(n),on_batch_change:n=>this.handleFilesChanged(n)}).start(),e.listen!==!1&&this.server.listen(this.options.port,this.options.host)}getHealth(){let e=!!this.runtime;return{ok:this.isStarted&&e,bridgePath:this.options.path,runtimeReady:e,routesIndexed:this.routes.length,sessionRequired:!!this.options.sessionKey}}stop(){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.closeHttpServerOnStop&&this.server.close();let e=L(),s=j(this.options);e.get(s)===this&&e.delete(s),this.isStarted=!1}createRuntime(e,s=null){let n=this.runtime;this.activeHistoryStore=e??null;let i=(this.dependencies.createRuntime??te)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,routes:this.routes,onEvent:o=>{"version"in o&&typeof o.version=="number"&&this.recordRuntimeVersion(o.version);for(let r of this.eventListeners)r(o)},...e?{historyStore:e}:{},initialDocumentVersion:this.runtimeVersionFloor,...this.options.mutationContextVerifier?{mutationContextVerifier:this.options.mutationContextVerifier}:{}});this.runtime=i,this.activeHistorySessionId=s,n?.dispose()}createBridgeEngine(){return{initializeSession:e=>this.mutationQueue.run(async()=>{if(e.mutationContext){let r=await this.verifyMutationContext(e.mutationContext);if(!r.ok)throw new Error(`${r.code}: ${r.message??"Mutation session verification failed"}`);let d=await this.bindHistorySession(e.mutationContext.editingSessionId);if(!d.ok)throw new Error(`${d.code}: ${d.message}`)}let{runtime:s,resolvedRouteId:n}=await this.activateRoute(e.routeId),i=s.getDocument(n).data;await this.recordRuntimeVersion(i.version);let o=(await s.getHistoryStatus()).data;return{ok:!0,data:{document:i,historyStatus:o}}}),getDocument:async e=>{let{runtime:s,resolvedRouteId:n}=await this.activateRoute(e),i=s.getDocument(n);return await this.recordRuntimeVersion(i.data.version),i},getHistoryStatus:async()=>this.getRuntime().getHistoryStatus(),getStyleContext:async e=>{let{runtime:s,resolvedRouteId:n}=await this.activateRoute(e);return s.getStyleContext(n)},getObjectEditContext:async(e,s)=>this.getRuntime().getObjectEditContext(e,s?this.resolveRouteId(s):void 0),getClassNameSource:async(e,s)=>{let n=this.getRuntime(),i=s?this.resolveRouteId(s):void 0;return n.getClassNameSource(e,i)},getSourceTree:async()=>this.getRuntime().getSourceTree(),getSourceFile:async e=>this.getRuntime().getSourceFile(e),resolveSourceLocation:async e=>this.getRuntime().resolveSourceLocation({key:e.key,routeId:e.routeId?this.resolveRouteId(e.routeId):void 0}),execute:(e,s)=>this.mutationQueue.run(async()=>{let n=s??e.mutationContext,i=await this.verifyMutationContext(n);if(!i.ok)return f(i,e);let o=await this.bindHistorySession(n.editingSessionId);if(!o.ok)return f(o,e);let r=await this.getRuntime().executeOperation(this.normalizeRequest(e),n);return r.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(r.data.resultVersion),r}),undo:e=>this.mutationQueue.run(async()=>{let s=await this.verifyMutationContext(e);if(!s.ok)return f(s);let n=await this.bindHistorySession(e.editingSessionId);if(!n.ok)return f(n);let i=await this.getRuntime().undo(e);return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion),i}),redo:e=>this.mutationQueue.run(async()=>{let s=await this.verifyMutationContext(e);if(!s.ok)return f(s);let n=await this.bindHistorySession(e.editingSessionId);if(!n.ok)return f(n);let i=await this.getRuntime().redo(e);return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion),i}),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 s=this.historySessionTransition.then(async()=>{if(this.activeHistorySessionId===e)return;let n=await(this.dependencies.openHistoryStore??se)(pe({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e}));if(!this.isStarted)return;let i=n.readDocumentVersion?await n.readDocumentVersion():0;this.runtimeVersionFloor=Math.max(this.runtimeVersionFloor,i),this.createRuntime(n,e)});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(e){this.runtimeVersionFloor=Math.max(this.runtimeVersionFloor,e);let s=this.activeHistoryStore;return s?.writeDocumentVersion?(this.versionPersistence=this.versionPersistence.then(()=>s.writeDocumentVersion(this.runtimeVersionFloor)).catch(()=>{}),this.versionPersistence):Promise.resolve()}async activateRoute(e){let s=this.getRuntime(),n=this.resolveRouteId(e);return await s.setActiveRoute(n),{runtime:s,resolvedRouteId:n}}normalizeRequest(e){return e.target.kind!=="route"?e.routeId?{...e,routeId:this.resolveRouteId(e.routeId)}:e:{...e,target:{...e.target,routeId:this.resolveRouteId(e.target.routeId)}}}handleFileChanged(e){this.handleFilesChanged([e])}handleFilesChanged(e){for(let n of e)this.pendingChangedFiles.add(n);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let s=this.options.runtime.watch_debounce_ms??O;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 e=[...this.pendingChangedFiles].sort((s,n)=>s.localeCompare(n));this.pendingChangedFiles.clear(),this.refreshRoutes();for(let s of e)this.runtime?.notifyFileChanged(s)}refreshRoutes(){let e=this.options.runtime.routes?.length?this.options.runtime.routes:ie(this.options.projectRoot),s=new Map(this.routes.map(i=>[i.routeId,i.entryFile])),n=new Set(e.map(i=>i.routeId));this.routes=e;for(let i of e)s.get(i.routeId)!==i.entryFile&&this.runtime?.registerRoute(i.routeId,i.entryFile);for(let i of s.keys())n.has(i)||this.runtime?.unregisterRoute(i)}resolveRouteId(e){let s=ne(e,this.routes,this.options.basePath);if(!s)throw new Error(`Route is not indexed by the preview bridge: ${e}`);return s}getRuntime(){if(!this.runtime)throw new Error("VCP site edit preview bridge runtime is not ready");return this.runtime}async verifyMutationContext(e){if(!e)return{ok:!1,code:"MUTATION_SESSION_REQUIRED",message:"A mutation session is required for write operations."};if(!this.options.mutationContextVerifier||!this.options.tenantId)return{ok:!1,code:"LEASE_ASSERTION_INVALID",message:"Lease assertion verification is unavailable."};try{return await this.options.mutationContextVerifier.verify(e,{tenantId:this.options.tenantId,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e.editingSessionId,lockId:e.lockId,ownerId:e.ownerId})}catch{return{ok:!1,code:"LEASE_ASSERTION_INVALID",message:"Lease assertion verification failed."}}}};function A(t){return{tenantId:t.tenantId,siteId:t.siteId,snapshotId:t.snapshotId,projectRoot:C.resolve(t.projectRoot??process.cwd()),basePath:t.basePath??"",host:t.host??ae,port:t.port??ue,path:t.path??ce,sessionKey:t.sessionKey??Se(),historyRoot:t.historyRoot,mutationContextVerifier:t.mutationContextVerifier,allowedOrigins:t.allowedOrigins??[],runtime:t.runtime??{}}}function pe(t){return C.join(C.resolve(t.root),D(t.siteId),D(t.snapshotId),U("sha256").update(t.editingSessionId).digest("hex").slice(0,32))}function D(t){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(t)&&t!=="."&&t!==".."?t:U("sha256").update(t).digest("hex").slice(0,32)}function L(){let t=globalThis;return t[H]??=new Map,t[H]}function j(t){return`${t.projectRoot}:${t.host}:${t.port}:${t.path}:${t.historyRoot??""}`}function f(t,e){return{ok:!1,error:{code:t.code,message:t.message},data:{requestId:e?.id??"mutation-context",kind:e?.kind??"update-text",target:e?.target??{kind:"node",key:""},resultVersion:e?.documentVersion??0}}}function le(t){let e=new ee({maxPayload:q,noServer:!0}),s=new WeakSet,n=o=>{new m({socket:new b(o),engine:t.engine,validateOperationRequest:r=>re.safeParse(r).success})},i=(o,r,d)=>{if(!fe(o,t))return;let p=ge(o,t);if(!p){r.write(`HTTP/1.1 401 Unauthorized\r
1
+ import X from"node:http";import{createHash as U,randomBytes as Z,timingSafeEqual as ee}from"node:crypto";import C from"node:path";import{WebSocket as P,WebSocketServer as te}from"ws";import{createPreviewSiteEditEngineRuntime as se,openFileHistoryStore as ne}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as ie,scanNextAppRouterRoutes as oe}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as re}from"@vcp-dev/site-edit-engine/source-watcher";function I(t){let e=new Map,s=[],n=new Map,i=[],o=t.pageComposition.status==="supported"?t.pageComposition.placementUnitKeys:[],r=new Map(o.map((a,u)=>[a,u])),p=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},h=t.entries.flatMap((a,u)=>a.dynamicDataSummary?[[u,a.dynamicDataSummary]]:[]),y=a=>{let u=e.get(a);if(typeof u=="number")return u;let l=s.length;return s.push(a),e.set(a,l),l},W=a=>{let u=null;for(let l of N(a)){let v=`${u??"root"}\0${l}`,_=n.get(v);if(typeof _=="number"){u=_;continue}let T=i.length;i.push([u,y(l)]),n.set(v,T),u=T}if(u===null)throw new Error("Document key must not be empty");return u};return{encoding:"segment-trie-v1",siteId:t.siteId,snapshotId:t.snapshotId,routeId:t.routeId,version:t.version,pageComposition:p,tokens:s,nodes:i,entries:t.entries.map(a=>{let u=a.pageCompositionUnitKey?r.get(a.pageCompositionUnitKey)??-1:-1;if(a.pageCompositionUnitKey&&u<0)throw new Error(`Render entry ${a.key} references unknown page composition unit ${a.pageCompositionUnitKey}`);return[W(a.key),z(a.operationSummary),...u>=0?[u]:[]]}),...h.length>0?{dynamicDataSummaries:h}:{}}}function z(t){return(t.canUpdateText?1:0)|(t.canInsertChild?2:0)|(t.canMove?4:0)|(t.canRemove?8:0)}function N(t){if(!t)return[];let e=[],s=0,n=B(t,s);if(n<0)return e.push(t),e;for(e.push(t.slice(0,n)),s=n;s<t.length;){if(n=B(t,s+(t.startsWith("::",s)?2:1)),n<0){e.push(t.slice(s));break}e.push(t.slice(s,n)),s=n}return e}function B(t,e){let s=t.indexOf("/",e),n=t.indexOf("::",e);return s<0?n:n<0?s:Math.min(s,n)}var d=class t{constructor(){}static parseMessage(e,s={}){let n=JSON.parse(e);if(!t.isMessage(n,s))throw new Error("Invalid site edit bridge message");return n}static stringifyMessage(e){return JSON.stringify(e)}static extractRequestInfo(e){if(typeof e=="object"&&e!==null&&"payload"in e&&typeof e.payload=="object"&&e.payload!==null){let s=e.payload,n=typeof s.id=="string"?s.id:"unknown",i=t.isRequestKind(s.kind)?s.kind:"get-document";return{id:n,kind:i}}return{id:"unknown",kind:"get-document"}}static isRequestKind(e){return e==="initialize-session"||e==="get-document"||e==="get-history-status"||e==="get-style-context"||e==="get-object-edit-context"||e==="get-class-name-source"||e==="get-source-tree"||e==="get-source-file"||e==="resolve-source-location"||e==="execute"||e==="undo"||e==="redo"}static isMessage(e,s={}){return!c(e)||typeof e.type!="string"?!1:e.type==="request"?t.isRequestPayload(e.payload,s):e.type==="response"||e.type==="error"?c(e.payload)&&typeof e.payload.id=="string":e.type==="event"?c(e.payload)&&c(e.payload.event):e.type==="auth"?c(e.payload)&&typeof e.payload.key=="string":e.type==="auth-ok"?c(e.payload)&&e.payload.ok===!0:!1}static isRequestPayload(e,s={}){if(!c(e)||typeof e.id!="string"||!t.isRequestKind(e.kind))return!1;switch(e.kind){case"initialize-session":return typeof e.routeId=="string"&&(e.mutationContext===void 0||R(e.mutationContext));case"get-document":case"get-style-context":return typeof e.routeId=="string";case"get-object-edit-context":case"get-class-name-source":case"resolve-source-location":return typeof e.key=="string"&&(e.routeId===void 0||typeof e.routeId=="string");case"get-history-status":return e.mutationContext===void 0;case"get-source-tree":return!0;case"get-source-file":return typeof e.path=="string";case"execute":return Q(e.request)&&(e.request.mutationContext===void 0||R(e.request.mutationContext))&&(s.validateOperationRequest?.(e.request)??!0);case"undo":case"redo":return e.mutationContext===void 0||R(e.mutationContext)}}},g=class{constructor(e){this.options=e,this.messageQueue=new w,this.eventDispatcher=new S(s=>{this.sendJson(s)}),this.unsubscribe=e.engine.subscribe(s=>{this.eventDispatcher.push(s)}),e.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"}}),e.socket.close(1013,"bridge busy")}},e.socket.onclose=()=>{this.unsubscribe()}}async handleMessage(e){try{let s=e;if(s.type!=="request")return;let n=s.payload,{id:i}=n;switch(n.kind){case"initialize-session":{let o=await this.options.engine.initializeSession({routeId:n.routeId,...n.mutationContext?{mutationContext:n.mutationContext}:{}});this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:{ok:!0,data:{document:I(o.data.document),historyStatus:o.data.historyStatus}}}});return}case"get-document":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:{ok:!0,data:I((await this.options.engine.getDocument(n.routeId)).data)}}});return;case"get-history-status":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getHistoryStatus()}});return;case"get-style-context":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getStyleContext(n.routeId)}});return;case"get-object-edit-context":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getObjectEditContext(n.key,n.routeId)}});return;case"get-class-name-source":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getClassNameSource(n.key,n.routeId)}});return;case"get-source-tree":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getSourceTree()}});return;case"get-source-file":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.getSourceFile({path:n.path})}});return;case"resolve-source-location":this.sendJson({type:"response",payload:{id:i,kind:n.kind,result:await this.options.engine.resolveSourceLocation({key:n.key,routeId:n.routeId})}});return;case"execute":{await this.handleBufferedOperation(i,n.kind,()=>this.options.engine.execute(n.request));return}case"undo":await this.handleBufferedOperation(i,n.kind,()=>this.options.engine.undo());return;case"redo":await this.handleBufferedOperation(i,n.kind,()=>this.options.engine.redo());return}}catch(s){let n=d.extractRequestInfo(e);this.sendJson({type:"error",payload:{...n,message:H(s),code:D(s)}})}}async handleRawMessage(e){try{await this.handleMessage(d.parseMessage(e,{validateOperationRequest:this.options.validateOperationRequest}))}catch(s){let n=s instanceof Error?s.message:"Invalid websocket message",i=Y(e);this.sendJson({type:"error",payload:{...i,message:n}})}}sendJson(e){this.options.socket.send(d.stringifyMessage(e))}async handleBufferedOperation(e,s,n){this.eventDispatcher.beginBuffering();try{let i=await n();this.sendJson({type:"response",payload:{id:e,kind:s,result:i}}),this.eventDispatcher.flush()}catch(i){this.sendJson({type:"error",payload:{id:e,kind:s,message:H(i),code:D(i)}}),this.eventDispatcher.flush()}}},S=class{constructor(e){this.send=e,this.bufferedEvents=null}push(e){let s={type:"event",payload:{event:e}};if(this.bufferedEvents){this.bufferedEvents.push(s);return}this.send(s)}beginBuffering(){this.bufferedEvents=[]}flush(){let e=this.bufferedEvents??[];this.bufferedEvents=null,e.forEach(s=>{this.send(s)})}},w=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 s=async()=>{this.queued-=1,await e()},n=this.queue.then(s,s);return this.queue=n.then(()=>{},()=>{}),n}};function c(t){return typeof t=="object"&&t!==null}function Q(t){return c(t)&&typeof t.id=="string"&&typeof t.siteId=="string"&&typeof t.baseSnapshotId=="string"&&(t.routeId===void 0||typeof t.routeId=="string")&&typeof t.kind=="string"&&c(t.target)&&typeof t.documentVersion=="number"&&Number.isInteger(t.documentVersion)&&t.documentVersion>=0&&c(t.params)}function R(t){return G(t)}function D(t){if(!t||typeof t!="object")return;let e=t.code;return typeof e=="string"&&e.length>0?e:void 0}function H(t){if(t instanceof Error)return t.message;if(!t||typeof t!="object")return"Unknown websocket operation failure";let e=t.message;return typeof e=="string"&&e.length>0?e:"Unknown websocket operation failure"}function G(t){return c(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:!1}function Y(t){try{return d.extractRequestInfo(JSON.parse(t))}catch{return{id:"unknown",kind:"get-document"}}}var f=class{constructor(){this.tail=Promise.resolve()}run(e){let s=this.tail.then(e,e);return this.tail=s.then(()=>{},()=>{}),s}};import{SiteEditOperationRequestSchema as ae}from"@vcp-dev/contracts";var ue="127.0.0.1",ce=30218,de="/__vcp/site-edit/bridge",q=6*1024*1024,he=q*2,pe=3e3,M=50,O="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function Ke(t){let e=j(t),s=J(e),n=A(),i=n.get(s);if(i)return i;let o=new m(e);n.set(s,o);try{o.start()}catch(r){throw n.delete(s),o.stop(),r}return o}function Fe(t,e,s={}){let n=new m(j(e),t,!1,s);return n.start({listen:!1}),n}var m=class{constructor(e,s=X.createServer((o,r)=>{r.writeHead(404),r.end("VCP site edit bridge server")}),n=!0,i={}){this.options=e,this.server=s,this.closeHttpServerOnStop=n,this.dependencies=i,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new f,this.eventListeners=new Set,this.routes=[],this.stopWatching=null,this.stopWebSocketServer=null,this.pendingChangedFiles=new Set,this.fileChangeTimer=null,this.isStarted=!1,this.runtimeVersionFloor=0,this.activeHistoryStore=null,this.versionPersistence=Promise.resolve()}start(e={}){if(this.isStarted)return;this.refreshRoutes(),this.isStarted=!0,this.createRuntime();let s=ge({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 re({projectRoot:this.options.projectRoot,watch_dirs:this.options.runtime.watch_dirs,debounce_ms:this.options.runtime.watch_debounce_ms??M,max_dirs:this.options.runtime.watch_max_dirs,max_files:this.options.runtime.watch_max_files,max_depth:this.options.runtime.watch_max_depth,max_startup_ms:this.options.runtime.watch_max_startup_ms,on_change:n=>this.handleFileChanged(n),on_batch_change:n=>this.handleFilesChanged(n)}).start(),e.listen!==!1&&this.server.listen(this.options.port,this.options.host)}getHealth(){let e=!!this.runtime;return{ok:this.isStarted&&e,bridgePath:this.options.path,runtimeReady:e,routesIndexed:this.routes.length,sessionRequired:!!this.options.sessionKey}}stop(){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.closeHttpServerOnStop&&this.server.close();let e=A(),s=J(this.options);e.get(s)===this&&e.delete(s),this.isStarted=!1}createRuntime(e,s=null){let n=this.runtime;this.activeHistoryStore=e??null;let i=(this.dependencies.createRuntime??se)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,routes:this.routes,onEvent:o=>{"version"in o&&typeof o.version=="number"&&this.recordRuntimeVersion(o.version);for(let r of this.eventListeners)r(o)},...e?{historyStore:e}:{},initialDocumentVersion:this.runtimeVersionFloor});this.runtime=i,this.activeHistorySessionId=s,n?.dispose()}createBridgeEngine(){return{initializeSession:e=>this.mutationQueue.run(async()=>{let s=await this.bindHistorySession(this.options.snapshotId);if(!s.ok)throw new Error(`${s.code}: ${s.message}`);let{runtime:n,resolvedRouteId:i}=await this.activateRoute(e.routeId),o=n.getDocument(i).data;await this.recordRuntimeVersion(o.version);let r=(await n.getHistoryStatus()).data;return{ok:!0,data:{document:o,historyStatus:r}}}),getDocument:async e=>{let{runtime:s,resolvedRouteId:n}=await this.activateRoute(e),i=s.getDocument(n);return await this.recordRuntimeVersion(i.data.version),i},getHistoryStatus:async()=>this.getRuntime().getHistoryStatus(),getStyleContext:async e=>{let{runtime:s,resolvedRouteId:n}=await this.activateRoute(e);return s.getStyleContext(n)},getObjectEditContext:async(e,s)=>this.getRuntime().getObjectEditContext(e,s?this.resolveRouteId(s):void 0),getClassNameSource:async(e,s)=>{let n=this.getRuntime(),i=s?this.resolveRouteId(s):void 0;return n.getClassNameSource(e,i)},getSourceTree:async()=>this.getRuntime().getSourceTree(),getSourceFile:async e=>this.getRuntime().getSourceFile(e),resolveSourceLocation:async e=>this.getRuntime().resolveSourceLocation({key:e.key,routeId:e.routeId?this.resolveRouteId(e.routeId):void 0}),execute:(e,s)=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return k(n,e);let i=await this.getRuntime().executeOperation(this.normalizeRequest(e));return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion),i}),undo:()=>this.mutationQueue.run(async()=>{let e=await this.bindHistorySession(this.options.snapshotId);if(!e.ok)return k(e);let s=await this.getRuntime().undo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion),s}),redo:()=>this.mutationQueue.run(async()=>{let e=await this.bindHistorySession(this.options.snapshotId);if(!e.ok)return k(e);let s=await this.getRuntime().redo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion),s}),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 s=this.historySessionTransition.then(async()=>{if(this.activeHistorySessionId===e)return;let n=await(this.dependencies.openHistoryStore??ne)(le({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e}));if(!this.isStarted)return;let i=n.readDocumentVersion?await n.readDocumentVersion():0;this.runtimeVersionFloor=Math.max(this.runtimeVersionFloor,i),this.createRuntime(n,e)});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(e){this.runtimeVersionFloor=Math.max(this.runtimeVersionFloor,e);let s=this.activeHistoryStore;return s?.writeDocumentVersion?(this.versionPersistence=this.versionPersistence.then(()=>s.writeDocumentVersion(this.runtimeVersionFloor)).catch(()=>{}),this.versionPersistence):Promise.resolve()}async activateRoute(e){let s=this.getRuntime(),n=this.resolveRouteId(e);return await s.setActiveRoute(n),{runtime:s,resolvedRouteId:n}}normalizeRequest(e){return e.target.kind!=="route"?e.routeId?{...e,routeId:this.resolveRouteId(e.routeId)}:e:{...e,target:{...e.target,routeId:this.resolveRouteId(e.target.routeId)}}}handleFileChanged(e){this.handleFilesChanged([e])}handleFilesChanged(e){for(let n of e)this.pendingChangedFiles.add(n);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let s=this.options.runtime.watch_debounce_ms??M;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 e=[...this.pendingChangedFiles].sort((s,n)=>s.localeCompare(n));this.pendingChangedFiles.clear(),this.refreshRoutes();for(let s of e)this.runtime?.notifyFileChanged(s)}refreshRoutes(){let e=this.options.runtime.routes?.length?this.options.runtime.routes:oe(this.options.projectRoot),s=new Map(this.routes.map(i=>[i.routeId,i.entryFile])),n=new Set(e.map(i=>i.routeId));this.routes=e;for(let i of e)s.get(i.routeId)!==i.entryFile&&this.runtime?.registerRoute(i.routeId,i.entryFile);for(let i of s.keys())n.has(i)||this.runtime?.unregisterRoute(i)}resolveRouteId(e){let s=ie(e,this.routes,this.options.basePath);if(!s)throw new Error(`Route is not indexed by the preview bridge: ${e}`);return s}getRuntime(){if(!this.runtime)throw new Error("VCP site edit preview bridge runtime is not ready");return this.runtime}};function j(t){return{tenantId:t.tenantId,siteId:t.siteId,snapshotId:t.snapshotId,projectRoot:C.resolve(t.projectRoot??process.cwd()),basePath:t.basePath??"",host:t.host??ue,port:t.port??ce,path:t.path??de,sessionKey:t.sessionKey??we(),historyRoot:t.historyRoot,allowedOrigins:t.allowedOrigins??[],runtime:t.runtime??{}}}function le(t){return C.join(C.resolve(t.root),V(t.siteId),V(t.snapshotId),U("sha256").update(t.editingSessionId).digest("hex").slice(0,32))}function V(t){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(t)&&t!=="."&&t!==".."?t:U("sha256").update(t).digest("hex").slice(0,32)}function A(){let t=globalThis;return t[O]??=new Map,t[O]}function J(t){return`${t.projectRoot}:${t.host}:${t.port}:${t.path}:${t.historyRoot??""}`}function k(t,e){return{ok:!1,error:{code:t.code,message:t.message},data:{requestId:e?.id??"mutation-context",kind:e?.kind??"update-text",target:e?.target??{kind:"node",key:""},resultVersion:e?.documentVersion??0}}}function ge(t){let e=new te({maxPayload:q,noServer:!0}),s=new WeakSet,n=o=>{new g({socket:new b(o),engine:t.engine,validateOperationRequest:r=>ae.safeParse(r).success})},i=(o,r,p)=>{if(!fe(o,t))return;let h=me(o,t);if(!h){r.write(`HTTP/1.1 401 Unauthorized\r
2
2
  Connection: close\r
3
3
  \r
4
- `),r.destroy();return}p.authenticatedByHeader&&s.add(o),e.handleUpgrade(o,r,d,w=>{e.emit("connection",w,o)})};return t.server.on("upgrade",i),e.on("connection",(o,r)=>{if(s.has(r)){n(o);return}me(o,t.sessionKey,()=>{n(o)})}),e.stop=()=>{t.server.off("upgrade",i);for(let o of e.clients)o.close(1001,"bridge stopped");e.close()},e}var b=class{constructor(e){this.socket=e,this.onmessage=null,this.onclose=null,e.on("error",()=>{}),e.on("message",s=>{this.onmessage?.({data:s.toString()})}),e.on("close",()=>{this.onclose?.()})}send(e){if(this.socket.readyState===P.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(e,"utf8")>de){this.socket.close(1013,"bridge backpressure");return}this.socket.send(e)}}close(e,s){this.socket.close(e,s)}};function fe(t,e){return new URL(t.url??"/","http://localhost").pathname===e.path}function ge(t,e){if(new URL(t.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let n=k(t.headers["x-vcp-site-edit-key"]),i=N(n,e.sessionKey),o=k(t.headers.host),r=k(t.headers.origin);return o&&!ye(o,e.host,r)?null:r?we(r,o,e.allowedOrigins)?{authenticatedByHeader:i}:null:i?{authenticatedByHeader:!0}:null}function me(t,e,s){let n=setTimeout(()=>{t.close(1008,"bridge authentication timeout")},he),i,o=()=>{clearTimeout(n),t.off("message",i),t.off("close",o)},r=()=>{o(),t.close(1008,"bridge authentication failed")};i=d=>{try{let p=h.parseMessage(d.toString());if(p.type!=="auth"||!N(p.payload.key,e)){r();return}o(),t.readyState===P.OPEN&&t.send(h.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),s()}catch{r()}},t.on("message",i),t.on("close",o)}function N(t,e){if(!t)return!1;let s=Buffer.from(t),n=Buffer.from(e);return s.length===n.length&&Z(s,n)}function ye(t,e,s){let n=F(t),i=F(e);return n===i||K(i)&&K(n)&&(!s||v(s,t))||Ie(i)&&(!s||v(s,t))}function Ie(t){return t==="0.0.0.0"||t==="::"}function K(t){return t==="localhost"||t==="127.0.0.1"||t==="::1"}function we(t,e,s){return s.includes(t)||(e?v(t,e):!1)}function v(t,e){try{return new URL(t).host===$(e)}catch{return!1}}function F(t){let e=$(t);return e.startsWith("[")?e.slice(1,e.indexOf("]")):e.split(":").length-1===1?e.split(":",1)[0]??"":e}function $(t){return t.trim().toLowerCase()}function k(t){return Array.isArray(t)?t[0]:t}function Se(){return X(32).toString("base64url")}export{de as SITE_EDIT_BRIDGE_MAX_BUFFERED_BYTES,q as SITE_EDIT_BRIDGE_MAX_PAYLOAD_BYTES,I as SiteEditBridgeServer,Ke as attachSiteEditBridgeServer,le as attachSiteEditWebSocketServer,De as ensureSiteEditBridgeServerStarted};
4
+ `),r.destroy();return}h.authenticatedByHeader&&s.add(o),e.handleUpgrade(o,r,p,y=>{e.emit("connection",y,o)})};return t.server.on("upgrade",i),e.on("connection",(o,r)=>{if(s.has(r)){n(o);return}ye(o,t.sessionKey,()=>{n(o)})}),e.stop=()=>{t.server.off("upgrade",i);for(let o of e.clients)o.close(1001,"bridge stopped");e.close()},e}var b=class{constructor(e){this.socket=e,this.onmessage=null,this.onclose=null,e.on("error",()=>{}),e.on("message",s=>{this.onmessage?.({data:s.toString()})}),e.on("close",()=>{this.onclose?.()})}send(e){if(this.socket.readyState===P.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(e,"utf8")>he){this.socket.close(1013,"bridge backpressure");return}this.socket.send(e)}}close(e,s){this.socket.close(e,s)}};function fe(t,e){return new URL(t.url??"/","http://localhost").pathname===e.path}function me(t,e){if(new URL(t.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let n=x(t.headers["x-vcp-site-edit-key"]),i=L(n,e.sessionKey),o=x(t.headers.host),r=x(t.headers.origin);return o&&!Ie(o,e.host,r)?null:r?Se(r,o,e.allowedOrigins)?{authenticatedByHeader:i}:null:i?{authenticatedByHeader:!0}:null}function ye(t,e,s){let n=setTimeout(()=>{t.close(1008,"bridge authentication timeout")},pe),i,o=()=>{clearTimeout(n),t.off("message",i),t.off("close",o)},r=()=>{o(),t.close(1008,"bridge authentication failed")};i=p=>{try{let h=d.parseMessage(p.toString());if(h.type!=="auth"||!L(h.payload.key,e)){r();return}o(),t.readyState===P.OPEN&&t.send(d.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),s()}catch{r()}},t.on("message",i),t.on("close",o)}function L(t,e){if(!t)return!1;let s=Buffer.from(t),n=Buffer.from(e);return s.length===n.length&&ee(s,n)}function Ie(t,e,s){let n=F(t),i=F(e);return n===i||K(i)&&K(n)&&(!s||E(s,t))||Re(i)&&(!s||E(s,t))}function Re(t){return t==="0.0.0.0"||t==="::"}function K(t){return t==="localhost"||t==="127.0.0.1"||t==="::1"}function Se(t,e,s){return s.includes(t)||(e?E(t,e):!1)}function E(t,e){try{return new URL(t).host===$(e)}catch{return!1}}function F(t){let e=$(t);return e.startsWith("[")?e.slice(1,e.indexOf("]")):e.split(":").length-1===1?e.split(":",1)[0]??"":e}function $(t){return t.trim().toLowerCase()}function x(t){return Array.isArray(t)?t[0]:t}function we(){return Z(32).toString("base64url")}export{he as SITE_EDIT_BRIDGE_MAX_BUFFERED_BYTES,q as SITE_EDIT_BRIDGE_MAX_PAYLOAD_BYTES,m as SiteEditBridgeServer,Fe as attachSiteEditBridgeServer,ge as attachSiteEditWebSocketServer,Ke as ensureSiteEditBridgeServerStarted};
@@ -10,7 +10,6 @@ const preview = await startSiteEditNextDevServer({
10
10
  projectRoot,
11
11
  siteId: process.env.VCP_SITE_ID ?? "site_preview",
12
12
  snapshotId: process.env.VCP_SNAPSHOT_ID,
13
- requireMutationContextVerifier: true,
14
13
  });
15
14
  console.log(JSON.stringify({
16
15
  status: "ready",
@@ -1,4 +1,4 @@
1
- import type { SiteEditCommandSuccess, SiteEditClassNameSourceStyleContext, SiteEditEvent, SiteEditObjectEditContext, SiteEditGetClassNameSourceCommandResult, SiteEditMutationContext, SiteEditOperationCommandResult, SiteEditOperationRequest, SiteEditRenderDocument, SiteEditSourceFileResponse, SiteEditSourceLocation, SiteEditSourceTreeResponse } from "@vcp-dev/contracts";
1
+ import type { SiteEditCommandSuccess, SiteEditClassNameSourceStyleContext, SiteEditEvent, SiteEditObjectEditContext, SiteEditGetClassNameSourceCommandResult, SiteEditOperationCommandResult, SiteEditOperationRequest, SiteEditRenderDocument, SiteEditSourceFileResponse, SiteEditSourceLocation, SiteEditSourceTreeResponse } from "@vcp-dev/contracts";
2
2
  import type { SiteEditHistoryStatus } from "./protocol.js";
3
3
  export declare const VCP_WINDOW_MESSAGE_SOURCE = "vcp-window-message";
4
4
  export declare const VCP_WINDOW_MESSAGE_VERSION = 1;
@@ -214,7 +214,6 @@ export type SiteEditFrameRuntimeRequestBody = {
214
214
  kind: "bridge-initialize-session";
215
215
  payload: {
216
216
  routeId: string;
217
- mutationContext?: SiteEditMutationContext;
218
217
  };
219
218
  } | {
220
219
  type: "request";
@@ -251,15 +250,12 @@ export type SiteEditFrameRuntimeRequestBody = {
251
250
  kind: "bridge-execute";
252
251
  payload: {
253
252
  request: SiteEditOperationRequest;
254
- mutationContext: SiteEditMutationContext;
255
253
  };
256
254
  } | {
257
255
  type: "request";
258
256
  id: string;
259
257
  kind: "bridge-undo" | "bridge-redo";
260
- payload: {
261
- mutationContext: SiteEditMutationContext;
262
- };
258
+ payload: Record<string, never>;
263
259
  } | {
264
260
  type: "request";
265
261
  id: string;
@@ -268,11 +264,13 @@ export type SiteEditFrameRuntimeRequestBody = {
268
264
  subscriptionId: string;
269
265
  };
270
266
  };
271
- export type SiteEditFrameRuntimeSpacing = {
267
+ export type SiteEditFrameRuntimeMargin = {
272
268
  marginTop: number;
273
269
  marginRight: number;
274
270
  marginBottom: number;
275
271
  marginLeft: number;
272
+ };
273
+ export type SiteEditFrameRuntimeSpacing = SiteEditFrameRuntimeMargin & {
276
274
  paddingTop: number;
277
275
  paddingRight: number;
278
276
  paddingBottom: number;
@@ -290,6 +288,10 @@ export type SiteEditFrameRuntimeRect = {
290
288
  width: number;
291
289
  height: number;
292
290
  };
291
+ export type SiteEditFrameRuntimeChildBox = {
292
+ rect: SiteEditFrameRuntimeRect;
293
+ margin: SiteEditFrameRuntimeMargin;
294
+ };
293
295
  export type SiteEditFrameRuntimeHit = {
294
296
  ref: SiteEditFrameRuntimeRef;
295
297
  key: string;
@@ -317,6 +319,7 @@ export type SiteEditFrameRuntimeHit = {
317
319
  indicatorOrientation: "horizontal" | "vertical";
318
320
  };
319
321
  rect: SiteEditFrameRuntimeRect;
322
+ childBoxes?: SiteEditFrameRuntimeChildBox[];
320
323
  childRects?: SiteEditFrameRuntimeRect[];
321
324
  spacing?: SiteEditFrameRuntimeSpacing;
322
325
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vcp-dev/site-edit-bridge",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -45,11 +45,6 @@
45
45
  "types": "./dist/designer-trace.d.ts",
46
46
  "default": "./dist/designer-trace.js"
47
47
  },
48
- "./dev-demo-auth-fixture": {
49
- "development": "./src/dev-demo-auth-fixture.ts",
50
- "types": "./dist/dev-demo-auth-fixture.d.ts",
51
- "default": "./dist/dev-demo-auth-fixture.js"
52
- },
53
48
  "./window-message": {
54
49
  "development": "./src/window-message.ts",
55
50
  "types": "./dist/window-message.d.ts",
@@ -79,8 +74,8 @@
79
74
  "typecheck": "tsc -p tsconfig.json --noEmit"
80
75
  },
81
76
  "dependencies": {
82
- "@vcp-dev/contracts": "0.6.0",
83
- "@vcp-dev/site-edit-engine": "0.6.0",
77
+ "@vcp-dev/contracts": "0.6.2",
78
+ "@vcp-dev/site-edit-engine": "0.6.2",
84
79
  "@types/ws": "^8.18.1",
85
80
  "ws": "^8.20.1"
86
81
  },
@@ -1,36 +0,0 @@
1
- /**
2
- * Deterministic local demo identity for development servers and E2E only.
3
- * Production entrypoints must not import this fixture or treat it as a token.
4
- */
5
- import type { SiteEditMutationContextVerifier } from "@vcp-dev/site-edit-engine";
6
- export declare const DEV_DEMO_EDIT_AUTH_FIXTURE: {
7
- readonly tenantId: "tenant_site_preview_demo";
8
- readonly siteId: "site_f6a2_demo";
9
- readonly snapshotId: "snapshot_site_f6a2_demo";
10
- readonly context: {
11
- readonly editingSessionId: "dev-demo-designer-session";
12
- readonly lockId: "dev-demo-lock";
13
- readonly ownerId: "dev-demo-owner";
14
- readonly leaseAssertion: "dev-demo-lease-assertion";
15
- };
16
- };
17
- type DevDemoMutationExpected = {
18
- tenantId: string;
19
- siteId: string;
20
- snapshotId: string;
21
- editingSessionId: string;
22
- lockId: string;
23
- ownerId: string;
24
- };
25
- type DevDemoMutationContext = {
26
- editingSessionId: string;
27
- lockId: string;
28
- ownerId: string;
29
- };
30
- export declare function matchesDevDemoEditAuthFixture(expected: DevDemoMutationExpected, context: DevDemoMutationContext): boolean;
31
- /**
32
- * Creates the deliberately narrow verifier used by the local preview demo.
33
- * It only accepts the complete deterministic fixture scope and assertion.
34
- */
35
- export declare function createDevDemoMutationContextVerifier(): SiteEditMutationContextVerifier;
36
- export {};
@@ -1 +0,0 @@
1
- var n={tenantId:"tenant_site_preview_demo",siteId:"site_f6a2_demo",snapshotId:"snapshot_site_f6a2_demo",context:{editingSessionId:"dev-demo-designer-session",lockId:"dev-demo-lock",ownerId:"dev-demo-owner",leaseAssertion:"dev-demo-lease-assertion"}};function t(e,s){let o=n;return e.tenantId===o.tenantId&&e.siteId===o.siteId&&e.snapshotId===o.snapshotId&&e.editingSessionId===o.context.editingSessionId&&e.lockId===o.context.lockId&&e.ownerId===o.context.ownerId&&s.editingSessionId===e.editingSessionId&&s.lockId===e.lockId&&s.ownerId===e.ownerId}function i(){return{async verify(e,s){return t(s,e)?e.leaseAssertion===n.context.leaseAssertion?{ok:!0}:{ok:!1,code:"LEASE_ASSERTION_INVALID",message:"The dev demo assertion is invalid."}:{ok:!1,code:"LEASE_ASSERTION_SCOPE_MISMATCH",message:"The dev demo assertion scope does not match."}}}}export{n as DEV_DEMO_EDIT_AUTH_FIXTURE,i as createDevDemoMutationContextVerifier,t as matchesDevDemoEditAuthFixture};