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