@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/README.md +27 -8
- package/dist/auto-bootstrap.d.ts +19 -2
- package/dist/auto-bootstrap.js +11 -11
- package/dist/client.d.ts +75 -35
- package/dist/client.js +1 -1
- package/dist/document-codec.d.ts +2 -0
- package/dist/document-codec.js +1 -1
- package/dist/index.js +8 -8
- package/dist/next-dev-server.js +3 -3
- package/dist/protocol.d.ts +71 -143
- package/dist/protocol.js +281 -182
- package/dist/server.d.ts +7 -5
- package/dist/server.js +2 -2
- package/dist/window-message.d.ts +25 -12
- package/dist/window-message.js +1 -1
- package/package.json +3 -3
package/dist/protocol.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { encodeRenderDocument
|
|
1
|
+
import { encodeRenderDocument } from "./document-codec.js";
|
|
2
|
+
export const SITE_EDIT_COMPOSITION_SESSION_MISMATCH = "SITE_EDIT_COMPOSITION_SESSION_MISMATCH";
|
|
3
|
+
export const SITE_EDIT_COMPOSITION_SESSION_REQUIRED = "SITE_EDIT_COMPOSITION_SESSION_REQUIRED";
|
|
4
|
+
export const SITE_EDIT_COMPOSITION_REINITIALIZE_REQUIRED = "SITE_EDIT_COMPOSITION_REINITIALIZE_REQUIRED";
|
|
2
5
|
export class SiteEditBridgeTransport {
|
|
3
6
|
constructor() { }
|
|
4
7
|
static parseMessage(raw, options = {}) {
|
|
@@ -12,15 +15,10 @@ export class SiteEditBridgeTransport {
|
|
|
12
15
|
return JSON.stringify(message);
|
|
13
16
|
}
|
|
14
17
|
static extractRequestInfo(message) {
|
|
15
|
-
if (
|
|
16
|
-
message
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
message.payload !== null) {
|
|
20
|
-
const payload = message.payload;
|
|
21
|
-
const id = typeof payload.id === "string" ? payload.id : "unknown";
|
|
22
|
-
const kind = SiteEditBridgeTransport.isRequestKind(payload.kind)
|
|
23
|
-
? payload.kind
|
|
18
|
+
if (isRecord(message) && isRecord(message.payload)) {
|
|
19
|
+
const id = typeof message.payload.id === "string" ? message.payload.id : "unknown";
|
|
20
|
+
const kind = this.isRequestKind(message.payload.kind)
|
|
21
|
+
? message.payload.kind
|
|
24
22
|
: "get-document";
|
|
25
23
|
return { id, kind };
|
|
26
24
|
}
|
|
@@ -41,11 +39,10 @@ export class SiteEditBridgeTransport {
|
|
|
41
39
|
value === "redo");
|
|
42
40
|
}
|
|
43
41
|
static isMessage(value, options = {}) {
|
|
44
|
-
if (!isRecord(value) || typeof value.type !== "string")
|
|
42
|
+
if (!isRecord(value) || typeof value.type !== "string")
|
|
45
43
|
return false;
|
|
46
|
-
}
|
|
47
44
|
if (value.type === "request") {
|
|
48
|
-
return
|
|
45
|
+
return isRequestPayload(value.payload, options);
|
|
49
46
|
}
|
|
50
47
|
if (value.type === "response" || value.type === "error") {
|
|
51
48
|
return isRecord(value.payload) && typeof value.payload.id === "string";
|
|
@@ -56,57 +53,23 @@ export class SiteEditBridgeTransport {
|
|
|
56
53
|
if (value.type === "auth") {
|
|
57
54
|
return isRecord(value.payload) && typeof value.payload.key === "string";
|
|
58
55
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
static isRequestPayload(payload, options = {}) {
|
|
65
|
-
if (!isRecord(payload) ||
|
|
66
|
-
typeof payload.id !== "string" ||
|
|
67
|
-
!SiteEditBridgeTransport.isRequestKind(payload.kind)) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
switch (payload.kind) {
|
|
71
|
-
case "initialize-session":
|
|
72
|
-
return (typeof payload.routeId === "string" &&
|
|
73
|
-
(payload.mutationContext === undefined ||
|
|
74
|
-
isValidMutationContext(payload.mutationContext)));
|
|
75
|
-
case "get-document":
|
|
76
|
-
case "get-style-context":
|
|
77
|
-
return typeof payload.routeId === "string";
|
|
78
|
-
case "get-object-edit-context":
|
|
79
|
-
case "get-class-name-source":
|
|
80
|
-
case "resolve-source-location":
|
|
81
|
-
return (typeof payload.key === "string" &&
|
|
82
|
-
(payload.routeId === undefined || typeof payload.routeId === "string"));
|
|
83
|
-
case "get-history-status":
|
|
84
|
-
return payload.mutationContext === undefined;
|
|
85
|
-
case "get-source-tree":
|
|
86
|
-
return true;
|
|
87
|
-
case "get-source-file":
|
|
88
|
-
return typeof payload.path === "string";
|
|
89
|
-
case "execute":
|
|
90
|
-
return (isOperationRequestLike(payload.request) &&
|
|
91
|
-
(payload.request.mutationContext === undefined ||
|
|
92
|
-
isValidMutationContext(payload.request.mutationContext)) &&
|
|
93
|
-
(options.validateOperationRequest?.(payload.request) ?? true));
|
|
94
|
-
case "undo":
|
|
95
|
-
case "redo":
|
|
96
|
-
return (payload.mutationContext === undefined ||
|
|
97
|
-
isValidMutationContext(payload.mutationContext));
|
|
98
|
-
}
|
|
56
|
+
return (value.type === "auth-ok" &&
|
|
57
|
+
isRecord(value.payload) &&
|
|
58
|
+
value.payload.ok === true);
|
|
99
59
|
}
|
|
100
60
|
}
|
|
101
61
|
export class SiteEditBridgeSocketServer {
|
|
102
62
|
constructor(options) {
|
|
103
63
|
this.options = options;
|
|
64
|
+
this.session = {
|
|
65
|
+
subscribedCompositionKeys: new Set(),
|
|
66
|
+
};
|
|
104
67
|
this.messageQueue = new SerializedSiteEditMessageQueue();
|
|
105
68
|
this.eventDispatcher = new BufferedSiteEditEventDispatcher((message) => {
|
|
106
69
|
this.sendJson(message);
|
|
107
70
|
});
|
|
108
71
|
this.unsubscribe = options.engine.subscribe((event) => {
|
|
109
|
-
this.
|
|
72
|
+
this.handleEngineEvent(event);
|
|
110
73
|
});
|
|
111
74
|
options.socket.onmessage = (event) => {
|
|
112
75
|
try {
|
|
@@ -126,138 +89,112 @@ export class SiteEditBridgeSocketServer {
|
|
|
126
89
|
};
|
|
127
90
|
options.socket.onclose = () => {
|
|
128
91
|
this.unsubscribe();
|
|
92
|
+
this.session.activeComposition = undefined;
|
|
93
|
+
this.session.subscribedCompositionKeys.clear();
|
|
129
94
|
};
|
|
130
95
|
}
|
|
131
96
|
async handleMessage(message) {
|
|
132
97
|
try {
|
|
133
98
|
const parsed = message;
|
|
134
|
-
if (parsed.type !== "request")
|
|
99
|
+
if (parsed.type !== "request")
|
|
135
100
|
return;
|
|
136
|
-
}
|
|
137
101
|
const payload = parsed.payload;
|
|
138
102
|
const { id } = payload;
|
|
139
103
|
switch (payload.kind) {
|
|
140
104
|
case "initialize-session": {
|
|
105
|
+
const selector = requireCompositionSelector(payload);
|
|
106
|
+
const resolution = await this.resolve(selector);
|
|
141
107
|
const result = await this.options.engine.initializeSession({
|
|
142
|
-
|
|
108
|
+
composition: resolution.context,
|
|
143
109
|
...(payload.mutationContext
|
|
144
110
|
? { mutationContext: payload.mutationContext }
|
|
145
111
|
: {}),
|
|
146
112
|
});
|
|
113
|
+
this.activateSession(resolution.context);
|
|
147
114
|
this.sendJson({
|
|
148
115
|
type: "response",
|
|
149
116
|
payload: {
|
|
150
117
|
id,
|
|
151
118
|
kind: payload.kind,
|
|
152
|
-
result: {
|
|
153
|
-
|
|
119
|
+
result: addComposition({
|
|
120
|
+
...result,
|
|
154
121
|
data: {
|
|
155
|
-
|
|
156
|
-
|
|
122
|
+
...result.data,
|
|
123
|
+
document: encodeBridgeRenderDocument(result.data.document),
|
|
157
124
|
},
|
|
158
|
-
},
|
|
125
|
+
}, resolution.context),
|
|
159
126
|
},
|
|
160
127
|
});
|
|
161
128
|
return;
|
|
162
129
|
}
|
|
163
|
-
case "get-document":
|
|
164
|
-
this.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
id,
|
|
168
|
-
kind: payload.kind,
|
|
169
|
-
result: {
|
|
170
|
-
ok: true,
|
|
171
|
-
data: encodeRenderDocument((await this.options.engine.getDocument(payload.routeId)).data),
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
});
|
|
130
|
+
case "get-document": {
|
|
131
|
+
const context = await this.resolveWireContext(payload);
|
|
132
|
+
const result = await this.options.engine.getDocument(context);
|
|
133
|
+
this.sendResult(id, payload.kind, encodeDocumentResult(result, context));
|
|
175
134
|
return;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
kind: payload.kind,
|
|
182
|
-
result: await this.options.engine.getHistoryStatus(),
|
|
183
|
-
},
|
|
184
|
-
});
|
|
135
|
+
}
|
|
136
|
+
case "get-history-status": {
|
|
137
|
+
const context = await this.resolveWireContext(payload);
|
|
138
|
+
const result = await this.options.engine.getHistoryStatus(context);
|
|
139
|
+
this.sendResult(id, payload.kind, addComposition(result, context));
|
|
185
140
|
return;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
id,
|
|
191
|
-
kind: payload.kind,
|
|
192
|
-
result: await this.options.engine.getStyleContext(payload.routeId),
|
|
193
|
-
},
|
|
194
|
-
});
|
|
141
|
+
}
|
|
142
|
+
case "get-style-context": {
|
|
143
|
+
const context = await this.resolveWireContext(payload);
|
|
144
|
+
this.sendResult(id, payload.kind, addComposition(await this.options.engine.getStyleContext(context), context));
|
|
195
145
|
return;
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
id,
|
|
201
|
-
kind: payload.kind,
|
|
202
|
-
result: await this.options.engine.getObjectEditContext(payload.key, payload.routeId),
|
|
203
|
-
},
|
|
204
|
-
});
|
|
146
|
+
}
|
|
147
|
+
case "get-object-edit-context": {
|
|
148
|
+
const context = await this.resolveWireContext(payload);
|
|
149
|
+
this.sendResult(id, payload.kind, addComposition(await this.options.engine.getObjectEditContext(payload.key, context), context));
|
|
205
150
|
return;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
id,
|
|
211
|
-
kind: payload.kind,
|
|
212
|
-
result: await this.options.engine.getClassNameSource(payload.key, payload.routeId),
|
|
213
|
-
},
|
|
214
|
-
});
|
|
151
|
+
}
|
|
152
|
+
case "get-class-name-source": {
|
|
153
|
+
const context = await this.resolveWireContext(payload);
|
|
154
|
+
this.sendResult(id, payload.kind, addComposition(await this.options.engine.getClassNameSource(payload.key, context), context));
|
|
215
155
|
return;
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
id,
|
|
221
|
-
kind: payload.kind,
|
|
222
|
-
result: await this.options.engine.getSourceTree(),
|
|
223
|
-
},
|
|
224
|
-
});
|
|
156
|
+
}
|
|
157
|
+
case "get-source-tree": {
|
|
158
|
+
const context = await this.resolveWireContext(payload);
|
|
159
|
+
this.sendResult(id, payload.kind, addComposition(await this.options.engine.getSourceTree(context), context));
|
|
225
160
|
return;
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
path: payload.path,
|
|
234
|
-
}),
|
|
235
|
-
},
|
|
236
|
-
});
|
|
161
|
+
}
|
|
162
|
+
case "get-source-file": {
|
|
163
|
+
const context = await this.resolveWireContext(payload);
|
|
164
|
+
this.sendResult(id, payload.kind, addComposition(await this.options.engine.getSourceFile({
|
|
165
|
+
path: payload.path,
|
|
166
|
+
composition: context,
|
|
167
|
+
}), context));
|
|
237
168
|
return;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
routeId: payload.routeId,
|
|
247
|
-
}),
|
|
248
|
-
},
|
|
249
|
-
});
|
|
169
|
+
}
|
|
170
|
+
case "resolve-source-location": {
|
|
171
|
+
const context = await this.resolveWireContext(payload);
|
|
172
|
+
const result = addComposition(await this.options.engine.resolveSourceLocation({
|
|
173
|
+
key: payload.key,
|
|
174
|
+
composition: context,
|
|
175
|
+
}), context);
|
|
176
|
+
this.sendResult(id, payload.kind, result);
|
|
250
177
|
return;
|
|
178
|
+
}
|
|
251
179
|
case "execute": {
|
|
252
|
-
|
|
180
|
+
const context = await this.resolveOperationWireContext(payload.request);
|
|
181
|
+
const payloadContext = await this.resolveWireContext(payload);
|
|
182
|
+
if (computeSiteEditCompositionKey(context) !==
|
|
183
|
+
computeSiteEditCompositionKey(payloadContext)) {
|
|
184
|
+
throw new BridgeProtocolError(SITE_EDIT_COMPOSITION_SESSION_MISMATCH, "The execute request composition does not match its operation composition.");
|
|
185
|
+
}
|
|
186
|
+
const request = await this.normalizeOperationRequest(payload.request, context);
|
|
187
|
+
await this.handleBufferedOperation(id, payload.kind, () => this.options.engine.execute(request));
|
|
253
188
|
return;
|
|
254
189
|
}
|
|
255
190
|
case "undo":
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
191
|
+
case "redo": {
|
|
192
|
+
const context = await this.resolveWireContext(payload);
|
|
193
|
+
await this.handleBufferedOperation(id, payload.kind, () => payload.kind === "undo"
|
|
194
|
+
? this.options.engine.undo(context)
|
|
195
|
+
: this.options.engine.redo(context));
|
|
260
196
|
return;
|
|
197
|
+
}
|
|
261
198
|
}
|
|
262
199
|
}
|
|
263
200
|
catch (error) {
|
|
@@ -272,6 +209,67 @@ export class SiteEditBridgeSocketServer {
|
|
|
272
209
|
});
|
|
273
210
|
}
|
|
274
211
|
}
|
|
212
|
+
async resolve(selector) {
|
|
213
|
+
return this.options.engine.resolveComposition(selector);
|
|
214
|
+
}
|
|
215
|
+
async resolveAndAssert(selector) {
|
|
216
|
+
const resolution = await this.resolve(selector);
|
|
217
|
+
const active = this.session.activeComposition;
|
|
218
|
+
if (!active) {
|
|
219
|
+
throw new BridgeProtocolError(SITE_EDIT_COMPOSITION_SESSION_REQUIRED, "Initialize a composition session before issuing this request.");
|
|
220
|
+
}
|
|
221
|
+
if (computeSiteEditCompositionKey(active) !==
|
|
222
|
+
computeSiteEditCompositionKey(resolution.context)) {
|
|
223
|
+
throw new BridgeProtocolError(SITE_EDIT_COMPOSITION_SESSION_MISMATCH, "The request composition does not match the active socket session.");
|
|
224
|
+
}
|
|
225
|
+
return resolution.context;
|
|
226
|
+
}
|
|
227
|
+
async resolveWireContext(payload) {
|
|
228
|
+
const selector = requireCompositionSelector(payload);
|
|
229
|
+
return this.resolveAndAssert(selector);
|
|
230
|
+
}
|
|
231
|
+
async resolveOperationWireContext(request) {
|
|
232
|
+
const selector = requireOperationSelector(request);
|
|
233
|
+
return this.resolveAndAssert(selector);
|
|
234
|
+
}
|
|
235
|
+
activateSession(context) {
|
|
236
|
+
this.session.activeComposition = context;
|
|
237
|
+
this.session.subscribedCompositionKeys.clear();
|
|
238
|
+
this.session.subscribedCompositionKeys.add(computeSiteEditCompositionKey(context));
|
|
239
|
+
}
|
|
240
|
+
async normalizeOperationRequest(rawRequest, context) {
|
|
241
|
+
const request = {
|
|
242
|
+
...rawRequest,
|
|
243
|
+
composition: contextToSelector(context),
|
|
244
|
+
};
|
|
245
|
+
if (request.target.kind === "route") {
|
|
246
|
+
const rawTarget = request.target;
|
|
247
|
+
const targetSelector = requireCompositionSelector(rawTarget);
|
|
248
|
+
const targetContext = await this.resolveAndAssert(targetSelector);
|
|
249
|
+
request.target = {
|
|
250
|
+
...rawTarget,
|
|
251
|
+
composition: contextToSelector(targetContext),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return request;
|
|
255
|
+
}
|
|
256
|
+
handleEngineEvent(event) {
|
|
257
|
+
const composition = event?.composition;
|
|
258
|
+
if (!isCompositionContext(composition))
|
|
259
|
+
return;
|
|
260
|
+
const key = computeSiteEditCompositionKey(composition);
|
|
261
|
+
if (!this.session.subscribedCompositionKeys.has(key))
|
|
262
|
+
return;
|
|
263
|
+
this.eventDispatcher.push(event);
|
|
264
|
+
if (event.type === "site-edit.graph.invalidated" &&
|
|
265
|
+
key === activeKey(this.session)) {
|
|
266
|
+
this.session.activeComposition = undefined;
|
|
267
|
+
this.session.subscribedCompositionKeys.delete(key);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
sendResult(id, kind, result) {
|
|
271
|
+
this.sendJson({ type: "response", payload: { id, kind, result } });
|
|
272
|
+
}
|
|
275
273
|
async handleRawMessage(raw) {
|
|
276
274
|
try {
|
|
277
275
|
await this.handleMessage(SiteEditBridgeTransport.parseMessage(raw, {
|
|
@@ -279,11 +277,14 @@ export class SiteEditBridgeSocketServer {
|
|
|
279
277
|
}));
|
|
280
278
|
}
|
|
281
279
|
catch (error) {
|
|
282
|
-
const message = error instanceof Error ? error.message : "Invalid websocket message";
|
|
283
280
|
const requestInfo = extractRawRequestInfo(raw);
|
|
284
281
|
this.sendJson({
|
|
285
282
|
type: "error",
|
|
286
|
-
payload: {
|
|
283
|
+
payload: {
|
|
284
|
+
...requestInfo,
|
|
285
|
+
message: getBridgeErrorMessage(error),
|
|
286
|
+
code: getBridgeErrorCode(error),
|
|
287
|
+
},
|
|
287
288
|
});
|
|
288
289
|
}
|
|
289
290
|
}
|
|
@@ -294,8 +295,7 @@ export class SiteEditBridgeSocketServer {
|
|
|
294
295
|
this.eventDispatcher.beginBuffering();
|
|
295
296
|
try {
|
|
296
297
|
const result = await operation();
|
|
297
|
-
this.
|
|
298
|
-
this.eventDispatcher.flush();
|
|
298
|
+
this.sendResult(id, kind, result);
|
|
299
299
|
}
|
|
300
300
|
catch (error) {
|
|
301
301
|
this.sendJson({
|
|
@@ -307,10 +307,19 @@ export class SiteEditBridgeSocketServer {
|
|
|
307
307
|
code: getBridgeErrorCode(error),
|
|
308
308
|
},
|
|
309
309
|
});
|
|
310
|
+
}
|
|
311
|
+
finally {
|
|
310
312
|
this.eventDispatcher.flush();
|
|
311
313
|
}
|
|
312
314
|
}
|
|
313
315
|
}
|
|
316
|
+
class BridgeProtocolError extends Error {
|
|
317
|
+
constructor(code, message) {
|
|
318
|
+
super(message);
|
|
319
|
+
this.code = code;
|
|
320
|
+
this.name = "BridgeProtocolError";
|
|
321
|
+
}
|
|
322
|
+
}
|
|
314
323
|
class BufferedSiteEditEventDispatcher {
|
|
315
324
|
constructor(send) {
|
|
316
325
|
this.send = send;
|
|
@@ -333,9 +342,7 @@ class BufferedSiteEditEventDispatcher {
|
|
|
333
342
|
flush() {
|
|
334
343
|
const buffered = this.bufferedEvents ?? [];
|
|
335
344
|
this.bufferedEvents = null;
|
|
336
|
-
buffered.forEach((message) =>
|
|
337
|
-
this.send(message);
|
|
338
|
-
});
|
|
345
|
+
buffered.forEach((message) => this.send(message));
|
|
339
346
|
}
|
|
340
347
|
}
|
|
341
348
|
class SerializedSiteEditMessageQueue {
|
|
@@ -345,9 +352,8 @@ class SerializedSiteEditMessageQueue {
|
|
|
345
352
|
this.queued = 0;
|
|
346
353
|
}
|
|
347
354
|
enqueue(task) {
|
|
348
|
-
if (this.queued >= this.maxQueued)
|
|
355
|
+
if (this.queued >= this.maxQueued)
|
|
349
356
|
throw new Error("Bridge request queue is full");
|
|
350
|
-
}
|
|
351
357
|
this.queued += 1;
|
|
352
358
|
const run = async () => {
|
|
353
359
|
this.queued -= 1;
|
|
@@ -358,49 +364,126 @@ class SerializedSiteEditMessageQueue {
|
|
|
358
364
|
return next;
|
|
359
365
|
}
|
|
360
366
|
}
|
|
361
|
-
function
|
|
362
|
-
|
|
367
|
+
function isRequestPayload(payload, options) {
|
|
368
|
+
if (!isRecord(payload) ||
|
|
369
|
+
typeof payload.id !== "string" ||
|
|
370
|
+
!SiteEditBridgeTransport.isRequestKind(payload.kind))
|
|
371
|
+
return false;
|
|
372
|
+
const compositionOk = isCompositionSelector(payload.composition);
|
|
373
|
+
switch (payload.kind) {
|
|
374
|
+
case "initialize-session":
|
|
375
|
+
return (compositionOk &&
|
|
376
|
+
(payload.mutationContext === undefined ||
|
|
377
|
+
isMutationContextLike(payload.mutationContext)));
|
|
378
|
+
case "get-document":
|
|
379
|
+
case "get-style-context":
|
|
380
|
+
return compositionOk;
|
|
381
|
+
case "get-object-edit-context":
|
|
382
|
+
case "get-class-name-source":
|
|
383
|
+
case "resolve-source-location":
|
|
384
|
+
return typeof payload.key === "string" && compositionOk;
|
|
385
|
+
case "get-history-status":
|
|
386
|
+
return compositionOk;
|
|
387
|
+
case "get-source-tree":
|
|
388
|
+
return compositionOk;
|
|
389
|
+
case "get-source-file":
|
|
390
|
+
return typeof payload.path === "string" && compositionOk;
|
|
391
|
+
case "execute":
|
|
392
|
+
return (compositionOk &&
|
|
393
|
+
isOperationRequestLike(payload.request) &&
|
|
394
|
+
(payload.request.mutationContext === undefined ||
|
|
395
|
+
isMutationContextLike(payload.request.mutationContext)) &&
|
|
396
|
+
(options.validateOperationRequest?.(payload.request) ?? true));
|
|
397
|
+
case "undo":
|
|
398
|
+
case "redo":
|
|
399
|
+
return ((compositionOk && payload.mutationContext === undefined) ||
|
|
400
|
+
(compositionOk && isMutationContextLike(payload.mutationContext)));
|
|
401
|
+
}
|
|
363
402
|
}
|
|
364
403
|
function isOperationRequestLike(value) {
|
|
365
404
|
return (isRecord(value) &&
|
|
366
405
|
typeof value.id === "string" &&
|
|
367
406
|
typeof value.siteId === "string" &&
|
|
368
407
|
typeof value.baseSnapshotId === "string" &&
|
|
369
|
-
(value.
|
|
408
|
+
isCompositionSelector(value.composition) &&
|
|
370
409
|
typeof value.kind === "string" &&
|
|
371
410
|
isRecord(value.target) &&
|
|
411
|
+
(value.target.kind !== "route" ||
|
|
412
|
+
isCompositionSelector(value.target.composition)) &&
|
|
372
413
|
typeof value.documentVersion === "number" &&
|
|
373
414
|
Number.isInteger(value.documentVersion) &&
|
|
374
415
|
value.documentVersion >= 0 &&
|
|
375
416
|
isRecord(value.params));
|
|
376
417
|
}
|
|
377
|
-
function
|
|
378
|
-
return
|
|
418
|
+
function isCompositionSelector(value) {
|
|
419
|
+
return (isRecord(value) &&
|
|
420
|
+
typeof value.routeId === "string" &&
|
|
421
|
+
value.routeId.length > 0 &&
|
|
422
|
+
(value.templateId === undefined ||
|
|
423
|
+
(typeof value.templateId === "string" && value.templateId.length > 0)) &&
|
|
424
|
+
Object.keys(value).every((key) => key === "routeId" || key === "templateId"));
|
|
379
425
|
}
|
|
380
|
-
function
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
return typeof code === "string" && code.length > 0 ? code : undefined;
|
|
426
|
+
function isCompositionContext(value) {
|
|
427
|
+
return (isRecord(value) &&
|
|
428
|
+
typeof value.routeId === "string" &&
|
|
429
|
+
(value.effectiveTemplateId === undefined ||
|
|
430
|
+
typeof value.effectiveTemplateId === "string"));
|
|
386
431
|
}
|
|
387
|
-
function
|
|
388
|
-
if (
|
|
389
|
-
return
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
432
|
+
function requireCompositionSelector(payload) {
|
|
433
|
+
if (isCompositionSelector(payload.composition))
|
|
434
|
+
return payload.composition;
|
|
435
|
+
throw new BridgeProtocolError(SITE_EDIT_COMPOSITION_SESSION_REQUIRED, "A composition selector is required for this request.");
|
|
436
|
+
}
|
|
437
|
+
function requireOperationSelector(request) {
|
|
438
|
+
if (isRecord(request) && isCompositionSelector(request.composition))
|
|
439
|
+
return request.composition;
|
|
440
|
+
throw new BridgeProtocolError(SITE_EDIT_COMPOSITION_SESSION_REQUIRED, "A composition selector is required for execute.");
|
|
441
|
+
}
|
|
442
|
+
function contextToSelector(context) {
|
|
443
|
+
return {
|
|
444
|
+
routeId: context.routeId,
|
|
445
|
+
...(context.effectiveTemplateId
|
|
446
|
+
? { templateId: context.effectiveTemplateId }
|
|
447
|
+
: {}),
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function activeKey(session) {
|
|
451
|
+
return session.activeComposition
|
|
452
|
+
? computeSiteEditCompositionKey(session.activeComposition)
|
|
453
|
+
: null;
|
|
454
|
+
}
|
|
455
|
+
function computeSiteEditCompositionKey(context) {
|
|
456
|
+
return JSON.stringify([context.routeId, context.effectiveTemplateId ?? null]);
|
|
457
|
+
}
|
|
458
|
+
function addComposition(result, context) {
|
|
459
|
+
return isRecord(result) && result.ok === true
|
|
460
|
+
? { ...result, composition: context }
|
|
461
|
+
: result;
|
|
462
|
+
}
|
|
463
|
+
function encodeDocumentResult(result, context) {
|
|
464
|
+
if (!isRecord(result) || result.ok !== true)
|
|
465
|
+
return result;
|
|
466
|
+
return {
|
|
467
|
+
...result,
|
|
468
|
+
...(context ? { composition: context } : {}),
|
|
469
|
+
data: encodeBridgeRenderDocument(result.data),
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
function encodeBridgeRenderDocument(document) {
|
|
473
|
+
const value = document;
|
|
474
|
+
return encodeRenderDocument(value.routeId
|
|
475
|
+
? value
|
|
476
|
+
: {
|
|
477
|
+
...value,
|
|
478
|
+
routeId: value.composition.routeId,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
function isRecord(value) {
|
|
482
|
+
return typeof value === "object" && value !== null;
|
|
398
483
|
}
|
|
399
484
|
function isMutationContextLike(value) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
return (typeof value.editingSessionId === "string" &&
|
|
485
|
+
return (isRecord(value) &&
|
|
486
|
+
typeof value.editingSessionId === "string" &&
|
|
404
487
|
value.editingSessionId.length > 0 &&
|
|
405
488
|
typeof value.lockId === "string" &&
|
|
406
489
|
value.lockId.length > 0 &&
|
|
@@ -409,6 +492,22 @@ function isMutationContextLike(value) {
|
|
|
409
492
|
typeof value.leaseAssertion === "string" &&
|
|
410
493
|
value.leaseAssertion.length > 0);
|
|
411
494
|
}
|
|
495
|
+
function getBridgeErrorCode(error) {
|
|
496
|
+
if (!isRecord(error) ||
|
|
497
|
+
typeof error.code !== "string" ||
|
|
498
|
+
error.code.length === 0)
|
|
499
|
+
return undefined;
|
|
500
|
+
return error.code;
|
|
501
|
+
}
|
|
502
|
+
function getBridgeErrorMessage(error) {
|
|
503
|
+
if (error instanceof Error)
|
|
504
|
+
return error.message;
|
|
505
|
+
if (isRecord(error) &&
|
|
506
|
+
typeof error.message === "string" &&
|
|
507
|
+
error.message.length > 0)
|
|
508
|
+
return error.message;
|
|
509
|
+
return "Unknown websocket operation failure";
|
|
510
|
+
}
|
|
412
511
|
function extractRawRequestInfo(raw) {
|
|
413
512
|
try {
|
|
414
513
|
return SiteEditBridgeTransport.extractRequestInfo(JSON.parse(raw));
|
package/dist/server.d.ts
CHANGED
|
@@ -56,12 +56,15 @@ export declare class SiteEditBridgeServer {
|
|
|
56
56
|
private readonly mutationQueue;
|
|
57
57
|
private readonly eventListeners;
|
|
58
58
|
private routes;
|
|
59
|
+
private compositions;
|
|
60
|
+
private compositionFiles;
|
|
61
|
+
private compositionResolutionError;
|
|
59
62
|
private stopWatching;
|
|
60
63
|
private stopWebSocketServer;
|
|
61
64
|
private readonly pendingChangedFiles;
|
|
62
65
|
private fileChangeTimer;
|
|
63
66
|
private isStarted;
|
|
64
|
-
private
|
|
67
|
+
private readonly runtimeVersionFloors;
|
|
65
68
|
private activeHistoryStore;
|
|
66
69
|
private versionPersistence;
|
|
67
70
|
constructor(options: ReturnType<typeof normalizeOptions>, server?: HttpServer, closeHttpServerOnStop?: boolean, dependencies?: SiteEditBridgeServerDependencies);
|
|
@@ -74,13 +77,12 @@ export declare class SiteEditBridgeServer {
|
|
|
74
77
|
private createBridgeEngine;
|
|
75
78
|
private bindHistorySession;
|
|
76
79
|
private recordRuntimeVersion;
|
|
77
|
-
private activateRoute;
|
|
78
|
-
private normalizeRequest;
|
|
79
80
|
private handleFileChanged;
|
|
80
81
|
private handleFilesChanged;
|
|
81
82
|
private flushChangedFiles;
|
|
82
|
-
private
|
|
83
|
-
private
|
|
83
|
+
private refreshCompositions;
|
|
84
|
+
private resolveComposition;
|
|
85
|
+
private getInitialDocumentVersions;
|
|
84
86
|
private getRuntime;
|
|
85
87
|
}
|
|
86
88
|
declare function normalizeOptions(options: SiteEditBridgeServerOptions): {
|