@vcp-dev/site-edit-bridge 0.10.20 → 0.10.22
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/README.md +13 -10
- package/dist/auto-bootstrap.js +7 -7
- package/dist/engine-worker-client.d.ts +26 -0
- package/dist/engine-worker-client.js +257 -0
- package/dist/engine-worker-messages.js +44 -0
- package/dist/engine-worker.d.ts +1 -0
- package/dist/engine-worker.js +4 -0
- package/dist/index.js +9 -9
- package/dist/next-dev-server.d.ts +2 -1
- package/dist/next-dev-server.js +3 -3
- package/dist/server.d.ts +42 -0
- package/dist/server.js +2 -2
- package/package.json +8 -3
|
@@ -2,6 +2,7 @@ import http, { type IncomingMessage, type Server as HttpServer, type RequestList
|
|
|
2
2
|
import type { Duplex } from "node:stream";
|
|
3
3
|
import type { SiteEditRuntimeVersions } from "./auto-bootstrap.js";
|
|
4
4
|
import { attachSiteEditBridgeServer, type SiteEditBridgeHealth } from "./server.js";
|
|
5
|
+
export { attachSiteEditBridgeServerInWorker, type SiteEditBridgeWorkerAttachment, } from "./engine-worker-client.js";
|
|
5
6
|
export type SiteEditNextDevServerOptions = {
|
|
6
7
|
basePath?: string;
|
|
7
8
|
bridgePath?: string;
|
|
@@ -24,6 +25,7 @@ type NextDevApp = {
|
|
|
24
25
|
type SiteEditBridgeAttachment = {
|
|
25
26
|
getHealth(): SiteEditBridgeHealth;
|
|
26
27
|
stop(): void;
|
|
28
|
+
whenReady?(): Promise<void>;
|
|
27
29
|
};
|
|
28
30
|
type SiteEditNextDevServerProcess = {
|
|
29
31
|
env: NodeJS.ProcessEnv;
|
|
@@ -90,4 +92,3 @@ export declare function isSiteEditRenderProbeRequested(url: string | undefined):
|
|
|
90
92
|
export declare function prepareSiteEditHtmlInjectionRequest(request: Pick<IncomingMessage, "headers" | "method" | "url">): boolean;
|
|
91
93
|
export declare function prepareFirefoxNextDevChunkPatchRequest(request: Pick<IncomingMessage, "headers" | "method" | "url">): boolean;
|
|
92
94
|
export declare function patchNextFirefoxDevChunk(source: string): string;
|
|
93
|
-
export {};
|
package/dist/next-dev-server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import ue from"node:http";import{existsSync as _,readFileSync as b}from"node:fs";import{randomUUID as Nt}from"node:crypto";import{createRequire as Ne}from"node:module";import{Socket as Pe}from"node:net";import H from"node:path";import{basename as Be,dirname as A,join as N}from"node:path";import{fileURLToPath as De}from"node:url";import et from"node:http";import P from"node:fs";import{createHash as Ee,randomBytes as tt,timingSafeEqual as nt}from"node:crypto";import g from"node:path";import{WebSocket as ve,WebSocketServer as st}from"ws";import{createPreviewSiteEditEngineRuntime as it,openFileHistoryStore as ot,resolveManagedPageTemplateComposition as rt,scanSiteEditCompositionsFromFiles as at}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as ct,scanNextAppRouterRoutesFromFiles as ut}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as dt}from"@vcp-dev/site-edit-engine/source-watcher";function ge(e){let t=new Map,n=[],s=new Map,i=[],o=e.pageComposition.status==="supported"?e.pageComposition.placementUnitKeys:[],r=new Map(o.map((c,p)=>[c,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(c=>({placementKey:c.placementKey,placementUnitKey:c.placementUnitKey,relation:c.relation}))}:{status:"unsupported",reason:e.pageComposition.reason,diagnostics:e.pageComposition.diagnostics},d=e.entries.flatMap((c,p)=>c.dynamicDataSummary?[[p,c.dynamicDataSummary]]:[]),a=c=>{let p=t.get(c);if(typeof p=="number")return p;let f=n.length;return n.push(c),t.set(c,f),f},h=c=>{let p=null;for(let f of We(c)){let S=`${p??"root"}\0${f}`,I=s.get(S);if(typeof I=="number"){p=I;continue}let w=i.length;i.push([p,a(f)]),s.set(S,w),p=w}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:i,entries:e.entries.map(c=>{let p=c.pageCompositionUnitKey?r.get(c.pageCompositionUnitKey)??-1:-1;if(c.pageCompositionUnitKey&&p<0)throw new Error(`Render entry ${c.key} references unknown page composition unit ${c.pageCompositionUnitKey}`);return[h(c.key),$e(c.operationSummary),...p>=0?[p]:[]]}),...e.entries.every(c=>c.parentKey!==void 0)?{entryParentNodeIds:e.entries.map(c=>c.parentKey===null?null:h(c.parentKey))}:{},...d.length>0?{dynamicDataSummaries:d}:{}}}function $e(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,s=me(e,n);if(s<0)return t.push(e),t;for(t.push(e.slice(0,s)),n=s;n<e.length;){if(s=me(e,n+(e.startsWith("::",n)?2:1)),s<0){t.push(e.slice(n));break}t.push(e.slice(n,s)),n=s}return t}function me(e,t){let n=e.indexOf("/",t),s=e.indexOf("::",t);return n<0?s:s<0?n:Math.min(n,s)}var ye="SITE_EDIT_COMPOSITION_SESSION_MISMATCH",Z="SITE_EDIT_COMPOSITION_SESSION_REQUIRED";var m=class e{constructor(){}static parseMessage(t,n={}){let s=JSON.parse(t);if(!e.isMessage(s,n))throw new Error("Invalid site edit bridge message");return s}static stringifyMessage(t){return JSON.stringify(t)}static extractRequestInfo(t){if(l(t)&&l(t.payload)){let n=typeof t.payload.id=="string"?t.payload.id:"unknown",s=this.isRequestKind(t.payload.kind)?t.payload.kind:"get-document";return{id:n,kind:s}}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!l(t)||typeof t.type!="string"?!1:t.type==="request"?Le(t.payload,n):t.type==="response"||t.type==="error"?l(t.payload)&&typeof t.payload.id=="string":t.type==="event"?l(t.payload)&&l(t.payload.event):t.type==="auth"?l(t.payload)&&typeof t.payload.key=="string":t.type==="auth-ok"&&l(t.payload)&&t.payload.ok===!0}},j=class{constructor(t){this.options=t,this.session={subscribedCompositionKeys:new Set},this.messageQueue=new Y,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(s){this.sendJson({type:"error",payload:{id:"unknown",kind:"get-document",message:s instanceof Error?s.message:"Bridge queue overflow"}}),t.socket.close(1013,"bridge busy")}},t.socket.onclose=()=>{this.unsubscribe(),this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.clear()}}async handleMessage(t){try{let n=t;if(n.type!=="request")return;let s=n.payload,{id:i}=s;switch(s.kind){case"initialize-session":{let o=L(s),r=await this.resolve(o),u=await this.options.engine.initializeSession({composition:r.context,...s.mutationContext?{mutationContext:s.mutationContext}:{}});this.activateSession(r.context),this.sendJson({type:"response",payload:{id:i,kind:s.kind,result:x({...u,data:{...u.data,document:Ie(u.data.document)}},r.context)}});return}case"get-document":{let o=await this.resolveWireContext(s),r=await this.options.engine.getDocument(o);this.sendResult(i,s.kind,Ze(r,o));return}case"get-history-status":{let o=await this.resolveWireContext(s),r=await this.options.engine.getHistoryStatus(o);this.sendResult(i,s.kind,x(r,o));return}case"get-style-context":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getStyleContext(o),o));return}case"get-object-edit-context":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getObjectEditContext(s.key,o,s.selectionRef),o));return}case"get-class-name-source":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getClassNameSource(s.key,o),o));return}case"get-source-tree":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getSourceTree(o),o));return}case"get-source-file":{let o=await this.resolveWireContext(s);this.sendResult(i,s.kind,x(await this.options.engine.getSourceFile({path:s.path,composition:o}),o));return}case"resolve-source-location":{let o=await this.resolveWireContext(s),r=x(await this.options.engine.resolveSourceLocation({key:s.key,composition:o}),o);this.sendResult(i,s.kind,r);return}case"execute":{let o=await this.resolveOperationWireContext(s.request),r=await this.resolveWireContext(s);if(C(o)!==C(r))throw new R(ye,"The execute request composition does not match its operation composition.");let u=await this.normalizeOperationRequest(s.request,o);await this.handleBufferedOperation(i,s.kind,()=>this.options.engine.execute(u));return}case"undo":case"redo":{let o=await this.resolveWireContext(s);await this.handleBufferedOperation(i,s.kind,()=>s.kind==="undo"?this.options.engine.undo(o):this.options.engine.redo(o));return}}}catch(n){let s=m.extractRequestInfo(t);this.sendJson({type:"error",payload:{...s,message:Q(n),code:J(n)}})}}async resolve(t){return this.options.engine.resolveComposition(t)}async resolveAndAssert(t){let n=await this.resolve(t),s=this.session.activeComposition;if(!s)throw new R(Z,"Initialize a composition session before issuing this request.");if(C(s)!==C(n.context))throw new R(ye,"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=Ge(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 s={...t,composition:Se(n)};if(s.target.kind==="route"){let i=s.target,o=L(i),r=await this.resolveAndAssert(o);s.target={...i,composition:Se(r)}}return s}handleEngineEvent(t){let n=t?.composition;if(!Qe(n))return;let s=C(n);this.session.subscribedCompositionKeys.has(s)&&(this.eventDispatcher.push(t),t.type==="site-edit.graph.invalidated"&&s===Ye(this.session)&&(this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.delete(s)))}sendResult(t,n,s){this.sendJson({type:"response",payload:{id:t,kind:n,result:s}})}async handleRawMessage(t){try{await this.handleMessage(m.parseMessage(t,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let s=Xe(t);this.sendJson({type:"error",payload:{...s,message:Q(n),code:J(n)}})}}sendJson(t){this.options.socket.send(m.stringifyMessage(t))}async handleBufferedOperation(t,n,s){this.eventDispatcher.beginBuffering();try{let i=await s();this.sendResult(t,n,i)}catch(i){this.sendJson({type:"error",payload:{id:t,kind:n,message:Q(i),code:J(i)}})}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))}},Y=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()},s=this.queue.then(n,n);return this.queue=s.then(()=>{},()=>{}),s}};function Le(e,t){if(!l(e)||typeof e.id!="string"||!m.isRequestKind(e.kind))return!1;let n=k(e.composition);switch(e.kind){case"initialize-session":return n&&(e.mutationContext===void 0||z(e.mutationContext));case"get-document":case"get-style-context":return n;case"get-object-edit-context":return typeof e.key=="string"&&n&&ze(e.selectionRef);case"get-class-name-source":case"resolve-source-location":return typeof e.key=="string"&&n;case"get-history-status":return n;case"get-source-tree":return n;case"get-source-file":return typeof e.path=="string"&&n;case"execute":return n&&Je(e.request)&&(e.request.mutationContext===void 0||z(e.request.mutationContext))&&(t.validateOperationRequest?.(e.request)??!0);case"undo":case"redo":return n&&e.mutationContext===void 0||n&&z(e.mutationContext)}}function ze(e){if(e===void 0)return!0;if(!l(e)||typeof e.key!="string"||e.key.length===0)return!1;let t=e.instanceIndex,n=e.instanceCount;return t!==void 0&&(typeof t!="number"||!Number.isInteger(t)||t<0)||n!==void 0&&(typeof n!="number"||!Number.isInteger(n)||n<=0)||typeof t=="number"&&typeof n=="number"&&t>=n?!1:Object.keys(e).every(s=>s==="key"||s==="instanceIndex"||s==="instanceCount")}function Je(e){return l(e)&&typeof e.id=="string"&&typeof e.siteId=="string"&&typeof e.baseSnapshotId=="string"&&k(e.composition)&&typeof e.kind=="string"&&l(e.target)&&(e.target.kind!=="route"||k(e.target.composition))&&typeof e.documentVersion=="number"&&Number.isInteger(e.documentVersion)&&e.documentVersion>=0&&l(e.params)}function k(e){return l(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 Qe(e){return l(e)&&typeof e.routeId=="string"&&(e.effectiveTemplateId===void 0||typeof e.effectiveTemplateId=="string")}function L(e){if(k(e.composition))return e.composition;throw new R(Z,"A composition selector is required for this request.")}function Ge(e){if(l(e)&&k(e.composition))return e.composition;throw new R(Z,"A composition selector is required for execute.")}function Se(e){return{routeId:e.routeId,...e.effectiveTemplateId?{templateId:e.effectiveTemplateId}:{}}}function Ye(e){return e.activeComposition?C(e.activeComposition):null}function C(e){return JSON.stringify([e.routeId,e.effectiveTemplateId??null])}function x(e,t){return l(e)&&e.ok===!0?{...e,composition:t}:e}function Ze(e,t){return!l(e)||e.ok!==!0?e:{...e,...t?{composition:t}:{},data:Ie(e.data)}}function Ie(e){let t=e;return ge(t.routeId?t:{...t,routeId:t.composition.routeId})}function l(e){return typeof e=="object"&&e!==null}function z(e){return l(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 J(e){if(!(!l(e)||typeof e.code!="string"||e.code.length===0))return e.code}function Q(e){return e instanceof Error||l(e)&&typeof e.message=="string"&&e.message.length>0?e.message:"Unknown websocket operation failure"}function Xe(e){try{return m.extractRequestInfo(JSON.parse(e))}catch{return{id:"unknown",kind:"get-document"}}}var F=class{constructor(){this.tail=Promise.resolve()}run(t){let n=this.tail.then(t,t);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as ht,computeSiteEditCompositionKey as E}from"@vcp-dev/contracts";var pt="127.0.0.1",lt=30218,ft="/__vcp/site-edit/bridge",be=6*1024*1024,mt=be*2,gt=3e3,we=50,te=".vcp/current-snapshot.json",xe="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function _e(e,t,n={}){let s=new ne(yt(t),e,!1,n);return s.start({listen:!1}),s}var ne=class{constructor(t,n=et.createServer((o,r)=>{r.writeHead(404),r.end("VCP site edit bridge server")}),s=!0,i={}){this.options=t,this.server=n,this.closeHttpServerOnStop=s,this.dependencies=i,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new F,this.eventListeners=new Set,this.routes=[],this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.launchCandidateEntries=null,this.stopWatching=null,this.stopWebSocketServer=null,this.pendingChangedFiles=new Set,this.fileChangeTimer=null,this.isStarted=!1,this.stopping=!1,this.runtimeVersionFloors=new Map,this.activeHistoryStore=null,this.versionPersistence=Promise.resolve()}start(t={}){if(this.isStarted)return;this.stopping=!1;try{P.mkdirSync(g.join(this.options.projectRoot,".vcp"),{recursive:!0})}catch(s){console.warn(`[vcp-site-edit] failed to ensure .vcp directory: ${s instanceof Error?s.message:String(s)}`)}this.refreshCompositions(!0),this.isStarted=!0,this.createRuntime();let n=Et({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 dt({projectRoot:this.options.projectRoot,watch_dirs:St(this.options.runtime.watch_dirs),interval_ms:this.options.runtime.watch_interval_ms,debounce_ms:this.options.runtime.watch_debounce_ms??we,max_dirs:this.options.runtime.watch_max_dirs,max_files:this.options.runtime.watch_max_files,max_depth:this.options.runtime.watch_max_depth,max_startup_ms:this.options.runtime.watch_max_startup_ms,on_change:s=>this.handleFileChanged(s),on_batch_change:s=>this.handleFilesChanged(s)}).start(),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.stopping=!0,this.stopWatching?.(),this.stopWatching=null,this.flushChangedFiles(),this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.stopWebSocketServer?.(),this.stopWebSocketServer=null,this.runtime?.dispose(),this.runtime=null,this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.closeHttpServerOnStop&&this.server.close();let t=Ct(),n=Rt(this.options);t.get(n)===this&&t.delete(n),this.isStarted=!1}createRuntime(t,n=null){let s=this.runtime;this.activeHistoryStore=t??null;let i=(this.dependencies.createRuntime??it)({...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=i,this.activeHistorySessionId=n,s?.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 s=wt(t.composition),i=this.getRuntime(),o=y(s);await i.initializeComposition(o);let u=i.getDocument(o).data;await this.recordRuntimeVersion(u.version,s);let d=(await i.getHistoryStatus()).data;return{ok:!0,composition:s,data:{document:u,historyStatus:d}}}),getDocument:async t=>{let n=this.getRuntime(),s=y(t);await n.initializeComposition(s);let i=n.getDocument(s);return await this.recordRuntimeVersion(i.data.version,t),{...i,composition:t}},getHistoryStatus:async t=>({...await this.getRuntime().getHistoryStatus(),composition:t}),getStyleContext:async t=>this.getRuntime().getStyleContext(y(t)),getObjectEditContext:async(t,n,s)=>this.getRuntime().getObjectEditContext(t,y(n),s),getClassNameSource:async(t,n)=>this.getRuntime().getClassNameSource(t,y(n)),getSourceTree:async t=>this.getRuntime().getSourceTree(y(t)),getSourceFile:async t=>this.getRuntime().getSourceFile({path:t.path,composition:y(t.composition)}),resolveSourceLocation:async t=>this.getRuntime().resolveSourceLocation({key:t.key,composition:y(t.composition)}),execute:(t,n)=>this.mutationQueue.run(async()=>{let s=await this.bindHistorySession(this.options.snapshotId);if(!s.ok)return X(s,t);let i=await this.getRuntime().executeOperation(t);return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion,i.composition),i}),undo:t=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return X(n,void 0,t);let s=await this.getRuntime().undo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion,s.composition??t),s.composition?s:{...s,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 s=await this.getRuntime().redo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion,s.composition??t),s.composition?s:{...s,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 s=await(this.dependencies.openHistoryStore??ot)(xt({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:t}));if(!this.isStarted)return;let i=s.readDocumentVersion?await s.readDocumentVersion():0,o=this.compositions[0]?.context;if(o){let r=E(o);this.runtimeVersionFloors.set(r,Math.max(this.runtimeVersionFloors.get(r)??0,i))}this.createRuntime(s,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 s=n?E(n):null;s&&this.runtimeVersionFloors.set(s,Math.max(this.runtimeVersionFloors.get(s)??0,t));let i=this.activeHistoryStore;if(!i?.writeDocumentVersion)return Promise.resolve();let o=s?this.runtimeVersionFloors.get(s)??t:t;return this.versionPersistence=this.versionPersistence.then(()=>i.writeDocumentVersion(o)).catch(()=>{}),this.versionPersistence}handleFileChanged(t){this.handleFilesChanged([t])}handleFilesChanged(t){for(let s of t)this.pendingChangedFiles.add(s);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let n=this.options.runtime.watch_debounce_ms??we;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,s)=>n.localeCompare(s));if(this.pendingChangedFiles.clear(),t.some(At)&&this.scheduleSnapshotIdentityAdvance(),!!this.refreshCompositions(!1))for(let n of t)this.runtime?.notifyFileChanged(n)}scheduleSnapshotIdentityAdvance(){if(this.stopping||!this.isStarted)return;let t=this.readCurrentSnapshotMarker();!t||t===this.options.snapshotId||this.mutationQueue.run(()=>this.advanceSnapshotId(t)).catch(n=>{console.warn(`[vcp-site-edit] snapshot identity advance failed: ${n instanceof Error?n.message:String(n)}`)})}readCurrentSnapshotMarker(){try{let t=P.readFileSync(g.join(this.options.projectRoot,te),"utf8"),n=JSON.parse(t),s=typeof n.snapshot_id=="string"?n.snapshot_id:"";return!s||se(s)!==s?(console.warn("[vcp-site-edit] current-snapshot marker has no valid snapshot_id"),null):s}catch(t){return console.warn(`[vcp-site-edit] current-snapshot marker unreadable: ${t instanceof Error?t.message:String(t)}`),null}}async advanceSnapshotId(t){if(this.stopping||!this.isStarted||t===this.options.snapshotId)return;let n=this.options.snapshotId;this.options.snapshotId=t;try{if(this.options.historyRoot){let i=await this.bindHistorySession(t);if(!i.ok)throw new Error(`${i.code}: ${i.message}`)}else this.createRuntime()}catch(i){this.options.snapshotId=n,console.warn(`[vcp-site-edit] snapshot identity advance rolled back to ${n}: ${i instanceof Error?i.message:String(i)}`);return}let s=new Date().toISOString();for(let i of this.compositions){let o={type:"site-edit.snapshot.changed",siteId:this.options.siteId,composition:i.context,snapshotId:t,previousSnapshotId:n,at:s};for(let r of this.eventListeners)try{r(o)}catch(u){console.warn(`[vcp-site-edit] snapshot.changed listener failed: ${u instanceof Error?u.message:String(u)}`)}}try{this.options.onSnapshotIdChanged?.(t,n)}catch(i){console.warn(`[vcp-site-edit] onSnapshotIdChanged callback failed: ${i instanceof Error?i.message:String(i)}`)}}refreshCompositions(t=!1){try{let n=It(this.options.projectRoot),s=ut(n),i=s.length>0?s:(this.options.runtime.routes??[]).map(a=>({...a})),o=at({files:n});if(o.length===0)throw new Error("No canonical App Router composition is indexed by the preview bridge.");let r=JSON.stringify(o.filter(a=>a.context.effectiveTemplateId!==void 0).map(a=>a.entryFile).sort());if(t&&(this.launchCandidateEntries=r),this.launchCandidateEntries!==r)throw new Error("SITE_EDIT_RUNTIME_RELOAD_REQUIRED: Candidate route collection changed since preview launch");let u=new Map(this.compositions.map(a=>[E(a.context),a])),d=new Map(o.map(a=>[E(a.context),a]));if(this.runtime){for(let[a,h]of u){let c=d.get(a);(!c||c.entryFile!==h.entryFile)&&this.runtime.unregisterComposition(y(h.context))}for(let[a,h]of d){let c=u.get(a);(!c||c.entryFile!==h.entryFile)&&this.runtime.registerComposition(h.context,h.entryFile)}}return this.routes=i,this.compositions=o,this.compositionFiles=n,this.compositionResolutionError=null,!0}catch(n){let s=n instanceof Error?n:new Error(String(n));if(this.compositionResolutionError=s,t)throw s;return!1}}resolveComposition(t){if(this.compositionResolutionError)throw this.compositionResolutionError;let n=ct(t.routeId,this.routes,this.options.basePath);if(!n)throw new Error(`Route is not indexed by the preview bridge: ${t.routeId}`);let s=this.routes.find(r=>r.routeId===n);if(!s)throw new Error(`Route is not indexed by the preview bridge: ${t.routeId}`);let i=rt({selector:{routeId:n,...t.templateId!==void 0?{templateId:t.templateId}:{}},files:this.compositionFiles,canonicalEntryFile:s.entryFile}),o=E(i.context);if(!this.compositions.some(r=>E(r.context)===o))throw new Error(`Composition is not registered by the preview bridge: ${o}`);return i}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 yt(e){return{tenantId:e.tenantId,siteId:e.siteId,snapshotId:e.snapshotId,projectRoot:g.resolve(e.projectRoot??process.cwd()),basePath:e.basePath??"",host:e.host??pt,port:e.port??lt,path:e.path??ft,sessionKey:e.sessionKey??Ht(),historyRoot:e.historyRoot,allowedOrigins:e.allowedOrigins??[],runtime:e.runtime??{},onSnapshotIdChanged:e.onSnapshotIdChanged}}function St(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 It(e){let t=[],n=["app","src/app",".vcp"],s=new Set([".git",".next",".turbo","coverage","dist","node_modules","output","snapshot-store"]),i=o=>{if(!P.existsSync(o))return;let r;try{r=P.readdirSync(o,{withFileTypes:!0})}catch{return}for(let u of r){if(u.name.startsWith(".")&&u.name!==".vcp")continue;if(u.isDirectory()){if(s.has(u.name))continue;i(g.join(o,u.name));continue}if(!u.isFile())continue;let d=g.join(o,u.name),a="";try{a=P.readFileSync(d,"utf8")}catch{}t.push({path:g.relative(e,d).replaceAll(g.sep,"/"),content:a})}};for(let o of n)i(g.join(e,o));return t.sort((o,r)=>o.path.localeCompare(r.path))}function wt(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 y(e){return{routeId:e.routeId,...e.effectiveTemplateId?{templateId:e.effectiveTemplateId}:{}}}function xt(e){return g.join(g.resolve(e.root),se(e.siteId),se(e.snapshotId),Ee("sha256").update(e.editingSessionId).digest("hex").slice(0,32))}function se(e){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(e)&&e!=="."&&e!==".."?e:Ee("sha256").update(e).digest("hex").slice(0,32)}function Ct(){let e=globalThis;return e[xe]??=new Map,e[xe]}function Rt(e){return`${e.projectRoot}:${e.host}:${e.port}:${e.path}:${e.historyRoot??""}`}function X(e,t,n){let s=n??(t?.composition?{routeId:t.composition.routeId,...t.composition.templateId?{effectiveTemplateId:t.composition.templateId}:{}}:void 0);if(!s)throw new Error("A normalized composition is required to construct a mutation result.");return{ok:!1,composition:s,error:{code:e.code,message:e.message},data:{requestId:t?.id??"mutation-context",kind:t?.kind??"update-text",composition:s,target:t?.target??{kind:"node",key:""},resultVersion:t?.documentVersion??0}}}function Et(e){let t=new st({maxPayload:be,noServer:!0}),n=new WeakSet,s=o=>{new j({socket:new ie(o),engine:e.engine,validateOperationRequest:r=>ht.safeParse(r).success})},i=(o,r,u)=>{if(!vt(o,e))return;let d=bt(o,e);if(!d){r.write(`HTTP/1.1 401 Unauthorized\r
|
|
1
|
+
import Ee from"node:http";import{existsSync as U,readFileSync as j}from"node:fs";import{randomUUID as Qt}from"node:crypto";import{createRequire as Ve}from"node:module";import{Socket as Ke}from"node:net";import q from"node:path";import{basename as Le,dirname as z,join as G}from"node:path";import{fileURLToPath as qe}from"node:url";import{Worker as Kt}from"node:worker_threads";var Ce=["resolveComposition","initializeSession","getDocument","getHistoryStatus","getStyleContext","getObjectEditContext","getClassNameSource","getSourceTree","getSourceFile","resolveSourceLocation","execute","undo","redo"];function te(e){let t=new Error(e.message);return t.name=e.name,e.code!==void 0&&(t.code=e.code),t}import dt from"node:http";import L from"node:fs";import{createHash as Ae,randomBytes as ht,timingSafeEqual as pt}from"node:crypto";import v from"node:path";import{WebSocket as Oe,WebSocketServer as lt}from"ws";import{createPreviewSiteEditEngineRuntime as ft,openFileHistoryStore as gt,resolveManagedPageTemplateComposition as mt,scanSiteEditCompositionsFromFiles as yt}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as St,scanNextAppRouterRoutesFromFiles as It}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as wt}from"@vcp-dev/site-edit-engine/source-watcher";function Re(e){let t=new Map,n=[],i=new Map,s=[],o=e.pageComposition.status==="supported"?e.pageComposition.placementUnitKeys:[],r=new Map(o.map((a,f)=>[a,f])),u=e.pageComposition.status==="supported"?{status:"supported",containerKey:e.pageComposition.containerKey,hasBusinessMembers:e.pageComposition.hasBusinessMembers,placementUnitKeys:e.pageComposition.placementUnitKeys,placements:e.pageComposition.placements.map(a=>({placementKey:a.placementKey,placementUnitKey:a.placementUnitKey,relation:a.relation}))}:{status:"unsupported",reason:e.pageComposition.reason,diagnostics:e.pageComposition.diagnostics},p=e.entries.flatMap((a,f)=>a.dynamicDataSummary?[[f,a.dynamicDataSummary]]:[]),c=a=>{let f=t.get(a);if(typeof f=="number")return f;let m=n.length;return n.push(a),t.set(a,m),m},l=a=>{let f=null;for(let m of tt(a)){let y=`${f??"root"}\0${m}`,S=i.get(y);if(typeof S=="number"){f=S;continue}let E=s.length;s.push([f,c(m)]),i.set(y,E),f=E}if(f===null)throw new Error("Document key must not be empty");return f};return{encoding:"segment-trie-v1",siteId:e.siteId,snapshotId:e.snapshotId,composition:{...e.composition},routeId:e.composition.routeId,version:e.version,pageComposition:u,tokens:n,nodes:s,entries:e.entries.map(a=>{let f=a.pageCompositionUnitKey?r.get(a.pageCompositionUnitKey)??-1:-1;if(a.pageCompositionUnitKey&&f<0)throw new Error(`Render entry ${a.key} references unknown page composition unit ${a.pageCompositionUnitKey}`);return[l(a.key),et(a.operationSummary),...f>=0?[f]:[]]}),...e.entries.every(a=>a.parentKey!==void 0)?{entryParentNodeIds:e.entries.map(a=>a.parentKey===null?null:l(a.parentKey))}:{},...p.length>0?{dynamicDataSummaries:p}:{}}}function et(e){return(e.canUpdateText?1:0)|(e.canInsertChild?2:0)|(e.canMove?4:0)|(e.canRemove?8:0)}function tt(e){if(!e)return[];let t=[],n=0,i=ve(e,n);if(i<0)return t.push(e),t;for(t.push(e.slice(0,i)),n=i;n<e.length;){if(i=ve(e,n+(e.startsWith("::",n)?2:1)),i<0){t.push(e.slice(n));break}t.push(e.slice(n,i)),n=i}return t}function ve(e,t){let n=e.indexOf("/",t),i=e.indexOf("::",t);return n<0?i:i<0?n:Math.min(n,i)}var be="SITE_EDIT_COMPOSITION_SESSION_MISMATCH",ce="SITE_EDIT_COMPOSITION_SESSION_REQUIRED";var C=class e{constructor(){}static parseMessage(t,n={}){let i=JSON.parse(t);if(!e.isMessage(i,n))throw new Error("Invalid site edit bridge message");return i}static stringifyMessage(t){return JSON.stringify(t)}static extractRequestInfo(t){if(g(t)&&g(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!g(t)||typeof t.type!="string"?!1:t.type==="request"?nt(t.payload,n):t.type==="response"||t.type==="error"?g(t.payload)&&typeof t.payload.id=="string":t.type==="event"?g(t.payload)&&g(t.payload.event):t.type==="auth"?g(t.payload)&&typeof t.payload.key=="string":t.type==="auth-ok"&&g(t.payload)&&t.payload.ok===!0}},J=class{constructor(t){this.options=t,this.session={subscribedCompositionKeys:new Set},this.messageQueue=new ae,this.eventDispatcher=new re(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=ne(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:k({...u,data:{...u.data,document:Te(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,ct(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,k(r,o));return}case"get-style-context":{let o=await this.resolveWireContext(i);this.sendResult(s,i.kind,k(await this.options.engine.getStyleContext(o),o));return}case"get-object-edit-context":{let o=await this.resolveWireContext(i);this.sendResult(s,i.kind,k(await this.options.engine.getObjectEditContext(i.key,o,i.selectionRef),o));return}case"get-class-name-source":{let o=await this.resolveWireContext(i);this.sendResult(s,i.kind,k(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,k(await this.options.engine.getSourceTree(o),o));return}case"get-source-file":{let o=await this.resolveWireContext(i);this.sendResult(s,i.kind,k(await this.options.engine.getSourceFile({path:i.path,composition:o}),o));return}case"resolve-source-location":{let o=await this.resolveWireContext(i),r=k(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(N(o)!==N(r))throw new A(be,"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=C.extractRequestInfo(t);this.sendJson({type:"error",payload:{...i,message:oe(n),code:se(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 A(ce,"Initialize a composition session before issuing this request.");if(N(i)!==N(n.context))throw new A(be,"The request composition does not match the active socket session.");return n.context}async resolveWireContext(t){let n=ne(t);return this.resolveAndAssert(n)}async resolveOperationWireContext(t){let n=rt(t);return this.resolveAndAssert(n)}activateSession(t){this.session.activeComposition=t,this.session.subscribedCompositionKeys.clear(),this.session.subscribedCompositionKeys.add(N(t))}async normalizeOperationRequest(t,n){let i={...t,composition:_e(n)};if(i.target.kind==="route"){let s=i.target,o=ne(s),r=await this.resolveAndAssert(o);i.target={...s,composition:_e(r)}}return i}handleEngineEvent(t){let n=t?.composition;if(!ot(n))return;let i=N(n);this.session.subscribedCompositionKeys.has(i)&&(this.eventDispatcher.push(t),t.type==="site-edit.graph.invalidated"&&i===at(this.session)&&(this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.delete(i)))}sendResult(t,n,i){this.sendJson({type:"response",payload:{id:t,kind:n,result:i}})}async handleRawMessage(t){try{await this.handleMessage(C.parseMessage(t,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let i=ut(t);this.sendJson({type:"error",payload:{...i,message:oe(n),code:se(n)}})}}sendJson(t){this.options.socket.send(C.stringifyMessage(t))}async handleBufferedOperation(t,n,i){this.eventDispatcher.beginBuffering();try{let s=await i();this.sendResult(t,n,s)}catch(s){this.sendJson({type:"error",payload:{id:t,kind:n,message:oe(s),code:se(s)}})}finally{this.eventDispatcher.flush()}}},A=class extends Error{constructor(t,n){super(n),this.code=t,this.name="BridgeProtocolError"}},re=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))}},ae=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 nt(e,t){if(!g(e)||typeof e.id!="string"||!C.isRequestKind(e.kind))return!1;let n=V(e.composition);switch(e.kind){case"initialize-session":return n&&(e.mutationContext===void 0||ie(e.mutationContext));case"get-document":case"get-style-context":return n;case"get-object-edit-context":return typeof e.key=="string"&&n&&it(e.selectionRef);case"get-class-name-source":case"resolve-source-location":return typeof e.key=="string"&&n;case"get-history-status":return n;case"get-source-tree":return n;case"get-source-file":return typeof e.path=="string"&&n;case"execute":return n&&st(e.request)&&(e.request.mutationContext===void 0||ie(e.request.mutationContext))&&(t.validateOperationRequest?.(e.request)??!0);case"undo":case"redo":return n&&e.mutationContext===void 0||n&&ie(e.mutationContext)}}function it(e){if(e===void 0)return!0;if(!g(e)||typeof e.key!="string"||e.key.length===0)return!1;let t=e.instanceIndex,n=e.instanceCount;return t!==void 0&&(typeof t!="number"||!Number.isInteger(t)||t<0)||n!==void 0&&(typeof n!="number"||!Number.isInteger(n)||n<=0)||typeof t=="number"&&typeof n=="number"&&t>=n?!1:Object.keys(e).every(i=>i==="key"||i==="instanceIndex"||i==="instanceCount")}function st(e){return g(e)&&typeof e.id=="string"&&typeof e.siteId=="string"&&typeof e.baseSnapshotId=="string"&&V(e.composition)&&typeof e.kind=="string"&&g(e.target)&&(e.target.kind!=="route"||V(e.target.composition))&&typeof e.documentVersion=="number"&&Number.isInteger(e.documentVersion)&&e.documentVersion>=0&&g(e.params)}function V(e){return g(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 ot(e){return g(e)&&typeof e.routeId=="string"&&(e.effectiveTemplateId===void 0||typeof e.effectiveTemplateId=="string")}function ne(e){if(V(e.composition))return e.composition;throw new A(ce,"A composition selector is required for this request.")}function rt(e){if(g(e)&&V(e.composition))return e.composition;throw new A(ce,"A composition selector is required for execute.")}function _e(e){return{routeId:e.routeId,...e.effectiveTemplateId?{templateId:e.effectiveTemplateId}:{}}}function at(e){return e.activeComposition?N(e.activeComposition):null}function N(e){return JSON.stringify([e.routeId,e.effectiveTemplateId??null])}function k(e,t){return g(e)&&e.ok===!0?{...e,composition:t}:e}function ct(e,t){return!g(e)||e.ok!==!0?e:{...e,...t?{composition:t}:{},data:Te(e.data)}}function Te(e){let t=e;return Re(t.routeId?t:{...t,routeId:t.composition.routeId})}function g(e){return typeof e=="object"&&e!==null}function ie(e){return g(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 se(e){if(!(!g(e)||typeof e.code!="string"||e.code.length===0))return e.code}function oe(e){return e instanceof Error||g(e)&&typeof e.message=="string"&&e.message.length>0?e.message:"Unknown websocket operation failure"}function ut(e){try{return C.extractRequestInfo(JSON.parse(e))}catch{return{id:"unknown",kind:"get-document"}}}var Q=class{constructor(){this.tail=Promise.resolve()}run(t){let n=this.tail.then(t,t);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as Et,computeSiteEditCompositionKey as O}from"@vcp-dev/contracts";var xt="127.0.0.1",Ct=30218,vt="/__vcp/site-edit/bridge",De=6*1024*1024,Rt=De*2,bt=3e3,ke=50,he=".vcp/current-snapshot.json",Pe="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function Me(e,t,n={}){let i=new pe(je(t),e,!1,n);return i.start({listen:!1}),i}var pe=class{constructor(t,n=dt.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 Q,this.eventListeners=new Set,this.routes=[],this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.launchCandidateEntries=null,this.stopWatching=null,this.stopWebSocketServer=null,this.pendingChangedFiles=new Set,this.fileChangeTimer=null,this.isStarted=!1,this.stopping=!1,this.runtimeVersionFloors=new Map,this.activeHistoryStore=null,this.versionPersistence=Promise.resolve(),this.bridgeEngine=null,this.lastNotifiedHealth=null}start(t={}){if(!this.isStarted){this.stopping=!1;try{L.mkdirSync(v.join(this.options.projectRoot,".vcp"),{recursive:!0})}catch(n){console.warn(`[vcp-site-edit] failed to ensure .vcp directory: ${n instanceof Error?n.message:String(n)}`)}if(this.refreshCompositions(!0),this.isStarted=!0,this.createRuntime(),this.bridgeEngine=this.createBridgeEngine(),t.webSocket!==!1){let n=me({server:this.server,path:this.options.path,sessionKey:this.options.sessionKey,host:this.options.host,allowedOrigins:this.options.allowedOrigins,engine:this.bridgeEngine});this.stopWebSocketServer=n.stop}this.stopWatching=new wt({projectRoot:this.options.projectRoot,watch_dirs:_t(this.options.runtime.watch_dirs),interval_ms:this.options.runtime.watch_interval_ms,debounce_ms:this.options.runtime.watch_debounce_ms??ke,max_dirs:this.options.runtime.watch_max_dirs,max_files:this.options.runtime.watch_max_files,max_depth:this.options.runtime.watch_max_depth,max_startup_ms:this.options.runtime.watch_max_startup_ms,on_change:n=>this.handleFileChanged(n),on_batch_change:n=>this.handleFilesChanged(n)}).start(),t.listen!==!1&&this.server.listen(this.options.port,this.options.host),this.notifyHealthChanged()}}getEngine(){if(!this.bridgeEngine)throw new Error("VCP site edit preview bridge runtime is not ready");return this.bridgeEngine}getHealth(){let t=!!this.runtime&&!this.compositionResolutionError;return{ok:this.isStarted&&t,bridgePath:this.options.path,runtimeReady:t,routesIndexed:this.routes.length,sessionRequired:!!this.options.sessionKey}}stop(){this.stopping=!0,this.stopWatching?.(),this.stopWatching=null,this.flushChangedFiles(),this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.stopWebSocketServer?.(),this.stopWebSocketServer=null,this.runtime?.dispose(),this.runtime=null,this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.closeHttpServerOnStop&&this.server.close();let t=Ht(),n=Nt(this.options);t.get(n)===this&&t.delete(n),this.isStarted=!1,this.notifyHealthChanged()}notifyHealthChanged(){let t=this.options.onHealthChanged;if(!t)return;let n=this.getHealth(),i=JSON.stringify(n);if(i!==this.lastNotifiedHealth){this.lastNotifiedHealth=i;try{t(n)}catch(s){console.warn(`[vcp-site-edit] onHealthChanged callback failed: ${s instanceof Error?s.message:String(s)}`)}}}createRuntime(t,n=null){let i=this.runtime;this.activeHistoryStore=t??null;let s=(this.dependencies.createRuntime??ft)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,compositions:this.compositions,onEvent:o=>{"version"in o&&typeof o.version=="number"&&this.recordRuntimeVersion(o.version,"composition"in o?o.composition:void 0);for(let r of this.eventListeners)r(o)},...t?{historyStore:t}:{},...this.getInitialDocumentVersions().length>0?{initialDocumentVersions:this.getInitialDocumentVersions()}:{}});this.runtime=s,this.activeHistorySessionId=n,i?.dispose(),this.notifyHealthChanged()}createBridgeEngine(){return{resolveComposition:t=>this.resolveComposition(t),initializeSession:t=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)throw new Error(`${n.code}: ${n.message}`);let i=kt(t.composition),s=this.getRuntime(),o=R(i);await s.initializeComposition(o);let u=s.getDocument(o).data;await this.recordRuntimeVersion(u.version,i);let p=(await s.getHistoryStatus()).data;return{ok:!0,composition:i,data:{document:u,historyStatus:p}}}),getDocument:async t=>{let n=this.getRuntime(),i=R(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(R(t)),getObjectEditContext:async(t,n,i)=>this.getRuntime().getObjectEditContext(t,R(n),i),getClassNameSource:async(t,n)=>this.getRuntime().getClassNameSource(t,R(n)),getSourceTree:async t=>this.getRuntime().getSourceTree(R(t)),getSourceFile:async t=>this.getRuntime().getSourceFile({path:t.path,composition:R(t.composition)}),resolveSourceLocation:async t=>this.getRuntime().resolveSourceLocation({key:t.key,composition:R(t.composition)}),execute:(t,n)=>this.mutationQueue.run(async()=>{let i=await this.bindHistorySession(this.options.snapshotId);if(!i.ok)return ue(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 ue(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 ue(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??gt)(Pt({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=O(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?O(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??ke;this.fileChangeTimer=setTimeout(()=>{this.fileChangeTimer=null,this.flushChangedFiles()},n),this.fileChangeTimer?.unref?.()}flushChangedFiles(){if(this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.pendingChangedFiles.size===0)return;let t=[...this.pendingChangedFiles].sort((n,i)=>n.localeCompare(i));if(this.pendingChangedFiles.clear(),t.some(Ft)&&this.scheduleSnapshotIdentityAdvance(),!!this.refreshCompositions(!1))for(let n of t)this.runtime?.notifyFileChanged(n)}scheduleSnapshotIdentityAdvance(){if(this.stopping||!this.isStarted)return;let t=this.readCurrentSnapshotMarker();!t||t===this.options.snapshotId||this.mutationQueue.run(()=>this.advanceSnapshotId(t)).catch(n=>{console.warn(`[vcp-site-edit] snapshot identity advance failed: ${n instanceof Error?n.message:String(n)}`)})}readCurrentSnapshotMarker(){try{let t=L.readFileSync(v.join(this.options.projectRoot,he),"utf8"),n=JSON.parse(t),i=typeof n.snapshot_id=="string"?n.snapshot_id:"";return!i||le(i)!==i?(console.warn("[vcp-site-edit] current-snapshot marker has no valid snapshot_id"),null):i}catch(t){return console.warn(`[vcp-site-edit] current-snapshot marker unreadable: ${t instanceof Error?t.message:String(t)}`),null}}async advanceSnapshotId(t){if(this.stopping||!this.isStarted||t===this.options.snapshotId)return;let n=this.options.snapshotId;this.options.snapshotId=t;try{if(this.options.historyRoot){let s=await this.bindHistorySession(t);if(!s.ok)throw new Error(`${s.code}: ${s.message}`)}else this.createRuntime()}catch(s){this.options.snapshotId=n,console.warn(`[vcp-site-edit] snapshot identity advance rolled back to ${n}: ${s instanceof Error?s.message:String(s)}`);return}let i=new Date().toISOString();for(let s of this.compositions){let o={type:"site-edit.snapshot.changed",siteId:this.options.siteId,composition:s.context,snapshotId:t,previousSnapshotId:n,at:i};for(let r of this.eventListeners)try{r(o)}catch(u){console.warn(`[vcp-site-edit] snapshot.changed listener failed: ${u instanceof Error?u.message:String(u)}`)}}try{this.options.onSnapshotIdChanged?.(t,n)}catch(s){console.warn(`[vcp-site-edit] onSnapshotIdChanged callback failed: ${s instanceof Error?s.message:String(s)}`)}}refreshCompositions(t=!1){try{let n=Tt(this.options.projectRoot),i=It(n),s=i.length>0?i:(this.options.runtime.routes??[]).map(c=>({...c})),o=yt({files:n});if(o.length===0)throw new Error("No canonical App Router composition is indexed by the preview bridge.");let r=JSON.stringify(o.filter(c=>c.context.effectiveTemplateId!==void 0).map(c=>c.entryFile).sort());if(t&&(this.launchCandidateEntries=r),this.launchCandidateEntries!==r)throw new Error("SITE_EDIT_RUNTIME_RELOAD_REQUIRED: Candidate route collection changed since preview launch");let u=new Map(this.compositions.map(c=>[O(c.context),c])),p=new Map(o.map(c=>[O(c.context),c]));if(this.runtime){for(let[c,l]of u){let a=p.get(c);(!a||a.entryFile!==l.entryFile)&&this.runtime.unregisterComposition(R(l.context))}for(let[c,l]of p){let a=u.get(c);(!a||a.entryFile!==l.entryFile)&&this.runtime.registerComposition(l.context,l.entryFile)}}return this.routes=s,this.compositions=o,this.compositionFiles=n,this.compositionResolutionError=null,this.notifyHealthChanged(),!0}catch(n){let i=n instanceof Error?n:new Error(String(n));if(this.compositionResolutionError=i,t)throw i;return this.notifyHealthChanged(),!1}}resolveComposition(t){if(this.compositionResolutionError)throw this.compositionResolutionError;let n=St(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=mt({selector:{routeId:n,...t.templateId!==void 0?{templateId:t.templateId}:{}},files:this.compositionFiles,canonicalEntryFile:i.entryFile}),o=O(s.context);if(!this.compositions.some(r=>O(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(O(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 Be(e){return je(e)}function je(e){return{tenantId:e.tenantId,siteId:e.siteId,snapshotId:e.snapshotId,projectRoot:v.resolve(e.projectRoot??process.cwd()),basePath:e.basePath??"",host:e.host??xt,port:e.port??Ct,path:e.path??vt,sessionKey:e.sessionKey??Ut(),historyRoot:e.historyRoot,allowedOrigins:e.allowedOrigins??[],runtime:e.runtime??{},onSnapshotIdChanged:e.onSnapshotIdChanged,onHealthChanged:e.onHealthChanged}}function _t(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 Tt(e){let t=[],n=["app","src/app",".vcp"],i=new Set([".git",".next",".turbo","coverage","dist","node_modules","output","snapshot-store"]),s=o=>{if(!L.existsSync(o))return;let r;try{r=L.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(v.join(o,u.name));continue}if(!u.isFile())continue;let p=v.join(o,u.name),c="";try{c=L.readFileSync(p,"utf8")}catch{}t.push({path:v.relative(e,p).replaceAll(v.sep,"/"),content:c})}};for(let o of n)s(v.join(e,o));return t.sort((o,r)=>o.path.localeCompare(r.path))}function kt(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 R(e){return{routeId:e.routeId,...e.effectiveTemplateId?{templateId:e.effectiveTemplateId}:{}}}function Pt(e){return v.join(v.resolve(e.root),le(e.siteId),le(e.snapshotId),Ae("sha256").update(e.editingSessionId).digest("hex").slice(0,32))}function le(e){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(e)&&e!=="."&&e!==".."?e:Ae("sha256").update(e).digest("hex").slice(0,32)}function Ht(){let e=globalThis;return e[Pe]??=new Map,e[Pe]}function Nt(e){return`${e.projectRoot}:${e.host}:${e.port}:${e.path}:${e.historyRoot??""}`}function ue(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 me(e){let t=new lt({maxPayload:De,noServer:!0}),n=new WeakSet,i=o=>{new J({socket:new fe(o),engine:e.engine,validateOperationRequest:r=>Et.safeParse(r).success})},s=(o,r,u)=>{if(!At(o,e))return;let p=Ot(o,e);if(!p){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,u,a=>{t.emit("connection",a,o)})};return e.server.on("upgrade",i),t.on("connection",(o,r)=>{if(n.has(r)){s(o);return}_t(o,e.sessionKey,()=>{s(o)})}),t.stop=()=>{e.server.off("upgrade",i);for(let o of t.clients)o.close(1001,"bridge stopped");t.close()},t}var ie=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===ve.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(t,"utf8")>mt){this.socket.close(1013,"bridge backpressure");return}this.socket.send(t)}}close(t,n){this.socket.close(t,n)}};function vt(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 s=ee(e.headers["x-vcp-site-edit-key"]),i=Te(s,t.sessionKey),o=ee(e.headers.host),r=ee(e.headers.origin);return o&&!Tt(o,t.host,r)?null:r?Pt(r,o,t.allowedOrigins)?{authenticatedByHeader:i}:null:i?{authenticatedByHeader:!0}:null}function _t(e,t,n){let s=setTimeout(()=>{e.close(1008,"bridge authentication timeout")},gt),i,o=()=>{clearTimeout(s),e.off("message",i),e.off("close",o)},r=()=>{o(),e.close(1008,"bridge authentication failed")};i=u=>{try{let d=m.parseMessage(u.toString());if(d.type!=="auth"||!Te(d.payload.key,t)){r();return}o(),e.readyState===ve.OPEN&&e.send(m.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),n()}catch{r()}},e.on("message",i),e.on("close",o)}function Te(e,t){if(!e)return!1;let n=Buffer.from(e),s=Buffer.from(t);return n.length===s.length&&nt(n,s)}function Tt(e,t,n){let s=Re(e),i=Re(t);return s===i||Ce(i)&&Ce(s)&&(!n||oe(n,e))||kt(i)&&(!n||oe(n,e))}function kt(e){return e==="0.0.0.0"||e==="::"}function Ce(e){return e==="localhost"||e==="127.0.0.1"||e==="::1"}function Pt(e,t,n){return n.includes(e)||(t?oe(e,t):!1)}function oe(e,t){try{return new URL(e).host===ke(t)}catch{return!1}}function Re(e){let t=ke(e);return t.startsWith("[")?t.slice(1,t.indexOf("]")):t.split(":").length-1===1?t.split(":",1)[0]??"":t}function ke(e){return e.trim().toLowerCase()}function ee(e){return Array.isArray(e)?e[0]:e}function Ht(){return tt(32).toString("base64url")}function At(e){let t=e.replaceAll("\\","/");return t===te||t.endsWith(`/${te}`)}var Bt="next",Oe="127.0.0.1",Dt=3e3,V="/__vcp/site-edit/bridge",Me="/__vcp/site-edit/health",Ot="probe",Mt="render",He="/_error",q="/__vcp/site-edit/client/",jt=`${q}auto-bootstrap.js`,je='data-vcp-site-edit-bootstrap="true"',Ft=["next.config.ts","next.config.mts","next.config.cts","next.config.js","next.config.mjs","next.config.cjs"],Ut="@vcp-dev/site-edit-engine/webpack-loader",Kt=/@vcp\/site-edit-engine\/(?:webpack-loader|site-edit-instrumentation-loader)/,Vt=[/\bconfig\.webpack\s*=/,/\bwebpack\s*\([^)]*\)\s*\{/,/\bwebpack\s*:\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)?\s*=>/,/\bwebpack\s*:\s*(?:async\s+)?function\b/],qt=new Set(["auto-bootstrap.js","client.js","composition-key.js","designer-trace.js","document-codec.js","property-preview.js","protocol.js","window-message.js"]);async function ts(e={},t={}){let n=t.process??process,s=t.createServer??ue.createServer,i=t.attachBridgeServer??_e,o=t.loadNext??xn,r=H.resolve(e.projectRoot??process.cwd()),u=e.host??Oe,d=e.port??vn(n.env.PORT)??Dt,a=e.bridgePath??V,h=Gt(a),c=gn(),p=e.basePath??n.env.VCP_SITE_PREVIEW_BASE_PATH??"/",f=(e.tenantId??n.env.VCP_TENANT_ID)?.trim()||void 0,S=await o(r),I=$t(r),w=S({dev:!0,dir:r,hostname:u,port:d,...Wt(I)}),Ke=w.getRequestHandler(),Ve=e.siteId??n.env.VCP_SITE_ID??"018f6f4d-a9b8-7120-8000-000000000120",he=e.snapshotId??n.env.VCP_SNAPSHOT_ID??En(r),pe=e.historyRoot??n.env.VCP_SITE_EDIT_HISTORY_ROOT,le=pe?.trim()?H.resolve(pe):void 0,B=null,fe={launchId:Nt(),pid:process.pid,snapshotId:he},v=s((T,M)=>{sn({basePath:p,bridgePath:a,bridgeHealthPath:h,bridgeSessionKey:c,getBridgeHealth:()=>B?.getHealth()??dn(a),launch:fe,runRenderProbe:()=>an(w),handle:Ke,request:T,response:M}).catch(qe=>{console.error(qe),M.headersSent||(M.statusCode=500),M.end("site edit preview request failed")})}),$=null,D=null,W=()=>D||(K(()=>n.off("SIGINT",O)),K(()=>n.off("SIGTERM",O)),K(()=>$?.()),$=null,K(()=>B?.stop()),B=null,D=Promise.allSettled([_n(v),Tn(()=>w.close())]).then(()=>{}),D),O=()=>{W()};try{return await w.prepare(),B=i(v,{...f?{tenantId:f}:{},siteId:Ve,snapshotId:he,projectRoot:r,basePath:p,host:u,port:d,path:a,sessionKey:c,...le?{historyRoot:le}:{},onSnapshotIdChanged:T=>{fe.snapshotId=T}}),$=Jt({server:v,bridgePath:a,handleUpgrade:w.getUpgradeHandler()}),n.on("SIGINT",O),n.on("SIGTERM",O),await bn(v,d,u),{bundler:I,close:W,host:u,port:kn(v,d),server:v}}catch(T){throw await W(),T}}function $t(e){let t=Lt(e);return t&&zt(t)?"webpack":"turbopack"}function Wt(e){return e==="webpack"?{webpack:!0}:{turbopack:!0}}function Lt(e){for(let t of Ft){let n=N(e,t);if(_(n))return b(n,"utf8")}return null}function zt(e){return e.includes(Ut)?!0:Kt.test(e)&&Vt.some(t=>t.test(e))}function Jt(e){let t=(n,s,i)=>{Qt(n.url,e.bridgePath)||e.handleUpgrade(n,s,i).catch(o=>{console.error(o),s.destroyed||s.destroy()})};return e.server.on("upgrade",t),()=>e.server.off("upgrade",t)}function Qt(e,t){return new URL(e??"/","http://localhost").pathname===t}function Gt(e=V){if(e===V)return Me;let t=(e.startsWith("/")?e:`/${e}`).replace(/\/+$/,"");return t.endsWith("/bridge")?`${t.slice(0,-7)}/health`:t?`${t}/health`:"/health"}function Yt(e){let t=JSON.stringify({basePath:e.basePath,launch_id:e.launch?.launchId,snapshot_id:e.launch?.snapshotId,wsPath:e.bridgePath,bridge_session_key:e.bridgeSessionKey,defer_initial_sync:!0,runtime_versions:e.runtimeVersions??Zt()});return`<script type="module" ${je}>import { startSiteEditAutoBootstrap } from ${JSON.stringify(jt)};void startSiteEditAutoBootstrap(window, ${t}).catch(() => undefined);</script>`}function Zt(){return{bridge:re("@vcp-dev/site-edit-bridge"),contracts:re("@vcp-dev/contracts"),engine:re("@vcp-dev/site-edit-engine")}}function re(e){let t=Ne(import.meta.url);try{let n=t.resolve(e),s=Xt(n,e);if(!s)return;let i=JSON.parse(b(s,"utf8"));return typeof i.version=="string"?i.version:void 0}catch{return}}function Xt(e,t){let n=H.parse(e).root,s=A(e);for(;s&&s!==n;){let i=N(s,"package.json");if(_(i))try{if(JSON.parse(b(i,"utf8")).name===t)return i}catch{return}s=A(s)}}function en(e,t){if(e.includes(je))return e;let n=Yt(t),s=/<\/body\s*>/i.exec(e);return s?`${e.slice(0,s.index)}${n}${e.slice(s.index)}`:`${e}${n}`}function ns(e,t=A(De(import.meta.url)),n=_){return Fe(e,t,n)?.path??null}function Fe(e,t=A(De(import.meta.url)),n=_){let s=new URL(e??"/","http://localhost").pathname;if(!s.startsWith(q))return null;let i=s.slice(q.length);if(!qt.has(i))return null;let o=de(Cn(t),i);if(Be(t)!=="src")return{path:o,requiresTranspile:!1};let r=de(t,i.replace(/\.js$/,".ts"));return n(r)?{path:r,requiresTranspile:!0}:{path:o,requiresTranspile:!1}}async function tn(e,t){let n=Fe(e.url);if(!n)return!1;if(!_(n.path))return t.writeHead(404),t.end("site edit client asset not found"),!0;let s=n.requiresTranspile?await nn(b(n.path,"utf8")):b(n.path);return t.writeHead(200,{"cache-control":"no-store","content-type":"text/javascript; charset=utf-8"}),t.end(s),!0}async function nn(e){let t=await import("typescript");return t.transpileModule(e,{compilerOptions:{module:t.ModuleKind.ES2022,target:t.ScriptTarget.ES2022}}).outputText}async function sn({basePath:e,bridgePath:t,bridgeHealthPath:n,bridgeSessionKey:s,getBridgeHealth:i,launch:o,runRenderProbe:r,handle:u,request:d,response:a}){if(on(d.url,e)){a.statusCode=404,a.end();return}if(await un(d,a,n,i,o,r)||await tn(d,a))return;let h=hn(d),c=pn(d),p=h||c?mn(a,{basePath:e,bridgePath:t,bridgeSessionKey:s,patchNextDevChunk:c,launch:{...o}}):()=>{};try{await u(d,a)}catch(f){throw p(),f}}function on(e,t){let n=(e??"/").split(/[?#]/,1)[0]??"/";if(!/(\\|\/\/)/.test(n))return!1;let s;try{s=decodeURIComponent(n).replace(/\\/g,"/").replace(/\/{2,}/g,"/")}catch{return!1}let i=new URL(s,"http://namespace.invalid").pathname,o=t.replace(/\/+$/,"");return(o&&(i===o||i.startsWith(o+"/"))?i.slice(o.length)||"/":i).startsWith("/.vcp")}function rn(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 an(e){let t=new ue.IncomingMessage(new Pe);t.method="GET",t.url=He,t.headers={host:Oe};let n=new ue.ServerResponse(t);n.assignSocket(new Pe);try{return await e.renderToHTML(t,n,He,{}),{verdict:"ok"}}catch(s){return rn(s)}finally{try{let s=n.socket;s&&(n.detachSocket(s),s.destroy())}catch{}}}function cn(e){return new URL(e??"/","http://localhost").searchParams.get(Ot)===Mt}async function un(e,t,n,s,i,o){if(e.method!=="GET"||new URL(e.url??"/","http://localhost").pathname!==n)return!1;let u=s(),d=cn(e.url)?await o():void 0,a={...u,launch:i,...d?{render:d}:{}};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 dn(e){return{ok:!1,bridgePath:e,runtimeReady:!1,routesIndexed:0,sessionRequired:!0}}function hn(e){return ln(e)?(e.headers["accept-encoding"]="identity",!0):!1}function pn(e){return fn(e)?(e.headers["accept-encoding"]="identity",!0):!1}function ln(e){if(e.method!=="GET")return!1;let t=new URL(e.url??"/","http://localhost");if(e.headers["x-template-id"]!==void 0&&!t.searchParams.has("template_id"))return!1;let n=t.pathname;if(n.startsWith("/_next/")||n.startsWith(q)||n===V||n===Me)return!1;let s=e.headers.accept??"";return s===""||s.includes("text/html")}function fn(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 mn(e,t){let n=[],s=e,i=s.flushHeaders.bind(e),o=s.write.bind(e),r=s.end.bind(e),u=!1;s.flushHeaders=()=>{};let d=()=>{if(t.patchNextDevChunk||u)return!1;let a=Buffer.concat(n);if(!/<body\b[^>]*>/iu.test(a.toString("utf8")))return!1;let h=Ae(e,a,t);return h!==a&&!e.headersSent&&e.removeHeader("content-length"),o(h),n.length=0,u=!0,!0};return s.write=(a,h,c)=>u?(a!==void 0&&o(ae(a,ce(h))),U(h),U(c),!0):(a!==void 0&&(n.push(ae(a,ce(h))),d()),U(h),U(c),!0),s.end=(a,h,c)=>{let p=typeof a=="function"?void 0:a;if(u)return s.write=o,s.end=r,s.flushHeaders=i,p===void 0?typeof a=="function"?r(a):typeof h=="function"?r(h):typeof c=="function"?r(c):r():typeof h=="function"?r(p,h):typeof c=="function"?r(p,h,c):r(p,h);p!==void 0&&n.push(ae(p,ce(h))),s.write=o,s.end=r,s.flushHeaders=i;let f=Buffer.concat(n),S=Ae(e,f,t);S!==f&&!e.headersSent&&e.removeHeader("content-length");let I=typeof a=="function"?a:typeof h=="function"?h:typeof c=="function"?c:void 0;return I?r(S,I):r(S)},()=>{s.write=o,s.end=r,s.flushHeaders=i}}function Ae(e,t,n){if(yn(e,t))return Buffer.from(en(t.toString("utf8"),n));if(n.patchNextDevChunk&&Sn(e)){let s=t.toString("utf8"),i=In(s);if(i!==s)return Buffer.from(i)}return t}function gn(){let e=new Uint8Array(32);return globalThis.crypto.getRandomValues(e),Buffer.from(e).toString("base64url")}function yn(e,t){if(e.statusCode<200||e.statusCode>=300)return!1;let n=e.getHeader("content-type"),s=Array.isArray(n)?n.join(";"):String(n??"");return t.toString("utf8",0,256).includes("<html")&&(s===""||s.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 In(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}p.authenticatedByHeader&&n.add(o),t.handleUpgrade(o,r,u,c=>{t.emit("connection",c,o)})};return e.server.on("upgrade",s),t.on("connection",(o,r)=>{if(n.has(r)){i(o);return}Dt(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 fe=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===Oe.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(t,"utf8")>Rt){this.socket.close(1013,"bridge backpressure");return}this.socket.send(t)}}close(t,n){this.socket.close(t,n)}};function At(e,t){return new URL(e.url??"/","http://localhost").pathname===t.path}function Ot(e,t){if(new URL(e.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let i=de(e.headers["x-vcp-site-edit-key"]),s=Ue(i,t.sessionKey),o=de(e.headers.host),r=de(e.headers.origin);return o&&!Mt(o,t.host,r)?null:r?jt(r,o,t.allowedOrigins)?{authenticatedByHeader:s}:null:s?{authenticatedByHeader:!0}:null}function Dt(e,t,n){let i=setTimeout(()=>{e.close(1008,"bridge authentication timeout")},bt),s,o=()=>{clearTimeout(i),e.off("message",s),e.off("close",o)},r=()=>{o(),e.close(1008,"bridge authentication failed")};s=u=>{try{let p=C.parseMessage(u.toString());if(p.type!=="auth"||!Ue(p.payload.key,t)){r();return}o(),e.readyState===Oe.OPEN&&e.send(C.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),n()}catch{r()}},e.on("message",s),e.on("close",o)}function Ue(e,t){if(!e)return!1;let n=Buffer.from(e),i=Buffer.from(t);return n.length===i.length&&pt(n,i)}function Mt(e,t,n){let i=Ne(e),s=Ne(t);return i===s||He(s)&&He(i)&&(!n||ge(n,e))||Bt(s)&&(!n||ge(n,e))}function Bt(e){return e==="0.0.0.0"||e==="::"}function He(e){return e==="localhost"||e==="127.0.0.1"||e==="::1"}function jt(e,t,n){return n.includes(e)||(t?ge(e,t):!1)}function ge(e,t){try{return new URL(e).host===Fe(t)}catch{return!1}}function Ne(e){let t=Fe(e);return t.startsWith("[")?t.slice(1,t.indexOf("]")):t.split(":").length-1===1?t.split(":",1)[0]??"":t}function Fe(e){return e.trim().toLowerCase()}function de(e){return Array.isArray(e)?e[0]:e}function Ut(){return ht(32).toString("base64url")}function Ft(e){let t=e.replaceAll("\\","/");return t===he||t.endsWith(`/${he}`)}var Wt="SITE_EDIT_ENGINE_UNAVAILABLE",$t=5e3,Vt=1024,Lt="VCP_SITE_EDIT_ENGINE_MAX_HEAP_MB",qt=3,zt=10*6e4,Gt=1012;function ye(e,t,n={}){let i=Be(t),{onSnapshotIdChanged:s,onHealthChanged:o,...r}=i,u={options:r},p=n.maxHeapMb??Jt(process.env[Lt])??Vt,c=n.maxRestarts??qt,l=n.restartWindowMs??zt,a="starting",f,m=0,y=[],S=null,E=0,P=new Map,H=new Set,F,D,K=new Promise((d,h)=>{F=d,D=h});K.catch(()=>{});let x=d=>{let h=new Error(`VCP site edit preview bridge runtime is not ready: ${d}`);return h.code=Wt,h},W=d=>{for(let h of P.values())h.reject(d);P.clear()},b=d=>{switch(d.type){case"ready":S=d.health,a==="starting"?(a="ready",F()):a==="restarting"&&(a="ready",console.warn("[vcp-site-edit] engine worker restarted"));return;case"start-failed":a==="starting"&&_(te(d.error));return;case"result":{let h=P.get(d.id);if(!h)return;P.delete(d.id),d.ok?h.resolve(d.value):h.reject(te(d.error));return}case"event":for(let h of H)try{h(d.event)}catch(I){console.warn(`[vcp-site-edit] engine event listener failed: ${I instanceof Error?I.message:String(I)}`)}return;case"health":S=d.health;return;case"snapshot-id-changed":u.options.snapshotId=d.next;try{s?.(d.next,d.previous)}catch(h){console.warn(`[vcp-site-edit] onSnapshotIdChanged callback failed: ${h instanceof Error?h.message:String(h)}`)}return}},M=()=>{let d=m+=1,h=new Kt(n.workerUrl??new URL("./engine-worker.js",import.meta.url),{workerData:u,resourceLimits:{maxOldGenerationSizeMb:p},...n.execArgv?{execArgv:n.execArgv}:{}});h.unref();let I=()=>d===m;h.on("message",w=>{I()&&b(w)}),h.on("error",w=>{I()&&_(w instanceof Error?w:new Error(String(w)))}),h.on("exit",w=>{I()&&_(new Error(`engine worker exited with code ${w}`))}),f=h,n.onWorker?.(h)},_=d=>{if(a==="stopped"||a==="failed")return;if(W(x(d.message)),S=null,a==="starting"){a="failed",console.error(`[vcp-site-edit] engine worker failed to start: ${d.message}`),D(d);return}let h=Date.now();for(;y.length>0&&h-y[0]>l;)y.shift();if(y.length>=c){a="failed",console.error(`[vcp-site-edit] engine worker stopped unexpectedly and exhausted ${c} restarts: ${d.message}`);return}y.push(h),a="restarting";for(let I of T.clients)I.close(Gt,"site edit engine restarting");console.error(`[vcp-site-edit] engine worker stopped unexpectedly, restarting (${y.length}/${c}): ${d.message}`),M()},$=(d,h)=>{if(a==="failed"||a==="stopped"||a==="restarting")return Promise.reject(x(a));let I=E+=1;return new Promise((w,Xe)=>{P.set(I,{resolve:w,reject:Xe});let Ze={type:"call",id:I,method:d,args:h};f.postMessage(Ze)})},B=Object.fromEntries(Ce.map(d=>[d,(...h)=>$(d,h)])),T=me({server:e,path:i.path,sessionKey:i.sessionKey,host:i.host,allowedOrigins:i.allowedOrigins,engine:{...B,subscribe(d){return H.add(d),()=>H.delete(d)}}});return M(),{getHealth(){return a==="ready"&&S?S:{ok:!1,bridgePath:i.path,runtimeReady:!1,routesIndexed:S?.routesIndexed??0,sessionRequired:!!i.sessionKey}},stop(){if(a==="stopped")return;let d=a==="starting";a="stopped",T.stop(),d&&D(x("bridge stopped")),W(x("bridge stopped")),H.clear();let h=f,I={type:"stop"};h.postMessage(I);let w=setTimeout(()=>{h.terminate()},$t);w.unref(),h.once("exit",()=>clearTimeout(w))},whenReady(){return K},eventListeners:H}}function Jt(e){if(!e||!/^\d+$/.test(e.trim()))return;let t=Number(e.trim());return Number.isSafeInteger(t)&&t>0?t:void 0}var Yt="next",Xt="VCP_SITE_EDIT_ENGINE_IN_PROCESS",ze="127.0.0.1",Zt=3e3,Z="/__vcp/site-edit/bridge",Ge="/__vcp/site-edit/health",en="probe",tn="render",We="/_error",ee="/__vcp/site-edit/client/",nn=`${ee}auto-bootstrap.js`,Je='data-vcp-site-edit-bootstrap="true"',sn=["next.config.ts","next.config.mts","next.config.cts","next.config.js","next.config.mjs","next.config.cjs"],on="@vcp-dev/site-edit-engine/webpack-loader",rn=/@vcp\/site-edit-engine\/(?:webpack-loader|site-edit-instrumentation-loader)/,an=[/\bconfig\.webpack\s*=/,/\bwebpack\s*\([^)]*\)\s*\{/,/\bwebpack\s*:\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)?\s*=>/,/\bwebpack\s*:\s*(?:async\s+)?function\b/],cn=new Set(["auto-bootstrap.js","client.js","composition-key.js","designer-trace.js","document-codec.js","property-preview.js","protocol.js","window-message.js"]);async function Ri(e={},t={}){let n=t.process??process,i=t.createServer??Ee.createServer,s=t.attachBridgeServer??(n.env[Xt]==="1"?Me:ye),o=t.loadNext??Un,r=q.resolve(e.projectRoot??process.cwd()),u=e.host??ze,p=e.port??$n(n.env.PORT)??Zt,c=e.bridgePath??Z,l=gn(c),a=On(),f=e.basePath??n.env.VCP_SITE_PREVIEW_BASE_PATH??"/",m=(e.tenantId??n.env.VCP_TENANT_ID)?.trim()||void 0,y=await o(r),S=un(r),E=y({dev:!0,dir:r,hostname:u,port:p,...dn(S)}),P=E.getRequestHandler(),H=e.siteId??n.env.VCP_SITE_ID??"018f6f4d-a9b8-7120-8000-000000000120",F=e.snapshotId??n.env.VCP_SNAPSHOT_ID??Wn(r),D=e.historyRoot??n.env.VCP_SITE_EDIT_HISTORY_ROOT,K=D?.trim()?q.resolve(D):void 0,x=null,W={launchId:Qt(),pid:process.pid,snapshotId:F},b=i((T,d)=>{xn({basePath:f,bridgePath:c,bridgeHealthPath:l,bridgeSessionKey:a,getBridgeHealth:()=>x?.getHealth()??Tn(c),launch:W,runRenderProbe:()=>Rn(E),handle:P,request:T,response:d}).catch(h=>{console.error(h),d.headersSent||(d.statusCode=500),d.end("site edit preview request failed")})}),M=null,_=null,$=()=>_||(X(()=>n.off("SIGINT",B)),X(()=>n.off("SIGTERM",B)),X(()=>M?.()),M=null,X(()=>x?.stop()),x=null,_=Promise.allSettled([Ln(b),qn(()=>E.close())]).then(()=>{}),_),B=()=>{$()};try{return await E.prepare(),x=s(b,{...m?{tenantId:m}:{},siteId:H,snapshotId:F,projectRoot:r,basePath:f,host:u,port:p,path:c,sessionKey:a,...K?{historyRoot:K}:{},onSnapshotIdChanged:T=>{W.snapshotId=T}}),M=ln({server:b,bridgePath:c,handleUpgrade:E.getUpgradeHandler()}),n.on("SIGINT",B),n.on("SIGTERM",B),await x.whenReady?.(),await Vn(b,p,u),{bundler:S,close:$,host:u,port:zn(b,p),server:b}}catch(T){throw await $(),T}}function un(e){let t=hn(e);return t&&pn(t)?"webpack":"turbopack"}function dn(e){return e==="webpack"?{webpack:!0}:{turbopack:!0}}function hn(e){for(let t of sn){let n=G(e,t);if(U(n))return j(n,"utf8")}return null}function pn(e){return e.includes(on)?!0:rn.test(e)&&an.some(t=>t.test(e))}function ln(e){let t=(n,i,s)=>{fn(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 fn(e,t){return new URL(e??"/","http://localhost").pathname===t}function gn(e=Z){if(e===Z)return Ge;let t=(e.startsWith("/")?e:`/${e}`).replace(/\/+$/,"");return t.endsWith("/bridge")?`${t.slice(0,-7)}/health`:t?`${t}/health`:"/health"}function mn(e){let t=JSON.stringify({basePath:e.basePath,launch_id:e.launch?.launchId,snapshot_id:e.launch?.snapshotId,wsPath:e.bridgePath,bridge_session_key:e.bridgeSessionKey,defer_initial_sync:!0,runtime_versions:e.runtimeVersions??yn()});return`<script type="module" ${Je}>import { startSiteEditAutoBootstrap } from ${JSON.stringify(nn)};void startSiteEditAutoBootstrap(window, ${t}).catch(() => undefined);</script>`}function yn(){return{bridge:Se("@vcp-dev/site-edit-bridge"),contracts:Se("@vcp-dev/contracts"),engine:Se("@vcp-dev/site-edit-engine")}}function Se(e){let t=Ve(import.meta.url);try{let n=t.resolve(e),i=Sn(n,e);if(!i)return;let s=JSON.parse(j(i,"utf8"));return typeof s.version=="string"?s.version:void 0}catch{return}}function Sn(e,t){let n=q.parse(e).root,i=z(e);for(;i&&i!==n;){let s=G(i,"package.json");if(U(s))try{if(JSON.parse(j(s,"utf8")).name===t)return s}catch{return}i=z(i)}}function In(e,t){if(e.includes(Je))return e;let n=mn(t),i=/<\/body\s*>/i.exec(e);return i?`${e.slice(0,i.index)}${n}${e.slice(i.index)}`:`${e}${n}`}function bi(e,t=z(qe(import.meta.url)),n=U){return Qe(e,t,n)?.path??null}function Qe(e,t=z(qe(import.meta.url)),n=U){let i=new URL(e??"/","http://localhost").pathname;if(!i.startsWith(ee))return null;let s=i.slice(ee.length);if(!cn.has(s))return null;let o=xe(Fn(t),s);if(Le(t)!=="src")return{path:o,requiresTranspile:!1};let r=xe(t,s.replace(/\.js$/,".ts"));return n(r)?{path:r,requiresTranspile:!0}:{path:o,requiresTranspile:!1}}async function wn(e,t){let n=Qe(e.url);if(!n)return!1;if(!U(n.path))return t.writeHead(404),t.end("site edit client asset not found"),!0;let i=n.requiresTranspile?await En(j(n.path,"utf8")):j(n.path);return t.writeHead(200,{"cache-control":"no-store","content-type":"text/javascript; charset=utf-8"}),t.end(i),!0}async function En(e){let t=await import("typescript");return t.transpileModule(e,{compilerOptions:{module:t.ModuleKind.ES2022,target:t.ScriptTarget.ES2022}}).outputText}async function xn({basePath:e,bridgePath:t,bridgeHealthPath:n,bridgeSessionKey:i,getBridgeHealth:s,launch:o,runRenderProbe:r,handle:u,request:p,response:c}){if(Cn(p.url,e)){c.statusCode=404,c.end();return}if(await _n(p,c,n,s,o,r)||await wn(p,c))return;let l=kn(p),a=Pn(p),f=l||a?An(c,{basePath:e,bridgePath:t,bridgeSessionKey:i,patchNextDevChunk:a,launch:{...o}}):()=>{};try{await u(p,c)}catch(m){throw f(),m}}function Cn(e,t){let n=(e??"/").split(/[?#]/,1)[0]??"/";if(!/(\\|\/\/)/.test(n))return!1;let i;try{i=decodeURIComponent(n).replace(/\\/g,"/").replace(/\/{2,}/g,"/")}catch{return!1}let s=new URL(i,"http://namespace.invalid").pathname,o=t.replace(/\/+$/,"");return(o&&(s===o||s.startsWith(o+"/"))?s.slice(o.length)||"/":s).startsWith("/.vcp")}function vn(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 Rn(e){let t=new Ee.IncomingMessage(new Ke);t.method="GET",t.url=We,t.headers={host:ze};let n=new Ee.ServerResponse(t);n.assignSocket(new Ke);try{return await e.renderToHTML(t,n,We,{}),{verdict:"ok"}}catch(i){return vn(i)}finally{try{let i=n.socket;i&&(n.detachSocket(i),i.destroy())}catch{}}}function bn(e){return new URL(e??"/","http://localhost").searchParams.get(en)===tn}async function _n(e,t,n,i,s,o){if(e.method!=="GET"||new URL(e.url??"/","http://localhost").pathname!==n)return!1;let u=i(),p=bn(e.url)?await o():void 0,c={...u,launch:s,...p?{render:p}:{}};return t.writeHead(u.ok?200:503,{"cache-control":"no-store","content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify(c)),!0}function Tn(e){return{ok:!1,bridgePath:e,runtimeReady:!1,routesIndexed:0,sessionRequired:!0}}function kn(e){return Hn(e)?(e.headers["accept-encoding"]="identity",!0):!1}function Pn(e){return Nn(e)?(e.headers["accept-encoding"]="identity",!0):!1}function Hn(e){if(e.method!=="GET")return!1;let t=new URL(e.url??"/","http://localhost");if(e.headers["x-template-id"]!==void 0&&!t.searchParams.has("template_id"))return!1;let n=t.pathname;if(n.startsWith("/_next/")||n.startsWith(ee)||n===Z||n===Ge)return!1;let i=e.headers.accept??"";return i===""||i.includes("text/html")}function Nn(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 An(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 p=()=>{if(t.patchNextDevChunk||u)return!1;let c=Buffer.concat(n);if(!/<body\b[^>]*>/iu.test(c.toString("utf8")))return!1;let l=$e(e,c,t);return l!==c&&!e.headersSent&&e.removeHeader("content-length"),o(l),n.length=0,u=!0,!0};return i.write=(c,l,a)=>u?(c!==void 0&&o(Ie(c,we(l))),Y(l),Y(a),!0):(c!==void 0&&(n.push(Ie(c,we(l))),p()),Y(l),Y(a),!0),i.end=(c,l,a)=>{let f=typeof c=="function"?void 0:c;if(u)return i.write=o,i.end=r,i.flushHeaders=s,f===void 0?typeof c=="function"?r(c):typeof l=="function"?r(l):typeof a=="function"?r(a):r():typeof l=="function"?r(f,l):typeof a=="function"?r(f,l,a):r(f,l);f!==void 0&&n.push(Ie(f,we(l))),i.write=o,i.end=r,i.flushHeaders=s;let m=Buffer.concat(n),y=$e(e,m,t);y!==m&&!e.headersSent&&e.removeHeader("content-length");let S=typeof c=="function"?c:typeof l=="function"?l:typeof a=="function"?a:void 0;return S?r(y,S):r(y)},()=>{i.write=o,i.end=r,i.flushHeaders=s}}function $e(e,t,n){if(Dn(e,t))return Buffer.from(In(t.toString("utf8"),n));if(n.patchNextDevChunk&&Mn(e)){let i=t.toString("utf8"),s=Bn(i);if(s!==i)return Buffer.from(s)}return t}function On(){let e=new Uint8Array(32);return globalThis.crypto.getRandomValues(e),Buffer.from(e).toString("base64url")}function Dn(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 Mn(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 Bn(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,[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])=>jn(n,i,s),e)}function jn(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 Ie(e,t){return Buffer.isBuffer(e)?e:e instanceof Uint8Array?Buffer.from(e):Buffer.from(String(e),t)}function we(e){return typeof e=="string"?e:void 0}function Y(e){typeof e=="function"&&e()}async function Un(e){let n=Ve(G(e,"package.json"))(Yt);return n.default??n}function Fn(e){return Le(e)!=="src"?e:xe(Kn(e),"dist")}function Kn(e){return Ye(e)?q.posix.dirname(e):z(e)}function xe(e,t){return Ye(e)?q.posix.join(e,t):G(e,t)}function Ye(e){return e.includes("/")&&!e.includes("\\")}function Wn(e){let t=G(e,".vcp/current-snapshot.json");if(!U(t))return"sandbox_worktree";try{let n=JSON.parse(j(t,"utf8"));return typeof n.snapshot_id=="string"&&n.snapshot_id?n.snapshot_id:typeof n.snapshotId=="string"&&n.snapshotId?n.snapshotId:"sandbox_worktree"}catch{return"sandbox_worktree"}}function $n(e){if(!e)return null;let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0&&t<=65535?t:null}function Vn(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 X(e){try{e()}catch{}}function qn(e){try{return e()}catch(t){return Promise.reject(t)}}function zn(e,t){let n=e.address();return typeof n=="object"&&n?n.port:t}export{ln as attachNextDevUpgradeHandler,ye as attachSiteEditBridgeServerInWorker,vn as classifySiteEditRenderProbeError,mn as createSiteEditAutoBootstrapHtmlSnippet,In as injectSiteEditAutoBootstrap,bn as isSiteEditRenderProbeRequested,Bn as patchNextFirefoxDevChunk,Pn as prepareFirefoxNextDevChunkPatchRequest,kn as prepareSiteEditHtmlInjectionRequest,gn as resolveSiteEditBridgeHealthPath,Qe as resolveSiteEditClientAsset,bi as resolveSiteEditClientAssetPath,un as resolveSiteEditNextDevBundler,Rn as runSiteEditRenderProbe,Ri as startSiteEditNextDevServer,En as transpileSiteEditClientSource};
|
package/dist/server.d.ts
CHANGED
|
@@ -33,6 +33,11 @@ export type SiteEditBridgeServerOptions = {
|
|
|
33
33
|
* 事件已广播之后才调用,供 dev server 把 health 的 launch.snapshotId 同步过去。
|
|
34
34
|
*/
|
|
35
35
|
onSnapshotIdChanged?: (next: string, previous: string) => void;
|
|
36
|
+
/**
|
|
37
|
+
* health 任一字段变化后的回调。引擎跑在独立线程时,主线程靠它同步一份
|
|
38
|
+
* health 副本,HTTP 健康检查才能同步作答。
|
|
39
|
+
*/
|
|
40
|
+
onHealthChanged?: (health: SiteEditBridgeHealth) => void;
|
|
36
41
|
};
|
|
37
42
|
/** @internal Dependency seam for deterministic runtime and durable-store tests. */
|
|
38
43
|
export type SiteEditBridgeServerDependencies = {
|
|
@@ -74,12 +79,21 @@ export declare class SiteEditBridgeServer {
|
|
|
74
79
|
private readonly runtimeVersionFloors;
|
|
75
80
|
private activeHistoryStore;
|
|
76
81
|
private versionPersistence;
|
|
82
|
+
private bridgeEngine;
|
|
83
|
+
private lastNotifiedHealth;
|
|
77
84
|
constructor(options: ReturnType<typeof normalizeOptions>, server?: HttpServer, closeHttpServerOnStop?: boolean, dependencies?: SiteEditBridgeServerDependencies);
|
|
85
|
+
/**
|
|
86
|
+
* `webSocket: false` 只起引擎侧(运行时、历史、文件监听),不挂 WebSocket:
|
|
87
|
+
* 引擎跑在独立线程时由该线程使用,连接和鉴权仍留在主线程。
|
|
88
|
+
*/
|
|
78
89
|
start(options?: {
|
|
79
90
|
listen?: boolean;
|
|
91
|
+
webSocket?: boolean;
|
|
80
92
|
}): void;
|
|
93
|
+
getEngine(): SiteEditBridgeEngine;
|
|
81
94
|
getHealth(): SiteEditBridgeHealth;
|
|
82
95
|
stop(): void;
|
|
96
|
+
private notifyHealthChanged;
|
|
83
97
|
private createRuntime;
|
|
84
98
|
private createBridgeEngine;
|
|
85
99
|
private bindHistorySession;
|
|
@@ -95,6 +109,33 @@ export declare class SiteEditBridgeServer {
|
|
|
95
109
|
private getInitialDocumentVersions;
|
|
96
110
|
private getRuntime;
|
|
97
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* 只起引擎侧、不挂 WebSocket 也不监听端口的桥实例,给独立引擎线程用
|
|
114
|
+
* (见 engine-worker.ts)。
|
|
115
|
+
* @internal
|
|
116
|
+
*/
|
|
117
|
+
export declare function createSiteEditBridgeEngineHost(options: SiteEditBridgeServerOptions): {
|
|
118
|
+
engine: SiteEditBridgeEngine;
|
|
119
|
+
getHealth: () => SiteEditBridgeHealth;
|
|
120
|
+
stop: () => void;
|
|
121
|
+
};
|
|
122
|
+
/** @internal */
|
|
123
|
+
export declare function normalizeSiteEditBridgeServerOptions(options: SiteEditBridgeServerOptions): {
|
|
124
|
+
tenantId: string | undefined;
|
|
125
|
+
siteId: string;
|
|
126
|
+
snapshotId: string;
|
|
127
|
+
projectRoot: string;
|
|
128
|
+
basePath: string;
|
|
129
|
+
host: string;
|
|
130
|
+
port: number;
|
|
131
|
+
path: string;
|
|
132
|
+
sessionKey: string;
|
|
133
|
+
historyRoot: string | undefined;
|
|
134
|
+
allowedOrigins: string[];
|
|
135
|
+
runtime: SiteEditBridgeRuntimeOptions;
|
|
136
|
+
onSnapshotIdChanged: ((next: string, previous: string) => void) | undefined;
|
|
137
|
+
onHealthChanged: ((health: SiteEditBridgeHealth) => void) | undefined;
|
|
138
|
+
};
|
|
98
139
|
declare function normalizeOptions(options: SiteEditBridgeServerOptions): {
|
|
99
140
|
tenantId: string | undefined;
|
|
100
141
|
siteId: string;
|
|
@@ -109,6 +150,7 @@ declare function normalizeOptions(options: SiteEditBridgeServerOptions): {
|
|
|
109
150
|
allowedOrigins: string[];
|
|
110
151
|
runtime: SiteEditBridgeRuntimeOptions;
|
|
111
152
|
onSnapshotIdChanged: ((next: string, previous: string) => void) | undefined;
|
|
153
|
+
onHealthChanged: ((health: SiteEditBridgeHealth) => void) | undefined;
|
|
112
154
|
};
|
|
113
155
|
export declare function attachSiteEditWebSocketServer(options: {
|
|
114
156
|
server: HttpServer;
|
package/dist/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import ge from"node:http";import E from"node:fs";import{createHash as Z,randomBytes as ye,timingSafeEqual as Ie}from"node:crypto";import m from"node:path";import{WebSocket as X,WebSocketServer as Se}from"ws";import{createPreviewSiteEditEngineRuntime as we,openFileHistoryStore as Ce,resolveManagedPageTemplateComposition as Re,scanSiteEditCompositionsFromFiles as Ee}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as xe,scanNextAppRouterRoutesFromFiles as ve}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as be}from"@vcp-dev/site-edit-engine/source-watcher";function $(t){let e=new Map,n=[],s=new Map,o=[],i=t.pageComposition.status==="supported"?t.pageComposition.placementUnitKeys:[],r=new Map(i.map((a,h)=>[a,h])),c=t.pageComposition.status==="supported"?{status:"supported",containerKey:t.pageComposition.containerKey,hasBusinessMembers:t.pageComposition.hasBusinessMembers,placementUnitKeys:t.pageComposition.placementUnitKeys,placements:t.pageComposition.placements.map(a=>({placementKey:a.placementKey,placementUnitKey:a.placementUnitKey,relation:a.relation}))}:{status:"unsupported",reason:t.pageComposition.reason,diagnostics:t.pageComposition.diagnostics},p=t.entries.flatMap((a,h)=>a.dynamicDataSummary?[[h,a.dynamicDataSummary]]:[]),u=a=>{let h=e.get(a);if(typeof h=="number")return h;let C=n.length;return n.push(a),e.set(a,C),C},f=a=>{let h=null;for(let C of ae(a)){let q=`${h??"root"}\0${C}`,V=s.get(q);if(typeof V=="number"){h=V;continue}let U=o.length;o.push([h,u(C)]),s.set(q,U),h=U}if(h===null)throw new Error("Document key must not be empty");return h};return{encoding:"segment-trie-v1",siteId:t.siteId,snapshotId:t.snapshotId,composition:{...t.composition},routeId:t.composition.routeId,version:t.version,pageComposition:c,tokens:n,nodes:o,entries:t.entries.map(a=>{let h=a.pageCompositionUnitKey?r.get(a.pageCompositionUnitKey)??-1:-1;if(a.pageCompositionUnitKey&&h<0)throw new Error(`Render entry ${a.key} references unknown page composition unit ${a.pageCompositionUnitKey}`);return[f(a.key),re(a.operationSummary),...h>=0?[h]:[]]}),...t.entries.every(a=>a.parentKey!==void 0)?{entryParentNodeIds:t.entries.map(a=>a.parentKey===null?null:f(a.parentKey))}:{},...p.length>0?{dynamicDataSummaries:p}:{}}}function re(t){return(t.canUpdateText?1:0)|(t.canInsertChild?2:0)|(t.canMove?4:0)|(t.canRemove?8:0)}function ae(t){if(!t)return[];let e=[],n=0,s=W(t,n);if(s<0)return e.push(t),e;for(e.push(t.slice(0,s)),n=s;n<t.length;){if(s=W(t,n+(t.startsWith("::",n)?2:1)),s<0){e.push(t.slice(n));break}e.push(t.slice(n,s)),n=s}return e}function W(t,e){let n=t.indexOf("/",e),s=t.indexOf("::",e);return n<0?s:s<0?n:Math.min(n,s)}var j="SITE_EDIT_COMPOSITION_SESSION_MISMATCH",M="SITE_EDIT_COMPOSITION_SESSION_REQUIRED";var l=class t{constructor(){}static parseMessage(e,n={}){let s=JSON.parse(e);if(!t.isMessage(s,n))throw new Error("Invalid site edit bridge message");return s}static stringifyMessage(e){return JSON.stringify(e)}static extractRequestInfo(e){if(d(e)&&d(e.payload)){let n=typeof e.payload.id=="string"?e.payload.id:"unknown",s=this.isRequestKind(e.payload.kind)?e.payload.kind:"get-document";return{id:n,kind:s}}return{id:"unknown",kind:"get-document"}}static isRequestKind(e){return e==="initialize-session"||e==="get-document"||e==="get-history-status"||e==="get-style-context"||e==="get-object-edit-context"||e==="get-class-name-source"||e==="get-source-tree"||e==="get-source-file"||e==="resolve-source-location"||e==="execute"||e==="undo"||e==="redo"}static isMessage(e,n={}){return!d(e)||typeof e.type!="string"?!1:e.type==="request"?ce(e.payload,n):e.type==="response"||e.type==="error"?d(e.payload)&&typeof e.payload.id=="string":e.type==="event"?d(e.payload)&&d(e.payload.event):e.type==="auth"?d(e.payload)&&typeof e.payload.key=="string":e.type==="auth-ok"&&d(e.payload)&&e.payload.ok===!0}},x=class{constructor(e){this.options=e,this.session={subscribedCompositionKeys:new Set},this.messageQueue=new D,this.eventDispatcher=new A(n=>{this.sendJson(n)}),this.unsubscribe=e.engine.subscribe(n=>{this.handleEngineEvent(n)}),e.socket.onmessage=n=>{try{this.messageQueue.enqueue(()=>this.handleRawMessage(n.data))}catch(s){this.sendJson({type:"error",payload:{id:"unknown",kind:"get-document",message:s instanceof Error?s.message:"Bridge queue overflow"}}),e.socket.close(1013,"bridge busy")}},e.socket.onclose=()=>{this.unsubscribe(),this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.clear()}}async handleMessage(e){try{let n=e;if(n.type!=="request")return;let s=n.payload,{id:o}=s;switch(s.kind){case"initialize-session":{let i=k(s),r=await this.resolve(i),c=await this.options.engine.initializeSession({composition:r.context,...s.mutationContext?{mutationContext:s.mutationContext}:{}});this.activateSession(r.context),this.sendJson({type:"response",payload:{id:o,kind:s.kind,result:y({...c,data:{...c.data,document:z(c.data.document)}},r.context)}});return}case"get-document":{let i=await this.resolveWireContext(s),r=await this.options.engine.getDocument(i);this.sendResult(o,s.kind,me(r,i));return}case"get-history-status":{let i=await this.resolveWireContext(s),r=await this.options.engine.getHistoryStatus(i);this.sendResult(o,s.kind,y(r,i));return}case"get-style-context":{let i=await this.resolveWireContext(s);this.sendResult(o,s.kind,y(await this.options.engine.getStyleContext(i),i));return}case"get-object-edit-context":{let i=await this.resolveWireContext(s);this.sendResult(o,s.kind,y(await this.options.engine.getObjectEditContext(s.key,i,s.selectionRef),i));return}case"get-class-name-source":{let i=await this.resolveWireContext(s);this.sendResult(o,s.kind,y(await this.options.engine.getClassNameSource(s.key,i),i));return}case"get-source-tree":{let i=await this.resolveWireContext(s);this.sendResult(o,s.kind,y(await this.options.engine.getSourceTree(i),i));return}case"get-source-file":{let i=await this.resolveWireContext(s);this.sendResult(o,s.kind,y(await this.options.engine.getSourceFile({path:s.path,composition:i}),i));return}case"resolve-source-location":{let i=await this.resolveWireContext(s),r=y(await this.options.engine.resolveSourceLocation({key:s.key,composition:i}),i);this.sendResult(o,s.kind,r);return}case"execute":{let i=await this.resolveOperationWireContext(s.request),r=await this.resolveWireContext(s);if(I(i)!==I(r))throw new S(j,"The execute request composition does not match its operation composition.");let c=await this.normalizeOperationRequest(s.request,i);await this.handleBufferedOperation(o,s.kind,()=>this.options.engine.execute(c));return}case"undo":case"redo":{let i=await this.resolveWireContext(s);await this.handleBufferedOperation(o,s.kind,()=>s.kind==="undo"?this.options.engine.undo(i):this.options.engine.redo(i));return}}}catch(n){let s=l.extractRequestInfo(e);this.sendJson({type:"error",payload:{...s,message:O(n),code:T(n)}})}}async resolve(e){return this.options.engine.resolveComposition(e)}async resolveAndAssert(e){let n=await this.resolve(e),s=this.session.activeComposition;if(!s)throw new S(M,"Initialize a composition session before issuing this request.");if(I(s)!==I(n.context))throw new S(j,"The request composition does not match the active socket session.");return n.context}async resolveWireContext(e){let n=k(e);return this.resolveAndAssert(n)}async resolveOperationWireContext(e){let n=pe(e);return this.resolveAndAssert(n)}activateSession(e){this.session.activeComposition=e,this.session.subscribedCompositionKeys.clear(),this.session.subscribedCompositionKeys.add(I(e))}async normalizeOperationRequest(e,n){let s={...e,composition:L(n)};if(s.target.kind==="route"){let o=s.target,i=k(o),r=await this.resolveAndAssert(i);s.target={...o,composition:L(r)}}return s}handleEngineEvent(e){let n=e?.composition;if(!he(n))return;let s=I(n);this.session.subscribedCompositionKeys.has(s)&&(this.eventDispatcher.push(e),e.type==="site-edit.graph.invalidated"&&s===le(this.session)&&(this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.delete(s)))}sendResult(e,n,s){this.sendJson({type:"response",payload:{id:e,kind:n,result:s}})}async handleRawMessage(e){try{await this.handleMessage(l.parseMessage(e,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let s=fe(e);this.sendJson({type:"error",payload:{...s,message:O(n),code:T(n)}})}}sendJson(e){this.options.socket.send(l.stringifyMessage(e))}async handleBufferedOperation(e,n,s){this.eventDispatcher.beginBuffering();try{let o=await s();this.sendResult(e,n,o)}catch(o){this.sendJson({type:"error",payload:{id:e,kind:n,message:O(o),code:T(o)}})}finally{this.eventDispatcher.flush()}}},S=class extends Error{constructor(e,n){super(n),this.code=e,this.name="BridgeProtocolError"}},A=class{constructor(e){this.send=e,this.bufferedEvents=null}push(e){let n={type:"event",payload:{event:e}};if(this.bufferedEvents){this.bufferedEvents.push(n);return}this.send(n)}beginBuffering(){this.bufferedEvents=[]}flush(){let e=this.bufferedEvents??[];this.bufferedEvents=null,e.forEach(n=>this.send(n))}},D=class{constructor(e=64){this.maxQueued=e,this.queue=Promise.resolve(),this.queued=0}enqueue(e){if(this.queued>=this.maxQueued)throw new Error("Bridge request queue is full");this.queued+=1;let n=async()=>{this.queued-=1,await e()},s=this.queue.then(n,n);return this.queue=s.then(()=>{},()=>{}),s}};function ce(t,e){if(!d(t)||typeof t.id!="string"||!l.isRequestKind(t.kind))return!1;let n=R(t.composition);switch(t.kind){case"initialize-session":return n&&(t.mutationContext===void 0||_(t.mutationContext));case"get-document":case"get-style-context":return n;case"get-object-edit-context":return typeof t.key=="string"&&n&&ue(t.selectionRef);case"get-class-name-source":case"resolve-source-location":return typeof t.key=="string"&&n;case"get-history-status":return n;case"get-source-tree":return n;case"get-source-file":return typeof t.path=="string"&&n;case"execute":return n&&de(t.request)&&(t.request.mutationContext===void 0||_(t.request.mutationContext))&&(e.validateOperationRequest?.(t.request)??!0);case"undo":case"redo":return n&&t.mutationContext===void 0||n&&_(t.mutationContext)}}function ue(t){if(t===void 0)return!0;if(!d(t)||typeof t.key!="string"||t.key.length===0)return!1;let e=t.instanceIndex,n=t.instanceCount;return e!==void 0&&(typeof e!="number"||!Number.isInteger(e)||e<0)||n!==void 0&&(typeof n!="number"||!Number.isInteger(n)||n<=0)||typeof e=="number"&&typeof n=="number"&&e>=n?!1:Object.keys(t).every(s=>s==="key"||s==="instanceIndex"||s==="instanceCount")}function de(t){return d(t)&&typeof t.id=="string"&&typeof t.siteId=="string"&&typeof t.baseSnapshotId=="string"&&R(t.composition)&&typeof t.kind=="string"&&d(t.target)&&(t.target.kind!=="route"||R(t.target.composition))&&typeof t.documentVersion=="number"&&Number.isInteger(t.documentVersion)&&t.documentVersion>=0&&d(t.params)}function R(t){return d(t)&&typeof t.routeId=="string"&&t.routeId.length>0&&(t.templateId===void 0||typeof t.templateId=="string"&&t.templateId.length>0)&&Object.keys(t).every(e=>e==="routeId"||e==="templateId")}function he(t){return d(t)&&typeof t.routeId=="string"&&(t.effectiveTemplateId===void 0||typeof t.effectiveTemplateId=="string")}function k(t){if(R(t.composition))return t.composition;throw new S(M,"A composition selector is required for this request.")}function pe(t){if(d(t)&&R(t.composition))return t.composition;throw new S(M,"A composition selector is required for execute.")}function L(t){return{routeId:t.routeId,...t.effectiveTemplateId?{templateId:t.effectiveTemplateId}:{}}}function le(t){return t.activeComposition?I(t.activeComposition):null}function I(t){return JSON.stringify([t.routeId,t.effectiveTemplateId??null])}function y(t,e){return d(t)&&t.ok===!0?{...t,composition:e}:t}function me(t,e){return!d(t)||t.ok!==!0?t:{...t,...e?{composition:e}:{},data:z(t.data)}}function z(t){let e=t;return $(e.routeId?e:{...e,routeId:e.composition.routeId})}function d(t){return typeof t=="object"&&t!==null}function _(t){return d(t)&&typeof t.editingSessionId=="string"&&t.editingSessionId.length>0&&typeof t.lockId=="string"&&t.lockId.length>0&&typeof t.ownerId=="string"&&t.ownerId.length>0&&typeof t.leaseAssertion=="string"&&t.leaseAssertion.length>0}function T(t){if(!(!d(t)||typeof t.code!="string"||t.code.length===0))return t.code}function O(t){return t instanceof Error||d(t)&&typeof t.message=="string"&&t.message.length>0?t.message:"Unknown websocket operation failure"}function fe(t){try{return l.extractRequestInfo(JSON.parse(t))}catch{return{id:"unknown",kind:"get-document"}}}var v=class{constructor(){this.tail=Promise.resolve()}run(e){let n=this.tail.then(e,e);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as ke,computeSiteEditCompositionKey as w}from"@vcp-dev/contracts";var _e="127.0.0.1",Te=30218,Oe="/__vcp/site-edit/bridge",ee=6*1024*1024,Ae=ee*2,De=3e3,J=50,B=".vcp/current-snapshot.json",Q="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function at(t){let e=te(t),n=se(e),s=ne(),o=s.get(n);if(o)return o;let i=new b(e);s.set(n,i);try{i.start()}catch(r){throw s.delete(n),i.stop(),r}return i}function ct(t,e,n={}){let s=new b(te(e),t,!1,n);return s.start({listen:!1}),s}var b=class{constructor(e,n=ge.createServer((i,r)=>{r.writeHead(404),r.end("VCP site edit bridge server")}),s=!0,o={}){this.options=e,this.server=n,this.closeHttpServerOnStop=s,this.dependencies=o,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new v,this.eventListeners=new Set,this.routes=[],this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.launchCandidateEntries=null,this.stopWatching=null,this.stopWebSocketServer=null,this.pendingChangedFiles=new Set,this.fileChangeTimer=null,this.isStarted=!1,this.stopping=!1,this.runtimeVersionFloors=new Map,this.activeHistoryStore=null,this.versionPersistence=Promise.resolve()}start(e={}){if(this.isStarted)return;this.stopping=!1;try{E.mkdirSync(m.join(this.options.projectRoot,".vcp"),{recursive:!0})}catch(s){console.warn(`[vcp-site-edit] failed to ensure .vcp directory: ${s instanceof Error?s.message:String(s)}`)}this.refreshCompositions(!0),this.isStarted=!0,this.createRuntime();let n=Ne({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 be({projectRoot:this.options.projectRoot,watch_dirs:Me(this.options.runtime.watch_dirs),interval_ms:this.options.runtime.watch_interval_ms,debounce_ms:this.options.runtime.watch_debounce_ms??J,max_dirs:this.options.runtime.watch_max_dirs,max_files:this.options.runtime.watch_max_files,max_depth:this.options.runtime.watch_max_depth,max_startup_ms:this.options.runtime.watch_max_startup_ms,on_change:s=>this.handleFileChanged(s),on_batch_change:s=>this.handleFilesChanged(s)}).start(),e.listen!==!1&&this.server.listen(this.options.port,this.options.host)}getHealth(){let e=!!this.runtime&&!this.compositionResolutionError;return{ok:this.isStarted&&e,bridgePath:this.options.path,runtimeReady:e,routesIndexed:this.routes.length,sessionRequired:!!this.options.sessionKey}}stop(){this.stopping=!0,this.stopWatching?.(),this.stopWatching=null,this.flushChangedFiles(),this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.stopWebSocketServer?.(),this.stopWebSocketServer=null,this.runtime?.dispose(),this.runtime=null,this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.closeHttpServerOnStop&&this.server.close();let e=ne(),n=se(this.options);e.get(n)===this&&e.delete(n),this.isStarted=!1}createRuntime(e,n=null){let s=this.runtime;this.activeHistoryStore=e??null;let o=(this.dependencies.createRuntime??we)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,compositions:this.compositions,onEvent:i=>{"version"in i&&typeof i.version=="number"&&this.recordRuntimeVersion(i.version,"composition"in i?i.composition:void 0);for(let r of this.eventListeners)r(i)},...e?{historyStore:e}:{},...this.getInitialDocumentVersions().length>0?{initialDocumentVersions:this.getInitialDocumentVersions()}:{}});this.runtime=o,this.activeHistorySessionId=n,s?.dispose()}createBridgeEngine(){return{resolveComposition:e=>this.resolveComposition(e),initializeSession:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)throw new Error(`${n.code}: ${n.message}`);let s=Ke(e.composition),o=this.getRuntime(),i=g(s);await o.initializeComposition(i);let c=o.getDocument(i).data;await this.recordRuntimeVersion(c.version,s);let p=(await o.getHistoryStatus()).data;return{ok:!0,composition:s,data:{document:c,historyStatus:p}}}),getDocument:async e=>{let n=this.getRuntime(),s=g(e);await n.initializeComposition(s);let o=n.getDocument(s);return await this.recordRuntimeVersion(o.data.version,e),{...o,composition:e}},getHistoryStatus:async e=>({...await this.getRuntime().getHistoryStatus(),composition:e}),getStyleContext:async e=>this.getRuntime().getStyleContext(g(e)),getObjectEditContext:async(e,n,s)=>this.getRuntime().getObjectEditContext(e,g(n),s),getClassNameSource:async(e,n)=>this.getRuntime().getClassNameSource(e,g(n)),getSourceTree:async e=>this.getRuntime().getSourceTree(g(e)),getSourceFile:async e=>this.getRuntime().getSourceFile({path:e.path,composition:g(e.composition)}),resolveSourceLocation:async e=>this.getRuntime().resolveSourceLocation({key:e.key,composition:g(e.composition)}),execute:(e,n)=>this.mutationQueue.run(async()=>{let s=await this.bindHistorySession(this.options.snapshotId);if(!s.ok)return F(s,e);let o=await this.getRuntime().executeOperation(e);return o.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(o.data.resultVersion,o.composition),o}),undo:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return F(n,void 0,e);let s=await this.getRuntime().undo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion,s.composition??e),s.composition?s:{...s,composition:e}}),redo:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return F(n,void 0,e);let s=await this.getRuntime().redo();return s.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(s.data.resultVersion,s.composition??e),s.composition?s:{...s,composition:e}}),subscribe:e=>(this.eventListeners.add(e),()=>this.eventListeners.delete(e))}}async bindHistorySession(e){if(!this.options.historyRoot)return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history is not configured."};if(this.activeHistorySessionId===e)return{ok:!0};let n=this.historySessionTransition.then(async()=>{if(this.activeHistorySessionId===e)return;let s=await(this.dependencies.openHistoryStore??Ce)(Be({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e}));if(!this.isStarted)return;let o=s.readDocumentVersion?await s.readDocumentVersion():0,i=this.compositions[0]?.context;if(i){let r=w(i);this.runtimeVersionFloors.set(r,Math.max(this.runtimeVersionFloors.get(r)??0,o))}this.createRuntime(s,e)});this.historySessionTransition=n.catch(()=>{});try{return await n,{ok:!0}}catch{return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history could not be opened."}}}recordRuntimeVersion(e,n){let s=n?w(n):null;s&&this.runtimeVersionFloors.set(s,Math.max(this.runtimeVersionFloors.get(s)??0,e));let o=this.activeHistoryStore;if(!o?.writeDocumentVersion)return Promise.resolve();let i=s?this.runtimeVersionFloors.get(s)??e:e;return this.versionPersistence=this.versionPersistence.then(()=>o.writeDocumentVersion(i)).catch(()=>{}),this.versionPersistence}handleFileChanged(e){this.handleFilesChanged([e])}handleFilesChanged(e){for(let s of e)this.pendingChangedFiles.add(s);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let n=this.options.runtime.watch_debounce_ms??J;this.fileChangeTimer=setTimeout(()=>{this.fileChangeTimer=null,this.flushChangedFiles()},n),this.fileChangeTimer?.unref?.()}flushChangedFiles(){if(this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.pendingChangedFiles.size===0)return;let e=[...this.pendingChangedFiles].sort((n,s)=>n.localeCompare(s));if(this.pendingChangedFiles.clear(),e.some(je)&&this.scheduleSnapshotIdentityAdvance(),!!this.refreshCompositions(!1))for(let n of e)this.runtime?.notifyFileChanged(n)}scheduleSnapshotIdentityAdvance(){if(this.stopping||!this.isStarted)return;let e=this.readCurrentSnapshotMarker();!e||e===this.options.snapshotId||this.mutationQueue.run(()=>this.advanceSnapshotId(e)).catch(n=>{console.warn(`[vcp-site-edit] snapshot identity advance failed: ${n instanceof Error?n.message:String(n)}`)})}readCurrentSnapshotMarker(){try{let e=E.readFileSync(m.join(this.options.projectRoot,B),"utf8"),n=JSON.parse(e),s=typeof n.snapshot_id=="string"?n.snapshot_id:"";return!s||N(s)!==s?(console.warn("[vcp-site-edit] current-snapshot marker has no valid snapshot_id"),null):s}catch(e){return console.warn(`[vcp-site-edit] current-snapshot marker unreadable: ${e instanceof Error?e.message:String(e)}`),null}}async advanceSnapshotId(e){if(this.stopping||!this.isStarted||e===this.options.snapshotId)return;let n=this.options.snapshotId;this.options.snapshotId=e;try{if(this.options.historyRoot){let o=await this.bindHistorySession(e);if(!o.ok)throw new Error(`${o.code}: ${o.message}`)}else this.createRuntime()}catch(o){this.options.snapshotId=n,console.warn(`[vcp-site-edit] snapshot identity advance rolled back to ${n}: ${o instanceof Error?o.message:String(o)}`);return}let s=new Date().toISOString();for(let o of this.compositions){let i={type:"site-edit.snapshot.changed",siteId:this.options.siteId,composition:o.context,snapshotId:e,previousSnapshotId:n,at:s};for(let r of this.eventListeners)try{r(i)}catch(c){console.warn(`[vcp-site-edit] snapshot.changed listener failed: ${c instanceof Error?c.message:String(c)}`)}}try{this.options.onSnapshotIdChanged?.(e,n)}catch(o){console.warn(`[vcp-site-edit] onSnapshotIdChanged callback failed: ${o instanceof Error?o.message:String(o)}`)}}refreshCompositions(e=!1){try{let n=Fe(this.options.projectRoot),s=ve(n),o=s.length>0?s:(this.options.runtime.routes??[]).map(u=>({...u})),i=Ee({files:n});if(i.length===0)throw new Error("No canonical App Router composition is indexed by the preview bridge.");let r=JSON.stringify(i.filter(u=>u.context.effectiveTemplateId!==void 0).map(u=>u.entryFile).sort());if(e&&(this.launchCandidateEntries=r),this.launchCandidateEntries!==r)throw new Error("SITE_EDIT_RUNTIME_RELOAD_REQUIRED: Candidate route collection changed since preview launch");let c=new Map(this.compositions.map(u=>[w(u.context),u])),p=new Map(i.map(u=>[w(u.context),u]));if(this.runtime){for(let[u,f]of c){let a=p.get(u);(!a||a.entryFile!==f.entryFile)&&this.runtime.unregisterComposition(g(f.context))}for(let[u,f]of p){let a=c.get(u);(!a||a.entryFile!==f.entryFile)&&this.runtime.registerComposition(f.context,f.entryFile)}}return this.routes=o,this.compositions=i,this.compositionFiles=n,this.compositionResolutionError=null,!0}catch(n){let s=n instanceof Error?n:new Error(String(n));if(this.compositionResolutionError=s,e)throw s;return!1}}resolveComposition(e){if(this.compositionResolutionError)throw this.compositionResolutionError;let n=xe(e.routeId,this.routes,this.options.basePath);if(!n)throw new Error(`Route is not indexed by the preview bridge: ${e.routeId}`);let s=this.routes.find(r=>r.routeId===n);if(!s)throw new Error(`Route is not indexed by the preview bridge: ${e.routeId}`);let o=Re({selector:{routeId:n,...e.templateId!==void 0?{templateId:e.templateId}:{}},files:this.compositionFiles,canonicalEntryFile:s.entryFile}),i=w(o.context);if(!this.compositions.some(r=>w(r.context)===i))throw new Error(`Composition is not registered by the preview bridge: ${i}`);return o}getInitialDocumentVersions(){return this.compositions.flatMap(e=>{let n=this.runtimeVersionFloors.get(w(e.context));return n===void 0?[]:[{composition:e.context,version:n}]})}getRuntime(){if(!this.runtime)throw new Error("VCP site edit preview bridge runtime is not ready");return this.runtime}};function te(t){return{tenantId:t.tenantId,siteId:t.siteId,snapshotId:t.snapshotId,projectRoot:m.resolve(t.projectRoot??process.cwd()),basePath:t.basePath??"",host:t.host??_e,port:t.port??Te,path:t.path??Oe,sessionKey:t.sessionKey??$e(),historyRoot:t.historyRoot,allowedOrigins:t.allowedOrigins??[],runtime:t.runtime??{},onSnapshotIdChanged:t.onSnapshotIdChanged}}function Me(t){let e=["app","src/app","components/site","src/components/site","lib/generated","src/lib/generated","public"];return[...new Set([...t?.length?t:e,".vcp"])]}function Fe(t){let e=[],n=["app","src/app",".vcp"],s=new Set([".git",".next",".turbo","coverage","dist","node_modules","output","snapshot-store"]),o=i=>{if(!E.existsSync(i))return;let r;try{r=E.readdirSync(i,{withFileTypes:!0})}catch{return}for(let c of r){if(c.name.startsWith(".")&&c.name!==".vcp")continue;if(c.isDirectory()){if(s.has(c.name))continue;o(m.join(i,c.name));continue}if(!c.isFile())continue;let p=m.join(i,c.name),u="";try{u=E.readFileSync(p,"utf8")}catch{}e.push({path:m.relative(t,p).replaceAll(m.sep,"/"),content:u})}};for(let i of n)o(m.join(t,i));return e.sort((i,r)=>i.path.localeCompare(r.path))}function Ke(t){if(!t||typeof t!="object"||typeof t.routeId!="string")throw new Error("Normalized composition context is required.");if("effectiveTemplateId"in t)return t;let e=t;return{routeId:e.routeId,...e.templateId?{effectiveTemplateId:e.templateId}:{}}}function g(t){return{routeId:t.routeId,...t.effectiveTemplateId?{templateId:t.effectiveTemplateId}:{}}}function Be(t){return m.join(m.resolve(t.root),N(t.siteId),N(t.snapshotId),Z("sha256").update(t.editingSessionId).digest("hex").slice(0,32))}function N(t){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(t)&&t!=="."&&t!==".."?t:Z("sha256").update(t).digest("hex").slice(0,32)}function ne(){let t=globalThis;return t[Q]??=new Map,t[Q]}function se(t){return`${t.projectRoot}:${t.host}:${t.port}:${t.path}:${t.historyRoot??""}`}function F(t,e,n){let s=n??(e?.composition?{routeId:e.composition.routeId,...e.composition.templateId?{effectiveTemplateId:e.composition.templateId}:{}}:void 0);if(!s)throw new Error("A normalized composition is required to construct a mutation result.");return{ok:!1,composition:s,error:{code:t.code,message:t.message},data:{requestId:e?.id??"mutation-context",kind:e?.kind??"update-text",composition:s,target:e?.target??{kind:"node",key:""},resultVersion:e?.documentVersion??0}}}function Ne(t){let e=new Se({maxPayload:ee,noServer:!0}),n=new WeakSet,s=i=>{new x({socket:new H(i),engine:t.engine,validateOperationRequest:r=>ke.safeParse(r).success})},o=(i,r,c)=>{if(!He(i,t))return;let p=Pe(i,t);if(!p){r.write(`HTTP/1.1 401 Unauthorized\r
|
|
1
|
+
import X from"node:http";import R from"node:fs";import{createHash as ee,randomBytes as ye,timingSafeEqual as Ie}from"node:crypto";import f from"node:path";import{WebSocket as te,WebSocketServer as Se}from"ws";import{createPreviewSiteEditEngineRuntime as we,openFileHistoryStore as Ce,resolveManagedPageTemplateComposition as Ee,scanSiteEditCompositionsFromFiles as Re}from"@vcp-dev/site-edit-engine";import{resolveNextAppRouteId as xe,scanNextAppRouterRoutesFromFiles as be}from"@vcp-dev/site-edit-engine/next-app-router";import{SiteEditSourceWatcher as ve}from"@vcp-dev/site-edit-engine/source-watcher";function j(t){let e=new Map,n=[],i=new Map,o=[],s=t.pageComposition.status==="supported"?t.pageComposition.placementUnitKeys:[],r=new Map(s.map((a,h)=>[a,h])),c=t.pageComposition.status==="supported"?{status:"supported",containerKey:t.pageComposition.containerKey,hasBusinessMembers:t.pageComposition.hasBusinessMembers,placementUnitKeys:t.pageComposition.placementUnitKeys,placements:t.pageComposition.placements.map(a=>({placementKey:a.placementKey,placementUnitKey:a.placementUnitKey,relation:a.relation}))}:{status:"unsupported",reason:t.pageComposition.reason,diagnostics:t.pageComposition.diagnostics},p=t.entries.flatMap((a,h)=>a.dynamicDataSummary?[[h,a.dynamicDataSummary]]:[]),u=a=>{let h=e.get(a);if(typeof h=="number")return h;let C=n.length;return n.push(a),e.set(a,C),C},m=a=>{let h=null;for(let C of ce(a)){let V=`${h??"root"}\0${C}`,U=i.get(V);if(typeof U=="number"){h=U;continue}let W=o.length;o.push([h,u(C)]),i.set(V,W),h=W}if(h===null)throw new Error("Document key must not be empty");return h};return{encoding:"segment-trie-v1",siteId:t.siteId,snapshotId:t.snapshotId,composition:{...t.composition},routeId:t.composition.routeId,version:t.version,pageComposition:c,tokens:n,nodes:o,entries:t.entries.map(a=>{let h=a.pageCompositionUnitKey?r.get(a.pageCompositionUnitKey)??-1:-1;if(a.pageCompositionUnitKey&&h<0)throw new Error(`Render entry ${a.key} references unknown page composition unit ${a.pageCompositionUnitKey}`);return[m(a.key),ae(a.operationSummary),...h>=0?[h]:[]]}),...t.entries.every(a=>a.parentKey!==void 0)?{entryParentNodeIds:t.entries.map(a=>a.parentKey===null?null:m(a.parentKey))}:{},...p.length>0?{dynamicDataSummaries:p}:{}}}function ae(t){return(t.canUpdateText?1:0)|(t.canInsertChild?2:0)|(t.canMove?4:0)|(t.canRemove?8:0)}function ce(t){if(!t)return[];let e=[],n=0,i=$(t,n);if(i<0)return e.push(t),e;for(e.push(t.slice(0,i)),n=i;n<t.length;){if(i=$(t,n+(t.startsWith("::",n)?2:1)),i<0){e.push(t.slice(n));break}e.push(t.slice(n,i)),n=i}return e}function $(t,e){let n=t.indexOf("/",e),i=t.indexOf("::",e);return n<0?i:i<0?n:Math.min(n,i)}var z="SITE_EDIT_COMPOSITION_SESSION_MISMATCH",M="SITE_EDIT_COMPOSITION_SESSION_REQUIRED";var l=class t{constructor(){}static parseMessage(e,n={}){let i=JSON.parse(e);if(!t.isMessage(i,n))throw new Error("Invalid site edit bridge message");return i}static stringifyMessage(e){return JSON.stringify(e)}static extractRequestInfo(e){if(d(e)&&d(e.payload)){let n=typeof e.payload.id=="string"?e.payload.id:"unknown",i=this.isRequestKind(e.payload.kind)?e.payload.kind:"get-document";return{id:n,kind:i}}return{id:"unknown",kind:"get-document"}}static isRequestKind(e){return e==="initialize-session"||e==="get-document"||e==="get-history-status"||e==="get-style-context"||e==="get-object-edit-context"||e==="get-class-name-source"||e==="get-source-tree"||e==="get-source-file"||e==="resolve-source-location"||e==="execute"||e==="undo"||e==="redo"}static isMessage(e,n={}){return!d(e)||typeof e.type!="string"?!1:e.type==="request"?ue(e.payload,n):e.type==="response"||e.type==="error"?d(e.payload)&&typeof e.payload.id=="string":e.type==="event"?d(e.payload)&&d(e.payload.event):e.type==="auth"?d(e.payload)&&typeof e.payload.key=="string":e.type==="auth-ok"&&d(e.payload)&&e.payload.ok===!0}},b=class{constructor(e){this.options=e,this.session={subscribedCompositionKeys:new Set},this.messageQueue=new D,this.eventDispatcher=new A(n=>{this.sendJson(n)}),this.unsubscribe=e.engine.subscribe(n=>{this.handleEngineEvent(n)}),e.socket.onmessage=n=>{try{this.messageQueue.enqueue(()=>this.handleRawMessage(n.data))}catch(i){this.sendJson({type:"error",payload:{id:"unknown",kind:"get-document",message:i instanceof Error?i.message:"Bridge queue overflow"}}),e.socket.close(1013,"bridge busy")}},e.socket.onclose=()=>{this.unsubscribe(),this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.clear()}}async handleMessage(e){try{let n=e;if(n.type!=="request")return;let i=n.payload,{id:o}=i;switch(i.kind){case"initialize-session":{let s=_(i),r=await this.resolve(s),c=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({...c,data:{...c.data,document:J(c.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,me(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,i.selectionRef),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(I(s)!==I(r))throw new S(z,"The execute request composition does not match its operation composition.");let c=await this.normalizeOperationRequest(i.request,s);await this.handleBufferedOperation(o,i.kind,()=>this.options.engine.execute(c));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=l.extractRequestInfo(e);this.sendJson({type:"error",payload:{...i,message:H(n),code:O(n)}})}}async resolve(e){return this.options.engine.resolveComposition(e)}async resolveAndAssert(e){let n=await this.resolve(e),i=this.session.activeComposition;if(!i)throw new S(M,"Initialize a composition session before issuing this request.");if(I(i)!==I(n.context))throw new S(z,"The request composition does not match the active socket session.");return n.context}async resolveWireContext(e){let n=_(e);return this.resolveAndAssert(n)}async resolveOperationWireContext(e){let n=le(e);return this.resolveAndAssert(n)}activateSession(e){this.session.activeComposition=e,this.session.subscribedCompositionKeys.clear(),this.session.subscribedCompositionKeys.add(I(e))}async normalizeOperationRequest(e,n){let i={...e,composition:L(n)};if(i.target.kind==="route"){let o=i.target,s=_(o),r=await this.resolveAndAssert(s);i.target={...o,composition:L(r)}}return i}handleEngineEvent(e){let n=e?.composition;if(!pe(n))return;let i=I(n);this.session.subscribedCompositionKeys.has(i)&&(this.eventDispatcher.push(e),e.type==="site-edit.graph.invalidated"&&i===fe(this.session)&&(this.session.activeComposition=void 0,this.session.subscribedCompositionKeys.delete(i)))}sendResult(e,n,i){this.sendJson({type:"response",payload:{id:e,kind:n,result:i}})}async handleRawMessage(e){try{await this.handleMessage(l.parseMessage(e,{validateOperationRequest:this.options.validateOperationRequest}))}catch(n){let i=ge(e);this.sendJson({type:"error",payload:{...i,message:H(n),code:O(n)}})}}sendJson(e){this.options.socket.send(l.stringifyMessage(e))}async handleBufferedOperation(e,n,i){this.eventDispatcher.beginBuffering();try{let o=await i();this.sendResult(e,n,o)}catch(o){this.sendJson({type:"error",payload:{id:e,kind:n,message:H(o),code:O(o)}})}finally{this.eventDispatcher.flush()}}},S=class extends Error{constructor(e,n){super(n),this.code=e,this.name="BridgeProtocolError"}},A=class{constructor(e){this.send=e,this.bufferedEvents=null}push(e){let n={type:"event",payload:{event:e}};if(this.bufferedEvents){this.bufferedEvents.push(n);return}this.send(n)}beginBuffering(){this.bufferedEvents=[]}flush(){let e=this.bufferedEvents??[];this.bufferedEvents=null,e.forEach(n=>this.send(n))}},D=class{constructor(e=64){this.maxQueued=e,this.queue=Promise.resolve(),this.queued=0}enqueue(e){if(this.queued>=this.maxQueued)throw new Error("Bridge request queue is full");this.queued+=1;let n=async()=>{this.queued-=1,await e()},i=this.queue.then(n,n);return this.queue=i.then(()=>{},()=>{}),i}};function ue(t,e){if(!d(t)||typeof t.id!="string"||!l.isRequestKind(t.kind))return!1;let n=E(t.composition);switch(t.kind){case"initialize-session":return n&&(t.mutationContext===void 0||T(t.mutationContext));case"get-document":case"get-style-context":return n;case"get-object-edit-context":return typeof t.key=="string"&&n&&de(t.selectionRef);case"get-class-name-source":case"resolve-source-location":return typeof t.key=="string"&&n;case"get-history-status":return n;case"get-source-tree":return n;case"get-source-file":return typeof t.path=="string"&&n;case"execute":return n&&he(t.request)&&(t.request.mutationContext===void 0||T(t.request.mutationContext))&&(e.validateOperationRequest?.(t.request)??!0);case"undo":case"redo":return n&&t.mutationContext===void 0||n&&T(t.mutationContext)}}function de(t){if(t===void 0)return!0;if(!d(t)||typeof t.key!="string"||t.key.length===0)return!1;let e=t.instanceIndex,n=t.instanceCount;return e!==void 0&&(typeof e!="number"||!Number.isInteger(e)||e<0)||n!==void 0&&(typeof n!="number"||!Number.isInteger(n)||n<=0)||typeof e=="number"&&typeof n=="number"&&e>=n?!1:Object.keys(t).every(i=>i==="key"||i==="instanceIndex"||i==="instanceCount")}function he(t){return d(t)&&typeof t.id=="string"&&typeof t.siteId=="string"&&typeof t.baseSnapshotId=="string"&&E(t.composition)&&typeof t.kind=="string"&&d(t.target)&&(t.target.kind!=="route"||E(t.target.composition))&&typeof t.documentVersion=="number"&&Number.isInteger(t.documentVersion)&&t.documentVersion>=0&&d(t.params)}function E(t){return d(t)&&typeof t.routeId=="string"&&t.routeId.length>0&&(t.templateId===void 0||typeof t.templateId=="string"&&t.templateId.length>0)&&Object.keys(t).every(e=>e==="routeId"||e==="templateId")}function pe(t){return d(t)&&typeof t.routeId=="string"&&(t.effectiveTemplateId===void 0||typeof t.effectiveTemplateId=="string")}function _(t){if(E(t.composition))return t.composition;throw new S(M,"A composition selector is required for this request.")}function le(t){if(d(t)&&E(t.composition))return t.composition;throw new S(M,"A composition selector is required for execute.")}function L(t){return{routeId:t.routeId,...t.effectiveTemplateId?{templateId:t.effectiveTemplateId}:{}}}function fe(t){return t.activeComposition?I(t.activeComposition):null}function I(t){return JSON.stringify([t.routeId,t.effectiveTemplateId??null])}function y(t,e){return d(t)&&t.ok===!0?{...t,composition:e}:t}function me(t,e){return!d(t)||t.ok!==!0?t:{...t,...e?{composition:e}:{},data:J(t.data)}}function J(t){let e=t;return j(e.routeId?e:{...e,routeId:e.composition.routeId})}function d(t){return typeof t=="object"&&t!==null}function T(t){return d(t)&&typeof t.editingSessionId=="string"&&t.editingSessionId.length>0&&typeof t.lockId=="string"&&t.lockId.length>0&&typeof t.ownerId=="string"&&t.ownerId.length>0&&typeof t.leaseAssertion=="string"&&t.leaseAssertion.length>0}function O(t){if(!(!d(t)||typeof t.code!="string"||t.code.length===0))return t.code}function H(t){return t instanceof Error||d(t)&&typeof t.message=="string"&&t.message.length>0?t.message:"Unknown websocket operation failure"}function ge(t){try{return l.extractRequestInfo(JSON.parse(t))}catch{return{id:"unknown",kind:"get-document"}}}var v=class{constructor(){this.tail=Promise.resolve()}run(e){let n=this.tail.then(e,e);return this.tail=n.then(()=>{},()=>{}),n}};import{SiteEditOperationRequestSchema as ke,computeSiteEditCompositionKey as w}from"@vcp-dev/contracts";var _e="127.0.0.1",Te=30218,Oe="/__vcp/site-edit/bridge",ne=6*1024*1024,He=ne*2,Ae=3e3,Q=50,K=".vcp/current-snapshot.json",G="__VCP_SITE_EDIT_BRIDGE_SERVERS__";function at(t){let e=k(t),n=se(e),i=ie(),o=i.get(n);if(o)return o;let s=new x(e);i.set(n,s);try{s.start()}catch(r){throw i.delete(n),s.stop(),r}return s}function ct(t,e,n={}){let i=new x(k(e),t,!1,n);return i.start({listen:!1}),i}var x=class{constructor(e,n=X.createServer((s,r)=>{r.writeHead(404),r.end("VCP site edit bridge server")}),i=!0,o={}){this.options=e,this.server=n,this.closeHttpServerOnStop=i,this.dependencies=o,this.runtime=null,this.activeHistorySessionId=null,this.historySessionTransition=Promise.resolve(),this.mutationQueue=new v,this.eventListeners=new Set,this.routes=[],this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.launchCandidateEntries=null,this.stopWatching=null,this.stopWebSocketServer=null,this.pendingChangedFiles=new Set,this.fileChangeTimer=null,this.isStarted=!1,this.stopping=!1,this.runtimeVersionFloors=new Map,this.activeHistoryStore=null,this.versionPersistence=Promise.resolve(),this.bridgeEngine=null,this.lastNotifiedHealth=null}start(e={}){if(!this.isStarted){this.stopping=!1;try{R.mkdirSync(f.join(this.options.projectRoot,".vcp"),{recursive:!0})}catch(n){console.warn(`[vcp-site-edit] failed to ensure .vcp directory: ${n instanceof Error?n.message:String(n)}`)}if(this.refreshCompositions(!0),this.isStarted=!0,this.createRuntime(),this.bridgeEngine=this.createBridgeEngine(),e.webSocket!==!1){let n=Ke({server:this.server,path:this.options.path,sessionKey:this.options.sessionKey,host:this.options.host,allowedOrigins:this.options.allowedOrigins,engine:this.bridgeEngine});this.stopWebSocketServer=n.stop}this.stopWatching=new ve({projectRoot:this.options.projectRoot,watch_dirs:De(this.options.runtime.watch_dirs),interval_ms:this.options.runtime.watch_interval_ms,debounce_ms:this.options.runtime.watch_debounce_ms??Q,max_dirs:this.options.runtime.watch_max_dirs,max_files:this.options.runtime.watch_max_files,max_depth:this.options.runtime.watch_max_depth,max_startup_ms:this.options.runtime.watch_max_startup_ms,on_change:n=>this.handleFileChanged(n),on_batch_change:n=>this.handleFilesChanged(n)}).start(),e.listen!==!1&&this.server.listen(this.options.port,this.options.host),this.notifyHealthChanged()}}getEngine(){if(!this.bridgeEngine)throw new Error("VCP site edit preview bridge runtime is not ready");return this.bridgeEngine}getHealth(){let e=!!this.runtime&&!this.compositionResolutionError;return{ok:this.isStarted&&e,bridgePath:this.options.path,runtimeReady:e,routesIndexed:this.routes.length,sessionRequired:!!this.options.sessionKey}}stop(){this.stopping=!0,this.stopWatching?.(),this.stopWatching=null,this.flushChangedFiles(),this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.stopWebSocketServer?.(),this.stopWebSocketServer=null,this.runtime?.dispose(),this.runtime=null,this.compositions=[],this.compositionFiles=[],this.compositionResolutionError=null,this.closeHttpServerOnStop&&this.server.close();let e=ie(),n=se(this.options);e.get(n)===this&&e.delete(n),this.isStarted=!1,this.notifyHealthChanged()}notifyHealthChanged(){let e=this.options.onHealthChanged;if(!e)return;let n=this.getHealth(),i=JSON.stringify(n);if(i!==this.lastNotifiedHealth){this.lastNotifiedHealth=i;try{e(n)}catch(o){console.warn(`[vcp-site-edit] onHealthChanged callback failed: ${o instanceof Error?o.message:String(o)}`)}}}createRuntime(e,n=null){let i=this.runtime;this.activeHistoryStore=e??null;let o=(this.dependencies.createRuntime??we)({...this.options.tenantId?{tenantId:this.options.tenantId}:{},siteId:this.options.siteId,snapshotId:this.options.snapshotId,projectRoot:this.options.projectRoot,compositions:this.compositions,onEvent:s=>{"version"in s&&typeof s.version=="number"&&this.recordRuntimeVersion(s.version,"composition"in s?s.composition:void 0);for(let r of this.eventListeners)r(s)},...e?{historyStore:e}:{},...this.getInitialDocumentVersions().length>0?{initialDocumentVersions:this.getInitialDocumentVersions()}:{}});this.runtime=o,this.activeHistorySessionId=n,i?.dispose(),this.notifyHealthChanged()}createBridgeEngine(){return{resolveComposition:e=>this.resolveComposition(e),initializeSession:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)throw new Error(`${n.code}: ${n.message}`);let i=Fe(e.composition),o=this.getRuntime(),s=g(i);await o.initializeComposition(s);let c=o.getDocument(s).data;await this.recordRuntimeVersion(c.version,i);let p=(await o.getHistoryStatus()).data;return{ok:!0,composition:i,data:{document:c,historyStatus:p}}}),getDocument:async e=>{let n=this.getRuntime(),i=g(e);await n.initializeComposition(i);let o=n.getDocument(i);return await this.recordRuntimeVersion(o.data.version,e),{...o,composition:e}},getHistoryStatus:async e=>({...await this.getRuntime().getHistoryStatus(),composition:e}),getStyleContext:async e=>this.getRuntime().getStyleContext(g(e)),getObjectEditContext:async(e,n,i)=>this.getRuntime().getObjectEditContext(e,g(n),i),getClassNameSource:async(e,n)=>this.getRuntime().getClassNameSource(e,g(n)),getSourceTree:async e=>this.getRuntime().getSourceTree(g(e)),getSourceFile:async e=>this.getRuntime().getSourceFile({path:e.path,composition:g(e.composition)}),resolveSourceLocation:async e=>this.getRuntime().resolveSourceLocation({key:e.key,composition:g(e.composition)}),execute:(e,n)=>this.mutationQueue.run(async()=>{let i=await this.bindHistorySession(this.options.snapshotId);if(!i.ok)return F(i,e);let o=await this.getRuntime().executeOperation(e);return o.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(o.data.resultVersion,o.composition),o}),undo:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return F(n,void 0,e);let i=await this.getRuntime().undo();return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion,i.composition??e),i.composition?i:{...i,composition:e}}),redo:e=>this.mutationQueue.run(async()=>{let n=await this.bindHistorySession(this.options.snapshotId);if(!n.ok)return F(n,void 0,e);let i=await this.getRuntime().redo();return i.data?.resultVersion!==void 0&&await this.recordRuntimeVersion(i.data.resultVersion,i.composition??e),i.composition?i:{...i,composition:e}}),subscribe:e=>(this.eventListeners.add(e),()=>this.eventListeners.delete(e))}}async bindHistorySession(e){if(!this.options.historyRoot)return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history is not configured."};if(this.activeHistorySessionId===e)return{ok:!0};let n=this.historySessionTransition.then(async()=>{if(this.activeHistorySessionId===e)return;let i=await(this.dependencies.openHistoryStore??Ce)(Be({root:this.options.historyRoot,siteId:this.options.siteId,snapshotId:this.options.snapshotId,editingSessionId:e}));if(!this.isStarted)return;let o=i.readDocumentVersion?await i.readDocumentVersion():0,s=this.compositions[0]?.context;if(s){let r=w(s);this.runtimeVersionFloors.set(r,Math.max(this.runtimeVersionFloors.get(r)??0,o))}this.createRuntime(i,e)});this.historySessionTransition=n.catch(()=>{});try{return await n,{ok:!0}}catch{return{ok:!1,code:"RECOVERY_PERSIST_FAILED",message:"Durable edit history could not be opened."}}}recordRuntimeVersion(e,n){let i=n?w(n):null;i&&this.runtimeVersionFloors.set(i,Math.max(this.runtimeVersionFloors.get(i)??0,e));let o=this.activeHistoryStore;if(!o?.writeDocumentVersion)return Promise.resolve();let s=i?this.runtimeVersionFloors.get(i)??e:e;return this.versionPersistence=this.versionPersistence.then(()=>o.writeDocumentVersion(s)).catch(()=>{}),this.versionPersistence}handleFileChanged(e){this.handleFilesChanged([e])}handleFilesChanged(e){for(let i of e)this.pendingChangedFiles.add(i);this.fileChangeTimer&&clearTimeout(this.fileChangeTimer);let n=this.options.runtime.watch_debounce_ms??Q;this.fileChangeTimer=setTimeout(()=>{this.fileChangeTimer=null,this.flushChangedFiles()},n),this.fileChangeTimer?.unref?.()}flushChangedFiles(){if(this.fileChangeTimer&&(clearTimeout(this.fileChangeTimer),this.fileChangeTimer=null),this.pendingChangedFiles.size===0)return;let e=[...this.pendingChangedFiles].sort((n,i)=>n.localeCompare(i));if(this.pendingChangedFiles.clear(),e.some(je)&&this.scheduleSnapshotIdentityAdvance(),!!this.refreshCompositions(!1))for(let n of e)this.runtime?.notifyFileChanged(n)}scheduleSnapshotIdentityAdvance(){if(this.stopping||!this.isStarted)return;let e=this.readCurrentSnapshotMarker();!e||e===this.options.snapshotId||this.mutationQueue.run(()=>this.advanceSnapshotId(e)).catch(n=>{console.warn(`[vcp-site-edit] snapshot identity advance failed: ${n instanceof Error?n.message:String(n)}`)})}readCurrentSnapshotMarker(){try{let e=R.readFileSync(f.join(this.options.projectRoot,K),"utf8"),n=JSON.parse(e),i=typeof n.snapshot_id=="string"?n.snapshot_id:"";return!i||N(i)!==i?(console.warn("[vcp-site-edit] current-snapshot marker has no valid snapshot_id"),null):i}catch(e){return console.warn(`[vcp-site-edit] current-snapshot marker unreadable: ${e instanceof Error?e.message:String(e)}`),null}}async advanceSnapshotId(e){if(this.stopping||!this.isStarted||e===this.options.snapshotId)return;let n=this.options.snapshotId;this.options.snapshotId=e;try{if(this.options.historyRoot){let o=await this.bindHistorySession(e);if(!o.ok)throw new Error(`${o.code}: ${o.message}`)}else this.createRuntime()}catch(o){this.options.snapshotId=n,console.warn(`[vcp-site-edit] snapshot identity advance rolled back to ${n}: ${o instanceof Error?o.message:String(o)}`);return}let i=new Date().toISOString();for(let o of this.compositions){let s={type:"site-edit.snapshot.changed",siteId:this.options.siteId,composition:o.context,snapshotId:e,previousSnapshotId:n,at:i};for(let r of this.eventListeners)try{r(s)}catch(c){console.warn(`[vcp-site-edit] snapshot.changed listener failed: ${c instanceof Error?c.message:String(c)}`)}}try{this.options.onSnapshotIdChanged?.(e,n)}catch(o){console.warn(`[vcp-site-edit] onSnapshotIdChanged callback failed: ${o instanceof Error?o.message:String(o)}`)}}refreshCompositions(e=!1){try{let n=Me(this.options.projectRoot),i=be(n),o=i.length>0?i:(this.options.runtime.routes??[]).map(u=>({...u})),s=Re({files:n});if(s.length===0)throw new Error("No canonical App Router composition is indexed by the preview bridge.");let r=JSON.stringify(s.filter(u=>u.context.effectiveTemplateId!==void 0).map(u=>u.entryFile).sort());if(e&&(this.launchCandidateEntries=r),this.launchCandidateEntries!==r)throw new Error("SITE_EDIT_RUNTIME_RELOAD_REQUIRED: Candidate route collection changed since preview launch");let c=new Map(this.compositions.map(u=>[w(u.context),u])),p=new Map(s.map(u=>[w(u.context),u]));if(this.runtime){for(let[u,m]of c){let a=p.get(u);(!a||a.entryFile!==m.entryFile)&&this.runtime.unregisterComposition(g(m.context))}for(let[u,m]of p){let a=c.get(u);(!a||a.entryFile!==m.entryFile)&&this.runtime.registerComposition(m.context,m.entryFile)}}return this.routes=o,this.compositions=s,this.compositionFiles=n,this.compositionResolutionError=null,this.notifyHealthChanged(),!0}catch(n){let i=n instanceof Error?n:new Error(String(n));if(this.compositionResolutionError=i,e)throw i;return this.notifyHealthChanged(),!1}}resolveComposition(e){if(this.compositionResolutionError)throw this.compositionResolutionError;let n=xe(e.routeId,this.routes,this.options.basePath);if(!n)throw new Error(`Route is not indexed by the preview bridge: ${e.routeId}`);let i=this.routes.find(r=>r.routeId===n);if(!i)throw new Error(`Route is not indexed by the preview bridge: ${e.routeId}`);let o=Ee({selector:{routeId:n,...e.templateId!==void 0?{templateId:e.templateId}:{}},files:this.compositionFiles,canonicalEntryFile:i.entryFile}),s=w(o.context);if(!this.compositions.some(r=>w(r.context)===s))throw new Error(`Composition is not registered by the preview bridge: ${s}`);return o}getInitialDocumentVersions(){return this.compositions.flatMap(e=>{let n=this.runtimeVersionFloors.get(w(e.context));return n===void 0?[]:[{composition:e.context,version:n}]})}getRuntime(){if(!this.runtime)throw new Error("VCP site edit preview bridge runtime is not ready");return this.runtime}};function ut(t){let e=new x(k(t),X.createServer(),!1);return e.start({listen:!1,webSocket:!1}),{engine:e.getEngine(),getHealth:()=>e.getHealth(),stop:()=>e.stop()}}function dt(t){return k(t)}function k(t){return{tenantId:t.tenantId,siteId:t.siteId,snapshotId:t.snapshotId,projectRoot:f.resolve(t.projectRoot??process.cwd()),basePath:t.basePath??"",host:t.host??_e,port:t.port??Te,path:t.path??Oe,sessionKey:t.sessionKey??$e(),historyRoot:t.historyRoot,allowedOrigins:t.allowedOrigins??[],runtime:t.runtime??{},onSnapshotIdChanged:t.onSnapshotIdChanged,onHealthChanged:t.onHealthChanged}}function De(t){let e=["app","src/app","components/site","src/components/site","lib/generated","src/lib/generated","public"];return[...new Set([...t?.length?t:e,".vcp"])]}function Me(t){let e=[],n=["app","src/app",".vcp"],i=new Set([".git",".next",".turbo","coverage","dist","node_modules","output","snapshot-store"]),o=s=>{if(!R.existsSync(s))return;let r;try{r=R.readdirSync(s,{withFileTypes:!0})}catch{return}for(let c of r){if(c.name.startsWith(".")&&c.name!==".vcp")continue;if(c.isDirectory()){if(i.has(c.name))continue;o(f.join(s,c.name));continue}if(!c.isFile())continue;let p=f.join(s,c.name),u="";try{u=R.readFileSync(p,"utf8")}catch{}e.push({path:f.relative(t,p).replaceAll(f.sep,"/"),content:u})}};for(let s of n)o(f.join(t,s));return e.sort((s,r)=>s.path.localeCompare(r.path))}function Fe(t){if(!t||typeof t!="object"||typeof t.routeId!="string")throw new Error("Normalized composition context is required.");if("effectiveTemplateId"in t)return t;let e=t;return{routeId:e.routeId,...e.templateId?{effectiveTemplateId:e.templateId}:{}}}function g(t){return{routeId:t.routeId,...t.effectiveTemplateId?{templateId:t.effectiveTemplateId}:{}}}function Be(t){return f.join(f.resolve(t.root),N(t.siteId),N(t.snapshotId),ee("sha256").update(t.editingSessionId).digest("hex").slice(0,32))}function N(t){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(t)&&t!=="."&&t!==".."?t:ee("sha256").update(t).digest("hex").slice(0,32)}function ie(){let t=globalThis;return t[G]??=new Map,t[G]}function se(t){return`${t.projectRoot}:${t.host}:${t.port}:${t.path}:${t.historyRoot??""}`}function F(t,e,n){let i=n??(e?.composition?{routeId:e.composition.routeId,...e.composition.templateId?{effectiveTemplateId:e.composition.templateId}:{}}:void 0);if(!i)throw new Error("A normalized composition is required to construct a mutation result.");return{ok:!1,composition:i,error:{code:t.code,message:t.message},data:{requestId:e?.id??"mutation-context",kind:e?.kind??"update-text",composition:i,target:e?.target??{kind:"node",key:""},resultVersion:e?.documentVersion??0}}}function Ke(t){let e=new Se({maxPayload:ne,noServer:!0}),n=new WeakSet,i=s=>{new b({socket:new P(s),engine:t.engine,validateOperationRequest:r=>ke.safeParse(r).success})},o=(s,r,c)=>{if(!Ne(s,t))return;let p=Pe(s,t);if(!p){r.write(`HTTP/1.1 401 Unauthorized\r
|
|
2
2
|
Connection: close\r
|
|
3
3
|
\r
|
|
4
|
-
`),r.destroy();return}p.authenticatedByHeader&&n.add(
|
|
4
|
+
`),r.destroy();return}p.authenticatedByHeader&&n.add(s),e.handleUpgrade(s,r,c,u=>{e.emit("connection",u,s)})};return t.server.on("upgrade",o),e.on("connection",(s,r)=>{if(n.has(r)){i(s);return}qe(s,t.sessionKey,()=>{i(s)})}),e.stop=()=>{t.server.off("upgrade",o);for(let s of e.clients)s.close(1001,"bridge stopped");e.close()},e}var P=class{constructor(e){this.socket=e,this.onmessage=null,this.onclose=null,e.on("error",()=>{}),e.on("message",n=>{this.onmessage?.({data:n.toString()})}),e.on("close",()=>{this.onclose?.()})}send(e){if(this.socket.readyState===te.OPEN){if(this.socket.bufferedAmount+Buffer.byteLength(e,"utf8")>He){this.socket.close(1013,"bridge backpressure");return}this.socket.send(e)}}close(e,n){this.socket.close(e,n)}};function Ne(t,e){return new URL(t.url??"/","http://localhost").pathname===e.path}function Pe(t,e){if(new URL(t.url??"/","http://localhost").searchParams.has("vcp_bridge_key"))return null;let i=B(t.headers["x-vcp-site-edit-key"]),o=oe(i,e.sessionKey),s=B(t.headers.host),r=B(t.headers.origin);return s&&!Ve(s,e.host,r)?null:r?We(r,s,e.allowedOrigins)?{authenticatedByHeader:o}:null:o?{authenticatedByHeader:!0}:null}function qe(t,e,n){let i=setTimeout(()=>{t.close(1008,"bridge authentication timeout")},Ae),o,s=()=>{clearTimeout(i),t.off("message",o),t.off("close",s)},r=()=>{s(),t.close(1008,"bridge authentication failed")};o=c=>{try{let p=l.parseMessage(c.toString());if(p.type!=="auth"||!oe(p.payload.key,e)){r();return}s(),t.readyState===te.OPEN&&t.send(l.stringifyMessage({type:"auth-ok",payload:{ok:!0}})),n()}catch{r()}},t.on("message",o),t.on("close",s)}function oe(t,e){if(!t)return!1;let n=Buffer.from(t),i=Buffer.from(e);return n.length===i.length&&Ie(n,i)}function Ve(t,e,n){let i=Z(t),o=Z(e);return i===o||Y(o)&&Y(i)&&(!n||q(n,t))||Ue(o)&&(!n||q(n,t))}function Ue(t){return t==="0.0.0.0"||t==="::"}function Y(t){return t==="localhost"||t==="127.0.0.1"||t==="::1"}function We(t,e,n){return n.includes(t)||(e?q(t,e):!1)}function q(t,e){try{return new URL(t).host===re(e)}catch{return!1}}function Z(t){let e=re(t);return e.startsWith("[")?e.slice(1,e.indexOf("]")):e.split(":").length-1===1?e.split(":",1)[0]??"":e}function re(t){return t.trim().toLowerCase()}function B(t){return Array.isArray(t)?t[0]:t}function $e(){return ye(32).toString("base64url")}function je(t){let e=t.replaceAll("\\","/");return e===K||e.endsWith(`/${K}`)}export{He as SITE_EDIT_BRIDGE_MAX_BUFFERED_BYTES,ne as SITE_EDIT_BRIDGE_MAX_PAYLOAD_BYTES,x as SiteEditBridgeServer,ct as attachSiteEditBridgeServer,Ke as attachSiteEditWebSocketServer,ut as createSiteEditBridgeEngineHost,at as ensureSiteEditBridgeServerStarted,dt as normalizeSiteEditBridgeServerOptions};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vcp-dev/site-edit-bridge",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -59,6 +59,11 @@
|
|
|
59
59
|
"development": "./src/next-dev-server.ts",
|
|
60
60
|
"types": "./dist/next-dev-server.d.ts",
|
|
61
61
|
"default": "./dist/next-dev-server.js"
|
|
62
|
+
},
|
|
63
|
+
"./engine-worker": {
|
|
64
|
+
"development": "./src/engine-worker.ts",
|
|
65
|
+
"types": "./dist/engine-worker.d.ts",
|
|
66
|
+
"default": "./dist/engine-worker.js"
|
|
62
67
|
}
|
|
63
68
|
},
|
|
64
69
|
"files": [
|
|
@@ -74,8 +79,8 @@
|
|
|
74
79
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
75
80
|
},
|
|
76
81
|
"dependencies": {
|
|
77
|
-
"@vcp-dev/contracts": "0.10.
|
|
78
|
-
"@vcp-dev/site-edit-engine": "0.10.
|
|
82
|
+
"@vcp-dev/contracts": "0.10.22",
|
|
83
|
+
"@vcp-dev/site-edit-engine": "0.10.22",
|
|
79
84
|
"@types/ws": "^8.18.1",
|
|
80
85
|
"ws": "^8.20.1"
|
|
81
86
|
},
|