@vertexvis/stream-api 0.24.6-canary.0 → 1.0.0-canary.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1095 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var frameStreamingProtos = require('@vertexvis/frame-streaming-protos');
6
+ var utils = require('@vertexvis/utils');
7
+
8
+ function encode(message) {
9
+ return frameStreamingProtos.vertexvis.protobuf.stream.StreamMessage.encode(message).finish();
10
+ }
11
+ function decode(bufferOrBytes) {
12
+ const bytes = bufferOrBytes instanceof ArrayBuffer
13
+ ? new Uint8Array(bufferOrBytes)
14
+ : bufferOrBytes;
15
+ const message = frameStreamingProtos.vertexvis.protobuf.stream.StreamMessage.decode(bytes);
16
+ return frameStreamingProtos.vertexvis.protobuf.stream.StreamMessage.toObject(message, {
17
+ defaults: true,
18
+ });
19
+ }
20
+
21
+ class StreamRequestError extends Error {
22
+ constructor(requestId, requestPayload, summary, details) {
23
+ super(summary != null && summary.length > 0
24
+ ? `Stream request failed (${summary})`
25
+ : 'Stream request failed');
26
+ this.requestId = requestId;
27
+ this.requestPayload = requestPayload;
28
+ this.summary = summary;
29
+ this.details = details;
30
+ // Allows for `instanceof` checks. See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
31
+ Object.setPrototypeOf(this, StreamRequestError.prototype);
32
+ }
33
+ }
34
+
35
+ function defineParams(...definitions) {
36
+ return (settings) => {
37
+ return definitions.reduce((result, def) => ({ ...result, ...def(settings) }), {});
38
+ };
39
+ }
40
+ function defineBoolean(param, prop) {
41
+ return defineValue(param, prop, (v) => {
42
+ if (typeof v === 'boolean') {
43
+ return v ? 'on' : 'off';
44
+ }
45
+ else {
46
+ return undefined;
47
+ }
48
+ });
49
+ }
50
+ function defineNumber(param, prop) {
51
+ return defineValue(param, prop, (v) => typeof v === 'number' ? v.toString() : undefined);
52
+ }
53
+ function defineString(param, prop) {
54
+ return defineValue(param, prop, (v) => typeof v === 'string' ? v : undefined);
55
+ }
56
+ function defineValue(param, prop, f) {
57
+ return (settings) => {
58
+ const value = f(settings[prop]);
59
+ if (value != null) {
60
+ return { [param]: value };
61
+ }
62
+ else {
63
+ return {};
64
+ }
65
+ };
66
+ }
67
+
68
+ function appendSettingsToUrl(url, settings) {
69
+ const defaults = {
70
+ // Settings that you want to set on each WS connection should go here.
71
+ };
72
+ const uri = utils.Uri.parse(url);
73
+ const builder = defineParams(toFrameDeliverySettingsParams(defaults.EXPERIMENTAL_frameDelivery), toAdaptiveRenderingSettingsParams(defaults.EXPERIMENTAL_adaptiveRendering), toQualityOfServiceSettingsParams(defaults.EXPERIMENTAL_qualityOfService));
74
+ const params = builder(settings);
75
+ return utils.Uri.toString(utils.Uri.addQueryParams(params, uri));
76
+ }
77
+ function toFrameDeliverySettingsParams(defaults) {
78
+ return defineSettings((s) => s.EXPERIMENTAL_frameDelivery, defaults, defineParams(defineBoolean('frame-delivery.rate-limit-enabled', 'rateLimitingEnabled'), defineNumber('frame-delivery.packet-loss-threshold', 'packetLossThreshold'), defineNumber('frame-delivery.history-max-size', 'historyMaxSize'), defineString('frame-delivery.timeout', 'timeout'), defineNumber('frame-delivery.timeout-ratio-threshold', 'timeoutRatioThreshold')));
79
+ }
80
+ function toAdaptiveRenderingSettingsParams(defaults) {
81
+ return defineSettings((s) => s.EXPERIMENTAL_adaptiveRendering, defaults, defineParams(defineBoolean('adaptive-rendering.enabled', 'enabled'), defineString('adaptive-rendering.method', 'method'), defineNumber('adaptive-rendering.jpeg-quality-min', 'jpegMinQuality'), defineNumber('adaptive-rendering.jpeg-quality-max', 'jpegMaxQuality'), defineNumber('adaptive-rendering.image-scale-min', 'imageMinScale'), defineNumber('adaptive-rendering.image-scale-max', 'imageMaxScale'), defineNumber('adaptive-rendering.window-size', 'windowSize')));
82
+ }
83
+ function toQualityOfServiceSettingsParams(defaults) {
84
+ return defineSettings((s) => s.EXPERIMENTAL_qualityOfService, defaults, defineParams(defineNumber('qos.history-max-size', 'historyMaxSize')));
85
+ }
86
+ function defineSettings(getter, defaults, builder) {
87
+ return (settings) => {
88
+ const merged = utils.Objects.defaults(getter(settings) || {}, defaults);
89
+ return builder(merged);
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Converts a JS date type to a `google.protobuf.Timestamp`. The returned time
95
+ * will be represented as UTC.
96
+ *
97
+ * @param date The date to convert.
98
+ */
99
+ function toProtoTimestamp(date) {
100
+ const millis = date.getTime();
101
+ return frameStreamingProtos.google.protobuf.Timestamp.create(parseEpochMillis(millis));
102
+ }
103
+ /**
104
+ * Returns the current date and time as a `google.protobuf.Timestamp`.
105
+ */
106
+ function currentDateAsProtoTimestamp() {
107
+ return toProtoTimestamp(new Date());
108
+ }
109
+ function toProtoDuration(...args) {
110
+ if (args.length === 2) {
111
+ const start = args[0];
112
+ const end = args[1];
113
+ const millis = end.getTime() - start.getTime();
114
+ return frameStreamingProtos.google.protobuf.Duration.create(parseEpochMillis(millis));
115
+ }
116
+ else if (args.length === 1) {
117
+ const millis = args[0];
118
+ return frameStreamingProtos.google.protobuf.Duration.create(parseEpochMillis(millis));
119
+ }
120
+ else {
121
+ throw new Error('Expected input to be a number or start and end date.');
122
+ }
123
+ }
124
+ function protoToDate(time) {
125
+ if (time.seconds != null && time.nanos != null) {
126
+ const seconds = typeof time.seconds === 'number' ? time.seconds : time.seconds.toNumber();
127
+ return new Date(seconds * 1000 + time.nanos / 1000000);
128
+ }
129
+ }
130
+ /* eslint-enable padding-line-between-statements */
131
+ function parseEpochMillis(millis) {
132
+ const seconds = Math.floor(Math.abs(millis) / 1000);
133
+ const nanos = (millis % 1000) * 1000000;
134
+ return { seconds: seconds * (millis < 0 ? -1 : 1), nanos };
135
+ }
136
+
137
+ function validateBoundingBox(boundingBox) {
138
+ const boundingBoxXMaxValid = (boundingBox === null || boundingBox === void 0 ? void 0 : boundingBox.xmax) != null && Number.isFinite(boundingBox.xmax);
139
+ const boundingBoxXMinValid = (boundingBox === null || boundingBox === void 0 ? void 0 : boundingBox.xmin) != null && Number.isFinite(boundingBox.xmin);
140
+ const boundingBoxYMaxValid = (boundingBox === null || boundingBox === void 0 ? void 0 : boundingBox.ymax) != null && Number.isFinite(boundingBox.ymax);
141
+ const boundingBoxYMinValid = (boundingBox === null || boundingBox === void 0 ? void 0 : boundingBox.ymin) != null && Number.isFinite(boundingBox.ymin);
142
+ const boundingBoxZMaxValid = (boundingBox === null || boundingBox === void 0 ? void 0 : boundingBox.zmax) != null && Number.isFinite(boundingBox.zmax);
143
+ const boundingBoxZMinValid = (boundingBox === null || boundingBox === void 0 ? void 0 : boundingBox.zmin) != null && Number.isFinite(boundingBox.zmin);
144
+ return (boundingBoxXMaxValid &&
145
+ boundingBoxXMinValid &&
146
+ boundingBoxYMaxValid &&
147
+ boundingBoxYMinValid &&
148
+ boundingBoxZMaxValid &&
149
+ boundingBoxZMinValid);
150
+ }
151
+ function validateCamera(camera) {
152
+ // If a perspective camera is provided, verify it is valid
153
+ if (camera.perspective != null) {
154
+ const perspectiveCameraIsValid = validatePerspectiveCamera(camera.perspective);
155
+ return perspectiveCameraIsValid;
156
+ }
157
+ // If an orthographic camera is provided, verify it is valid
158
+ if (camera.orthographic != null) {
159
+ const orthographicCameraIsValid = validateOrthographicCamera(camera.orthographic);
160
+ return orthographicCameraIsValid;
161
+ }
162
+ return false;
163
+ }
164
+ function validatePerspectiveCamera(camera) {
165
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
166
+ const lookAtXValid = ((_a = camera.lookAt) === null || _a === void 0 ? void 0 : _a.x) != null && Number.isFinite(camera.lookAt.x);
167
+ const lookAtYValid = ((_b = camera.lookAt) === null || _b === void 0 ? void 0 : _b.y) != null && Number.isFinite(camera.lookAt.y);
168
+ const lookAtZValid = ((_c = camera.lookAt) === null || _c === void 0 ? void 0 : _c.z) != null && Number.isFinite(camera.lookAt.z);
169
+ const positionXValid = ((_d = camera.position) === null || _d === void 0 ? void 0 : _d.x) != null && Number.isFinite(camera.position.x);
170
+ const positionYValid = ((_e = camera.position) === null || _e === void 0 ? void 0 : _e.y) != null && Number.isFinite(camera.position.y);
171
+ const positionZValid = ((_f = camera.position) === null || _f === void 0 ? void 0 : _f.z) != null && Number.isFinite(camera.position.z);
172
+ const upXValid = ((_g = camera.up) === null || _g === void 0 ? void 0 : _g.x) != null && Number.isFinite(camera.up.x);
173
+ const upYValid = ((_h = camera.up) === null || _h === void 0 ? void 0 : _h.y) != null && Number.isFinite(camera.up.y);
174
+ const upZValid = ((_j = camera.up) === null || _j === void 0 ? void 0 : _j.z) != null && Number.isFinite(camera.up.z);
175
+ // Validate up vector has non-zero length
176
+ const upVectorValid = validateVector({ x: (_k = camera === null || camera === void 0 ? void 0 : camera.up) === null || _k === void 0 ? void 0 : _k.x, y: (_l = camera === null || camera === void 0 ? void 0 : camera.up) === null || _l === void 0 ? void 0 : _l.y, z: (_m = camera === null || camera === void 0 ? void 0 : camera.up) === null || _m === void 0 ? void 0 : _m.z }, true);
177
+ return (lookAtXValid &&
178
+ lookAtYValid &&
179
+ lookAtZValid &&
180
+ positionXValid &&
181
+ positionYValid &&
182
+ positionZValid &&
183
+ upXValid &&
184
+ upYValid &&
185
+ upZValid &&
186
+ upVectorValid);
187
+ }
188
+ function validateOrthographicCamera(camera) {
189
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
190
+ const fovHeightValid = camera.fovHeight != null && Number.isFinite(camera.fovHeight);
191
+ const lookAtXValid = ((_a = camera.lookAt) === null || _a === void 0 ? void 0 : _a.x) != null && Number.isFinite(camera.lookAt.x);
192
+ const lookAtYValid = ((_b = camera.lookAt) === null || _b === void 0 ? void 0 : _b.y) != null && Number.isFinite(camera.lookAt.y);
193
+ const lookAtZValid = ((_c = camera.lookAt) === null || _c === void 0 ? void 0 : _c.z) != null && Number.isFinite(camera.lookAt.z);
194
+ const upXValid = ((_d = camera.up) === null || _d === void 0 ? void 0 : _d.x) != null && Number.isFinite(camera.up.x);
195
+ const upYValid = ((_e = camera.up) === null || _e === void 0 ? void 0 : _e.y) != null && Number.isFinite(camera.up.y);
196
+ const upZValid = ((_f = camera.up) === null || _f === void 0 ? void 0 : _f.z) != null && Number.isFinite(camera.up.z);
197
+ const viewVectorXValid = ((_g = camera.viewVector) === null || _g === void 0 ? void 0 : _g.x) != null && Number.isFinite(camera.viewVector.x);
198
+ const viewVectorYValid = ((_h = camera.viewVector) === null || _h === void 0 ? void 0 : _h.y) != null && Number.isFinite(camera.viewVector.y);
199
+ const viewVectorZValid = ((_j = camera.viewVector) === null || _j === void 0 ? void 0 : _j.z) != null && Number.isFinite(camera.viewVector.z);
200
+ // Validate up vector has non-zero length
201
+ const upVectorValid = validateVector({ x: (_k = camera === null || camera === void 0 ? void 0 : camera.up) === null || _k === void 0 ? void 0 : _k.x, y: (_l = camera === null || camera === void 0 ? void 0 : camera.up) === null || _l === void 0 ? void 0 : _l.y, z: (_m = camera === null || camera === void 0 ? void 0 : camera.up) === null || _m === void 0 ? void 0 : _m.z }, true);
202
+ return (fovHeightValid &&
203
+ lookAtXValid &&
204
+ lookAtYValid &&
205
+ lookAtZValid &&
206
+ viewVectorXValid &&
207
+ viewVectorYValid &&
208
+ viewVectorZValid &&
209
+ upXValid &&
210
+ upYValid &&
211
+ upZValid &&
212
+ upVectorValid);
213
+ }
214
+ function validateDimensions(dimensions) {
215
+ const heightValid = (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) != null &&
216
+ Number.isFinite(dimensions.height) &&
217
+ dimensions.height > 0;
218
+ const widthValid = (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) != null &&
219
+ Number.isFinite(dimensions.width) &&
220
+ dimensions.width > 0;
221
+ return heightValid && widthValid;
222
+ }
223
+ function validateNumber(number) {
224
+ return number != null && Number.isFinite(number);
225
+ }
226
+ function validatePoint(point) {
227
+ const xValid = (point === null || point === void 0 ? void 0 : point.x) != null && Number.isFinite(point.x);
228
+ const yValid = (point === null || point === void 0 ? void 0 : point.y) != null && Number.isFinite(point.y);
229
+ return xValid && yValid;
230
+ }
231
+ function validateVector(vector, verifyNonZeroLength) {
232
+ const xValid = (vector === null || vector === void 0 ? void 0 : vector.x) != null && Number.isFinite(vector.x);
233
+ const yValid = (vector === null || vector === void 0 ? void 0 : vector.y) != null && Number.isFinite(vector.y);
234
+ const zValid = (vector === null || vector === void 0 ? void 0 : vector.z) != null && Number.isFinite(vector.z);
235
+ const vectorComponentsValid = xValid && yValid && zValid;
236
+ if (verifyNonZeroLength) {
237
+ if ((vector === null || vector === void 0 ? void 0 : vector.x) != null && (vector === null || vector === void 0 ? void 0 : vector.y) != null && (vector === null || vector === void 0 ? void 0 : vector.z) != null) {
238
+ const vectorMagnitudeSquared = vector.x * vector.x + vector.y * vector.y + vector.z * vector.z;
239
+ const vectorHasNonZeroLength = vectorMagnitudeSquared !== 0;
240
+ return vectorComponentsValid && vectorHasNonZeroLength;
241
+ }
242
+ else {
243
+ // If one or more components are undefined, then the vector does not have non-zero length
244
+ return false;
245
+ }
246
+ }
247
+ return vectorComponentsValid;
248
+ }
249
+
250
+ class WebSocketClientImpl {
251
+ constructor() {
252
+ this.listeners = {};
253
+ this.onMessageDispatcher = new utils.EventDispatcher();
254
+ this.onCloseDispatcher = new utils.EventDispatcher();
255
+ this.addWebSocketListeners = (ws, wsId, resolve, reject) => {
256
+ const onOpen = () => this.onOpen(resolve);
257
+ const onError = () => reject();
258
+ const onClose = (event) => this.handleClose(event, wsId);
259
+ ws.addEventListener('message', this.handleMessage);
260
+ ws.addEventListener('open', onOpen);
261
+ ws.addEventListener('error', onError);
262
+ ws.addEventListener('close', onClose);
263
+ return {
264
+ dispose: () => {
265
+ ws.removeEventListener('message', this.handleMessage);
266
+ ws.removeEventListener('open', onOpen);
267
+ ws.removeEventListener('error', onError);
268
+ ws.removeEventListener('close', onClose);
269
+ },
270
+ };
271
+ };
272
+ this.handleMessage = (event) => {
273
+ this.onMessageDispatcher.emit(event);
274
+ };
275
+ this.handleClose = (event, webSocketId) => {
276
+ this.onCloseDispatcher.emit(event);
277
+ this.removeWebSocketListeners(webSocketId);
278
+ };
279
+ }
280
+ close() {
281
+ if (this.webSocket != null) {
282
+ this.webSocket.close();
283
+ }
284
+ }
285
+ async connect(descriptor) {
286
+ const id = utils.UUID.create();
287
+ // Ensure that any existing websocket connection has been closed prior
288
+ // to opening a new one to ensure we don't leave open connections.
289
+ if (this.webSocket != null) {
290
+ this.close();
291
+ }
292
+ this.webSocket = new WebSocket(descriptor.url, descriptor.protocols);
293
+ this.webSocket.binaryType = 'arraybuffer';
294
+ return new Promise((resolve, reject) => {
295
+ if (this.webSocket != null) {
296
+ this.listeners[id] = this.addWebSocketListeners(this.webSocket, id, resolve, reject);
297
+ }
298
+ });
299
+ }
300
+ onMessage(handler) {
301
+ return this.onMessageDispatcher.on(handler);
302
+ }
303
+ onClose(handler) {
304
+ return this.onCloseDispatcher.on(handler);
305
+ }
306
+ send(data) {
307
+ if (this.webSocket != null) {
308
+ this.webSocket.send(data);
309
+ }
310
+ }
311
+ removeWebSocketListeners(webSocketId) {
312
+ var _a;
313
+ (_a = this.listeners[webSocketId]) === null || _a === void 0 ? void 0 : _a.dispose();
314
+ }
315
+ onOpen(resolve) {
316
+ resolve();
317
+ }
318
+ }
319
+
320
+ /**
321
+ * The API client to interact with Vertex's streaming API.
322
+ */
323
+ class StreamApi {
324
+ constructor(websocket = new WebSocketClientImpl(), opts = {}) {
325
+ var _a;
326
+ this.websocket = websocket;
327
+ this.onResponseDispatcher = new utils.EventDispatcher();
328
+ this.onRequestDispatcher = new utils.EventDispatcher();
329
+ this.onEventDispatcher = new utils.EventDispatcher();
330
+ this.opts = {
331
+ loggingEnabled: (_a = opts.loggingEnabled) !== null && _a !== void 0 ? _a : false,
332
+ };
333
+ }
334
+ /**
335
+ * Initiates a websocket connection to Vertex's streaming API. Returns a
336
+ * promise that resolves once the connection is established and can begin
337
+ * accepting messages.
338
+ *
339
+ * @param descriptor A function that returns a description of how to establish
340
+ * a WS connection.
341
+ * @param settings A configuration to use when initializing the WS connection.
342
+ */
343
+ async connect(descriptor, settings = {}) {
344
+ const desc = {
345
+ ...descriptor,
346
+ url: appendSettingsToUrl(descriptor.url, settings),
347
+ };
348
+ await this.websocket.connect(desc);
349
+ this.messageSubscription = this.websocket.onMessage((message) => {
350
+ this.handleMessage(message);
351
+ });
352
+ return { dispose: () => this.dispose() };
353
+ }
354
+ /**
355
+ * Closes any open WS connections and disposes of resources.
356
+ */
357
+ dispose() {
358
+ var _a;
359
+ this.websocket.close();
360
+ (_a = this.messageSubscription) === null || _a === void 0 ? void 0 : _a.dispose();
361
+ }
362
+ /**
363
+ * Adds a callback that is invoked when the client receives a request from the
364
+ * server. Returns a `Disposable` that can be used to remove the listener.
365
+ *
366
+ * @param handler A handler function.
367
+ */
368
+ onRequest(handler) {
369
+ return this.onRequestDispatcher.on(handler);
370
+ }
371
+ /**
372
+ * Adds a callback that is invoked when the client receives an event from the
373
+ * server. Returns a `Disposable` that can be used to remove the listener.
374
+ *
375
+ * @param handler - A handler function.
376
+ */
377
+ onEvent(handler) {
378
+ return this.onEventDispatcher.on(handler);
379
+ }
380
+ /**
381
+ * Adds a callback that is invoked when the websocket connection is closed.
382
+ *
383
+ * @param handler A handler function.
384
+ */
385
+ onClose(handler) {
386
+ return this.websocket.onClose(handler);
387
+ }
388
+ /**
389
+ * Sends a request to initiate a streaming session.
390
+ *
391
+ * The payload accepts an optional `frameCorrelationId` that will be sent
392
+ * back on the frame that is associated to this request. Use `onRequest` to
393
+ * add a callback that'll be invoked when the server sends a request to draw
394
+ * the frame.
395
+ *
396
+ * Use `withResponse` to indicate if the server should reply with a response.
397
+ * If `false`, the returned promise will complete immediately. Otherwise,
398
+ * it'll complete when a response is received.
399
+ *
400
+ * @param payload The payload of the request.
401
+ * @param withResponse Indicates if the server should reply with a response.
402
+ * Defaults to `true`.
403
+ */
404
+ startStream(payload, withResponse = true) {
405
+ return this.sendRequest({ startStream: payload }, withResponse);
406
+ }
407
+ /**
408
+ * Sends a request to reconnect to an existing streaming session.
409
+ *
410
+ * The payload accepts an optional `frameCorrelationId` that will be sent
411
+ * back on the frame that is associated to this request. Use `onRequest` to
412
+ * add a callback that'll be invoked when the server sends a request to draw
413
+ * the frame.
414
+ *
415
+ * Use `withResponse` to indicate if the server should reply with a response.
416
+ * If `false`, the returned promise will complete immediately. Otherwise,
417
+ * it'll complete when a response is received.
418
+ *
419
+ * @param payload The payload of the request.
420
+ * @param withResponse Indicates if the server should reply with a response.
421
+ * Defaults to `true`.
422
+ */
423
+ async reconnect(payload, withResponse = true) {
424
+ return this.sendRequest({ reconnect: payload }, withResponse);
425
+ }
426
+ async updateStream(payload, withResponse = false) {
427
+ return this.sendRequest({ updateStream: payload }, withResponse);
428
+ }
429
+ /**
430
+ * Sends a request to signal to the rendering pipeline that an interaction has
431
+ * started. The rendering pipeline will use this as a hint to perform more
432
+ * aggressive rendering optimizations at the expense of rendering quality.
433
+ * Call `endInteraction` to signal to the rendering pipeline that an
434
+ * interaction has finished.
435
+ *
436
+ * Use `withResponse` to indicate if the server should reply with a response.
437
+ * If `false`, the returned promise will complete immediately. Otherwise,
438
+ * it'll complete when a response is received.
439
+ *
440
+ * @param data The payload of the request.
441
+ * @param withResponse Indicates if the server should reply with a response.
442
+ * Defaults to `true`.
443
+ */
444
+ beginInteraction(payload = {}, withResponse = true) {
445
+ return this.sendRequest({ beginInteraction: payload }, withResponse);
446
+ }
447
+ /**
448
+ * Sends a request to update the position of the scene's camera.
449
+ *
450
+ * The payload accepts an optional `frameCorrelationId` that will be sent
451
+ * back on the frame that is associated to this request. Use `onRequest` to
452
+ * add a callback that'll be invoked when the server sends a request to draw
453
+ * the frame.
454
+ *
455
+ * Use `withResponse` to indicate if the server should reply with a response.
456
+ * If `false`, the returned promise will complete immediately. Otherwise,
457
+ * it'll complete when a response is received.
458
+ *
459
+ * @param payload The payload of the request.
460
+ * @param withResponse Indicates if the server should reply with a response.
461
+ * Defaults to `true`.
462
+ */
463
+ replaceCamera(payload, withResponse = true) {
464
+ // If a camera is provided, verify it is valid
465
+ if (payload.camera != null) {
466
+ const cameraIsValid = validateCamera(payload.camera);
467
+ if (!cameraIsValid) {
468
+ console.warn('Invalid camera provided. Canceling replaceCamera operation.');
469
+ return Promise.resolve({});
470
+ }
471
+ }
472
+ return this.sendRequest({ updateCamera: payload }, withResponse);
473
+ }
474
+ /**
475
+ * Sends a request to update the position of the scene's camera as a fly operation
476
+ *
477
+ * The payload accepts an optional `frameCorrelationId` that will be sent
478
+ * back on the frame that is associated to this request. Use `onRequest` to
479
+ * add a callback that'll be invoked when the server sends a request to draw
480
+ * the frame.
481
+ *
482
+ * Use `withResponse` to indicate if the server should reply with a response.
483
+ * If `false`, the returned promise will complete immediately. Otherwise,
484
+ * it'll complete when a response is received.
485
+ *
486
+ * @param payload
487
+ * @param withResponse
488
+ */
489
+ flyTo(payload, withResponse = true) {
490
+ // If a bounding box is provided, verify it is valid
491
+ if (payload.boundingBox != null) {
492
+ const validBoundingBox = validateBoundingBox(payload.boundingBox);
493
+ if (!validBoundingBox) {
494
+ console.warn('Invalid bounding box provided. Canceling flyTo operation.');
495
+ return Promise.resolve({});
496
+ }
497
+ }
498
+ // If a camera is provided, verify it is valid
499
+ if (payload.camera != null) {
500
+ const cameraIsValid = validateCamera(payload.camera);
501
+ if (!cameraIsValid) {
502
+ console.warn('Invalid camera provided. Canceling flyTo operation.');
503
+ return Promise.resolve({});
504
+ }
505
+ }
506
+ // If a base camera is provided, verify it is valid
507
+ if (payload.baseCamera != null) {
508
+ const baseCameraIsValid = validateCamera(payload.baseCamera);
509
+ if (!baseCameraIsValid) {
510
+ console.warn('Invalid base camera provided. Canceling flyTo operation.');
511
+ return Promise.resolve({});
512
+ }
513
+ }
514
+ return this.sendRequest({ flyTo: payload }, withResponse);
515
+ }
516
+ /**
517
+ * Sends a request to update the specified interaction.
518
+ *
519
+ * Use `withResponse` to indicate if the server should reply with a response.
520
+ * If `false`, the returned promise will complete immediately. Otherwise,
521
+ * it'll complete when a response is received.
522
+ *
523
+ * @param payload The payload of the request.
524
+ * @param withResponse Indicates if the server should reply with a response.
525
+ * Defaults to `true`.
526
+ */
527
+ updateInteraction(payload, withResponse = true) {
528
+ return this.sendRequest({ updateInteraction: payload }, withResponse);
529
+ }
530
+ /**
531
+ * Sends a request to update the dimensions of the frame.
532
+ *
533
+ * The payload accepts an optional `frameCorrelationId` that will be sent
534
+ * back on the frame that is associated to this request. Use `onRequest` to
535
+ * add a callback that'll be invoked when the server sends a request to draw
536
+ * the frame.
537
+ *
538
+ * Use `withResponse` to indicate if the server should reply with a response.
539
+ * If `false`, the returned promise will complete immediately. Otherwise,
540
+ * it'll complete when a response is received.
541
+ *
542
+ * @param payload The payload of the request.
543
+ * @param withResponse Indicates if the server should reply with a response.
544
+ * Defaults to `true`.
545
+ */
546
+ updateDimensions(payload, withResponse = true) {
547
+ // Verify the provided dimensions are valid
548
+ if (payload.dimensions != null) {
549
+ const validDimensions = validateDimensions(payload.dimensions);
550
+ if (!validDimensions) {
551
+ console.warn('Invalid dimensions provided. Canceling updateDimensions operation.');
552
+ return Promise.resolve({});
553
+ }
554
+ }
555
+ return this.sendRequest({ updateDimensions: payload }, withResponse);
556
+ }
557
+ /**
558
+ * Sends a request to update the cross sectioning planes of the frame.
559
+ *
560
+ * The payload accepts an optional `frameCorrelationId` that will be sent
561
+ * back on the frame that is associated to this request. Use `onRequest` to
562
+ * add a callback that'll be invoked when the server sends a request to draw
563
+ * the frame.
564
+ *
565
+ * Use `withResponse` to indicate if the server should reply with a response.
566
+ * If `false`, the returned promise will complete immediately. Otherwise,
567
+ * it'll complete when a response is received.
568
+ *
569
+ * @param payload The payload of the request.
570
+ * @param withResponse Indicates if the server should reply with a response.
571
+ * Defaults to `true`.
572
+ */
573
+ updateCrossSectioning(payload, withResponse = true) {
574
+ var _a, _b;
575
+ // If a section plane is provided, verify it is valid
576
+ const invalidSectionPlane = (_b = (_a = payload.crossSectioning) === null || _a === void 0 ? void 0 : _a.sectionPlanes) === null || _b === void 0 ? void 0 : _b.some((plane) => {
577
+ const validNormal = plane.normal != null && validateVector(plane.normal, true);
578
+ const validOffset = plane.offset != null && validateNumber(plane.offset);
579
+ return !validNormal || !validOffset;
580
+ });
581
+ if (invalidSectionPlane) {
582
+ console.warn('Invalid cross section plane provided. Canceling updateCrossSectioning operation.');
583
+ return Promise.resolve({});
584
+ }
585
+ return this.sendRequest({ updateCrossSectioning: payload }, withResponse);
586
+ }
587
+ /**
588
+ * Sends a request to set or clear the model view of the frame.
589
+ *
590
+ * The payload accepts an optional `frameCorrelationId` that will be sent
591
+ * back on the frame that is associated to this request. Use `onRequest` to
592
+ * add a callback that'll be invoked when the server sends a request to draw
593
+ * the frame.
594
+ *
595
+ * Use `withResponse` to indicate if the server should reply with a response.
596
+ * If `false`, the returned promise will complete immediately. Otherwise,
597
+ * it'll complete when a response is received.
598
+ *
599
+ * @param payload The payload of the request.
600
+ * @param withResponse Indicates if the server should reply with a response.
601
+ * Defaults to `true`.
602
+ */
603
+ updateModelView(payload, withResponse = true) {
604
+ return this.sendRequest({ updateModelView: payload }, withResponse);
605
+ }
606
+ /**
607
+ * Sends a request to perform hit detection.
608
+ *
609
+ * Use `withResponse` to indicate if the server should reply with a response.
610
+ * If `false`, the returned promise will complete immediately. Otherwise,
611
+ * it'll complete when a response is received.
612
+ *
613
+ * @param payload The payload of the request.
614
+ * @param withResponse Indicates if the server should reply with a response.
615
+ * Defaults to `true`.
616
+ */
617
+ hitItems(payload, withResponse = true) {
618
+ // If a point is provided, verify it is valid
619
+ if (payload.point != null) {
620
+ const validPoint = validatePoint(payload.point);
621
+ if (!validPoint) {
622
+ console.warn('Invalid point provided. Canceling hitItems operation.');
623
+ return Promise.resolve({});
624
+ }
625
+ }
626
+ return this.sendRequest({ hitItems: payload }, withResponse);
627
+ }
628
+ /**
629
+ * Sends a request to perform an alteration to a scene. Alterations include
630
+ * changing item visibility and changing the materials of items.
631
+ *
632
+ * Use `withResponse` to indicate if the server should reply with a response.
633
+ * If `false`, the returned promise will complete immediately. Otherwise,
634
+ * it'll complete when a response is received.
635
+ *
636
+ * @param payload The payload of the request.
637
+ * @param withResponse Indicates if the server should reply with a response.
638
+ * Defaults to `true`.
639
+ */
640
+ createSceneAlteration(payload, withResponse = true) {
641
+ return this.sendRequest({ createSceneAlteration: payload }, withResponse);
642
+ }
643
+ /**
644
+ * Sends a request to reset all overrides for a given scene and optionally to
645
+ * reset the camera to that of the base scene.
646
+ *
647
+ * Use `withResponse` to indicate if the server should reply with a response.
648
+ * If `false`, the returned promise will complete immediately. Otherwise,
649
+ * it'll complete when a response is received.
650
+ *
651
+ * @param payload The payload of the request.
652
+ * @param withResponse Indicates if the server should reply with a response.
653
+ * Defaults to `true`.
654
+ */
655
+ resetSceneView(payload, withResponse = true) {
656
+ return this.sendRequest({ resetView: payload }, withResponse);
657
+ }
658
+ /**
659
+ * Sends a request to tell the rendering pipeline that an interaction has
660
+ * ended.
661
+ *
662
+ * Use `withResponse` to indicate if the server should reply with a response.
663
+ * If `false`, the returned promise will complete immediately. Otherwise,
664
+ * it'll complete when a response is received.
665
+ *
666
+ * @param withResponse Indicates if the server should reply with a response.
667
+ * Defaults to `true`.
668
+ */
669
+ endInteraction(payload = {}, withResponse = true) {
670
+ return this.sendRequest({ endInteraction: payload }, withResponse);
671
+ }
672
+ /**
673
+ * Sends a request to sync the clocks between the client and server.
674
+ *
675
+ * Use `withResponse` to indicate if the server should reply with a response.
676
+ * If `false`, the returned promise will complete immediately. Otherwise,
677
+ * it'll complete when a response is received.
678
+ *
679
+ * @param payload The request payload.
680
+ * @param withResponse Indicates if the server should reply with a response.
681
+ * Defaults to `true`.
682
+ */
683
+ syncTime(payload, withResponse = true) {
684
+ return this.sendRequest({ syncTime: payload }, withResponse);
685
+ }
686
+ /**
687
+ * Sends a request to record performance timings that were measured in the
688
+ * client. The server may use these timings as hints to optimize the rendering
689
+ * performance to provide a better experience.
690
+ *
691
+ * Use `withResponse` to indicate if the server should reply with a response.
692
+ * If `false`, the returned promise will complete immediately. Otherwise,
693
+ * it'll complete when a response is received.
694
+ *
695
+ * @param payload The request payload
696
+ * @param withResponse Indicates if the server should reply with a response.
697
+ * Defaults to `true`.
698
+ */
699
+ recordPerformance(payload, withResponse = true) {
700
+ return this.sendRequest({ recordPerformance: payload }, withResponse);
701
+ }
702
+ /**
703
+ * Sends a request to update the current scene view with the state present
704
+ * in the specified scene view state.
705
+ *
706
+ * Use `withResponse` to indicate if the server should reply with a response.
707
+ * If `false`, the returned promise will complete immediately. Otherwise,
708
+ * it'll complete when a response is received.
709
+ *
710
+ * @param payload
711
+ * @param withResponse
712
+ */
713
+ loadSceneViewState(payload, withResponse = true) {
714
+ return this.sendRequest({ loadSceneViewState: payload }, withResponse);
715
+ }
716
+ /**
717
+ * Sends a request to get a stencil buffer image for the current scene view.
718
+ */
719
+ getStencilBuffer(payload, withResponse = true) {
720
+ return this.sendRequest({ getStencilBuffer: payload }, withResponse);
721
+ }
722
+ /**
723
+ * Sends a request to retrieve a new token. This token can be used to
724
+ * authenticate with other Vertex services.
725
+ *
726
+ * @param withResponse Indicates if the server should reply with a response.
727
+ * @returns A promise that completes with the refreshed token.
728
+ */
729
+ refreshToken(withResponse = true) {
730
+ return this.sendRequest({ refreshToken: {} }, withResponse);
731
+ }
732
+ /**
733
+ * Acknowledges a successful request by sending a reply back to the server
734
+ * with an optional result body.
735
+ *
736
+ * @param reqId The ID of the received request.
737
+ * @param result A result to reply with.
738
+ */
739
+ replyResult(reqId, result) {
740
+ this.sendResponse({ requestId: { value: reqId }, ...result });
741
+ }
742
+ /**
743
+ * Acknowledges a failed request by sending a reply back to the server.
744
+ *
745
+ * @param reqId The ID of the received request.
746
+ * @param error An error to reply with.
747
+ */
748
+ replyError(reqId, error) {
749
+ this.sendResponse({ requestId: { value: reqId }, error });
750
+ }
751
+ handleMessage(message) {
752
+ const msg = decode(message.data);
753
+ this.log('WS message received', msg);
754
+ if ((msg === null || msg === void 0 ? void 0 : msg.sentAtTime) != null) {
755
+ if (msg.response != null) {
756
+ this.onResponseDispatcher.emit({
757
+ sentAtTime: msg.sentAtTime,
758
+ response: msg.response,
759
+ });
760
+ }
761
+ if (msg.request != null) {
762
+ this.onRequestDispatcher.emit({
763
+ sentAtTime: msg.sentAtTime,
764
+ request: msg.request,
765
+ });
766
+ }
767
+ if (msg.event != null) {
768
+ this.onEventDispatcher.emit({
769
+ sentAtTime: msg.sentAtTime,
770
+ event: msg.event,
771
+ });
772
+ }
773
+ }
774
+ }
775
+ onResponse(handler) {
776
+ return this.onResponseDispatcher.on(handler);
777
+ }
778
+ log(msg, ...other) {
779
+ if (this.opts.loggingEnabled) {
780
+ console.debug(msg, ...other);
781
+ }
782
+ }
783
+ sendRequest(req, withResponse) {
784
+ const sentAtTime = currentDateAsProtoTimestamp();
785
+ if (withResponse) {
786
+ const requestId = utils.UUID.create();
787
+ const request = { ...req, requestId: { value: requestId } };
788
+ return new Promise((resolve, reject) => {
789
+ const subscription = this.onResponse((msg) => {
790
+ var _a;
791
+ if (requestId === ((_a = msg.response.requestId) === null || _a === void 0 ? void 0 : _a.value)) {
792
+ if (msg.response.error == null) {
793
+ resolve(msg.response);
794
+ }
795
+ else {
796
+ const { message: summary, details } = msg.response.error;
797
+ reject(new StreamRequestError(requestId, req, summary === null || summary === void 0 ? void 0 : summary.value, details === null || details === void 0 ? void 0 : details.value));
798
+ }
799
+ subscription.dispose();
800
+ }
801
+ });
802
+ this.sendMessage({ sentAtTime, request });
803
+ });
804
+ }
805
+ this.sendMessage({ sentAtTime, request: req });
806
+ return Promise.resolve({});
807
+ }
808
+ sendMessage(msg) {
809
+ this.websocket.send(encode(msg));
810
+ this.log('WS message sent', msg);
811
+ }
812
+ sendResponse(response) {
813
+ const sentAtTime = currentDateAsProtoTimestamp();
814
+ this.websocket.send(encode({ sentAtTime, response }));
815
+ }
816
+ }
817
+
818
+ function event(req, meta) {
819
+ return {
820
+ sentAtTime: currentDateAsProtoTimestamp(),
821
+ event: {
822
+ ...req,
823
+ },
824
+ ...meta,
825
+ };
826
+ }
827
+ function animationCompleted(id) {
828
+ const def = {
829
+ animationId: { hex: id },
830
+ };
831
+ return event({
832
+ animationCompleted: {
833
+ ...def,
834
+ },
835
+ });
836
+ }
837
+
838
+ var events = /*#__PURE__*/Object.freeze({
839
+ __proto__: null,
840
+ animationCompleted: animationCompleted
841
+ });
842
+
843
+ function request(req, meta) {
844
+ return {
845
+ sentAtTime: currentDateAsProtoTimestamp(),
846
+ ...meta,
847
+ request: {
848
+ requestId: req.requestId != null ? { value: req.requestId } : undefined,
849
+ ...req.payload,
850
+ },
851
+ };
852
+ }
853
+ function drawFrame(req = {}, meta) {
854
+ const def = {
855
+ sequenceNumber: 1,
856
+ sceneAttributes: {
857
+ camera: {
858
+ position: { x: 0, y: 0, z: 1 },
859
+ lookAt: { x: 0, y: 0, z: 0 },
860
+ up: { x: 0, y: 1, z: 0 },
861
+ },
862
+ visibleBoundingBox: {
863
+ xmin: -1,
864
+ ymin: -1,
865
+ zmin: -1,
866
+ xmax: 1,
867
+ ymax: 1,
868
+ zmax: 1,
869
+ },
870
+ crossSectioning: {
871
+ sectionPlanes: [],
872
+ },
873
+ displayListSummary: {
874
+ visibleSummary: {
875
+ count: 100,
876
+ },
877
+ selectedVisibleSummary: {
878
+ count: 0,
879
+ },
880
+ },
881
+ },
882
+ imageAttributes: {
883
+ frameDimensions: { width: 200, height: 150 },
884
+ imageRect: { x: 0, y: 0, width: 200, height: 150 },
885
+ scaleFactor: 1,
886
+ },
887
+ frameCorrelationIds: ['123'],
888
+ image: new Uint8Array(),
889
+ };
890
+ return request({
891
+ requestId: req.requestId,
892
+ payload: { drawFrame: utils.Objects.defaults(req.payload, def) },
893
+ }, meta);
894
+ }
895
+ function gracefulReconnect(req = {}, meta) {
896
+ const def = {
897
+ streamId: { hex: utils.UUID.create() },
898
+ timeToReconnectDuration: { seconds: 1, nanos: 0 },
899
+ };
900
+ return request({
901
+ requestId: req.requestId,
902
+ payload: { gracefulReconnection: utils.Objects.defaults(req.payload, def) },
903
+ }, meta);
904
+ }
905
+
906
+ var requests = /*#__PURE__*/Object.freeze({
907
+ __proto__: null,
908
+ drawFrame: drawFrame,
909
+ gracefulReconnect: gracefulReconnect
910
+ });
911
+
912
+ function response(res, meta) {
913
+ return {
914
+ sentAtTime: currentDateAsProtoTimestamp(),
915
+ ...meta,
916
+ response: {
917
+ requestId: res.requestId != null ? { value: res.requestId } : undefined,
918
+ ...res.result,
919
+ },
920
+ };
921
+ }
922
+ function startStream(res = {}, meta) {
923
+ const def = {
924
+ streamId: { hex: utils.UUID.create() },
925
+ sceneId: { hex: utils.UUID.create() },
926
+ sceneViewId: { hex: utils.UUID.create() },
927
+ sessionId: { hex: utils.UUID.create() },
928
+ jwt: 'jwt',
929
+ token: { token: 'token', expiresIn: new Date().getTime() + 10000 },
930
+ worldOrientation: {
931
+ front: { x: 0, y: 0, z: 1 },
932
+ up: { x: 0, y: 1, z: 0 },
933
+ },
934
+ };
935
+ return response({
936
+ requestId: res.requestId,
937
+ result: { startStream: utils.Objects.defaults(res.result, def) },
938
+ }, meta);
939
+ }
940
+ function syncTime(res = {}, meta) {
941
+ const def = { replyTime: { seconds: 1, nanos: 0 } };
942
+ return response({
943
+ requestId: res.requestId,
944
+ result: { syncTime: utils.Objects.defaults(res.result, def) },
945
+ });
946
+ }
947
+ function reconnect(res = {}, meta) {
948
+ const def = {
949
+ jwt: 'jwt',
950
+ token: { token: 'token', expiresIn: new Date().getTime() + 10000 },
951
+ };
952
+ return response({
953
+ requestId: res.requestId,
954
+ result: { reconnect: utils.Objects.defaults(res.result, def) },
955
+ }, meta);
956
+ }
957
+ function loadSceneViewState(res = {}, meta) {
958
+ const def = {};
959
+ return response({
960
+ requestId: res.requestId,
961
+ result: { loadSceneViewState: utils.Objects.defaults(res.result, def) },
962
+ }, meta);
963
+ }
964
+ function refreshToken(res = {}, meta) {
965
+ const def = {
966
+ token: { token: 'token', expiresIn: new Date().getTime() + 10000 },
967
+ };
968
+ return response({
969
+ requestId: res.requestId,
970
+ result: { refreshToken: utils.Objects.defaults(res.result, def) },
971
+ }, meta);
972
+ }
973
+
974
+ var responses = /*#__PURE__*/Object.freeze({
975
+ __proto__: null,
976
+ startStream: startStream,
977
+ syncTime: syncTime,
978
+ reconnect: reconnect,
979
+ loadSceneViewState: loadSceneViewState,
980
+ refreshToken: refreshToken
981
+ });
982
+
983
+ var index = /*#__PURE__*/Object.freeze({
984
+ __proto__: null,
985
+ Events: events,
986
+ Requests: requests,
987
+ Responses: responses
988
+ });
989
+
990
+ class WebSocketClientMock {
991
+ constructor() {
992
+ this.closeHandlers = new Set();
993
+ this.msgHandlers = new Set();
994
+ this.sentMessages = new Array();
995
+ }
996
+ close() {
997
+ this.closeHandlers.forEach((handler) => handler(new CloseEvent('close')));
998
+ }
999
+ connect(descriptor) {
1000
+ return Promise.resolve();
1001
+ }
1002
+ onClose(handler) {
1003
+ this.closeHandlers.add(handler);
1004
+ return { dispose: () => this.closeHandlers.delete(handler) };
1005
+ }
1006
+ onMessage(handler) {
1007
+ this.msgHandlers.add(handler);
1008
+ return { dispose: () => this.msgHandlers.delete(handler) };
1009
+ }
1010
+ send(data) {
1011
+ this.sentMessages.push(data);
1012
+ }
1013
+ reconnect(descriptor) {
1014
+ return Promise.resolve();
1015
+ }
1016
+ /**
1017
+ * Simulates a websocket message that was sent by the server and received by
1018
+ * the client.
1019
+ *
1020
+ * @param data The websocket message data.
1021
+ */
1022
+ receiveMessage(data) {
1023
+ this.msgHandlers.forEach((handler) => handler(new MessageEvent('message', { data })));
1024
+ }
1025
+ /**
1026
+ * Resets the internal state of the mock by clearing any accrued sent messages
1027
+ * and removing all handlers.
1028
+ */
1029
+ reset() {
1030
+ this.sentMessages = [];
1031
+ this.msgHandlers.clear();
1032
+ this.closeHandlers.clear();
1033
+ }
1034
+ /**
1035
+ * Returns `true` if there are remaining sent messages..
1036
+ */
1037
+ hasNextSent() {
1038
+ return this.sentMessages.length > 0;
1039
+ }
1040
+ nextSent(decoder) {
1041
+ const next = this.sentMessages.shift();
1042
+ if (next != null) {
1043
+ if (decoder != null) {
1044
+ return decoder(next);
1045
+ }
1046
+ else {
1047
+ return next;
1048
+ }
1049
+ }
1050
+ else {
1051
+ throw new Error('Sent messages is empty');
1052
+ }
1053
+ }
1054
+ /**
1055
+ * Skips the next N sent messages.
1056
+ *
1057
+ * @example
1058
+ * ```
1059
+ * const ws = new WebSocketClientMock();
1060
+ * ws.send("1");
1061
+ * ws.send("2");
1062
+ * ws.send("3");
1063
+ *
1064
+ * ws.skipSent(2);
1065
+ * ws.nextSent(); // "3"
1066
+ * ```
1067
+ *
1068
+ * @param n The number of sent messages to skip over.
1069
+ */
1070
+ skipSent(n = 1) {
1071
+ if (n <= this.sentMessages.length) {
1072
+ for (let i = 0; i < n; i++) {
1073
+ this.nextSent();
1074
+ }
1075
+ }
1076
+ else {
1077
+ throw new Error(`Cannot skip the next ${n} messages. Sent message queue only has ${this.sentMessages.length} messages.`);
1078
+ }
1079
+ return this;
1080
+ }
1081
+ }
1082
+
1083
+ exports.Fixtures = index;
1084
+ exports.StreamApi = StreamApi;
1085
+ exports.StreamRequestError = StreamRequestError;
1086
+ exports.WebSocketClientImpl = WebSocketClientImpl;
1087
+ exports.WebSocketClientMock = WebSocketClientMock;
1088
+ exports.appendSettingsToUrl = appendSettingsToUrl;
1089
+ exports.currentDateAsProtoTimestamp = currentDateAsProtoTimestamp;
1090
+ exports.decode = decode;
1091
+ exports.encode = encode;
1092
+ exports.protoToDate = protoToDate;
1093
+ exports.toProtoDuration = toProtoDuration;
1094
+ exports.toProtoTimestamp = toProtoTimestamp;
1095
+ //# sourceMappingURL=bundle.cjs.map