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