@vcp-dev/site-edit-bridge 0.6.11 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import type { SiteEditCommandSuccess, SiteEditClassNameSourceStyleContext, SiteEditEvent, SiteEditGetClassNameSourceCommandResult, SiteEditObjectEditContext, SiteEditMutationContext, SiteEditInitializeSessionInput, SiteEditOperationCommandResult, SiteEditOperationRequest, SiteEditRenderDocument, SiteEditResolveSourceLocationInput, SiteEditSourceFileResponse, SiteEditSourceLocation, SiteEditSourceTreeResponse } from "@vcp-dev/contracts";
2
- import { type SiteEditHistoryStatus, type SiteEditBridgeRequestKind } from "./protocol.js";
1
+ import type { SiteEditCommandSuccess, SiteEditClassNameSourceStyleContext, SiteEditCompositionSelector, SiteEditEvent, SiteEditGetClassNameSourceCommandResult, SiteEditObjectEditContext, SiteEditMutationContext, SiteEditOperationCommandResult, SiteEditOperationRequest, SiteEditRenderDocument, SiteEditResolveSourceLocationInput, SiteEditSourceFileResponse, SiteEditSourceLocation, SiteEditSourceTreeResponse } from "@vcp-dev/contracts";
2
+ import { type SiteEditBridgeInitializeInput, type SiteEditHistoryStatus, type SiteEditBridgeRequestKind } from "./protocol.js";
3
3
  export type { SiteEditHistoryStatus } from "./protocol.js";
4
+ type SiteEditBridgeCompositionInput = SiteEditCompositionSelector;
4
5
  export type SiteEditBrowserSocketLike = {
5
6
  readonly readyState: number;
6
7
  onopen: (() => void) | null;
@@ -23,25 +24,26 @@ export type SiteEditBridgeConnectionStatus = {
23
24
  message?: string;
24
25
  };
25
26
  export type SiteEditBridgeClient = {
26
- initializeSession?(input: SiteEditInitializeSessionInput): Promise<SiteEditCommandSuccess<{
27
+ initializeSession?(input: SiteEditBridgeInitializeInput): Promise<SiteEditCommandSuccess<{
27
28
  document: SiteEditRenderDocument;
28
29
  historyStatus: SiteEditHistoryStatus;
29
30
  }>>;
30
- getDocument(routeId: string): Promise<SiteEditCommandSuccess<SiteEditRenderDocument>>;
31
- getHistoryStatus(): Promise<SiteEditCommandSuccess<SiteEditHistoryStatus>>;
32
- getStyleContext(routeId: string): Promise<SiteEditCommandSuccess<SiteEditClassNameSourceStyleContext | null>>;
33
- ensureDocumentVersion?(routeId: string, minVersion: number): Promise<SiteEditCommandSuccess<SiteEditRenderDocument>>;
34
- acknowledgeDocumentVersion?(routeId: string, version: number): Promise<SiteEditCommandSuccess<SiteEditRenderDocument>>;
35
- getObjectEditContext(key: string, routeId?: string): Promise<SiteEditCommandSuccess<SiteEditObjectEditContext>>;
36
- getClassNameSource(key: string, routeId?: string): Promise<SiteEditGetClassNameSourceCommandResult>;
37
- getSourceTree(): Promise<SiteEditCommandSuccess<SiteEditSourceTreeResponse>>;
31
+ getDocument(composition: SiteEditBridgeCompositionInput): Promise<SiteEditCommandSuccess<SiteEditRenderDocument>>;
32
+ getHistoryStatus(composition: SiteEditBridgeCompositionInput): Promise<SiteEditCommandSuccess<SiteEditHistoryStatus>>;
33
+ getStyleContext(composition: SiteEditBridgeCompositionInput): Promise<SiteEditCommandSuccess<SiteEditClassNameSourceStyleContext | null>>;
34
+ ensureDocumentVersion?(composition: SiteEditBridgeCompositionInput, minVersion: number): Promise<SiteEditCommandSuccess<SiteEditRenderDocument>>;
35
+ acknowledgeDocumentVersion?(composition: SiteEditBridgeCompositionInput, version: number): Promise<SiteEditCommandSuccess<SiteEditRenderDocument>>;
36
+ getObjectEditContext(key: string, composition: SiteEditBridgeCompositionInput): Promise<SiteEditCommandSuccess<SiteEditObjectEditContext>>;
37
+ getClassNameSource(key: string, composition: SiteEditBridgeCompositionInput): Promise<SiteEditGetClassNameSourceCommandResult>;
38
+ getSourceTree(composition: SiteEditBridgeCompositionInput): Promise<SiteEditCommandSuccess<SiteEditSourceTreeResponse>>;
38
39
  getSourceFile(input: {
39
40
  path: string;
41
+ composition: SiteEditBridgeCompositionInput;
40
42
  }): Promise<SiteEditCommandSuccess<SiteEditSourceFileResponse>>;
41
43
  resolveSourceLocation(input: SiteEditResolveSourceLocationInput): Promise<SiteEditCommandSuccess<SiteEditSourceLocation>>;
42
- execute(request: SiteEditOperationRequest, mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
43
- undo(mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
44
- redo(mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
44
+ execute(request: SiteEditOperationRequest | Record<string, unknown>, mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
45
+ undo(composition: SiteEditBridgeCompositionInput, mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
46
+ redo(composition: SiteEditBridgeCompositionInput, mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
45
47
  subscribe(listener: (event: SiteEditEvent) => void): () => void;
46
48
  };
47
49
  export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
@@ -66,10 +68,13 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
66
68
  disconnect(): void;
67
69
  getConnectionState(): SiteEditBridgeConnectionState;
68
70
  subscribeConnectionStatus(listener: (status: SiteEditBridgeConnectionStatus) => void): () => void;
69
- getDocument(routeId: string): Promise<SiteEditCommandSuccess<{
71
+ getDocument(composition: SiteEditBridgeCompositionInput): Promise<SiteEditCommandSuccess<{
70
72
  siteId: string;
71
73
  snapshotId: string;
72
- routeId: string;
74
+ composition: {
75
+ routeId: string;
76
+ effectiveTemplateId?: string | undefined;
77
+ };
73
78
  version: number;
74
79
  entries: {
75
80
  key: string;
@@ -107,12 +112,16 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
107
112
  }[];
108
113
  };
109
114
  }>>;
110
- initializeSession(input: SiteEditInitializeSessionInput): Promise<SiteEditCommandSuccess<{
115
+ initializeSession(input: SiteEditBridgeInitializeInput): Promise<SiteEditCommandSuccess<{
111
116
  document: SiteEditRenderDocument;
112
117
  historyStatus: SiteEditHistoryStatus;
113
118
  }>>;
114
- getHistoryStatus(): Promise<SiteEditCommandSuccess<SiteEditHistoryStatus>>;
115
- getStyleContext(routeId: string): Promise<SiteEditCommandSuccess<{
119
+ getHistoryStatus(composition: SiteEditBridgeCompositionInput): Promise<SiteEditCommandSuccess<SiteEditHistoryStatus>>;
120
+ getStyleContext(composition: SiteEditBridgeCompositionInput): Promise<SiteEditCommandSuccess<{
121
+ composition: {
122
+ routeId: string;
123
+ effectiveTemplateId?: string | undefined;
124
+ };
116
125
  revision: string;
117
126
  entryCss: {
118
127
  path: string;
@@ -125,7 +134,11 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
125
134
  hash?: string | undefined;
126
135
  }[];
127
136
  } | null>>;
128
- getObjectEditContext(key: string, routeId?: string): Promise<SiteEditCommandSuccess<{
137
+ getObjectEditContext(key: string, composition: SiteEditBridgeCompositionInput): Promise<SiteEditCommandSuccess<{
138
+ composition: {
139
+ routeId: string;
140
+ effectiveTemplateId?: string | undefined;
141
+ };
129
142
  key: string;
130
143
  tag: string;
131
144
  label: string;
@@ -138,6 +151,10 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
138
151
  componentName: string;
139
152
  sourceFile: string;
140
153
  capabilities: {
154
+ composition: {
155
+ routeId: string;
156
+ effectiveTemplateId?: string | undefined;
157
+ };
141
158
  key: string;
142
159
  canUpdateText: boolean;
143
160
  canInsertChild: boolean;
@@ -149,19 +166,19 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
149
166
  }[];
150
167
  editModes?: {
151
168
  updateText?: {
152
- mode: "unsupported" | "readonly" | "direct" | "proxy" | "upstream";
169
+ mode: "unsupported" | "direct" | "readonly" | "proxy" | "upstream";
153
170
  reasonCode?: string | undefined;
154
171
  } | undefined;
155
172
  insertChild?: {
156
- mode: "unsupported" | "readonly" | "direct" | "proxy" | "upstream";
173
+ mode: "unsupported" | "direct" | "readonly" | "proxy" | "upstream";
157
174
  reasonCode?: string | undefined;
158
175
  } | undefined;
159
176
  move?: {
160
- mode: "unsupported" | "readonly" | "direct" | "proxy" | "upstream";
177
+ mode: "unsupported" | "direct" | "readonly" | "proxy" | "upstream";
161
178
  reasonCode?: string | undefined;
162
179
  } | undefined;
163
180
  remove?: {
164
- mode: "unsupported" | "readonly" | "direct" | "proxy" | "upstream";
181
+ mode: "unsupported" | "direct" | "readonly" | "proxy" | "upstream";
165
182
  reasonCode?: string | undefined;
166
183
  } | undefined;
167
184
  } | undefined;
@@ -185,7 +202,7 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
185
202
  textPreview?: string | undefined;
186
203
  } | undefined;
187
204
  provenanceChain?: {
188
- editMode: "unsupported" | "readonly" | "direct" | "proxy" | "upstream";
205
+ editMode: "unsupported" | "direct" | "readonly" | "proxy" | "upstream";
189
206
  diagnostics: {
190
207
  code: string;
191
208
  message: string;
@@ -196,7 +213,7 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
196
213
  bindings: {
197
214
  sourceKind: "vcp-cms-collection" | "vcp-cms-form" | "vcp-cms-settings" | "vcp-cms-navigation" | "site-api-route" | "external-api" | "unknown-api";
198
215
  sourceType: "cms-field" | "api-field";
199
- targetKind: "text" | "repeat-region" | "richtext" | "attribute" | "component-prop";
216
+ targetKind: "repeat-region" | "text" | "richtext" | "attribute" | "component-prop";
200
217
  targetName: string;
201
218
  confidence: "unknown" | "exact" | "partial" | "safe-derived";
202
219
  readonly: true;
@@ -213,8 +230,12 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
213
230
  }[];
214
231
  } | undefined;
215
232
  }>>;
216
- getClassNameSource(key: string, routeId?: string): Promise<SiteEditGetClassNameSourceCommandResult>;
217
- getSourceTree(): Promise<SiteEditCommandSuccess<{
233
+ getClassNameSource(key: string, composition: SiteEditBridgeCompositionInput): Promise<SiteEditGetClassNameSourceCommandResult>;
234
+ getSourceTree(composition: SiteEditBridgeCompositionInput): Promise<SiteEditCommandSuccess<{
235
+ composition: {
236
+ routeId: string;
237
+ effectiveTemplateId?: string | undefined;
238
+ };
218
239
  nodes: import("@vcp-dev/contracts").SiteEditSourceTreeNode[];
219
240
  totalFiles: number;
220
241
  defaultPath?: string | undefined;
@@ -227,7 +248,12 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
227
248
  }>>;
228
249
  getSourceFile(input: {
229
250
  path: string;
251
+ composition: SiteEditBridgeCompositionInput;
230
252
  }): Promise<SiteEditCommandSuccess<{
253
+ composition: {
254
+ routeId: string;
255
+ effectiveTemplateId?: string | undefined;
256
+ };
231
257
  path: string;
232
258
  sizeBytes: number;
233
259
  kind: "text";
@@ -236,6 +262,10 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
236
262
  content: string;
237
263
  contentHash?: string | undefined;
238
264
  } | {
265
+ composition: {
266
+ routeId: string;
267
+ effectiveTemplateId?: string | undefined;
268
+ };
239
269
  path: string;
240
270
  sizeBytes: number;
241
271
  kind: "binary";
@@ -248,6 +278,10 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
248
278
  }>>;
249
279
  resolveSourceLocation(input: SiteEditResolveSourceLocationInput): Promise<SiteEditCommandSuccess<{
250
280
  key: string;
281
+ composition: {
282
+ routeId: string;
283
+ effectiveTemplateId?: string | undefined;
284
+ };
251
285
  sourceFile: string;
252
286
  componentName: string;
253
287
  openingElementRange: {
@@ -257,13 +291,13 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
257
291
  endColumn: number;
258
292
  };
259
293
  structuralPath: string;
260
- routeId?: string | undefined;
261
294
  textPreview?: string | undefined;
262
295
  }>>;
263
- execute(request: SiteEditOperationRequest, _mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
264
- undo(_mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
265
- redo(_mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
296
+ execute(request: SiteEditOperationRequest | Record<string, unknown>, _mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
297
+ undo(composition: SiteEditBridgeCompositionInput, _mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
298
+ redo(composition: SiteEditBridgeCompositionInput, _mutationContext?: SiteEditMutationContext): Promise<SiteEditOperationCommandResult>;
266
299
  subscribe(listener: (event: SiteEditEvent) => void): () => void;
300
+ private selector;
267
301
  private request;
268
302
  private handleAuthHandshakeMessage;
269
303
  private handleMessage;
@@ -276,13 +310,19 @@ export declare class SiteEditWebSocketClient implements SiteEditBridgeClient {
276
310
  private createSocket;
277
311
  }
278
312
  export type SiteEditBridge = SiteEditBridgeClient & {
279
- initializeSession(input: SiteEditInitializeSessionInput): Promise<SiteEditCommandSuccess<{
313
+ initializeSession(input: SiteEditBridgeInitializeInput): Promise<SiteEditCommandSuccess<{
280
314
  document: SiteEditRenderDocument;
281
315
  historyStatus: SiteEditHistoryStatus;
282
316
  }>>;
283
317
  };
284
- export declare function createSiteEditBridge(params: {
318
+ type SiteEditBridgeCreateParams = {
319
+ composition: SiteEditCompositionSelector;
320
+ initialDocument: SiteEditRenderDocument;
321
+ client: SiteEditBridgeClient;
322
+ } | {
323
+ /** Compatibility form for callers that intentionally select no template. */
285
324
  routeId: string;
286
325
  initialDocument: SiteEditRenderDocument;
287
326
  client: SiteEditBridgeClient;
288
- }): SiteEditBridge;
327
+ };
328
+ export declare function createSiteEditBridge(params: SiteEditBridgeCreateParams): SiteEditBridge;
package/dist/client.js CHANGED
@@ -1 +1 @@
1
- function y(n){if(!M(n))return n;let e=new Map,t=n.pageComposition.status==="supported"?n.pageComposition.placementUnitKeys:[],s=new Map(n.dynamicDataSummaries??[]),r=o=>{let a=e.get(o);if(a)return a;let i=n.nodes[o];if(!i)throw new Error(`Missing compact document node ${o}`);let[c,d]=i,u=n.tokens[d];if(typeof u!="string")throw new Error(`Missing compact document token ${d}`);let f=`${c===null?"":r(c)}${u}`;return e.set(o,f),f};return{siteId:n.siteId,snapshotId:n.snapshotId,routeId:n.routeId,version:n.version,pageComposition:n.pageComposition,entries:n.entries.map((o,a)=>{let[i,c,d]=o,u=d===void 0?void 0:t[d];if(d!==void 0&&!u)throw new Error(`Missing page composition unit ${d}`);return{key:r(i),...u?{pageCompositionUnitKey:u}:{},operationSummary:D(c),...s.get(a)?{dynamicDataSummary:s.get(a)}:{}}})}}function D(n){return{canUpdateText:(n&1)!==0,canInsertChild:(n&2)!==0,canMove:(n&4)!==0,canRemove:(n&8)!==0}}function M(n){if(typeof n!="object"||n===null)return!1;let e=n;if(e.encoding!=="segment-trie-v1"||typeof e.siteId!="string"||typeof e.snapshotId!="string"||typeof e.routeId!="string"||!Number.isInteger(e.version)||e.version<0||!x(e.pageComposition)||!Array.isArray(e.tokens)||!e.tokens.every(r=>typeof r=="string")||!Array.isArray(e.nodes)||!Array.isArray(e.entries))return!1;let t=e.nodes;if(!t.every((r,o)=>Array.isArray(r)&&r.length===2&&(r[0]===null||g(r[0])&&r[0]<o)&&g(r[1])&&r[1]<e.tokens.length)||!e.entries.every(r=>Array.isArray(r)&&(r.length===2||r.length===3)&&g(r[0])&&r[0]<t.length&&g(r[1])&&r[1]<=15&&(r.length===2||g(r[2]))))return!1;let s=e.dynamicDataSummaries;return s===void 0||Array.isArray(s)&&s.every(r=>Array.isArray(r)&&r.length===2&&g(r[0])&&r[0]<e.entries.length&&x(r[1]))}function x(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function g(n){return typeof n=="number"&&Number.isInteger(n)&&n>=0}var p=class n{constructor(){}static parseMessage(e,t={}){let s=JSON.parse(e);if(!n.isMessage(s,t))throw new Error("Invalid site edit bridge message");return s}static stringifyMessage(e){return JSON.stringify(e)}static extractRequestInfo(e){if(typeof e=="object"&&e!==null&&"payload"in e&&typeof e.payload=="object"&&e.payload!==null){let t=e.payload,s=typeof t.id=="string"?t.id:"unknown",r=n.isRequestKind(t.kind)?t.kind:"get-document";return{id:s,kind:r}}return{id:"unknown",kind:"get-document"}}static isRequestKind(e){return e==="initialize-session"||e==="get-document"||e==="get-history-status"||e==="get-style-context"||e==="get-object-edit-context"||e==="get-class-name-source"||e==="get-source-tree"||e==="get-source-file"||e==="resolve-source-location"||e==="execute"||e==="undo"||e==="redo"}static isMessage(e,t={}){return!h(e)||typeof e.type!="string"?!1:e.type==="request"?n.isRequestPayload(e.payload,t):e.type==="response"||e.type==="error"?h(e.payload)&&typeof e.payload.id=="string":e.type==="event"?h(e.payload)&&h(e.payload.event):e.type==="auth"?h(e.payload)&&typeof e.payload.key=="string":e.type==="auth-ok"?h(e.payload)&&e.payload.ok===!0:!1}static isRequestPayload(e,t={}){if(!h(e)||typeof e.id!="string"||!n.isRequestKind(e.kind))return!1;switch(e.kind){case"initialize-session":return typeof e.routeId=="string"&&(e.mutationContext===void 0||I(e.mutationContext));case"get-document":case"get-style-context":return typeof e.routeId=="string";case"get-object-edit-context":case"get-class-name-source":case"resolve-source-location":return typeof e.key=="string"&&(e.routeId===void 0||typeof e.routeId=="string");case"get-history-status":return e.mutationContext===void 0;case"get-source-tree":return!0;case"get-source-file":return typeof e.path=="string";case"execute":return R(e.request)&&(e.request.mutationContext===void 0||I(e.request.mutationContext))&&(t.validateOperationRequest?.(e.request)??!0);case"undo":case"redo":return e.mutationContext===void 0||I(e.mutationContext)}}};function h(n){return typeof n=="object"&&n!==null}function R(n){return h(n)&&typeof n.id=="string"&&typeof n.siteId=="string"&&typeof n.baseSnapshotId=="string"&&(n.routeId===void 0||typeof n.routeId=="string")&&typeof n.kind=="string"&&h(n.target)&&typeof n.documentVersion=="number"&&Number.isInteger(n.documentVersion)&&n.documentVersion>=0&&h(n.params)}function I(n){return T(n)}function T(n){return h(n)?typeof n.editingSessionId=="string"&&n.editingSessionId.length>0&&typeof n.lockId=="string"&&n.lockId.length>0&&typeof n.ownerId=="string"&&n.ownerId.length>0&&typeof n.leaseAssertion=="string"&&n.leaseAssertion.length>0:!1}var A="vcp:designer:trace",_="designer_trace",k="__VCP_DESIGNER_TRACE__";var F=/(authorization|cookie|token|secret|providerTrace|headers?|sourceCode|rawSource|fullSource)/i;function K(n=globalThis){let e=n;try{if(e.localStorage?.getItem(A)==="1")return!0}catch{}try{let t=e.location?.search??"";return new URLSearchParams(t).get(_)==="1"}catch{return!1}}function l(n,e={},t=globalThis){if(!K(t))return;let s={type:n,timestamp:Date.now(),payload:j(e)},r=t,o=r[k]??[];o.push(s),o.length>1e3&&o.splice(0,o.length-1e3),r[k]=o;let a=t.console?.debug;s.payload.diagnosticOnly!==!0&&typeof a=="function"&&a.call(t.console,"[designer-trace]",s)}function j(n){return S(n,0)}function S(n,e){if(e>4)return"[truncated]";if(Array.isArray(n))return n.map(s=>S(s,e+1));if(!n||typeof n!="object")return n;let t={};for(let[s,r]of Object.entries(n)){if(F.test(s)){t[s]="[redacted]";continue}t[s]=S(r,e+1)}return t}var w=class{constructor(e){this.options=e,this.socket=null,this.connectPromise=null,this.connectAttempt=null,this.connectionState="idle",this.intentionalClose=!1,this.sequence=0,this.pending=new Map,this.listeners=new Set,this.connectionStatusListeners=new Set}connect(){return this.socket?.readyState===C&&this.connectionState==="open"?(this.setConnectionState("open"),Promise.resolve()):this.connectPromise?this.connectPromise:(this.intentionalClose=!1,this.setConnectionState("connecting"),this.connectPromise=new Promise((e,t)=>{let s;try{s=this.createSocket(this.options.url)}catch(i){let c=m(i,"WebSocket failed");this.socket=null,this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("failed",c.message),t(c);return}let r={socket:s,settled:!1,reject:t};this.connectAttempt=r,this.socket=s;let o=()=>{this.socket!==s||this.connectAttempt!==r||(r.settled=!0,this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("open"),e())},a=(i,c)=>{this.cleanupSocket(s,i,c)};s.onopen=()=>{if(!(this.socket!==s||this.connectAttempt!==r)){if(this.options.authKey){try{s.send(p.stringifyMessage({type:"auth",payload:{key:this.options.authKey}}))}catch(i){a(m(i,"WebSocket auth failed"),"failed")}return}o()}},s.onerror=i=>{if(this.socket!==s)return;let c=m(i,"WebSocket failed");a(c,"failed")},s.onmessage=i=>{if(this.socket===s){if(this.options.authKey&&this.connectAttempt===r&&!r.settled){this.handleAuthHandshakeMessage(i.data,s,r,()=>{o()});return}this.handleMessage(i.data)}},s.onclose=i=>{if(this.socket!==s)return;let c=this.intentionalClose,d=new Error($(i,{intentional:c}));a(d,c?"closed":"failed")}}),this.connectPromise)}disconnect(){this.intentionalClose=!0,this.setConnectionState("closing");let e=this.socket;if(!e){this.rejectAll(new Error("WebSocket disconnected")),this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("closed");return}this.cleanupSocket(e,new Error("WebSocket disconnected"),"closed")}getConnectionState(){return this.connectionState}subscribeConnectionStatus(e){return this.connectionStatusListeners.add(e),e({state:this.connectionState}),()=>{this.connectionStatusListeners.delete(e)}}getDocument(e){return this.request("get-document",{routeId:e})}initializeSession(e){return this.request("initialize-session",e)}getHistoryStatus(){return this.request("get-history-status",{})}getStyleContext(e){return this.request("get-style-context",{routeId:e})}getObjectEditContext(e,t){return this.request("get-object-edit-context",{key:e,routeId:t})}getClassNameSource(e,t){return this.request("get-class-name-source",{key:e,routeId:t})}getSourceTree(){return this.request("get-source-tree",{})}getSourceFile(e){return this.request("get-source-file",e)}resolveSourceLocation(e){return this.request("resolve-source-location",e)}execute(e,t){return this.request("execute",{request:e})}undo(e){return this.request("undo",{})}redo(e){return this.request("redo",{})}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}request(e,t){let s=this.socket;if(!s||s.readyState!==C||this.connectionState!=="open")return Promise.reject(new Error(`WebSocket is not open (state: ${this.connectionState}, readyState: ${s?.readyState??"none"})`));let r=`${e}-${this.sequence+=1}`,o=v(r,e,t);return l("bridge.request",{id:r,kind:e,routeId:typeof t.routeId=="string"?t.routeId:null,key:typeof t.key=="string"?t.key:null,operationKind:E(t.request,"kind"),documentVersion:E(t.request,"documentVersion")}),new Promise((a,i)=>{let c=this.resolveRequestTimeoutMs(e),d=c>0?setTimeout(()=>{let u=this.pending.get(r);u&&(this.pending.delete(r),l("bridge.response",{id:r,kind:e,ok:!1,timedOut:!0}),u.reject(new Error(`Bridge request timed out after ${c}ms: ${e}`)))},c):null;d?.unref?.(),this.pending.set(r,{kind:e,timeout:d,resolve:u=>a(u),reject:i});try{s.send(p.stringifyMessage(o))}catch(u){let f=m(u,"WebSocket send failed");this.clearPendingRequest(r),i(f),this.cleanupSocket(s,f,"failed")}})}handleAuthHandshakeMessage(e,t,s,r){try{if(p.parseMessage(e).type==="auth-ok"){r();return}throw new Error("Bridge authentication failed")}catch(o){let a=m(o,"Bridge authentication failed");this.connectAttempt===s&&this.cleanupSocket(t,a,"failed")}}handleMessage(e){let t=p.parseMessage(e);if(t.type==="event"){for(let r of this.listeners)r(t.payload.event);return}if(t.type!=="response"&&t.type!=="error")return;let s=this.pending.get(t.payload.id);if(s){if(this.clearPendingRequest(t.payload.id),t.type==="error"){l("bridge.response",{id:t.payload.id,kind:s.kind,ok:!1}),s.reject(new Error(t.payload.message));return}l("bridge.response",{id:t.payload.id,kind:s.kind,ok:!0,resultVersion:P(t.payload.result)}),s.resolve(s.kind==="get-document"?{...t.payload.result,data:y(t.payload.result.data)}:s.kind==="initialize-session"?B(t.payload.result):t.payload.result)}}rejectAll(e){for(let[t,s]of[...this.pending])this.clearPendingRequest(t),s.reject(e)}clearPendingRequest(e){let t=this.pending.get(e);t&&(t.timeout&&clearTimeout(t.timeout),this.pending.delete(e))}resolveRequestTimeoutMs(e){let t=this.options.requestTimeoutMsByKind?.[e];return typeof t=="number"?Math.max(0,t):typeof this.options.requestTimeoutMs=="number"?Math.max(0,this.options.requestTimeoutMs):U[e]??N}cleanupSocket(e,t,s){if(this.socket===e&&(this.socket=null),this.connectAttempt?.socket===e){let r=this.connectAttempt;this.connectAttempt=null,r.settled||(r.settled=!0,r.reject(t))}this.connectPromise=null,this.rejectAll(t),this.setConnectionState(s,t.message),e.onopen=null,e.onmessage=null,e.onerror=null,e.onclose=null;try{(e.readyState===O||e.readyState===C)&&e.close()}catch{}}setConnectionState(e,t){if(this.connectionState===e&&!t)return;this.connectionState=e;let s={state:e,...t?{message:t}:{}};this.options.onConnectionStatus?.(s);for(let r of this.connectionStatusListeners)r(s)}createSocket(e){if(this.options.createSocket)return this.options.createSocket(e);if(typeof WebSocket>"u")throw new Error("Site edit preview bridge requires WebSocket");return new WebSocket(e)}},O=0,C=1,N=15e3,U={"get-source-tree":1e4,"get-source-file":1e4,"resolve-source-location":1e4,execute:6e4,undo:2e4,redo:2e4};function m(n,e){return n instanceof Error?n:new Error(e)}function $(n,e){return e.intentional?"WebSocket disconnected":n?.code===1008?n.reason?`Bridge session authentication failed: ${n.reason}`:"Bridge session authentication failed":n?.reason?`WebSocket closed: ${n.reason}`:typeof n?.code=="number"&&n.code!==1005?`WebSocket closed with code ${n.code}`:"WebSocket closed"}function E(n,e){if(!n||typeof n!="object")return null;let s=n[e];return typeof s=="string"||typeof s=="number"?s:null}function P(n){if(!n||typeof n!="object")return null;let e=n,t=e.data?.resultVersion??e.data?.version??null;return typeof t=="number"?t:null}function Y(n){return new b(n)}var b=class{constructor(e){this.params=e,this.refreshInFlight=null,this.objectGeneration=0,this.styleGeneration=0,this.editContextCache=new Map,this.editContextInFlight=new Map,this.classNameSourceCache=new Map,this.classNameSourceInFlight=new Map,this.styleContextCache=null,this.styleContextInFlight=null,this.currentDocument=e.initialDocument,this.observedDocumentVersion=e.initialDocument.version,this.sessionInitialized=!q(e.initialDocument)}async getDocument(e){if(e!==this.params.routeId)throw new Error(`Site edit bridge is bound to route ${this.params.routeId}, received ${e}`);let t=q(this.currentDocument);return(t||this.currentDocument.version<this.observedDocumentVersion)&&(this.currentDocument=await this.refreshSnapshot(t?void 0:this.observedDocumentVersion,"get-document"),this.sessionInitialized=!0),{ok:!0,data:this.currentDocument}}async initializeSession(e){if(e.routeId!==this.params.routeId)throw new Error(`Site edit bridge is bound to route ${this.params.routeId}, received ${e.routeId}`);if(!this.params.client.initializeSession)throw new Error("Site edit bridge client does not support initialize-session");let t=await this.params.client.initializeSession(e);return this.currentDocument=t.data.document,this.observedDocumentVersion=t.data.document.version,this.sessionInitialized=!0,this.clearObjectCaches(),t}async getHistoryStatus(){return this.params.client.getHistoryStatus()}async getStyleContext(e){if(e!==this.params.routeId)throw new Error(`Site edit bridge is bound to route ${this.params.routeId}, received ${e}`);let t=this.styleContextCache;if(t?.generation===this.styleGeneration)return{ok:!0,data:t.value};let s=this.styleContextInFlight;if(s?.generation===this.styleGeneration)return s.request;let r=this.styleGeneration,o=this.params.client.getStyleContext(e).then(a=>(this.styleGeneration===r&&(this.styleContextCache={generation:r,value:a.data}),a)).finally(()=>{this.styleContextInFlight?.request===o&&(this.styleContextInFlight=null)});return this.styleContextInFlight={generation:r,request:o},o}async ensureDocumentVersion(e,t){if(e!==this.params.routeId)throw new Error(`Site edit bridge is bound to route ${this.params.routeId}, received ${e}`);let s=await this.refreshSnapshot(t,"ensure-version");if(s.version<t)throw new Error(`Document refresh returned version ${s.version}, expected at least ${t}`);return{ok:!0,data:s}}async acknowledgeDocumentVersion(e,t){if(e!==this.params.routeId)throw new Error(`Site edit bridge is bound to route ${this.params.routeId}, received ${e}`);return t>this.currentDocument.version&&(this.currentDocument={...this.currentDocument,version:t},this.clearObjectCaches({preserveStyleContext:!0})),this.observedDocumentVersion=Math.max(this.observedDocumentVersion,t),{ok:!0,data:this.currentDocument}}async getObjectEditContext(e,t){let s=this.objectCacheKey(e,t),r=this.editContextCache.get(s);if(r)return{ok:!0,data:r};let o=this.editContextInFlight.get(s);if(o)return o;let a=this.objectGeneration,i=this.params.client.getObjectEditContext(e,t??this.params.routeId).then(c=>(this.objectGeneration===a&&this.editContextCache.set(s,c.data),c)).finally(()=>{this.editContextInFlight.get(s)===i&&this.editContextInFlight.delete(s)});return this.editContextInFlight.set(s,i),i}async getClassNameSource(e,t){let s=this.objectCacheKey(e,t),r=this.classNameSourceCache.get(s);if(r)return r;let o=this.classNameSourceInFlight.get(s);if(o)return o;let a=this.objectGeneration,i=this.params.client.getClassNameSource(e,t??this.params.routeId).then(c=>(this.objectGeneration===a&&this.classNameSourceCache.set(s,c),c)).finally(()=>{this.classNameSourceInFlight.get(s)===i&&this.classNameSourceInFlight.delete(s)});return this.classNameSourceInFlight.set(s,i),i}async getSourceTree(){return this.params.client.getSourceTree()}async getSourceFile(e){return this.params.client.getSourceFile(e)}async resolveSourceLocation(e){return this.params.client.resolveSourceLocation({...e,routeId:e.routeId??this.params.routeId})}async execute(e,t){let s=e.target.kind==="node"&&!e.routeId?{...e,routeId:this.params.routeId}:e;return this.params.client.execute(s)}async undo(e){return this.params.client.undo()}async redo(e){return this.params.client.redo()}subscribe(e){return this.params.client.subscribe(t=>{if(t.type==="site-edit.document.changed"&&t.routeId===this.params.routeId&&this.sessionInitialized){this.observedDocumentVersion=Math.max(this.observedDocumentVersion,t.version),t.version>this.currentDocument.version&&this.clearObjectCaches(),e(t);return}e(t)})}async refreshSnapshot(e,t="ensure-version"){if(typeof e=="number"&&this.currentDocument.version>=e)return l("bridge.document-refresh",{routeId:this.params.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!0,skipped:!0,source:t}),this.currentDocument;if(this.refreshInFlight){l("bridge.document-refresh",{routeId:this.params.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!0,source:t});let r=await this.refreshInFlight;if(typeof e!="number"||r.version>=e||this.currentDocument.version>=e)return this.currentDocument}l("bridge.document-refresh",{routeId:this.params.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!1,source:t});let s=this.params.client.getDocument(this.params.routeId).then(r=>(this.currentDocument=r.data,this.observedDocumentVersion=Math.max(this.observedDocumentVersion,r.data.version),this.clearObjectCaches(),this.currentDocument));this.refreshInFlight=s;try{return await s}finally{this.refreshInFlight===s&&(this.refreshInFlight=null)}}clearObjectCaches(e){this.objectGeneration+=1,this.editContextCache.clear(),this.editContextInFlight.clear(),this.classNameSourceCache.clear(),this.classNameSourceInFlight.clear(),!e?.preserveStyleContext&&(this.styleGeneration+=1,this.styleContextCache=null,this.styleContextInFlight=null)}objectCacheKey(e,t){return`${t??this.params.routeId}\0${e}`}};function q(n){return n.version===0&&n.entries.length===0}function B(n){let e=n;return{...e,data:{...e.data,document:y(e.data.document)}}}function v(n,e,t){switch(e){case"initialize-session":return{type:"request",payload:{id:n,kind:e,routeId:String(t.routeId),...t.mutationContext?{mutationContext:t.mutationContext}:{}}};case"get-document":return{type:"request",payload:{id:n,kind:e,routeId:String(t.routeId)}};case"get-history-status":return{type:"request",payload:{id:n,kind:e}};case"get-style-context":return{type:"request",payload:{id:n,kind:e,routeId:String(t.routeId)}};case"get-object-edit-context":return{type:"request",payload:{id:n,kind:e,key:String(t.key),routeId:typeof t.routeId=="string"?t.routeId:void 0}};case"get-class-name-source":return{type:"request",payload:{id:n,kind:e,key:String(t.key),routeId:typeof t.routeId=="string"?t.routeId:void 0}};case"get-source-tree":return{type:"request",payload:{id:n,kind:e}};case"get-source-file":return{type:"request",payload:{id:n,kind:e,path:String(t.path)}};case"resolve-source-location":return{type:"request",payload:{id:n,kind:e,key:String(t.key),routeId:typeof t.routeId=="string"?t.routeId:void 0}};case"execute":return{type:"request",payload:{id:n,kind:e,request:t.request}};case"undo":case"redo":return{type:"request",payload:{id:n,kind:e,mutationContext:t.mutationContext}}}}export{w as SiteEditWebSocketClient,Y as createSiteEditBridge};
1
+ function q(n){if(!v(n))return n;let e=new Map,t=n.pageComposition.status==="supported"?n.pageComposition.placementUnitKeys:[],o=new Map(n.dynamicDataSummaries??[]),s=i=>{let r=e.get(i);if(r)return r;let a=n.nodes[i];if(!a)throw new Error(`Missing compact document node ${i}`);let[c,u]=a,h=n.tokens[u];if(typeof h!="string")throw new Error(`Missing compact document token ${u}`);let g=`${c===null?"":s(c)}${h}`;return e.set(i,g),g};return{siteId:n.siteId,snapshotId:n.snapshotId,composition:{...n.composition},routeId:n.composition.routeId,version:n.version,pageComposition:n.pageComposition,entries:n.entries.map((i,r)=>{let[a,c,u]=i,h=u===void 0?void 0:t[u];if(u!==void 0&&!h)throw new Error(`Missing page composition unit ${u}`);return{key:s(a),...h?{pageCompositionUnitKey:h}:{},operationSummary:A(c),...o.get(r)?{dynamicDataSummary:o.get(r)}:{}}})}}function A(n){return{canUpdateText:(n&1)!==0,canInsertChild:(n&2)!==0,canMove:(n&4)!==0,canRemove:(n&8)!==0}}function v(n){if(typeof n!="object"||n===null)return!1;let e=n;if(e.encoding!=="segment-trie-v1"||typeof e.siteId!="string"||typeof e.snapshotId!="string"||!K(e.composition)||typeof e.routeId!="string"||e.routeId!==e.composition.routeId||!Number.isInteger(e.version)||e.version<0||!S(e.pageComposition)||!Array.isArray(e.tokens)||!e.tokens.every(s=>typeof s=="string")||!Array.isArray(e.nodes)||!Array.isArray(e.entries))return!1;let t=e.nodes;if(!t.every((s,i)=>Array.isArray(s)&&s.length===2&&(s[0]===null||f(s[0])&&s[0]<i)&&f(s[1])&&s[1]<e.tokens.length)||!e.entries.every(s=>Array.isArray(s)&&(s.length===2||s.length===3)&&f(s[0])&&s[0]<t.length&&f(s[1])&&s[1]<=15&&(s.length===2||f(s[2]))))return!1;let o=e.dynamicDataSummaries;return o===void 0||Array.isArray(o)&&o.every(s=>Array.isArray(s)&&s.length===2&&f(s[0])&&s[0]<e.entries.length&&S(s[1]))}function S(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function K(n){return!S(n)||typeof n.routeId!="string"?!1:n.effectiveTemplateId===void 0||typeof n.effectiveTemplateId=="string"}function f(n){return typeof n=="number"&&Number.isInteger(n)&&n>=0}var p=class n{constructor(){}static parseMessage(e,t={}){let o=JSON.parse(e);if(!n.isMessage(o,t))throw new Error("Invalid site edit bridge message");return o}static stringifyMessage(e){return JSON.stringify(e)}static extractRequestInfo(e){if(d(e)&&d(e.payload)){let t=typeof e.payload.id=="string"?e.payload.id:"unknown",o=this.isRequestKind(e.payload.kind)?e.payload.kind:"get-document";return{id:t,kind:o}}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,t={}){return!d(e)||typeof e.type!="string"?!1:e.type==="request"?N(e.payload,t):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}};function N(n,e){if(!d(n)||typeof n.id!="string"||!p.isRequestKind(n.kind))return!1;let t=b(n.composition);switch(n.kind){case"initialize-session":return t&&(n.mutationContext===void 0||x(n.mutationContext));case"get-document":case"get-style-context":return t;case"get-object-edit-context":case"get-class-name-source":case"resolve-source-location":return typeof n.key=="string"&&t;case"get-history-status":return t;case"get-source-tree":return t;case"get-source-file":return typeof n.path=="string"&&t;case"execute":return t&&B(n.request)&&(n.request.mutationContext===void 0||x(n.request.mutationContext))&&(e.validateOperationRequest?.(n.request)??!0);case"undo":case"redo":return t&&n.mutationContext===void 0||t&&x(n.mutationContext)}}function B(n){return d(n)&&typeof n.id=="string"&&typeof n.siteId=="string"&&typeof n.baseSnapshotId=="string"&&b(n.composition)&&typeof n.kind=="string"&&d(n.target)&&(n.target.kind!=="route"||b(n.target.composition))&&typeof n.documentVersion=="number"&&Number.isInteger(n.documentVersion)&&n.documentVersion>=0&&d(n.params)}function b(n){return d(n)&&typeof n.routeId=="string"&&n.routeId.length>0&&(n.templateId===void 0||typeof n.templateId=="string"&&n.templateId.length>0)&&Object.keys(n).every(e=>e==="routeId"||e==="templateId")}function d(n){return typeof n=="object"&&n!==null}function x(n){return d(n)&&typeof n.editingSessionId=="string"&&n.editingSessionId.length>0&&typeof n.lockId=="string"&&n.lockId.length>0&&typeof n.ownerId=="string"&&n.ownerId.length>0&&typeof n.leaseAssertion=="string"&&n.leaseAssertion.length>0}var F="vcp:designer:trace",j="designer_trace",T="__VCP_DESIGNER_TRACE__";var U=/(authorization|cookie|token|secret|providerTrace|headers?|sourceCode|rawSource|fullSource)/i;function P(n=globalThis){let e=n;try{if(e.localStorage?.getItem(F)==="1")return!0}catch{}try{let t=e.location?.search??"";return new URLSearchParams(t).get(j)==="1"}catch{return!1}}function l(n,e={},t=globalThis){if(!P(t))return;let o={type:n,timestamp:Date.now(),payload:W(e)},s=t,i=s[T]??[];i.push(o),i.length>1e3&&i.splice(0,i.length-1e3),s[T]=i;let r=t.console?.debug;o.payload.diagnosticOnly!==!0&&typeof r=="function"&&r.call(t.console,"[designer-trace]",o)}function W(n){return w(n,0)}function w(n,e){if(e>4)return"[truncated]";if(Array.isArray(n))return n.map(o=>w(o,e+1));if(!n||typeof n!="object")return n;let t={};for(let[o,s]of Object.entries(n)){if(U.test(o)){t[o]="[redacted]";continue}t[o]=w(s,e+1)}return t}var D=class{constructor(e){this.options=e,this.socket=null,this.connectPromise=null,this.connectAttempt=null,this.connectionState="idle",this.intentionalClose=!1,this.sequence=0,this.pending=new Map,this.listeners=new Set,this.connectionStatusListeners=new Set}connect(){return this.socket?.readyState===E&&this.connectionState==="open"?(this.setConnectionState("open"),Promise.resolve()):this.connectPromise?this.connectPromise:(this.intentionalClose=!1,this.setConnectionState("connecting"),this.connectPromise=new Promise((e,t)=>{let o;try{o=this.createSocket(this.options.url)}catch(a){let c=y(a,"WebSocket failed");this.socket=null,this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("failed",c.message),t(c);return}let s={socket:o,settled:!1,reject:t};this.connectAttempt=s,this.socket=o;let i=()=>{this.socket!==o||this.connectAttempt!==s||(s.settled=!0,this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("open"),e())},r=(a,c)=>{this.cleanupSocket(o,a,c)};o.onopen=()=>{if(!(this.socket!==o||this.connectAttempt!==s)){if(this.options.authKey){try{o.send(p.stringifyMessage({type:"auth",payload:{key:this.options.authKey}}))}catch(a){r(y(a,"WebSocket auth failed"),"failed")}return}i()}},o.onerror=a=>{if(this.socket!==o)return;let c=y(a,"WebSocket failed");r(c,"failed")},o.onmessage=a=>{if(this.socket===o){if(this.options.authKey&&this.connectAttempt===s&&!s.settled){this.handleAuthHandshakeMessage(a.data,o,s,()=>{i()});return}this.handleMessage(a.data)}},o.onclose=a=>{if(this.socket!==o)return;let c=this.intentionalClose,u=new Error(G(a,{intentional:c}));r(u,c?"closed":"failed")}}),this.connectPromise)}disconnect(){this.intentionalClose=!0,this.setConnectionState("closing");let e=this.socket;if(!e){this.rejectAll(new Error("WebSocket disconnected")),this.connectAttempt=null,this.connectPromise=null,this.setConnectionState("closed");return}this.cleanupSocket(e,new Error("WebSocket disconnected"),"closed")}getConnectionState(){return this.connectionState}subscribeConnectionStatus(e){return this.connectionStatusListeners.add(e),e({state:this.connectionState}),()=>{this.connectionStatusListeners.delete(e)}}getDocument(e){let t=this.selector(e);return this.request("get-document",{composition:t})}async initializeSession(e){let t=this.selector(e.composition);return await this.request("initialize-session",{composition:t,...e.mutationContext?{mutationContext:e.mutationContext}:{}})}getHistoryStatus(e){return this.request("get-history-status",{composition:this.selector(e)})}getStyleContext(e){let t=this.selector(e);return this.request("get-style-context",{composition:t})}getObjectEditContext(e,t){let o=this.selector(t);return this.request("get-object-edit-context",{key:e,composition:o})}getClassNameSource(e,t){let o=this.selector(t);return this.request("get-class-name-source",{key:e,composition:o})}getSourceTree(e){return this.request("get-source-tree",{composition:this.selector(e)})}getSourceFile(e){return this.request("get-source-file",{path:e.path,composition:this.selector(e.composition)})}resolveSourceLocation(e){return this.request("resolve-source-location",{key:e.key,composition:this.selector(e.composition)})}execute(e,t){let o=C(e),s={...e,composition:m(o)};return this.request("execute",{request:s})}undo(e,t){return this.request("undo",{composition:this.selector(e)})}redo(e,t){return this.request("redo",{composition:this.selector(e)})}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}selector(e){return m(e)}request(e,t){let o=this.socket;if(!o||o.readyState!==E||this.connectionState!=="open")return Promise.reject(new Error(`WebSocket is not open (state: ${this.connectionState}, readyState: ${o?.readyState??"none"})`));let s=`${e}-${this.sequence+=1}`,i=H(s,e,t);return l("bridge.request",{id:s,kind:e,routeId:typeof t.routeId=="string"?t.routeId:null,key:typeof t.key=="string"?t.key:null,operationKind:R(t.request,"kind"),documentVersion:R(t.request,"documentVersion")}),new Promise((r,a)=>{let c=this.resolveRequestTimeoutMs(e),u=c>0?setTimeout(()=>{let h=this.pending.get(s);h&&(this.pending.delete(s),l("bridge.response",{id:s,kind:e,ok:!1,timedOut:!0}),h.reject(new Error(`Bridge request timed out after ${c}ms: ${e}`)))},c):null;u?.unref?.(),this.pending.set(s,{kind:e,timeout:u,resolve:h=>r(h),reject:a});try{o.send(p.stringifyMessage(i))}catch(h){let g=y(h,"WebSocket send failed");this.clearPendingRequest(s),a(g),this.cleanupSocket(o,g,"failed")}})}handleAuthHandshakeMessage(e,t,o,s){try{if(p.parseMessage(e).type==="auth-ok"){s();return}throw new Error("Bridge authentication failed")}catch(i){let r=y(i,"Bridge authentication failed");this.connectAttempt===o&&this.cleanupSocket(t,r,"failed")}}handleMessage(e){let t=p.parseMessage(e);if(t.type==="event"){for(let s of this.listeners)s(t.payload.event);return}if(t.type!=="response"&&t.type!=="error")return;let o=this.pending.get(t.payload.id);if(o){if(this.clearPendingRequest(t.payload.id),t.type==="error"){l("bridge.response",{id:t.payload.id,kind:o.kind,ok:!1});let s=new Error(t.payload.message);t.payload.code&&(s.code=t.payload.code),o.reject(s);return}l("bridge.response",{id:t.payload.id,kind:o.kind,ok:!0,resultVersion:J(t.payload.result)}),o.resolve(o.kind==="get-document"?{...t.payload.result,data:M(t.payload.result.data,t.payload.result.composition)}:o.kind==="initialize-session"?V(t.payload.result):t.payload.result)}}rejectAll(e){for(let[t,o]of[...this.pending])this.clearPendingRequest(t),o.reject(e)}clearPendingRequest(e){let t=this.pending.get(e);t&&(t.timeout&&clearTimeout(t.timeout),this.pending.delete(e))}resolveRequestTimeoutMs(e){let t=this.options.requestTimeoutMsByKind?.[e];return typeof t=="number"?Math.max(0,t):typeof this.options.requestTimeoutMs=="number"?Math.max(0,this.options.requestTimeoutMs):L[e]??z}cleanupSocket(e,t,o){if(this.socket===e&&(this.socket=null),this.connectAttempt?.socket===e){let s=this.connectAttempt;this.connectAttempt=null,s.settled||(s.settled=!0,s.reject(t))}this.connectPromise=null,this.rejectAll(t),this.setConnectionState(o,t.message),e.onopen=null,e.onmessage=null,e.onerror=null,e.onclose=null;try{(e.readyState===$||e.readyState===E)&&e.close()}catch{}}setConnectionState(e,t){if(this.connectionState===e&&!t)return;this.connectionState=e;let o={state:e,...t?{message:t}:{}};this.options.onConnectionStatus?.(o);for(let s of this.connectionStatusListeners)s(o)}createSocket(e){if(this.options.createSocket)return this.options.createSocket(e);if(typeof WebSocket>"u")throw new Error("Site edit preview bridge requires WebSocket");return new WebSocket(e)}},$=0,E=1,z=15e3,L={"get-source-tree":1e4,"get-source-file":1e4,"resolve-source-location":1e4,execute:6e4,undo:2e4,redo:2e4};function y(n,e){return n instanceof Error?n:new Error(e)}function G(n,e){return e.intentional?"WebSocket disconnected":n?.code===1008?n.reason?`Bridge session authentication failed: ${n.reason}`:"Bridge session authentication failed":n?.reason?`WebSocket closed: ${n.reason}`:typeof n?.code=="number"&&n.code!==1005?`WebSocket closed with code ${n.code}`:"WebSocket closed"}function R(n,e){if(!n||typeof n!="object")return null;let o=n[e];return typeof o=="string"||typeof o=="number"?o:null}function J(n){if(!n||typeof n!="object")return null;let e=n,t=e.data?.resultVersion??e.data?.version??null;return typeof t=="number"?t:null}function ie(n){let e="composition"in n?m(n.composition):m({routeId:n.routeId});return new k({composition:e,initialDocument:n.initialDocument,client:n.client})}var k=class{constructor(e){this.params=e,this.refreshInFlight=null,this.objectGeneration=0,this.styleGeneration=0,this.editContextCache=new Map,this.editContextInFlight=new Map,this.classNameSourceCache=new Map,this.classNameSourceInFlight=new Map,this.styleContextCache=new Map,this.styleContextInFlight=new Map,this.composition=m(e.composition);let t=e.initialDocument.composition.effectiveTemplateId;if(t!==void 0&&(this.composition.templateId===void 0||this.composition.templateId!==t))throw new Error(`Site edit bridge requires a matching templateId for initial document composition ${JSON.stringify(e.initialDocument.composition)}`);this.currentDocument=e.initialDocument,this.observedDocumentVersion=e.initialDocument.version,this.sessionInitialized=!_(e.initialDocument),this.activeCompositionContext=this.sessionInitialized?{...e.initialDocument.composition}:null}async getDocument(e){this.requireBoundComposition(e);let t=_(this.currentDocument);return(t||this.currentDocument.version<this.observedDocumentVersion)&&(this.currentDocument=await this.refreshSnapshot(t?void 0:this.observedDocumentVersion,"get-document"),this.sessionInitialized=!0),{ok:!0,data:this.currentDocument}}async initializeSession(e){let t=this.requireBoundComposition(e.composition);if(!this.params.client.initializeSession)throw new Error("Site edit bridge client does not support initialize-session");let o=await this.params.client.initializeSession({...e,composition:t});return this.currentDocument=o.data.document,this.observedDocumentVersion=o.data.document.version,this.activeCompositionContext={...o.composition},this.sessionInitialized=!0,this.clearObjectCaches(),o}async getHistoryStatus(e){return this.params.client.getHistoryStatus(this.requireBoundComposition(e))}async getStyleContext(e){let t=this.requireBoundComposition(e),o=I(t),s=this.styleContextCache.get(o);if(s?.generation===this.styleGeneration)return{ok:!0,data:s.value};let i=this.styleContextInFlight.get(o);if(i?.generation===this.styleGeneration)return i.request;let r=this.styleGeneration,a=this.params.client.getStyleContext(t).then(c=>(this.styleGeneration===r&&this.styleContextCache.set(o,{generation:r,value:c.data}),c)).finally(()=>{this.styleContextInFlight.get(o)?.request===a&&this.styleContextInFlight.delete(o)});return this.styleContextInFlight.set(o,{generation:r,request:a}),a}async ensureDocumentVersion(e,t){this.requireBoundComposition(e);let o=await this.refreshSnapshot(t,"ensure-version");if(o.version<t)throw new Error(`Document refresh returned version ${o.version}, expected at least ${t}`);return{ok:!0,data:o}}async acknowledgeDocumentVersion(e,t){if(this.requireBoundComposition(e),t>this.currentDocument.version){if(this.currentDocument.pageComposition.status==="supported"){let o=await this.refreshSnapshot(t,"ensure-version");if(o.version<t)throw new Error(`Document refresh returned version ${o.version}, expected at least ${t}`);return{ok:!0,data:o}}this.currentDocument={...this.currentDocument,version:t},this.clearObjectCaches({preserveStyleContext:!0})}return this.observedDocumentVersion=Math.max(this.observedDocumentVersion,t),{ok:!0,data:this.currentDocument}}async getObjectEditContext(e,t){let o=this.requireBoundComposition(t),s=this.objectCacheKey(e,o),i=this.editContextCache.get(s);if(i)return{ok:!0,data:i};let r=this.editContextInFlight.get(s);if(r)return r;let a=this.objectGeneration,c=this.params.client.getObjectEditContext(e,o).then(u=>(this.objectGeneration===a&&this.editContextCache.set(s,u.data),u)).finally(()=>{this.editContextInFlight.get(s)===c&&this.editContextInFlight.delete(s)});return this.editContextInFlight.set(s,c),c}async getClassNameSource(e,t){let o=this.requireBoundComposition(t),s=this.objectCacheKey(e,o),i=this.classNameSourceCache.get(s);if(i)return i;let r=this.classNameSourceInFlight.get(s);if(r)return r;let a=this.objectGeneration,c=this.params.client.getClassNameSource(e,o).then(u=>(this.objectGeneration===a&&this.classNameSourceCache.set(s,u),u)).finally(()=>{this.classNameSourceInFlight.get(s)===c&&this.classNameSourceInFlight.delete(s)});return this.classNameSourceInFlight.set(s,c),c}async getSourceTree(e){return this.params.client.getSourceTree(this.requireBoundComposition(e))}async getSourceFile(e){return this.params.client.getSourceFile({path:e.path,composition:this.requireBoundComposition(e.composition)})}async resolveSourceLocation(e){return this.params.client.resolveSourceLocation({key:e.key,composition:this.requireBoundComposition(e.composition)})}async execute(e,t){let o=C(e);o&&this.composition.templateId!==void 0&&this.requireBoundComposition(o);let s={...e,composition:o??this.composition};return this.params.client.execute(s)}async undo(e,t){return this.params.client.undo(this.requireBoundComposition(e))}async redo(e,t){return this.params.client.redo(this.requireBoundComposition(e))}subscribe(e){return this.params.client.subscribe(t=>{if(t.type==="site-edit.document.changed"&&this.sessionInitialized&&this.isActiveComposition(t.composition)){this.observedDocumentVersion=Math.max(this.observedDocumentVersion,t.version),t.version>this.currentDocument.version&&this.clearObjectCaches(),e(t);return}e(t)})}async refreshSnapshot(e,t="ensure-version"){if(typeof e=="number"&&this.currentDocument.version>=e)return l("bridge.document-refresh",{composition:this.composition,routeId:this.composition.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!0,skipped:!0,source:t}),this.currentDocument;let o=I(this.composition),s=this.refreshInFlight;if(s?.compositionKey===o){l("bridge.document-refresh",{composition:this.composition,routeId:this.composition.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!0,source:t});let r=await s.request;if(typeof e!="number"||r.version>=e||this.currentDocument.version>=e)return this.currentDocument}l("bridge.document-refresh",{composition:this.composition,routeId:this.composition.routeId,minVersion:e,currentVersion:this.currentDocument.version,deduped:!1,source:t});let i=this.params.client.getDocument(this.composition).then(r=>(this.currentDocument=r.data,this.activeCompositionContext={...r.data.composition},this.observedDocumentVersion=Math.max(this.observedDocumentVersion,r.data.version),this.clearObjectCaches(),this.currentDocument));this.refreshInFlight={compositionKey:o,request:i};try{return await i}finally{this.refreshInFlight?.request===i&&(this.refreshInFlight=null)}}clearObjectCaches(e){this.objectGeneration+=1,this.editContextCache.clear(),this.editContextInFlight.clear(),this.classNameSourceCache.clear(),this.classNameSourceInFlight.clear(),!e?.preserveStyleContext&&(this.styleGeneration+=1,this.styleContextCache.clear(),this.styleContextInFlight.clear())}objectCacheKey(e,t){return`${I(t)}\0${e}`}requireBoundComposition(e){let t=e===void 0?this.composition:m(e);if(I(t)!==I(this.composition))throw new Error(`Site edit bridge is bound to composition ${JSON.stringify(this.composition)}, received ${JSON.stringify(t)}`);return m(t)}isActiveComposition(e){let t=this.activeCompositionContext??Q(this.composition);return O(e)===O(t)}};function _(n){return n.version===0&&n.entries.length===0}function V(n){let e=n;return{...e,data:{...e.data,document:M(e.data.document,e.composition)}}}function M(n,e){let t=n,o=q(t.routeId?t:{...t,routeId:e?.routeId??t.composition?.routeId});return e?{...o,composition:e}:o}function m(n){if(n&&typeof n=="object"&&typeof n.routeId=="string"){let e="effectiveTemplateId"in n?n.effectiveTemplateId:"templateId"in n?n.templateId:void 0;return{routeId:n.routeId,...typeof e=="string"?{templateId:e}:{}}}throw new Error("SITE_EDIT_COMPOSITION_SESSION_REQUIRED: a composition selector is required")}function I(n){return JSON.stringify([n.routeId,n.templateId??null])}function O(n){return JSON.stringify([n.routeId,n.effectiveTemplateId??null])}function Q(n){return{routeId:n.routeId,...n.templateId?{effectiveTemplateId:n.templateId}:{}}}function C(n){if(n&&typeof n=="object"){let e=n;if(e.composition&&typeof e.composition=="object"){let t=e.composition;if(typeof t.routeId=="string")return{routeId:t.routeId,...typeof t.templateId=="string"?{templateId:t.templateId}:{}}}}}function H(n,e,t){let o=m(e==="execute"?C(t.request):t.composition),s={id:n,kind:e,composition:o};switch(e){case"initialize-session":case"get-document":case"get-history-status":case"get-style-context":case"get-source-tree":case"undo":case"redo":return{type:"request",payload:{...s,...t.mutationContext?{mutationContext:t.mutationContext}:{}}};case"get-object-edit-context":case"get-class-name-source":case"resolve-source-location":return{type:"request",payload:{...s,key:String(t.key)}};case"get-source-file":return{type:"request",payload:{...s,path:String(t.path)}};case"execute":return{type:"request",payload:{...s,request:{...t.request,composition:C(t.request)??o}}}}}export{D as SiteEditWebSocketClient,ie as createSiteEditBridge};
@@ -3,6 +3,8 @@ export type CompactRenderDocument = {
3
3
  encoding: "segment-trie-v1";
4
4
  siteId: string;
5
5
  snapshotId: string;
6
+ /** The full composition identity must survive compact transport. */
7
+ composition: SiteEditRenderDocument["composition"];
6
8
  routeId: string;
7
9
  version: number;
8
10
  pageComposition: SiteEditRenderDocument["pageComposition"];
@@ -1 +1 @@
1
- function A(e){let n=new Map,o=[],i=new Map,t=[],a=e.pageComposition.status==="supported"?e.pageComposition.placementUnitKeys:[],u=new Map(a.map((s,r)=>[s,r])),g=e.pageComposition.status==="supported"?{status:"supported",containerKey:e.pageComposition.containerKey,hasBusinessMembers:e.pageComposition.hasBusinessMembers,placementUnitKeys:e.pageComposition.placementUnitKeys,placements:e.pageComposition.placements.map(s=>({placementKey:s.placementKey,placementUnitKey:s.placementUnitKey,relation:s.relation}))}:{status:"unsupported",reason:e.pageComposition.reason,diagnostics:e.pageComposition.diagnostics},m=e.entries.flatMap((s,r)=>s.dynamicDataSummary?[[r,s.dynamicDataSummary]]:[]),p=s=>{let r=n.get(s);if(typeof r=="number")return r;let d=o.length;return o.push(s),n.set(s,d),d},c=s=>{let r=null;for(let d of x(s)){let l=`${r??"root"}\0${d}`,y=i.get(l);if(typeof y=="number"){r=y;continue}let h=t.length;t.push([r,p(d)]),i.set(l,h),r=h}if(r===null)throw new Error("Document key must not be empty");return r};return{encoding:"segment-trie-v1",siteId:e.siteId,snapshotId:e.snapshotId,routeId:e.routeId,version:e.version,pageComposition:g,tokens:o,nodes:t,entries:e.entries.map(s=>{let r=s.pageCompositionUnitKey?u.get(s.pageCompositionUnitKey)??-1:-1;if(s.pageCompositionUnitKey&&r<0)throw new Error(`Render entry ${s.key} references unknown page composition unit ${s.pageCompositionUnitKey}`);return[c(s.key),K(s.operationSummary),...r>=0?[r]:[]]}),...m.length>0?{dynamicDataSummaries:m}:{}}}function M(e){if(!w(e))return e;let n=new Map,o=e.pageComposition.status==="supported"?e.pageComposition.placementUnitKeys:[],i=new Map(e.dynamicDataSummaries??[]),t=a=>{let u=n.get(a);if(u)return u;let g=e.nodes[a];if(!g)throw new Error(`Missing compact document node ${a}`);let[m,p]=g,c=e.tokens[p];if(typeof c!="string")throw new Error(`Missing compact document token ${p}`);let s=`${m===null?"":t(m)}${c}`;return n.set(a,s),s};return{siteId:e.siteId,snapshotId:e.snapshotId,routeId:e.routeId,version:e.version,pageComposition:e.pageComposition,entries:e.entries.map((a,u)=>{let[g,m,p]=a,c=p===void 0?void 0:o[p];if(p!==void 0&&!c)throw new Error(`Missing page composition unit ${p}`);return{key:t(g),...c?{pageCompositionUnitKey:c}:{},operationSummary:U(m),...i.get(u)?{dynamicDataSummary:i.get(u)}:{}}})}}function K(e){return(e.canUpdateText?1:0)|(e.canInsertChild?2:0)|(e.canMove?4:0)|(e.canRemove?8:0)}function U(e){return{canUpdateText:(e&1)!==0,canInsertChild:(e&2)!==0,canMove:(e&4)!==0,canRemove:(e&8)!==0}}function w(e){if(typeof e!="object"||e===null)return!1;let n=e;if(n.encoding!=="segment-trie-v1"||typeof n.siteId!="string"||typeof n.snapshotId!="string"||typeof n.routeId!="string"||!Number.isInteger(n.version)||n.version<0||!I(n.pageComposition)||!Array.isArray(n.tokens)||!n.tokens.every(t=>typeof t=="string")||!Array.isArray(n.nodes)||!Array.isArray(n.entries))return!1;let o=n.nodes;if(!o.every((t,a)=>Array.isArray(t)&&t.length===2&&(t[0]===null||f(t[0])&&t[0]<a)&&f(t[1])&&t[1]<n.tokens.length)||!n.entries.every(t=>Array.isArray(t)&&(t.length===2||t.length===3)&&f(t[0])&&t[0]<o.length&&f(t[1])&&t[1]<=15&&(t.length===2||f(t[2]))))return!1;let i=n.dynamicDataSummaries;return i===void 0||Array.isArray(i)&&i.every(t=>Array.isArray(t)&&t.length===2&&f(t[0])&&t[0]<n.entries.length&&I(t[1]))}function I(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function f(e){return typeof e=="number"&&Number.isInteger(e)&&e>=0}function x(e){if(!e)return[];let n=[],o=0,i=C(e,o);if(i<0)return n.push(e),n;for(n.push(e.slice(0,i)),o=i;o<e.length;){if(i=C(e,o+(e.startsWith("::",o)?2:1)),i<0){n.push(e.slice(o));break}n.push(e.slice(o,i)),o=i}return n}function C(e,n){let o=e.indexOf("/",n),i=e.indexOf("::",n);return o<0?i:i<0?o:Math.min(o,i)}export{M as decodeRenderDocument,A as encodeRenderDocument,w as isCompactRenderDocument};
1
+ function M(e){let n=new Map,i=[],s=new Map,t=[],a=e.pageComposition.status==="supported"?e.pageComposition.placementUnitKeys:[],m=new Map(a.map((o,r)=>[o,r])),g=e.pageComposition.status==="supported"?{status:"supported",containerKey:e.pageComposition.containerKey,hasBusinessMembers:e.pageComposition.hasBusinessMembers,placementUnitKeys:e.pageComposition.placementUnitKeys,placements:e.pageComposition.placements.map(o=>({placementKey:o.placementKey,placementUnitKey:o.placementUnitKey,relation:o.relation}))}:{status:"unsupported",reason:e.pageComposition.reason,diagnostics:e.pageComposition.diagnostics},u=e.entries.flatMap((o,r)=>o.dynamicDataSummary?[[r,o.dynamicDataSummary]]:[]),p=o=>{let r=n.get(o);if(typeof r=="number")return r;let d=i.length;return i.push(o),n.set(o,d),d},c=o=>{let r=null;for(let d of A(o)){let l=`${r??"root"}\0${d}`,h=s.get(l);if(typeof h=="number"){r=h;continue}let I=t.length;t.push([r,p(d)]),s.set(l,I),r=I}if(r===null)throw new Error("Document key must not be empty");return r};return{encoding:"segment-trie-v1",siteId:e.siteId,snapshotId:e.snapshotId,composition:{...e.composition},routeId:e.composition.routeId,version:e.version,pageComposition:g,tokens:i,nodes:t,entries:e.entries.map(o=>{let r=o.pageCompositionUnitKey?m.get(o.pageCompositionUnitKey)??-1:-1;if(o.pageCompositionUnitKey&&r<0)throw new Error(`Render entry ${o.key} references unknown page composition unit ${o.pageCompositionUnitKey}`);return[c(o.key),K(o.operationSummary),...r>=0?[r]:[]]}),...u.length>0?{dynamicDataSummaries:u}:{}}}function S(e){if(!U(e))return e;let n=new Map,i=e.pageComposition.status==="supported"?e.pageComposition.placementUnitKeys:[],s=new Map(e.dynamicDataSummaries??[]),t=a=>{let m=n.get(a);if(m)return m;let g=e.nodes[a];if(!g)throw new Error(`Missing compact document node ${a}`);let[u,p]=g,c=e.tokens[p];if(typeof c!="string")throw new Error(`Missing compact document token ${p}`);let o=`${u===null?"":t(u)}${c}`;return n.set(a,o),o};return{siteId:e.siteId,snapshotId:e.snapshotId,composition:{...e.composition},routeId:e.composition.routeId,version:e.version,pageComposition:e.pageComposition,entries:e.entries.map((a,m)=>{let[g,u,p]=a,c=p===void 0?void 0:i[p];if(p!==void 0&&!c)throw new Error(`Missing page composition unit ${p}`);return{key:t(g),...c?{pageCompositionUnitKey:c}:{},operationSummary:x(u),...s.get(m)?{dynamicDataSummary:s.get(m)}:{}}})}}function K(e){return(e.canUpdateText?1:0)|(e.canInsertChild?2:0)|(e.canMove?4:0)|(e.canRemove?8:0)}function x(e){return{canUpdateText:(e&1)!==0,canInsertChild:(e&2)!==0,canMove:(e&4)!==0,canRemove:(e&8)!==0}}function U(e){if(typeof e!="object"||e===null)return!1;let n=e;if(n.encoding!=="segment-trie-v1"||typeof n.siteId!="string"||typeof n.snapshotId!="string"||!w(n.composition)||typeof n.routeId!="string"||n.routeId!==n.composition.routeId||!Number.isInteger(n.version)||n.version<0||!y(n.pageComposition)||!Array.isArray(n.tokens)||!n.tokens.every(t=>typeof t=="string")||!Array.isArray(n.nodes)||!Array.isArray(n.entries))return!1;let i=n.nodes;if(!i.every((t,a)=>Array.isArray(t)&&t.length===2&&(t[0]===null||f(t[0])&&t[0]<a)&&f(t[1])&&t[1]<n.tokens.length)||!n.entries.every(t=>Array.isArray(t)&&(t.length===2||t.length===3)&&f(t[0])&&t[0]<i.length&&f(t[1])&&t[1]<=15&&(t.length===2||f(t[2]))))return!1;let s=n.dynamicDataSummaries;return s===void 0||Array.isArray(s)&&s.every(t=>Array.isArray(t)&&t.length===2&&f(t[0])&&t[0]<n.entries.length&&y(t[1]))}function y(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function w(e){return!y(e)||typeof e.routeId!="string"?!1:e.effectiveTemplateId===void 0||typeof e.effectiveTemplateId=="string"}function f(e){return typeof e=="number"&&Number.isInteger(e)&&e>=0}function A(e){if(!e)return[];let n=[],i=0,s=C(e,i);if(s<0)return n.push(e),n;for(n.push(e.slice(0,s)),i=s;i<e.length;){if(s=C(e,i+(e.startsWith("::",i)?2:1)),s<0){n.push(e.slice(i));break}n.push(e.slice(i,s)),i=s}return n}function C(e,n){let i=e.indexOf("/",n),s=e.indexOf("::",n);return i<0?s:s<0?i:Math.min(i,s)}export{S as decodeRenderDocument,M as encodeRenderDocument,U as isCompactRenderDocument};