@vuu-ui/vuu-data-remote 2.1.19-beta.1 → 2.1.19-beta.2

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,591 @@
1
+ import { KeySet, RangeMonitor, getFullRange, logger } from "@vuu-ui/vuu-utils";
2
+ import { getFirstAndLastRows } from "../message-utils.js";
3
+ import { ArrayBackedMovingWindow } from "./array-backed-moving-window.js";
4
+ import * as __rspack_external__messages_js_85065717 from "./messages.js";
5
+ const { debug: debug, debugEnabled: debugEnabled, error: error, info: info, infoEnabled: infoEnabled, warn: warn } = logger("Viewport");
6
+ const isLeafUpdate = ({ rowKey, updateType })=>"U" === updateType && !rowKey.startsWith("$root");
7
+ const NO_DATA_UPDATE = [
8
+ void 0,
9
+ void 0
10
+ ];
11
+ const NO_UPDATE_STATUS = {
12
+ count: 0,
13
+ mode: void 0,
14
+ size: 0,
15
+ ts: 0
16
+ };
17
+ class Viewport {
18
+ #status = "";
19
+ aggregations;
20
+ batchMode = false;
21
+ bufferSize;
22
+ #clientRange;
23
+ columns;
24
+ dataWindow;
25
+ filter;
26
+ groupBy;
27
+ sort;
28
+ hasUpdates = false;
29
+ pendingUpdates = [];
30
+ keys;
31
+ pendingLinkedParent;
32
+ pendingOperations = new Map();
33
+ pendingRangeRequests = [];
34
+ postMessageToClient;
35
+ rowCountChanged = false;
36
+ lastUpdateStatus = NO_UPDATE_STATUS;
37
+ updateThrottleTimer = void 0;
38
+ rangeMonitor = new RangeMonitor("ViewPort");
39
+ clientViewportId;
40
+ disabled = false;
41
+ disabledActive = false;
42
+ frozen = false;
43
+ isTree = false;
44
+ links;
45
+ linkedParent;
46
+ serverViewportId;
47
+ suspended = false;
48
+ suspendTimer = null;
49
+ table;
50
+ title;
51
+ constructor({ aggregations, bufferSize = 50, columns, filterSpec: filter, groupBy = [], table, range, sort, title, viewport, visualLink }, postMessageToClient){
52
+ this.aggregations = aggregations;
53
+ this.bufferSize = bufferSize;
54
+ this.#clientRange = range;
55
+ this.clientViewportId = viewport;
56
+ this.columns = columns;
57
+ this.filter = filter;
58
+ this.groupBy = groupBy;
59
+ this.keys = new KeySet(range);
60
+ this.pendingLinkedParent = visualLink;
61
+ this.table = table;
62
+ this.sort = sort;
63
+ this.title = title;
64
+ infoEnabled && info?.(`${table.table} #${viewport}, bufferSize=${bufferSize}`);
65
+ this.dataWindow = new ArrayBackedMovingWindow(this.#clientRange, range, this.bufferSize);
66
+ this.postMessageToClient = postMessageToClient;
67
+ }
68
+ setLastSizeOnlyUpdateSize = (size)=>{
69
+ this.lastUpdateStatus.size = size;
70
+ };
71
+ setLastUpdate = (mode)=>{
72
+ const { ts: lastTS, mode: lastMode } = this.lastUpdateStatus;
73
+ let elapsedTime = 0;
74
+ if (lastMode === mode) {
75
+ const ts = Date.now();
76
+ this.lastUpdateStatus.count += 1;
77
+ this.lastUpdateStatus.ts = ts;
78
+ elapsedTime = 0 === lastTS ? 0 : ts - lastTS;
79
+ } else {
80
+ this.lastUpdateStatus.count = 1;
81
+ this.lastUpdateStatus.ts = 0;
82
+ elapsedTime = 0;
83
+ }
84
+ this.lastUpdateStatus.mode = mode;
85
+ return elapsedTime;
86
+ };
87
+ get hasUpdatesToProcess() {
88
+ if (this.suspended) return false;
89
+ return this.rowCountChanged || this.hasUpdates;
90
+ }
91
+ get size() {
92
+ return this.dataWindow.rowCount ?? 0;
93
+ }
94
+ get clientRange() {
95
+ return this.#clientRange;
96
+ }
97
+ get status() {
98
+ return this.#status;
99
+ }
100
+ set status(status) {
101
+ this.#status = status;
102
+ }
103
+ subscribe() {
104
+ const { filter } = this.filter;
105
+ this.status = "subscribed" === this.#status ? "resubscribing" : "subscribing";
106
+ return {
107
+ type: __rspack_external__messages_js_85065717.CREATE_VP,
108
+ table: this.table,
109
+ range: getFullRange(this.#clientRange, this.bufferSize),
110
+ aggregations: this.aggregations,
111
+ columns: this.columns,
112
+ sort: this.sort,
113
+ groupBy: this.groupBy,
114
+ filterSpec: {
115
+ filter
116
+ }
117
+ };
118
+ }
119
+ handleSubscribed({ viewPortId, aggregations, columns, filterSpec: filter, range, sort, groupBy, table }, baseTableSchema) {
120
+ this.serverViewportId = viewPortId;
121
+ this.status = "subscribed";
122
+ this.aggregations = aggregations;
123
+ this.columns = columns;
124
+ this.groupBy = groupBy;
125
+ this.isTree = groupBy && groupBy.length > 0;
126
+ this.dataWindow.setRange(range.from, range.to);
127
+ const tableSchema = table === baseTableSchema.table.table ? baseTableSchema : {
128
+ ...baseTableSchema,
129
+ table: {
130
+ ...baseTableSchema.table,
131
+ session: table
132
+ }
133
+ };
134
+ return {
135
+ aggregations,
136
+ type: "subscribed",
137
+ clientViewportId: this.clientViewportId,
138
+ columns,
139
+ filterSpec: filter,
140
+ groupBy,
141
+ range,
142
+ sort,
143
+ tableSchema
144
+ };
145
+ }
146
+ awaitOperation(requestId, msg) {
147
+ this.pendingOperations.set(requestId, msg);
148
+ }
149
+ completeOperation(requestId, ...params) {
150
+ const { clientViewportId, pendingOperations } = this;
151
+ const pendingOperation = pendingOperations.get(requestId);
152
+ if (!pendingOperation) return void error(`no matching operation found to complete for requestId ${requestId}`);
153
+ const { type } = pendingOperation;
154
+ info?.(`completeOperation ${type}`);
155
+ pendingOperations.delete(requestId);
156
+ if ("CHANGE_VP_RANGE" === type) {
157
+ const [from, to] = params;
158
+ infoEnabled && info(`completeOperation CHANGE_VP_RANGE
159
+ window setRange (${from}:${to}) ${this.pendingRangeRequests.length} range requests pending`);
160
+ this.dataWindow?.setRange(from, to);
161
+ for(let i = this.pendingRangeRequests.length - 1; i >= 0; i--){
162
+ const pendingRangeRequest = this.pendingRangeRequests[i];
163
+ if (pendingRangeRequest.requestId === requestId) {
164
+ pendingRangeRequest.acked = true;
165
+ break;
166
+ }
167
+ warn?.("range requests sent faster than they are being ACKed");
168
+ }
169
+ } else if ("config" === type) {
170
+ const { aggregations, columns, filterSpec: filter, groupBy, sort } = pendingOperation.data;
171
+ this.aggregations = aggregations;
172
+ this.columns = columns;
173
+ this.filter = filter;
174
+ this.groupBy = groupBy;
175
+ this.sort = sort;
176
+ if (groupBy.length > 0) this.isTree = true;
177
+ else if (this.isTree) this.isTree = false;
178
+ debug?.(`config change confirmed, isTree : ${this.isTree}`);
179
+ return {
180
+ clientViewportId,
181
+ type,
182
+ config: pendingOperation.data
183
+ };
184
+ } else if ("selection" === type) ;
185
+ else if ("disable" === type) {
186
+ this.suspended = false;
187
+ this.disabled = true;
188
+ this.disabledActive = pendingOperation.disableActive;
189
+ return {
190
+ type: "disabled",
191
+ clientViewportId
192
+ };
193
+ } else if ("enable" === type) {
194
+ this.disabled = false;
195
+ this.disabledActive = false;
196
+ return {
197
+ type: "enabled",
198
+ clientViewportId
199
+ };
200
+ } else if ("freeze" === type) {
201
+ this.frozen = true;
202
+ return {
203
+ type: "frozen",
204
+ clientViewportId
205
+ };
206
+ } else if ("unfreeze" === type) {
207
+ this.frozen = false;
208
+ return {
209
+ type: "unfrozen",
210
+ clientViewportId
211
+ };
212
+ } else if ("CREATE_VISUAL_LINK" === type) {
213
+ const [colName, parentViewportId, parentColName] = params;
214
+ this.linkedParent = {
215
+ colName,
216
+ parentViewportId,
217
+ parentColName
218
+ };
219
+ this.pendingLinkedParent = void 0;
220
+ return {
221
+ requestId,
222
+ type: "vuu-link-created",
223
+ clientViewportId,
224
+ colName,
225
+ parentViewportId,
226
+ parentColName
227
+ };
228
+ } else if ("REMOVE_VISUAL_LINK" === type) {
229
+ this.linkedParent = void 0;
230
+ return {
231
+ requestId,
232
+ type: "vuu-link-removed",
233
+ clientViewportId
234
+ };
235
+ }
236
+ }
237
+ rangeRequest(requestId, range) {
238
+ if (debugEnabled) this.rangeMonitor.set(range);
239
+ infoEnabled && info(`(bufferSize ${this.bufferSize}) rangeRequest (${range.from}:${range.to}) current: window client (${this.dataWindow.clientRange.from}:${this.dataWindow.clientRange.to}), full (${this.dataWindow.range.from}:${this.dataWindow.range.to}) `);
240
+ const type = "CHANGE_VP_RANGE";
241
+ if (!this.dataWindow) return [
242
+ null
243
+ ];
244
+ {
245
+ const [serverDataRequired, clientRows] = this.dataWindow.setClientRange(range.from, range.to);
246
+ infoEnabled && info(`updated: dataWindow clientRange (${this.dataWindow.clientRange.from}:${this.dataWindow.clientRange.to}), fullRange (${this.dataWindow.range.from}:${this.dataWindow.range.to}) serverDataRequired ${serverDataRequired ? "Y" : "N"} ${clientRows.length} rows returned from local buffer`);
247
+ let debounceRequest;
248
+ const maxRange = this.dataWindow.rowCount || void 0;
249
+ const serverRequest = serverDataRequired && !this.rangeRequestAlreadyPending(range) ? {
250
+ type,
251
+ viewPortId: this.serverViewportId,
252
+ ...getFullRange(range, this.bufferSize, maxRange)
253
+ } : null;
254
+ if (serverRequest) {
255
+ infoEnabled && info(`create CHANGE_VP_RANGE: (${serverRequest.from} - ${serverRequest.to})`);
256
+ debugEnabled && debug?.(`create CHANGE_VP_RANGE: [${serverRequest.from} - ${serverRequest.to}]`);
257
+ this.awaitOperation(requestId, {
258
+ type
259
+ });
260
+ const pendingRequest = this.pendingRangeRequests.at(-1);
261
+ if (pendingRequest) if (pendingRequest.acked) console.warn("Range Request before previous request is filled");
262
+ else {
263
+ const { from, to } = pendingRequest;
264
+ if (this.dataWindow.outOfRange(from, to)) debounceRequest = {
265
+ clientViewportId: this.clientViewportId,
266
+ type: "debounce-begin"
267
+ };
268
+ else warn?.("Range Request before previous request is acked");
269
+ }
270
+ this.pendingRangeRequests.push({
271
+ ...serverRequest,
272
+ requestId
273
+ });
274
+ } else if (clientRows.length > 0) this.batchMode = false;
275
+ this.keys.reset(this.dataWindow.clientRange);
276
+ const toClient = this.isTree ? toClientRowTree : toClientRow;
277
+ if (clientRows.length) return [
278
+ serverRequest,
279
+ clientRows.map((row)=>toClient(row, this.keys))
280
+ ];
281
+ if (debounceRequest) return [
282
+ serverRequest,
283
+ void 0,
284
+ debounceRequest
285
+ ];
286
+ return [
287
+ serverRequest
288
+ ];
289
+ }
290
+ }
291
+ setLinks(links) {
292
+ this.links = links.filter((link)=>link.parentVpId !== this.serverViewportId);
293
+ return [
294
+ {
295
+ type: "vuu-links",
296
+ links: this.links,
297
+ clientViewportId: this.clientViewportId
298
+ },
299
+ this.pendingLinkedParent
300
+ ];
301
+ }
302
+ setMenu(menu) {
303
+ return {
304
+ type: "vuu-menu",
305
+ menu,
306
+ clientViewportId: this.clientViewportId
307
+ };
308
+ }
309
+ openTreeNode(requestId, message) {
310
+ const treeKey = void 0 === message.index ? message.key : this.getKeyForRowAtIndex(message.index);
311
+ infoEnabled && info(`treeKey ${treeKey}`);
312
+ return {
313
+ type: __rspack_external__messages_js_85065717.OPEN_TREE_NODE,
314
+ vpId: this.serverViewportId,
315
+ treeKey
316
+ };
317
+ }
318
+ closeTreeNode(requestId, message) {
319
+ const treeKey = void 0 === message.index ? message.key : this.getKeyForRowAtIndex(message.index);
320
+ return {
321
+ type: __rspack_external__messages_js_85065717.CLOSE_TREE_NODE,
322
+ vpId: this.serverViewportId,
323
+ treeKey
324
+ };
325
+ }
326
+ createLink(requestId, vuuCreateVisualLink) {
327
+ const message = {
328
+ ...vuuCreateVisualLink,
329
+ childVpId: this.serverViewportId
330
+ };
331
+ this.awaitOperation(requestId, message);
332
+ return message;
333
+ }
334
+ removeLink(requestId) {
335
+ const message = {
336
+ type: "REMOVE_VISUAL_LINK",
337
+ childVpId: this.serverViewportId
338
+ };
339
+ this.awaitOperation(requestId, message);
340
+ return message;
341
+ }
342
+ suspend() {
343
+ this.suspended = true;
344
+ this.pendingUpdates.length = 0;
345
+ info?.("suspend");
346
+ }
347
+ resume() {
348
+ this.suspended = false;
349
+ if (debugEnabled) debug?.(`resume: ${this.currentData()}`);
350
+ return [
351
+ this.size,
352
+ this.currentData()
353
+ ];
354
+ }
355
+ currentData() {
356
+ const out = [];
357
+ if (this.dataWindow) {
358
+ const records = this.dataWindow.getData();
359
+ const { keys } = this;
360
+ const toClient = this.isTree ? toClientRowTree : toClientRow;
361
+ for (const row of records)if (row) out.push(toClient(row, keys));
362
+ }
363
+ return out;
364
+ }
365
+ enable(requestId) {
366
+ this.awaitOperation(requestId, {
367
+ type: "enable"
368
+ });
369
+ info?.(`enable: ${this.serverViewportId}`);
370
+ return {
371
+ type: __rspack_external__messages_js_85065717.ENABLE_VP,
372
+ viewPortId: this.serverViewportId
373
+ };
374
+ }
375
+ disable(requestId, disableActive = false) {
376
+ this.awaitOperation(requestId, {
377
+ type: "disable",
378
+ disableActive
379
+ });
380
+ info?.(`disable: ${this.serverViewportId}`);
381
+ return {
382
+ type: __rspack_external__messages_js_85065717.DISABLE_VP,
383
+ viewPortId: this.serverViewportId
384
+ };
385
+ }
386
+ freeze(requestId) {
387
+ this.awaitOperation(requestId, {
388
+ type: "freeze"
389
+ });
390
+ info?.(`freeze: ${this.serverViewportId}`);
391
+ return {
392
+ type: "FREEZE_VP",
393
+ viewPortId: this.serverViewportId
394
+ };
395
+ }
396
+ unfreeze(requestId) {
397
+ this.awaitOperation(requestId, {
398
+ type: "unfreeze"
399
+ });
400
+ info?.(`unfreeze: ${this.serverViewportId}`);
401
+ this.frozen = false;
402
+ return {
403
+ type: "UNFREEZE_VP",
404
+ viewPortId: this.serverViewportId
405
+ };
406
+ }
407
+ setConfig(requestId, config) {
408
+ this.awaitOperation(requestId, {
409
+ type: "config",
410
+ data: config
411
+ });
412
+ const { filterSpec: filter, ...remainingConfig } = config;
413
+ debugEnabled ? debug?.(`setConfig ${JSON.stringify(config)}`) : info?.("setConfig");
414
+ if (!this.isTree && config.groupBy.length > 0) this.dataWindow?.clear();
415
+ return this.createRequest({
416
+ ...remainingConfig,
417
+ filterSpec: "string" == typeof filter?.filter ? {
418
+ filter: filter.filter
419
+ } : {
420
+ filter: ""
421
+ }
422
+ }, true);
423
+ }
424
+ selectRequest(request) {
425
+ info?.(`selectRequest: ${request.type}`);
426
+ if (this.serverViewportId) return {
427
+ ...request,
428
+ vpId: this.serverViewportId
429
+ };
430
+ throw Error(`[Viewport] cannot process ${request.type} before serverViewportId has been set`);
431
+ }
432
+ removePendingRangeRequest(firstIndex, lastIndex) {
433
+ for(let i = this.pendingRangeRequests.length - 1; i >= 0; i--){
434
+ const { from, to } = this.pendingRangeRequests[i];
435
+ let isLast = true;
436
+ if (firstIndex >= from && firstIndex < to || lastIndex > from && lastIndex < to) {
437
+ if (!isLast) console.warn("removePendingRangeRequest TABLE_ROWS are not for latest request");
438
+ this.pendingRangeRequests.splice(i, 1);
439
+ break;
440
+ }
441
+ isLast = false;
442
+ }
443
+ }
444
+ rangeRequestAlreadyPending = (range)=>{
445
+ const { bufferSize } = this;
446
+ const bufferThreshold = 0.25 * bufferSize;
447
+ let { from: stillPendingFrom } = range;
448
+ for (const { from, to } of this.pendingRangeRequests)if (stillPendingFrom >= from && stillPendingFrom < to) if (range.to + bufferThreshold <= to) return true;
449
+ else stillPendingFrom = to;
450
+ return false;
451
+ };
452
+ clearCache() {
453
+ this.dataWindow.setRowCount(0);
454
+ this.postMessageToClient({
455
+ clientViewportId: this.clientViewportId,
456
+ type: "viewport-clear"
457
+ });
458
+ }
459
+ updateRows(rows) {
460
+ const [firstRow, lastRow] = getFirstAndLastRows(rows);
461
+ if (firstRow && lastRow) this.removePendingRangeRequest(firstRow.rowIndex, lastRow.rowIndex);
462
+ if (1 === rows.length) {
463
+ if (0 === firstRow.vpSize && this.disabled) return void debug?.(`ignore a SIZE=0 message on disabled viewport (${rows.length} rows)`);
464
+ }
465
+ for (const row of rows)if (this.isTree && isLeafUpdate(row)) continue;
466
+ else {
467
+ if ("SIZE" === row.updateType || this.dataWindow?.rowCount !== row.vpSize) {
468
+ this.dataWindow?.setRowCount(row.vpSize);
469
+ this.rowCountChanged = true;
470
+ }
471
+ if ("U" === row.updateType) {
472
+ if (this.dataWindow?.setAtIndex(row)) {
473
+ if (true !== this.suspended) {
474
+ this.hasUpdates = true;
475
+ if (!this.batchMode) this.pendingUpdates.push(row);
476
+ }
477
+ }
478
+ }
479
+ }
480
+ }
481
+ getKeyForRowAtIndex(rowIndex) {
482
+ const row = this.dataWindow.getAtIndex(rowIndex);
483
+ return row?.rowKey;
484
+ }
485
+ getClientRows() {
486
+ let out;
487
+ let mode = "size-only";
488
+ if (!this.hasUpdates && !this.rowCountChanged) return NO_DATA_UPDATE;
489
+ if (this.hasUpdates) {
490
+ const { keys } = this;
491
+ const toClient = this.isTree ? toClientRowTree : toClientRow;
492
+ if (this.updateThrottleTimer) {
493
+ self.clearTimeout(this.updateThrottleTimer);
494
+ this.updateThrottleTimer = void 0;
495
+ }
496
+ if (this.pendingUpdates.length > 0) {
497
+ out = [];
498
+ mode = "update";
499
+ for (const row of this.pendingUpdates)out.push(toClient(row, keys));
500
+ }
501
+ this.pendingUpdates.length = 0;
502
+ this.hasUpdates = false;
503
+ }
504
+ if (this.throttleMessage(mode)) return NO_DATA_UPDATE;
505
+ return [
506
+ out,
507
+ mode
508
+ ];
509
+ }
510
+ sendThrottledSizeMessage = ()=>{
511
+ this.updateThrottleTimer = void 0;
512
+ this.lastUpdateStatus.count = 3;
513
+ this.postMessageToClient({
514
+ clientViewportId: this.clientViewportId,
515
+ mode: "size-only",
516
+ size: this.lastUpdateStatus.size,
517
+ type: "viewport-update"
518
+ });
519
+ };
520
+ shouldThrottleMessage = (mode)=>{
521
+ const elapsedTime = this.setLastUpdate(mode);
522
+ return "size-only" === mode && elapsedTime > 0 && elapsedTime < 500 && this.lastUpdateStatus.count > 3;
523
+ };
524
+ throttleMessage = (mode)=>{
525
+ if (this.shouldThrottleMessage(mode)) {
526
+ info?.("[Viewport] throttling updates setTimeout to 300");
527
+ this.setLastSizeOnlyUpdateSize(this.dataWindow.rowCount);
528
+ if (void 0 === this.updateThrottleTimer) this.updateThrottleTimer = setTimeout(this.sendThrottledSizeMessage, 100);
529
+ return true;
530
+ }
531
+ if (void 0 !== this.updateThrottleTimer) {
532
+ clearTimeout(this.updateThrottleTimer);
533
+ this.updateThrottleTimer = void 0;
534
+ }
535
+ return false;
536
+ };
537
+ getNewRowCount = ()=>{
538
+ if (this.rowCountChanged && this.dataWindow) {
539
+ this.rowCountChanged = false;
540
+ return this.dataWindow.rowCount;
541
+ }
542
+ };
543
+ createRequest(params, overWrite = false) {
544
+ if (overWrite) return {
545
+ type: "CHANGE_VP",
546
+ viewPortId: this.serverViewportId,
547
+ ...params
548
+ };
549
+ return {
550
+ type: "CHANGE_VP",
551
+ viewPortId: this.serverViewportId,
552
+ aggregations: this.aggregations,
553
+ columns: this.columns,
554
+ sort: this.sort,
555
+ groupBy: this.groupBy,
556
+ filterSpec: {
557
+ filter: this.filter.filter
558
+ },
559
+ ...params
560
+ };
561
+ }
562
+ }
563
+ const isNew = false;
564
+ const toClientRow = ({ rowIndex, rowKey, sel: isSelected, data, ts }, keys)=>[
565
+ rowIndex,
566
+ keys.keyFor(rowIndex),
567
+ true,
568
+ false,
569
+ 0,
570
+ 0,
571
+ rowKey,
572
+ isSelected,
573
+ ts,
574
+ isNew
575
+ ].concat(data);
576
+ const toClientRowTree = ({ rowIndex, rowKey, sel: isSelected, data, ts }, keys)=>{
577
+ const [depth, isExpanded, , isLeaf, , count, ...rest] = data;
578
+ return [
579
+ rowIndex,
580
+ keys.keyFor(rowIndex),
581
+ isLeaf,
582
+ isExpanded,
583
+ depth,
584
+ count,
585
+ rowKey,
586
+ isSelected,
587
+ ts,
588
+ isNew
589
+ ].concat(rest);
590
+ };
591
+ export { NO_DATA_UPDATE, Viewport };
package/src/worker.js ADDED
@@ -0,0 +1,65 @@
1
+ import { isConnectionQualityMetrics, logger } from "@vuu-ui/vuu-utils";
2
+ import { ServerProxy } from "./server-proxy/server-proxy.js";
3
+ import { WebSocketConnection } from "./WebSocketConnection.js";
4
+ let serverProxy;
5
+ let webSocketConnection;
6
+ const { info: info, infoEnabled: infoEnabled } = logger("worker");
7
+ const sendMessageToClient = (message)=>{
8
+ postMessage(message);
9
+ };
10
+ async function connectToServer(url, protocols, token) {
11
+ if (void 0 === webSocketConnection && void 0 === serverProxy) {
12
+ webSocketConnection = new WebSocketConnection({
13
+ callback: (msg)=>{
14
+ if (isConnectionQualityMetrics(msg)) postMessage({
15
+ type: "connection-metrics",
16
+ messages: msg
17
+ });
18
+ else serverProxy.handleMessageFromServer(msg);
19
+ },
20
+ protocols,
21
+ url
22
+ });
23
+ webSocketConnection.on("connection-status", postMessage);
24
+ serverProxy = new ServerProxy(webSocketConnection, sendMessageToClient);
25
+ }
26
+ await webSocketConnection.openWebSocket();
27
+ return serverProxy.login(token);
28
+ }
29
+ const handleMessageFromClient = async ({ data: message })=>{
30
+ switch(message.type){
31
+ case "connect":
32
+ try {
33
+ const sessionId = await connectToServer(message.url, message.protocol, message.token);
34
+ postMessage({
35
+ type: "connected",
36
+ sessionId
37
+ });
38
+ } catch (err) {
39
+ postMessage({
40
+ type: "connection-failed",
41
+ reason: String(err)
42
+ });
43
+ }
44
+ break;
45
+ case "disconnect":
46
+ serverProxy.disconnect();
47
+ webSocketConnection?.close();
48
+ break;
49
+ case "subscribe":
50
+ infoEnabled && info(`===> ${JSON.stringify(message)}`);
51
+ serverProxy.subscribe(message);
52
+ break;
53
+ case "unsubscribe":
54
+ infoEnabled && info(`===> ${JSON.stringify(message)}`);
55
+ serverProxy.unsubscribe(message.viewport);
56
+ break;
57
+ default:
58
+ infoEnabled && info(`===> ${JSON.stringify(message)}`);
59
+ serverProxy.handleMessageFromClient(message);
60
+ }
61
+ };
62
+ self.addEventListener("message", handleMessageFromClient);
63
+ postMessage({
64
+ type: "ready"
65
+ });