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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- import 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
1
+ import st from"node:http";import{existsSync as w,readFileSync as S}from"node:fs";import{createRequire as pe}from"node:module";import R from"node:path";import{basename as ge,dirname as x,join as b}from"node:path";import{fileURLToPath as me}from"node:url";import Te from"node:http";import{createHash as ie,randomBytes as He,timingSafeEqual as Be}from"node:crypto";import L from"node:path";import{WebSocket as oe,WebSocketServer as je}from"ws";import{createPreviewSiteEditEngineRuntime as Ae,openFileHistoryStore as De}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as Me,scanNextAppRouterRoutes as Ue}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as Ne}from"@vcp-dev/site-edit-engine/source-watcher";function U(t){let e=new Map,n=[],s=new Map,r=[],i=t.pageComposition.status==="supported"?t.pageComposition.placementUnitKeys:[],o=new Map(i.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 l=n.length;return n.push(a),e.set(a,l),l},g=a=>{let u=null;for(let l of Ce(a)){let k=`${u??"root"}\0${l}`,I=s.get(k);if(typeof I=="number"){u=I;continue}let m=r.length;r.push([u,h(l)]),s.set(k,m),u=m}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:r,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[g(a.key),ve(a.operationSummary),...u>=0?[u]:[]]}),...d.length>0?{dynamicDataSummaries:d}:{}}}function ve(t){return(t.canUpdateText?1:0)|(t.canInsertChild?2:0)|(t.canMove?4:0)|(t.canRemove?8:0)}function Ce(t){if(!t)return[];let e=[],n=0,s=Y(t,n);if(s<0)return e.push(t),e;for(e.push(t.slice(0,s)),n=s;n<t.length;){if(s=Y(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 Y(t,e){let n=t.indexOf("/",e),s=t.indexOf("::",e);return n<0?s:s<0?n:Math.min(n,s)}var p=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",r=t.isRequestKind(n.kind)?n.kind:"get-document";return{id:s,kind:r}}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!f(e)||typeof e.type!="string"?!1:e.type==="request"?t.isRequestPayload(e.payload,n):e.type==="response"||e.type==="error"?f(e.payload)&&typeof e.payload.id=="string":e.type==="event"?f(e.payload)&&f(e.payload.event):e.type==="auth"?f(e.payload)&&typeof e.payload.key=="string":e.type==="auth-ok"?f(e.payload)&&e.payload.ok===!0:!1}static isRequestPayload(e,n={}){if(!f(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||N(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 Ee(e.request)&&(e.request.mutationContext===void 0||N(e.request.mutationContext))&&(n.validateOperationRequest?.(e.request)??!0);case"undo":case"redo":return e.mutationContext===void 0||N(e.mutationContext)}}},P=class{constructor(e){this.options=e,this.messageQueue=new V,this.eventDispatcher=new O(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:r}=s;switch(s.kind){case"initialize-session":{let i=await this.options.engine.initializeSession({routeId:s.routeId,...s.mutationContext?{mutationContext:s.mutationContext}:{}});this.sendJson({type:"response",payload:{id:r,kind:s.kind,result:{ok:!0,data:{document:U(i.data.document),historyStatus:i.data.historyStatus}}}});return}case"get-document":this.sendJson({type:"response",payload:{id:r,kind:s.kind,result:{ok:!0,data:U((await this.options.engine.getDocument(s.routeId)).data)}}});return;case"get-history-status":this.sendJson({type:"response",payload:{id:r,kind:s.kind,result:await this.options.engine.getHistoryStatus()}});return;case"get-style-context":this.sendJson({type:"response",payload:{id:r,kind:s.kind,result:await this.options.engine.getStyleContext(s.routeId)}});return;case"get-object-edit-context":this.sendJson({type:"response",payload:{id:r,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:r,kind:s.kind,result:await this.options.engine.getClassNameSource(s.key,s.routeId)}});return;case"get-source-tree":this.sendJson({type:"response",payload:{id:r,kind:s.kind,result:await this.options.engine.getSourceTree()}});return;case"get-source-file":this.sendJson({type:"response",payload:{id:r,kind:s.kind,result:await this.options.engine.getSourceFile({path:s.path})}});return;case"resolve-source-location":this.sendJson({type:"response",payload:{id:r,kind:s.kind,result:await this.options.engine.resolveSourceLocation({key:s.key,routeId:s.routeId})}});return;case"execute":{await this.handleBufferedOperation(r,s.kind,()=>this.options.engine.execute(s.request));return}case"undo":await this.handleBufferedOperation(r,s.kind,()=>this.options.engine.undo());return;case"redo":await this.handleBufferedOperation(r,s.kind,()=>this.options.engine.redo());return}}catch(n){let s=p.extractRequestInfo(e);this.sendJson({type:"error",payload:{...s,message:X(n),code:Z(n)}})}}async handleRawMessage(e){try{await this.handleMessage(p.parseMessage(e,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let s=n instanceof Error?n.message:"Invalid websocket message",r=Pe(e);this.sendJson({type:"error",payload:{...r,message:s}})}}sendJson(e){this.options.socket.send(p.stringifyMessage(e))}async handleBufferedOperation(e,n,s){this.eventDispatcher.beginBuffering();try{let r=await s();this.sendJson({type:"response",payload:{id:e,kind:n,result:r}}),this.eventDispatcher.flush()}catch(r){this.sendJson({type:"error",payload:{id:e,kind:n,message:X(r),code:Z(r)}}),this.eventDispatcher.flush()}}},O=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)})}},V=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 f(t){return typeof t=="object"&&t!==null}function Ee(t){return f(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"&&f(t.target)&&typeof t.documentVersion=="number"&&Number.isInteger(t.documentVersion)&&t.documentVersion>=0&&f(t.params)}function N(t){return _e(t)}function Z(t){if(!t||typeof t!="object")return;let e=t.code;return typeof e=="string"&&e.length>0?e:void 0}function X(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 _e(t){return f(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 Pe(t){try{return p.extractRequestInfo(JSON.parse(t))}catch{return{id:"unknown",kind:"get-document"}}}var T=class{constructor(){this.tail=Promise.resolve()}run(e){let n=this.tail.then(e,e);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as Oe}from"@vcp-dev/contracts";var Ve="127.0.0.1",Fe=30218,Ke="/__vcp/site-edit/bridge",ae=6*1024*1024,Le=ae*2,qe=3e3,ee=50,te="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function ue(t,e,n={}){let s=new q($e(e),t,!1,n);return s.start({listen:!1}),s}var q=class{constructor(e,n=Te.createServer((i,o)=>{o.writeHead(404),o.end("VCP site edit bridge server")}),s=!0,r={}){this.options=e,this.server=n,this.closeHttpServerOnStop=s,this.dependencies=r,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new T,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=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=n.stop,this.stopWatching=new Ne({projectRoot:this.options.projectRoot,watch_dirs:this.options.runtime.watch_dirs,debounce_ms:this.options.runtime.watch_debounce_ms??ee,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=We(),n=ze(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 r=(this.dependencies.createRuntime??Ae)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,routes:this.routes,onEvent:i=>{"version"in i&&typeof i.version=="number"&&this.recordRuntimeVersion(i.version);for(let o of this.eventListeners)o(i)},...e?{historyStore:e}:{},initialDocumentVersion:this.runtimeVersionFloor});this.runtime=r,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:r}=await this.activateRoute(e.routeId),i=s.getDocument(r).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:n,resolvedRouteId:s}=await this.activateRoute(e),r=n.getDocument(s);return await this.recordRuntimeVersion(r.data.version),r},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(),r=n?this.resolveRouteId(n):void 0;return s.getClassNameSource(e,r)},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 F(s,e);let r=await this.getRuntime().executeOperation(this.normalizeRequest(e));return r.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(r.data.resultVersion),r}),undo:()=>this.mutationQueue.run(async()=>{let e=await this.bindHistorySession(this.options.snapshotId);if(!e.ok)return F(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 F(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??De)(Je({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e}));if(!this.isStarted)return;let r=s.readDocumentVersion?await s.readDocumentVersion():0;this.runtimeVersionFloor=Math.max(this.runtimeVersionFloor,r),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??ee;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:Ue(this.options.projectRoot),n=new Map(this.routes.map(r=>[r.routeId,r.entryFile])),s=new Set(e.map(r=>r.routeId));this.routes=e;for(let r of e)n.get(r.routeId)!==r.entryFile&&this.runtime?.registerRoute(r.routeId,r.entryFile);for(let r of n.keys())s.has(r)||this.runtime?.unregisterRoute(r)}resolveRouteId(e){let n=Me(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 $e(t){return{tenantId:t.tenantId,siteId:t.siteId,snapshotId:t.snapshotId,projectRoot:L.resolve(t.projectRoot??process.cwd()),basePath:t.basePath??"",host:t.host??Ve,port:t.port??Fe,path:t.path??Ke,sessionKey:t.sessionKey??nt(),historyRoot:t.historyRoot,allowedOrigins:t.allowedOrigins??[],runtime:t.runtime??{}}}function Je(t){return L.join(L.resolve(t.root),ne(t.siteId),ne(t.snapshotId),ie("sha256").update(t.editingSessionId).digest("hex").slice(0,32))}function ne(t){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(t)&&t!=="."&&t!==".."?t:ie("sha256").update(t).digest("hex").slice(0,32)}function We(){let t=globalThis;return t[te]??=new Map,t[te]}function ze(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 Ge(t){let e=new je({maxPayload:ae,noServer:!0}),n=new WeakSet,s=i=>{new P({socket:new $(i),engine:t.engine,validateOperationRequest:o=>Oe.safeParse(o).success})},r=(i,o,c)=>{if(!Qe(i,t))return;let d=Ye(i,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,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(`
4
+ `),o.destroy();return}d.authenticatedByHeader&&n.add(i),e.handleUpgrade(i,o,c,h=>{e.emit("connection",h,i)})};return t.server.on("upgrade",r),e.on("connection",(i,o)=>{if(n.has(o)){s(i);return}Ze(i,t.sessionKey,()=>{s(i)})}),e.stop=()=>{t.server.off("upgrade",r);for(let i of e.clients)i.close(1001,"bridge stopped");e.close()},e}var $=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===oe.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(e,"utf8")>Le){this.socket.close(1013,"bridge backpressure");return}this.socket.send(e)}}close(e,n){this.socket.close(e,n)}};function Qe(t,e){return new URL(t.url??"/","http://localhost").pathname===e.path}function Ye(t,e){if(new URL(t.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let s=K(t.headers["x-vcp-site-edit-key"]),r=ce(s,e.sessionKey),i=K(t.headers.host),o=K(t.headers.origin);return i&&!Xe(i,e.host,o)?null:o?tt(o,i,e.allowedOrigins)?{authenticatedByHeader:r}:null:r?{authenticatedByHeader:!0}:null}function Ze(t,e,n){let s=setTimeout(()=>{t.close(1008,"bridge authentication timeout")},qe),r,i=()=>{clearTimeout(s),t.off("message",r),t.off("close",i)},o=()=>{i(),t.close(1008,"bridge authentication failed")};r=c=>{try{let d=p.parseMessage(c.toString());if(d.type!=="auth"||!ce(d.payload.key,e)){o();return}i(),t.readyState===oe.OPEN&&t.send(p.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),n()}catch{o()}},t.on("message",r),t.on("close",i)}function ce(t,e){if(!t)return!1;let n=Buffer.from(t),s=Buffer.from(e);return n.length===s.length&&Be(n,s)}function Xe(t,e,n){let s=re(t),r=re(e);return s===r||se(r)&&se(s)&&(!n||J(n,t))||et(r)&&(!n||J(n,t))}function et(t){return t==="0.0.0.0"||t==="::"}function se(t){return t==="localhost"||t==="127.0.0.1"||t==="::1"}function tt(t,e,n){return n.includes(t)||(e?J(t,e):!1)}function J(t,e){try{return new URL(t).host===de(e)}catch{return!1}}function re(t){let e=de(t);return e.startsWith("[")?e.slice(1,e.indexOf("]")):e.split(":").length-1===1?e.split(":",1)[0]??"":e}function de(t){return t.trim().toLowerCase()}function K(t){return Array.isArray(t)?t[0]:t}function nt(){return He(32).toString("base64url")}var rt="next",it="127.0.0.1",ot=3e3,B="/__vcp/site-edit/bridge",ye="/__vcp/site-edit/health",j="/__vcp/site-edit/client/",at=`${j}auto-bootstrap.js`,Se='data-vcp-site-edit-bootstrap="true"',ut=["next.config.ts","next.config.mts","next.config.cts","next.config.js","next.config.mjs","next.config.cjs"],ct="@vcp-dev/site-edit-engine/webpack-loader",dt=/@vcp\/site-edit-engine\/(?:webpack-loader|site-edit-instrumentation-loader)/,ht=[/\bconfig\.webpack\s*=/,/\bwebpack\s*\([^)]*\)\s*\{/,/\bwebpack\s*:\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)?\s*=>/,/\bwebpack\s*:\s*(?:async\s+)?function\b/],lt=new Set(["auto-bootstrap.js","client.js","designer-trace.js","document-codec.js","protocol.js","window-message.js"]);async function Sn(t={},e={}){let n=e.process??process,s=e.createServer??st.createServer,r=e.attachBridgeServer??ue,i=e.loadNext??Vt,o=R.resolve(t.projectRoot??process.cwd()),c=t.host??it,d=t.port??qt(n.env.PORT)??ot,h=t.bridgePath??B,g=wt(h),a=Dt(),u=t.basePath??n.env.VCP_SITE_PREVIEW_BASE_PATH??"/",l=(t.tenantId??n.env.VCP_TENANT_ID)?.trim()||void 0,k=await i(o),I=ft(o),m=k({dev:!0,dir:o,hostname:c,port:d,...pt(I)}),Re=m.getRequestHandler(),xe=t.siteId??n.env.VCP_SITE_ID??"site_preview",be=t.snapshotId??n.env.VCP_SNAPSHOT_ID??Lt(o),G=t.historyRoot??n.env.VCP_SITE_EDIT_HISTORY_ROOT,Q=G?.trim()?R.resolve(G):void 0,v=null,y=s((M,_)=>{Ct({basePath:u,bridgePath:h,bridgeHealthPath:g,bridgeSessionKey:a,getBridgeHealth:()=>v?.getHealth()??_t(h),handle:Re,request:M,response:_}).catch(ke=>{console.error(ke),_.headersSent||(_.statusCode=500),_.end("site edit preview request failed")})}),A=null,C=null,D=()=>C||(H(()=>n.off("SIGINT",E)),H(()=>n.off("SIGTERM",E)),H(()=>A?.()),A=null,H(()=>v?.stop()),v=null,C=Promise.allSettled([Jt(y),Wt(()=>m.close())]).then(()=>{}),C),E=()=>{D()};try{return await m.prepare(),v=r(y,{...l?{tenantId:l}:{},siteId:xe,snapshotId:be,projectRoot:o,basePath:u,host:c,port:d,path:h,sessionKey:a,...Q?{historyRoot:Q}:{}}),A=yt({server:y,bridgePath:h,handleUpgrade:m.getUpgradeHandler()}),n.on("SIGINT",E),n.on("SIGTERM",E),await $t(y,d,c),{bundler:I,close:D,host:c,port:zt(y,d),server:y}}catch(M){throw await D(),M}}function ft(t){let e=gt(t);return e&&mt(e)?"webpack":"turbopack"}function pt(t){return t==="webpack"?{webpack:!0}:{turbopack:!0}}function gt(t){for(let e of ut){let n=b(t,e);if(w(n))return S(n,"utf8")}return null}function mt(t){return t.includes(ct)?!0:dt.test(t)&&ht.some(e=>e.test(t))}function yt(t){let e=(n,s,r)=>{St(n.url,t.bridgePath)||t.handleUpgrade(n,s,r).catch(i=>{console.error(i),s.destroyed||s.destroy()})};return t.server.on("upgrade",e),()=>t.server.off("upgrade",e)}function St(t,e){return new URL(t??"/","http://localhost").pathname===e}function wt(t=B){if(t===B)return ye;let e=(t.startsWith("/")?t:`/${t}`).replace(/\/+$/,"");return e.endsWith("/bridge")?`${e.slice(0,-7)}/health`:e?`${e}/health`:"/health"}function It(t){let e=JSON.stringify({basePath:t.basePath,wsPath:t.bridgePath,bridge_session_key:t.bridgeSessionKey,defer_initial_sync:!0,runtime_versions:t.runtimeVersions??Rt()});return`<script type="module" ${Se}>import { startSiteEditAutoBootstrap } from ${JSON.stringify(at)};void startSiteEditAutoBootstrap(window, ${e}).catch(() => undefined);</script>`}function Rt(){return{bridge:W("@vcp-dev/site-edit-bridge"),contracts:W("@vcp-dev/contracts"),engine:W("@vcp-dev/site-edit-engine")}}function W(t){let e=pe(import.meta.url);try{let n=e.resolve(t),s=xt(n,t);if(!s)return;let r=JSON.parse(S(s,"utf8"));return typeof r.version=="string"?r.version:void 0}catch{return}}function xt(t,e){let n=R.parse(t).root,s=x(t);for(;s&&s!==n;){let r=b(s,"package.json");if(w(r))try{if(JSON.parse(S(r,"utf8")).name===e)return r}catch{return}s=x(s)}}function bt(t,e){if(t.includes(Se))return t;let n=It(e),s=/<\/body\s*>/i.exec(t);return s?`${t.slice(0,s.index)}${n}${t.slice(s.index)}`:`${t}${n}`}function wn(t,e=x(me(import.meta.url)),n=w){return we(t,e,n)?.path??null}function we(t,e=x(me(import.meta.url)),n=w){let s=new URL(t??"/","http://localhost").pathname;if(!s.startsWith(j))return null;let r=s.slice(j.length);if(!lt.has(r))return null;let i=z(Ft(e),r);if(ge(e)!=="src")return{path:i,requiresTranspile:!1};let o=z(e,r.replace(/\.js$/,".ts"));return n(o)?{path:o,requiresTranspile:!0}:{path:i,requiresTranspile:!1}}async function kt(t,e){let n=we(t.url);if(!n)return!1;if(!w(n.path))return e.writeHead(404),e.end("site edit client asset not found"),!0;let s=n.requiresTranspile?await vt(S(n.path,"utf8")):S(n.path);return e.writeHead(200,{"cache-control":"no-store","content-type":"text/javascript; charset=utf-8"}),e.end(s),!0}async function vt(t){let e=await import("typescript");return e.transpileModule(t,{compilerOptions:{module:e.ModuleKind.ES2022,target:e.ScriptTarget.ES2022}}).outputText}async function Ct({basePath:t,bridgePath:e,bridgeHealthPath:n,bridgeSessionKey:s,getBridgeHealth:r,handle:i,request:o,response:c}){if(Et(o,c,n,r)||await kt(o,c))return;let d=Pt(o),h=Tt(o),g=d||h?jt(c,{basePath:t,bridgePath:e,bridgeSessionKey:s,patchNextDevChunk:h}):()=>{};try{await i(o,c)}catch(a){throw g(),a}}function Et(t,e,n,s){if(t.method!=="GET"||new URL(t.url??"/","http://localhost").pathname!==n)return!1;let i=s();return e.writeHead(i.ok?200:503,{"cache-control":"no-store","content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify(i)),!0}function _t(t){return{ok:!1,bridgePath:t,runtimeReady:!1,routesIndexed:0,sessionRequired:!0}}function Pt(t){return Ht(t)?(t.headers["accept-encoding"]="identity",!0):!1}function Tt(t){return Bt(t)?(t.headers["accept-encoding"]="identity",!0):!1}function Ht(t){if(t.method!=="GET")return!1;let e=new URL(t.url??"/","http://localhost").pathname;if(e.startsWith("/_next/")||e.startsWith(j)||e===B||e===ye)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 jt(t,e){let n=[],s=t,r=s.flushHeaders.bind(t),i=s.write.bind(t),o=s.end.bind(t);return s.flushHeaders=()=>{},s.write=(c,d,h)=>(c!==void 0&&n.push(he(c,le(d))),fe(d),fe(h),!0),s.end=(c,d,h)=>{let g=typeof c=="function"?void 0:c;g!==void 0&&n.push(he(g,le(d))),s.write=i,s.end=o,s.flushHeaders=r;let a=Buffer.concat(n),u=At(t,a,e);u!==a&&!t.headersSent&&t.removeHeader("content-length");let l=typeof c=="function"?c:typeof d=="function"?d:typeof h=="function"?h:void 0;return l?o(u,l):o(u)},()=>{s.write=i,s.end=o,s.flushHeaders=r}}function At(t,e,n){if(Mt(t,e))return Buffer.from(bt(e.toString("utf8"),n));if(n.patchNextDevChunk&&Ut(t)){let s=e.toString("utf8"),r=Nt(s);if(r!==s)return Buffer.from(r)}return e}function Dt(){let t=new Uint8Array(32);return globalThis.crypto.getRandomValues(t),Buffer.from(t).toString("base64url")}function Mt(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 Ut(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 Nt(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(`
5
5
  `),["unstable_allowPartialStream: true,"," debugChannel,"," startTime: 0"].join(`
6
- `)],[["findSourceMapURL: _appfindsourcemapurl.findSourceMapURL,"," debugChannel,"," startTime: 0"].join("\\n"),["unstable_allowPartialStream: true,"," debugChannel,"," startTime: 0"].join("\\n")],["debugChannel: createDebugChannel && createDebugChannel(requestHeaders)","debugChannel: undefined"],["debugChannel: createDebugChannel && createDebugChannel(headers)","debugChannel: undefined"]].reduce((n,[s,i])=>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};
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,r])=>Ot(n,s,r),t)}function Ot(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 he(t,e){return Buffer.isBuffer(t)?t:t instanceof Uint8Array?Buffer.from(t):Buffer.from(String(t),e)}function le(t){return typeof t=="string"?t:void 0}function fe(t){typeof t=="function"&&t()}async function Vt(t){let n=pe(b(t,"package.json"))(rt);return n.default??n}function Ft(t){return ge(t)!=="src"?t:z(Kt(t),"dist")}function Kt(t){return Ie(t)?R.posix.dirname(t):x(t)}function z(t,e){return Ie(t)?R.posix.join(t,e):b(t,e)}function Ie(t){return t.includes("/")&&!t.includes("\\")}function Lt(t){let e=b(t,".vcp/current-snapshot.json");if(!w(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 qt(t){if(!t)return null;let e=Number.parseInt(t,10);return Number.isInteger(e)&&e>0&&e<=65535?e:null}function $t(t,e,n){return new Promise((s,r)=>{t.once("error",r),t.listen(e,n,()=>{t.off("error",r),s()})})}function Jt(t){return new Promise(e=>{t.close(()=>e())})}function H(t){try{t()}catch{}}function Wt(t){try{return t()}catch(e){return Promise.reject(e)}}function zt(t,e){let n=t.address();return typeof n=="object"&&n?n.port:e}export{yt as attachNextDevUpgradeHandler,It as createSiteEditAutoBootstrapHtmlSnippet,bt as injectSiteEditAutoBootstrap,Nt as patchNextFirefoxDevChunk,Tt as prepareFirefoxNextDevChunkPatchRequest,Pt as prepareSiteEditHtmlInjectionRequest,wt as resolveSiteEditBridgeHealthPath,we as resolveSiteEditClientAsset,wn as resolveSiteEditClientAssetPath,ft as resolveSiteEditNextDevBundler,Sn as startSiteEditNextDevServer,vt as transpileSiteEditClientSource};
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
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
1
+ import X from"node:http";import{createHash as F,randomBytes as Z,timingSafeEqual as ee}from"node:crypto";import C from"node:path";import{WebSocket as U,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},$=a=>{let u=null;for(let l of z(a)){let E=`${u??"root"}\0${l}`,_=n.get(E);if(typeof _=="number"){u=_;continue}let T=i.length;i.push([u,y(l)]),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: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[$(a.key),W(a.operationSummary),...u>=0?[u]:[]]}),...h.length>0?{dynamicDataSummaries:h}:{}}}function W(t){return(t.canUpdateText?1:0)|(t.canInsertChild?2:0)|(t.canMove?4:0)|(t.canRemove?8:0)}function z(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",P=6*1024*1024,he=P*2,pe=3e3,M=50,O="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function Ae(t){let e=q(t),s=J(e),n=j(),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 Ke(t,e,s={}){let n=new m(q(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=j(),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 q(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),F("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:F("sha256").update(t).digest("hex").slice(0,32)}function j(){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:P,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}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};
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===U.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===U.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=K(t),i=K(e);return n===i||A(i)&&A(n)&&(!s||v(s,t))||Re(i)&&(!s||v(s,t))}function Re(t){return t==="0.0.0.0"||t==="::"}function A(t){return t==="localhost"||t==="127.0.0.1"||t==="::1"}function Se(t,e,s){return s.includes(t)||(e?v(t,e):!1)}function v(t,e){try{return new URL(t).host===N(e)}catch{return!1}}function K(t){let e=N(t);return e.startsWith("[")?e.slice(1,e.indexOf("]")):e.split(":").length-1===1?e.split(":",1)[0]??"":e}function N(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,P as SITE_EDIT_BRIDGE_MAX_PAYLOAD_BYTES,m as SiteEditBridgeServer,Ke as attachSiteEditBridgeServer,ge as attachSiteEditWebSocketServer,Ae as ensureSiteEditBridgeServerStarted};
@@ -1,31 +1,39 @@
1
1
  #!/usr/bin/env node
2
+ import { pathToFileURL } from "node:url";
2
3
  import { startSiteEditNextDevServer } from "./next-dev-server.js";
3
- const input = parseInput(process.argv.slice(2));
4
- const port = readPort(input.port ?? process.env.PORT ?? process.env.VCP_PREVIEW_PORT, 3000);
5
- const host = input.host ?? process.env.HOST ?? "0.0.0.0";
6
- const projectRoot = input.projectRoot ?? process.cwd();
7
- const preview = await startSiteEditNextDevServer({
8
- host,
9
- port,
10
- projectRoot,
11
- siteId: process.env.VCP_SITE_ID ?? "site_preview",
12
- snapshotId: process.env.VCP_SNAPSHOT_ID,
13
- });
14
- console.log(JSON.stringify({
15
- status: "ready",
16
- kind: "site-edit-next-dev-server",
17
- projectRoot: projectRoot,
18
- preview_url: `http://127.0.0.1:${port}`,
19
- bridge_path: "/__vcp/site-edit/bridge",
20
- bundler: preview.bundler,
21
- }, null, 2));
22
- for (const signal of ["SIGINT", "SIGTERM"]) {
23
- process.on(signal, () => {
24
- preview.close();
25
- process.exit(0);
4
+ export async function runStartNextDevServerCli(argv, dependencies = {}) {
5
+ const env = dependencies.env ?? process.env;
6
+ const input = parseInput(argv);
7
+ const port = readPort(input.port ?? env.PORT ?? env.VCP_PREVIEW_PORT, 3000);
8
+ const host = input.host ?? env.HOST ?? "0.0.0.0";
9
+ const projectRoot = input.projectRoot ?? (dependencies.cwd ?? process.cwd)();
10
+ const preview = await (dependencies.startServer ?? startSiteEditNextDevServer)({
11
+ host,
12
+ port,
13
+ projectRoot,
14
+ siteId: env.VCP_SITE_ID ?? "site_preview",
15
+ snapshotId: env.VCP_SNAPSHOT_ID,
26
16
  });
17
+ (dependencies.log ?? console.log)(JSON.stringify({
18
+ status: "ready",
19
+ kind: "site-edit-next-dev-server",
20
+ projectRoot: projectRoot,
21
+ preview_url: `http://127.0.0.1:${preview.port}`,
22
+ bridge_path: "/__vcp/site-edit/bridge",
23
+ bundler: preview.bundler,
24
+ }, null, 2));
25
+ return preview;
27
26
  }
28
- function parseInput(argv) {
27
+ export async function main(argv, dependencies = {}) {
28
+ try {
29
+ await runStartNextDevServerCli(argv, dependencies);
30
+ }
31
+ catch (error) {
32
+ (dependencies.error ?? console.error)(error);
33
+ (dependencies.setExitCode ?? ((exitCode) => (process.exitCode = exitCode)))(1);
34
+ }
35
+ }
36
+ export function parseInput(argv) {
29
37
  const input = {};
30
38
  for (let index = 0; index < argv.length; index += 1) {
31
39
  const arg = argv[index];
@@ -47,14 +55,29 @@ function parseInput(argv) {
47
55
  }
48
56
  function readValue(argv, index, optionName) {
49
57
  const value = argv[index];
50
- if (!value || value.startsWith("-")) {
58
+ if (!value ||
59
+ (value.startsWith("-") && !(optionName === "--port" && /^-\d/.test(value)))) {
51
60
  throw new Error(`${optionName} requires a value.`);
52
61
  }
53
62
  return value;
54
63
  }
55
- function readPort(value, fallback) {
56
- const parsed = Number.parseInt(String(value ?? ""), 10);
57
- return Number.isInteger(parsed) && parsed > 0 && parsed <= 65_535
58
- ? parsed
59
- : fallback;
64
+ export function readPort(value, fallback) {
65
+ if (value === undefined || value.trim() === "") {
66
+ return fallback;
67
+ }
68
+ if (!/^(?:0|[1-9]\d*)$/.test(value)) {
69
+ throw new Error(`Invalid port: ${value}. Expected an integer from 0 to 65535.`);
70
+ }
71
+ const parsed = Number(value);
72
+ if (parsed > 65_535) {
73
+ throw new Error(`Invalid port: ${value}. Expected an integer from 0 to 65535.`);
74
+ }
75
+ return parsed;
76
+ }
77
+ function isMainModule() {
78
+ const entryPath = process.argv[1];
79
+ return Boolean(entryPath && import.meta.url === pathToFileURL(entryPath).href);
80
+ }
81
+ if (isMainModule()) {
82
+ void main(process.argv.slice(2));
60
83
  }
@@ -1 +1 @@
1
- var s="vcp-window-message",i=1;function r(e){return{source:s,version:1,namespace:e.namespace,channel:e.channel,...e.sessionKey?{sessionKey:e.sessionKey}:{},body:e.body}}function c(e){if(typeof e!="object"||e===null)return!1;let n=e;return n.source===s&&n.version===1&&typeof n.namespace=="string"&&typeof n.channel=="string"&&"body"in n}var o="site-edit",t="frame-runtime";function E(e,n={}){return r({namespace:o,channel:t,sessionKey:n.sessionKey,body:e})}function a(e){return!c(e)||e.namespace!==o||e.channel!==t?!1:typeof e.body?.type=="string"}export{t as SITE_EDIT_FRAME_RUNTIME_CHANNEL,o as SITE_EDIT_WINDOW_MESSAGE_NAMESPACE,s as VCP_WINDOW_MESSAGE_SOURCE,i as VCP_WINDOW_MESSAGE_VERSION,E as createSiteEditFrameRuntimeMessage,r as createVcpWindowMessageEnvelope,a as isSiteEditFrameRuntimeMessage,c as isVcpWindowMessageEnvelope};
1
+ var l="vcp-window-message",T=1;function I(e){return{source:l,version:1,namespace:e.namespace,channel:e.channel,...e.sessionKey?{sessionKey:e.sessionKey}:{},body:e.body}}function x(e){try{return s(e)?d(e,"source",l)&&d(e,"version",1)&&i(e,"namespace")&&i(e,"channel")&&a(e,"body")&&(!u(e,"sessionKey")||i(e,"sessionKey")):!1}catch{return!1}}var m="site-edit",h="frame-runtime";function q(e,r={}){return I({namespace:m,channel:h,sessionKey:r.sessionKey,body:e})}function B(e){try{return!x(e)||e.namespace!==m||e.channel!==h?!1:C(e.body)}catch{return!1}}var u=(e,r)=>Object.prototype.hasOwnProperty.call(e,r);function a(e,r){let t=Object.getOwnPropertyDescriptor(e,r);return t!==void 0&&"value"in t}function n(e,r){return Object.getOwnPropertyDescriptor(e,r)?.value}function d(e,r,t){return a(e,r)&&n(e,r)===t}function i(e,r){return a(e,r)&&typeof n(e,r)=="string"}function E(e,r){return a(e,r)&&typeof n(e,r)=="boolean"}function o(e,r){let t=n(e,r);return a(e,r)&&typeof t=="number"&&Number.isFinite(t)}function y(e,r){return!u(e,r)||n(e,r)===void 0||i(e,r)}function p(e,r){return!u(e,r)||n(e,r)===void 0||o(e,r)}function b(e,r){let t=n(e,r);return!u(e,r)||t===void 0||a(e,r)&&typeof t=="number"&&Number.isInteger(t)&&t>=0}function c(e,r){return!u(e,r)||n(e,r)===void 0}function s(e){if(typeof e!="object"||e===null||Array.isArray(e))return!1;let r=Object.getPrototypeOf(e);return r===Object.prototype||r===null}function N(e){return s(e)&&Reflect.ownKeys(e).length===0}function w(e){if(!Array.isArray(e))return!1;for(let r=0;r<e.length;r+=1){let t=Object.getOwnPropertyDescriptor(e,String(r));if(t===void 0||!("value"in t)||typeof t.value!="string")return!1}return!0}function g(e){return s(e)&&i(e,"key")&&b(e,"instanceIndex")&&b(e,"instanceCount")&&b(e,"textSegmentIndex")}function R(e){return s(e)&&Reflect.ownKeys(e).every(r=>typeof r=="string"&&a(e,r)&&(typeof n(e,r)=="string"||n(e,r)===null))}function O(e){return e==null||typeof e=="string"||typeof e=="number"&&Number.isFinite(e)}function _(e){return s(e)&&Reflect.ownKeys(e).every(r=>typeof r=="string"&&a(e,r)&&O(n(e,r)))}function A(e){if(!s(e))return!1;let r=n(e,"priority");return i(e,"property")&&a(e,"value")&&O(n(e,"value"))&&(!u(e,"priority")||r===void 0||r==="important")}function P(e){return s(e)?(c(e,"attributes")||R(n(e,"attributes")))&&y(e,"className")&&(c(e,"style")||_(n(e,"style")))&&(c(e,"styleDeclarations")||Array.isArray(n(e,"styleDeclarations"))&&n(e,"styleDeclarations").every(A)):!1}function D(e){return s(e)&&o(e,"x")&&o(e,"y")&&(c(e,"detailLevel")||d(e,"detailLevel","summary")||d(e,"detailLevel","full"))}function V(e){return s(e)&&g(n(e,"ref"))}function K(e){if(!s(e)||!s(n(e,"variables")))return!1;let r=n(e,"variables");return Reflect.ownKeys(r).every(t=>typeof t=="string"&&i(r,t))?c(e,"stylesheets")||w(n(e,"stylesheets")):!1}function W(e){if(!d(e,"type","request")||!i(e,"id")||!i(e,"kind")||!a(e,"payload"))return!1;let r=n(e,"kind"),t=n(e,"payload");switch(r){case"hit-test-at-point":case"start-text-edit-at-point":return D(t);case"bridge-get-source-tree":case"get-scroll-metrics":case"clear-theme-override":case"bridge-get-history-status":case"bridge-undo":case"bridge-redo":return N(t);case"bridge-get-source-file":return s(t)&&i(t,"path");case"bridge-resolve-source-location":case"bridge-get-object-edit-context":case"bridge-get-class-name-source":return s(t)&&i(t,"key")&&y(t,"routeId");case"scroll-to":return s(t)&&o(t,"x")&&o(t,"y");case"scroll-by":return s(t)&&o(t,"deltaX")&&o(t,"deltaY");case"set-interaction-mode":return s(t)&&E(t,"enabled");case"set-theme-override":return K(t);case"get-theme-variables":return s(t)&&w(n(t,"names"));case"compute-drop-target-at-point":return s(t)&&o(t,"x")&&o(t,"y")&&(d(t,"dragKind","insert")||d(t,"dragKind","move"))&&(c(t,"movingRef")||g(n(t,"movingRef")));case"observe-node-bounds":case"clear-node-bounds-observer":return s(t)&&(c(t,"ref")||g(n(t,"ref")));case"apply-node-patch":return s(t)&&g(n(t,"ref"))&&P(n(t,"patch"))&&(c(t,"preview")||s(n(t,"preview"))&&i(n(t,"preview"),"sessionId")&&b(n(t,"preview"),"revision")&&u(n(t,"preview"),"revision"));case"get-hit-by-ref":case"get-path-by-ref":case"restore-text-edit":case"clear-text-edit-rollback":return V(t);case"bridge-initialize-session":case"bridge-get-style-context":return s(t)&&i(t,"routeId");case"bridge-get-document":case"bridge-ensure-document-version":case"bridge-acknowledge-document-version":return s(t)&&i(t,"routeId")&&p(t,"minVersion")&&p(t,"version");case"bridge-execute":return s(t)&&s(n(t,"request"));case"bridge-subscribe":case"bridge-unsubscribe":return s(t)&&i(t,"subscriptionId");default:return!1}}function M(e){return d(e,"type","response")&&i(e,"id")&&E(e,"ok")&&(c(e,"error")||i(e,"error"))}var F=new Set(["pointer-down","pointer-move","pointer-up","pointer-cancel","pointer-click","pointer-double-click"]);function j(e){if(!d(e,"type","event")||!i(e,"kind")||!s(n(e,"payload")))return!1;let r=n(e,"kind"),t=n(e,"payload");switch(r){case"text-edit-commit":case"text-edit-cancel":case"text-edit-exit":case"text-edit-input":return g(n(t,"ref"))&&y(t,"value")&&(c(t,"hit")||s(n(t,"hit")));case"node-bounds-change":return s(n(t,"hit"));case"bridge-event":return i(t,"subscriptionId")&&s(n(t,"event"));case"route-ready":return i(t,"routeId")&&p(t,"version");case"bridge-status":{let f=n(t,"state");return(f==="connecting"||f==="ready"||f==="reconnecting"||f==="failed"||f==="reload-required")&&y(t,"routeId")&&p(t,"attempt")&&p(t,"retryInMs")&&y(t,"messageKey")}case"scroll":{let f=n(t,"metrics");return s(f)&&["scrollX","scrollY","scrollWidth","scrollHeight","viewportWidth","viewportHeight"].every(S=>o(f,S))}default:return F.has(r)&&o(t,"clientX")&&o(t,"clientY")&&p(t,"buttons")&&(n(t,"hit")===null||s(n(t,"hit")))}}function C(e){if(!s(e))return!1;switch(n(e,"type")){case"request":return W(e);case"response":return M(e);case"event":return j(e);default:return!1}}export{h as SITE_EDIT_FRAME_RUNTIME_CHANNEL,m as SITE_EDIT_WINDOW_MESSAGE_NAMESPACE,l as VCP_WINDOW_MESSAGE_SOURCE,T as VCP_WINDOW_MESSAGE_VERSION,q as createSiteEditFrameRuntimeMessage,I as createVcpWindowMessageEnvelope,B as isSiteEditFrameRuntimeMessage,x as isVcpWindowMessageEnvelope};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vcp-dev/site-edit-bridge",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -74,8 +74,8 @@
74
74
  "typecheck": "tsc -p tsconfig.json --noEmit"
75
75
  },
76
76
  "dependencies": {
77
- "@vcp-dev/contracts": "0.6.2",
78
- "@vcp-dev/site-edit-engine": "0.6.2",
77
+ "@vcp-dev/contracts": "0.6.3",
78
+ "@vcp-dev/site-edit-engine": "0.6.3",
79
79
  "@types/ws": "^8.18.1",
80
80
  "ws": "^8.20.1"
81
81
  },