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