@vcp-dev/site-edit-bridge 0.7.3 → 0.8.1
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.js +18 -8
- package/dist/index.js +20 -10
- package/dist/next-dev-server.d.ts +18 -0
- package/dist/next-dev-server.js +3 -3
- package/package.json +3 -3
|
@@ -16,6 +16,7 @@ export type SiteEditNextDevServerOptions = {
|
|
|
16
16
|
export type SiteEditNextDevBundler = "turbopack" | "webpack";
|
|
17
17
|
type NextDevApp = {
|
|
18
18
|
close(): Promise<void>;
|
|
19
|
+
renderToHTML(request: IncomingMessage, response: ServerResponse, pathname: string, query: Record<string, string>): Promise<string | null>;
|
|
19
20
|
getRequestHandler(): (request: IncomingMessage, response: ServerResponse) => Promise<void>;
|
|
20
21
|
getUpgradeHandler(): (request: IncomingMessage, socket: Duplex, head: Buffer) => Promise<void>;
|
|
21
22
|
prepare(): Promise<void>;
|
|
@@ -67,6 +68,23 @@ export declare function injectSiteEditAutoBootstrap(html: string, input: {
|
|
|
67
68
|
export declare function resolveSiteEditClientAssetPath(requestUrl: string | undefined, moduleDir?: string, fileExists?: (path: string) => boolean): string | null;
|
|
68
69
|
export declare function resolveSiteEditClientAsset(requestUrl: string | undefined, moduleDir?: string, fileExists?: (path: string) => boolean): SiteEditClientAsset | null;
|
|
69
70
|
export declare function transpileSiteEditClientSource(source: string): Promise<string>;
|
|
71
|
+
export type SiteEditRenderProbeVerdict = "ok" | "artifact_broken" | "inconclusive";
|
|
72
|
+
export type SiteEditRenderProbeResult = {
|
|
73
|
+
verdict: SiteEditRenderProbeVerdict;
|
|
74
|
+
detail?: string;
|
|
75
|
+
};
|
|
76
|
+
export type SiteEditLaunchIdentity = {
|
|
77
|
+
launchId: string;
|
|
78
|
+
pid: number;
|
|
79
|
+
snapshotId: string;
|
|
80
|
+
};
|
|
81
|
+
export type SiteEditBridgeHealthPayload = SiteEditBridgeHealth & {
|
|
82
|
+
launch: SiteEditLaunchIdentity;
|
|
83
|
+
render?: SiteEditRenderProbeResult;
|
|
84
|
+
};
|
|
85
|
+
export declare function classifySiteEditRenderProbeError(error: unknown): SiteEditRenderProbeResult;
|
|
86
|
+
export declare function runSiteEditRenderProbe(app: Pick<NextDevApp, "renderToHTML">): Promise<SiteEditRenderProbeResult>;
|
|
87
|
+
export declare function isSiteEditRenderProbeRequested(url: string | undefined): boolean;
|
|
70
88
|
export declare function prepareSiteEditHtmlInjectionRequest(request: Pick<IncomingMessage, "headers" | "method" | "url">): boolean;
|
|
71
89
|
export declare function prepareFirefoxNextDevChunkPatchRequest(request: Pick<IncomingMessage, "headers" | "method" | "url">): boolean;
|
|
72
90
|
export declare function patchNextFirefoxDevChunk(source: string): string;
|
package/dist/next-dev-server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
|
|
1
|
+
import ae from"node:http";import{existsSync as _,readFileSync as v}from"node:fs";import{randomUUID as Pt}from"node:crypto";import{createRequire as He}from"node:module";import{Socket as Te}from"node:net";import k from"node:path";import{basename as Be,dirname as P,join as H}from"node:path";import{fileURLToPath as Ae}from"node:url";import Ze from"node:http";import Z from"node:fs";import{createHash as Ce,randomBytes as Xe,timingSafeEqual as et}from"node:crypto";import x from"node:path";import{WebSocket as Re,WebSocketServer as tt}from"ws";import{createPreviewSiteEditEngineRuntime as nt,openFileHistoryStore as it,resolveManagedPageTemplateComposition as ot,scanSiteEditCompositionsFromFiles as st}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as rt,scanNextAppRouterRoutesFromFiles as at}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as ct}from"@vcp-dev/site-edit-engine/source-watcher";function fe(e){let t=new Map,n=[],i=new Map,s=[],o=e.pageComposition.status==="supported"?e.pageComposition.placementUnitKeys:[],r=new Map(o.map((d,p)=>[d,p])),u=e.pageComposition.status==="supported"?{status:"supported",containerKey:e.pageComposition.containerKey,hasBusinessMembers:e.pageComposition.hasBusinessMembers,placementUnitKeys:e.pageComposition.placementUnitKeys,placements:e.pageComposition.placements.map(d=>({placementKey:d.placementKey,placementUnitKey:d.placementUnitKey,relation:d.relation}))}:{status:"unsupported",reason:e.pageComposition.reason,diagnostics:e.pageComposition.diagnostics},c=e.entries.flatMap((d,p)=>d.dynamicDataSummary?[[p,d.dynamicDataSummary]]:[]),a=d=>{let p=t.get(d);if(typeof p=="number")return p;let l=n.length;return n.push(d),t.set(d,l),l},h=d=>{let p=null;for(let l of We(d)){let y=`${p??"root"}\0${l}`,S=i.get(y);if(typeof S=="number"){p=S;continue}let I=s.length;s.push([p,a(l)]),i.set(y,I),p=I}if(p===null)throw new Error("Document key must not be empty");return p};return{encoding:"segment-trie-v1",siteId:e.siteId,snapshotId:e.snapshotId,composition:{...e.composition},routeId:e.composition.routeId,version:e.version,pageComposition:u,tokens:n,nodes:s,entries:e.entries.map(d=>{let p=d.pageCompositionUnitKey?r.get(d.pageCompositionUnitKey)??-1:-1;if(d.pageCompositionUnitKey&&p<0)throw new Error(`Render entry ${d.key} references unknown page composition unit ${d.pageCompositionUnitKey}`);return[h(d.key),qe(d.operationSummary),...p>=0?[p]:[]]}),...c.length>0?{dynamicDataSummaries:c}:{}}}function qe(e){return(e.canUpdateText?1:0)|(e.canInsertChild?2:0)|(e.canMove?4:0)|(e.canRemove?8:0)}function We(e){if(!e)return[];let t=[],n=0,i=pe(e,n);if(i<0)return t.push(e),t;for(t.push(e.slice(0,i)),n=i;n<e.length;){if(i=pe(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 pe(e,t){let n=e.indexOf("/",t),i=e.indexOf("::",t);return n<0?i:i<0?n:Math.min(n,i)}var le="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"?Le(t.payload,n):t.type==="response"||t.type==="error"?f(t.payload)&&typeof t.payload.id=="string":t.type==="event"?f(t.payload)&&f(t.payload.event):t.type==="auth"?f(t.payload)&&typeof t.payload.key=="string":t.type==="auth-ok"&&f(t.payload)&&t.payload.ok===!0}},M=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),u=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:w({...u,data:{...u.data,document:ge(u.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,Qe(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,w(r,o));return}case"get-style-context":{let o=await this.resolveWireContext(i);this.sendResult(s,i.kind,w(await this.options.engine.getStyleContext(o),o));return}case"get-object-edit-context":{let o=await this.resolveWireContext(i);this.sendResult(s,i.kind,w(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,w(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,w(await this.options.engine.getSourceTree(o),o));return}case"get-source-file":{let o=await this.resolveWireContext(i);this.sendResult(s,i.kind,w(await this.options.engine.getSourceFile({path:i.path,composition:o}),o));return}case"resolve-source-location":{let o=await this.resolveWireContext(i),r=w(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(le,"The execute request composition does not match its operation composition.");let u=await this.normalizeOperationRequest(i.request,o);await this.handleBufferedOperation(s,i.kind,()=>this.options.engine.execute(u));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(le,"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=Je(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:me(n)};if(i.target.kind==="route"){let s=i.target,o=L(s),r=await this.resolveAndAssert(o);i.target={...s,composition:me(r)}}return i}handleEngineEvent(t){let n=t?.composition;if(!ze(n))return;let i=C(n);this.session.subscribedCompositionKeys.has(i)&&(this.eventDispatcher.push(t),t.type==="site-edit.graph.invalidated"&&i===Ge(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=Ye(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 Le(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&&$e(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 $e(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 ze(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 Je(e){if(f(e)&&T(e.composition))return e.composition;throw new R(Y,"A composition selector is required for execute.")}function me(e){return{routeId:e.routeId,...e.effectiveTemplateId?{templateId:e.effectiveTemplateId}:{}}}function Ge(e){return e.activeComposition?C(e.activeComposition):null}function C(e){return JSON.stringify([e.routeId,e.effectiveTemplateId??null])}function w(e,t){return f(e)&&e.ok===!0?{...e,composition:t}:e}function Qe(e,t){return!f(e)||e.ok!==!0?e:{...e,...t?{composition:t}:{},data:ge(e.data)}}function ge(e){let t=e;return fe(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 Ye(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 ut,computeSiteEditCompositionKey as b}from"@vcp-dev/contracts";var dt="127.0.0.1",ht=30218,pt="/__vcp/site-edit/bridge",be=6*1024*1024,ft=be*2,lt=3e3,ye=50,Se="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function Ee(e,t,n={}){let i=new te(mt(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=Ct({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 ct({projectRoot:this.options.projectRoot,watch_dirs:gt(this.options.runtime.watch_dirs),interval_ms:this.options.runtime.watch_interval_ms,debounce_ms:this.options.runtime.watch_debounce_ms??ye,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=wt(),n=xt(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??nt)({...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=St(t.composition),s=this.getRuntime(),o=g(i);await s.initializeComposition(o);let u=s.getDocument(o).data;await this.recordRuntimeVersion(u.version,i);let c=(await s.getHistoryStatus()).data;return{ok:!0,composition:i,data:{document:u,historyStatus:c}}}),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??it)(It({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=b(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?b(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??ye;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=yt(this.options.projectRoot),i=at(n),s=i.length>0?i:(this.options.runtime.routes??[]).map(c=>({...c})),o=st({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(c=>[b(c.context),c])),u=new Map(o.map(c=>[b(c.context),c]));if(this.runtime){for(let[c,a]of r){let h=u.get(c);(!h||h.entryFile!==a.entryFile)&&this.runtime.unregisterComposition(g(a.context))}for(let[c,a]of u){let h=r.get(c);(!h||h.entryFile!==a.entryFile)&&this.runtime.registerComposition(a.context,a.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=rt(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=ot({selector:{routeId:n,...t.templateId?{templateId:t.templateId}:{}},files:this.compositionFiles,canonicalEntryFile:i.entryFile}),o=b(s.context);if(!this.compositions.some(r=>b(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(b(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 mt(e){return{tenantId:e.tenantId,siteId:e.siteId,snapshotId:e.snapshotId,projectRoot:x.resolve(e.projectRoot??process.cwd()),basePath:e.basePath??"",host:e.host??dt,port:e.port??ht,path:e.path??pt,sessionKey:e.sessionKey??kt(),historyRoot:e.historyRoot,allowedOrigins:e.allowedOrigins??[],runtime:e.runtime??{}}}function gt(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 yt(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 u of r){if(u.name.startsWith(".")&&u.name!==".vcp")continue;if(u.isDirectory()){if(i.has(u.name))continue;s(x.join(o,u.name));continue}if(!u.isFile())continue;let c=x.join(o,u.name),a="";try{a=Z.readFileSync(c,"utf8")}catch{}t.push({path:x.relative(e,c).replaceAll(x.sep,"/"),content:a})}};for(let o of n)s(x.join(e,o));return t.sort((o,r)=>o.path.localeCompare(r.path))}function St(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 It(e){return x.join(x.resolve(e.root),Ie(e.siteId),Ie(e.snapshotId),Ce("sha256").update(e.editingSessionId).digest("hex").slice(0,32))}function Ie(e){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(e)&&e!=="."&&e!==".."?e:Ce("sha256").update(e).digest("hex").slice(0,32)}function wt(){let e=globalThis;return e[Se]??=new Map,e[Se]}function xt(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 Ct(e){let t=new tt({maxPayload:be,noServer:!0}),n=new WeakSet,i=o=>{new M({socket:new ne(o),engine:e.engine,validateOperationRequest:r=>ut.safeParse(r).success})},s=(o,r,u)=>{if(!Rt(o,e))return;let c=bt(o,e);if(!c){r.write(`HTTP/1.1 401 Unauthorized\r
|
|
2
2
|
Connection: close\r
|
|
3
3
|
\r
|
|
4
|
-
`),r.destroy();return}d.authenticatedByHeader&&n.add(o),t.handleUpgrade(o,r,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(`
|
|
4
|
+
`),r.destroy();return}c.authenticatedByHeader&&n.add(o),t.handleUpgrade(o,r,u,a=>{t.emit("connection",a,o)})};return e.server.on("upgrade",s),t.on("connection",(o,r)=>{if(n.has(r)){i(o);return}Et(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===Re.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(t,"utf8")>ft){this.socket.close(1013,"bridge backpressure");return}this.socket.send(t)}}close(t,n){this.socket.close(t,n)}};function Rt(e,t){return new URL(e.url??"/","http://localhost").pathname===t.path}function bt(e,t){if(new URL(e.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let i=ee(e.headers["x-vcp-site-edit-key"]),s=ve(i,t.sessionKey),o=ee(e.headers.host),r=ee(e.headers.origin);return o&&!vt(o,t.host,r)?null:r?Tt(r,o,t.allowedOrigins)?{authenticatedByHeader:s}:null:s?{authenticatedByHeader:!0}:null}function Et(e,t,n){let i=setTimeout(()=>{e.close(1008,"bridge authentication timeout")},lt),s,o=()=>{clearTimeout(i),e.off("message",s),e.off("close",o)},r=()=>{o(),e.close(1008,"bridge authentication failed")};s=u=>{try{let c=m.parseMessage(u.toString());if(c.type!=="auth"||!ve(c.payload.key,t)){r();return}o(),e.readyState===Re.OPEN&&e.send(m.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&&et(n,i)}function vt(e,t,n){let i=xe(e),s=xe(t);return i===s||we(s)&&we(i)&&(!n||ie(n,e))||_t(s)&&(!n||ie(n,e))}function _t(e){return e==="0.0.0.0"||e==="::"}function we(e){return e==="localhost"||e==="127.0.0.1"||e==="::1"}function Tt(e,t,n){return n.includes(e)||(t?ie(e,t):!1)}function ie(e,t){try{return new URL(e).host===_e(t)}catch{return!1}}function xe(e){let t=_e(e);return t.startsWith("[")?t.slice(1,t.indexOf("]")):t.split(":").length-1===1?t.split(":",1)[0]??"":t}function _e(e){return e.trim().toLowerCase()}function ee(e){return Array.isArray(e)?e[0]:e}function kt(){return Xe(32).toString("base64url")}var Ht="next",De="127.0.0.1",Bt=3e3,U="/__vcp/site-edit/bridge",Oe="/__vcp/site-edit/health",At="probe",Dt="render",ke="/_error",K="/__vcp/site-edit/client/",Ot=`${K}auto-bootstrap.js`,Me='data-vcp-site-edit-bootstrap="true"',Mt=["next.config.ts","next.config.mts","next.config.cts","next.config.js","next.config.mjs","next.config.cjs"],Nt="@vcp-dev/site-edit-engine/webpack-loader",jt=/@vcp\/site-edit-engine\/(?:webpack-loader|site-edit-instrumentation-loader)/,Ft=[/\bconfig\.webpack\s*=/,/\bwebpack\s*\([^)]*\)\s*\{/,/\bwebpack\s*:\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)?\s*=>/,/\bwebpack\s*:\s*(?:async\s+)?function\b/],Ut=new Set(["auto-bootstrap.js","client.js","composition-key.js","designer-trace.js","document-codec.js","protocol.js","window-message.js"]);async function Yn(e={},t={}){let n=t.process??process,i=t.createServer??ae.createServer,s=t.attachBridgeServer??Ee,o=t.loadNext??yn,r=k.resolve(e.projectRoot??process.cwd()),u=e.host??De,c=e.port??xn(n.env.PORT)??Bt,a=e.bridgePath??U,h=zt(a),d=pn(),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=Kt(r),I=y({dev:!0,dir:r,hostname:u,port:c,...Vt(S)}),Fe=I.getRequestHandler(),Ue=e.siteId??n.env.VCP_SITE_ID??"018f6f4d-a9b8-7120-8000-000000000120",ue=e.snapshotId??n.env.VCP_SNAPSHOT_ID??wn(r),de=e.historyRoot??n.env.VCP_SITE_EDIT_HISTORY_ROOT,he=de?.trim()?k.resolve(de):void 0,B=null,Ke={launchId:Pt(),pid:process.pid,snapshotId:ue},E=i((W,O)=>{en({basePath:p,bridgePath:a,bridgeHealthPath:h,bridgeSessionKey:d,getBridgeHealth:()=>B?.getHealth()??rn(a),launch:Ke,runRenderProbe:()=>nn(I),handle:Fe,request:W,response:O}).catch(Ve=>{console.error(Ve),O.headersSent||(O.statusCode=500),O.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([Rn(E),bn(()=>I.close())]).then(()=>{}),A),D=()=>{q()};try{return await I.prepare(),B=s(E,{...l?{tenantId:l}:{},siteId:Ue,snapshotId:ue,projectRoot:r,basePath:p,host:u,port:c,path:a,sessionKey:d,...he?{historyRoot:he}:{}}),V=Lt({server:E,bridgePath:a,handleUpgrade:I.getUpgradeHandler()}),n.on("SIGINT",D),n.on("SIGTERM",D),await Cn(E,c,u),{bundler:S,close:q,host:u,port:En(E,c),server:E}}catch(W){throw await q(),W}}function Kt(e){let t=qt(e);return t&&Wt(t)?"webpack":"turbopack"}function Vt(e){return e==="webpack"?{webpack:!0}:{turbopack:!0}}function qt(e){for(let t of Mt){let n=H(e,t);if(_(n))return v(n,"utf8")}return null}function Wt(e){return e.includes(Nt)?!0:jt.test(e)&&Ft.some(t=>t.test(e))}function Lt(e){let t=(n,i,s)=>{$t(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 $t(e,t){return new URL(e??"/","http://localhost").pathname===t}function zt(e=U){if(e===U)return Oe;let t=(e.startsWith("/")?e:`/${e}`).replace(/\/+$/,"");return t.endsWith("/bridge")?`${t.slice(0,-7)}/health`:t?`${t}/health`:"/health"}function Jt(e){let t=JSON.stringify({basePath:e.basePath,wsPath:e.bridgePath,bridge_session_key:e.bridgeSessionKey,defer_initial_sync:!0,runtime_versions:e.runtimeVersions??Gt()});return`<script type="module" ${Me}>import { startSiteEditAutoBootstrap } from ${JSON.stringify(Ot)};void startSiteEditAutoBootstrap(window, ${t}).catch(() => undefined);</script>`}function Gt(){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=He(import.meta.url);try{let n=t.resolve(e),i=Qt(n,e);if(!i)return;let s=JSON.parse(v(i,"utf8"));return typeof s.version=="string"?s.version:void 0}catch{return}}function Qt(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 Yt(e,t){if(e.includes(Me))return e;let n=Jt(t),i=/<\/body\s*>/i.exec(e);return i?`${e.slice(0,i.index)}${n}${e.slice(i.index)}`:`${e}${n}`}function Zn(e,t=P(Ae(import.meta.url)),n=_){return Ne(e,t,n)?.path??null}function Ne(e,t=P(Ae(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(!Ut.has(s))return null;let o=ce(Sn(t),s);if(Be(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 Zt(e,t){let n=Ne(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 Xt(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 Xt(e){let t=await import("typescript");return t.transpileModule(e,{compilerOptions:{module:t.ModuleKind.ES2022,target:t.ScriptTarget.ES2022}}).outputText}async function en({basePath:e,bridgePath:t,bridgeHealthPath:n,bridgeSessionKey:i,getBridgeHealth:s,launch:o,runRenderProbe:r,handle:u,request:c,response:a}){if(await sn(c,a,n,s,o,r)||await Zt(c,a))return;let h=an(c),d=cn(c),p=h||d?hn(a,{basePath:e,bridgePath:t,bridgeSessionKey:i,patchNextDevChunk:d}):()=>{};try{await u(c,a)}catch(l){throw p(),l}}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 nn(e){let t=new ae.IncomingMessage(new Te);t.method="GET",t.url=ke,t.headers={host:De};let n=new ae.ServerResponse(t);n.assignSocket(new Te);try{return await e.renderToHTML(t,n,ke,{}),{verdict:"ok"}}catch(i){return tn(i)}finally{try{let i=n.socket;i&&(n.detachSocket(i),i.destroy())}catch{}}}function on(e){return new URL(e??"/","http://localhost").searchParams.get(At)===Dt}async function sn(e,t,n,i,s,o){if(e.method!=="GET"||new URL(e.url??"/","http://localhost").pathname!==n)return!1;let u=i(),c=on(e.url)?await o():void 0,a={...u,launch:s,...c?{render:c}:{}};return t.writeHead(u.ok?200:503,{"cache-control":"no-store","content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify(a)),!0}function rn(e){return{ok:!1,bridgePath:e,runtimeReady:!1,routesIndexed:0,sessionRequired:!0}}function an(e){return un(e)?(e.headers["accept-encoding"]="identity",!0):!1}function cn(e){return dn(e)?(e.headers["accept-encoding"]="identity",!0):!1}function un(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===Oe)return!1;let n=e.headers.accept??"";return n===""||n.includes("text/html")}function dn(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 hn(e,t){let n=[],i=e,s=i.flushHeaders.bind(e),o=i.write.bind(e),r=i.end.bind(e),u=!1;i.flushHeaders=()=>{};let c=()=>{if(t.patchNextDevChunk||u)return!1;let a=Buffer.concat(n);if(!/<body\b[^>]*>/iu.test(a.toString("utf8")))return!1;let h=Pe(e,a,t);return h!==a&&!e.headersSent&&e.removeHeader("content-length"),o(h),n.length=0,u=!0,!0};return i.write=(a,h,d)=>u?(a!==void 0&&o(se(a,re(h))),j(h),j(d),!0):(a!==void 0&&(n.push(se(a,re(h))),c()),j(h),j(d),!0),i.end=(a,h,d)=>{let p=typeof a=="function"?void 0:a;if(u)return i.write=o,i.end=r,i.flushHeaders=s,p===void 0?typeof a=="function"?r(a):typeof h=="function"?r(h):typeof d=="function"?r(d):r():typeof h=="function"?r(p,h):typeof d=="function"?r(p,h,d):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=Pe(e,l,t);y!==l&&!e.headersSent&&e.removeHeader("content-length");let S=typeof a=="function"?a:typeof h=="function"?h:typeof d=="function"?d:void 0;return S?r(y,S):r(y)},()=>{i.write=o,i.end=r,i.flushHeaders=s}}function Pe(e,t,n){if(fn(e,t))return Buffer.from(Yt(t.toString("utf8"),n));if(n.patchNextDevChunk&&ln(e)){let i=t.toString("utf8"),s=mn(i);if(s!==i)return Buffer.from(s)}return t}function pn(){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 ln(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 mn(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,s])=>
|
|
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])=>gn(n,i,s),e)}function gn(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 yn(e){let n=He(H(e,"package.json"))(Ht);return n.default??n}function Sn(e){return Be(e)!=="src"?e:ce(In(e),"dist")}function In(e){return je(e)?k.posix.dirname(e):P(e)}function ce(e,t){return je(e)?k.posix.join(e,t):H(e,t)}function je(e){return e.includes("/")&&!e.includes("\\")}function wn(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 xn(e){if(!e)return null;let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0&&t<=65535?t:null}function Cn(e,t,n){return new Promise((i,s)=>{e.once("error",s),e.listen(t,n,()=>{e.off("error",s),i()})})}function Rn(e){return new Promise(t=>{e.close(()=>t())})}function F(e){try{e()}catch{}}function bn(e){try{return e()}catch(t){return Promise.reject(t)}}function En(e,t){let n=e.address();return typeof n=="object"&&n?n.port:t}export{Lt as attachNextDevUpgradeHandler,tn as classifySiteEditRenderProbeError,Jt as createSiteEditAutoBootstrapHtmlSnippet,Yt as injectSiteEditAutoBootstrap,on as isSiteEditRenderProbeRequested,mn as patchNextFirefoxDevChunk,cn as prepareFirefoxNextDevChunkPatchRequest,an as prepareSiteEditHtmlInjectionRequest,zt as resolveSiteEditBridgeHealthPath,Ne as resolveSiteEditClientAsset,Zn as resolveSiteEditClientAssetPath,Kt as resolveSiteEditNextDevBundler,nn as runSiteEditRenderProbe,Yn as startSiteEditNextDevServer,Xt as transpileSiteEditClientSource};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vcp-dev/site-edit-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@vcp-dev/contracts": "0.
|
|
78
|
-
"@vcp-dev/site-edit-engine": "0.
|
|
77
|
+
"@vcp-dev/contracts": "0.8.1",
|
|
78
|
+
"@vcp-dev/site-edit-engine": "0.8.1",
|
|
79
79
|
"@types/ws": "^8.18.1",
|
|
80
80
|
"ws": "^8.20.1"
|
|
81
81
|
},
|