@affino/datagrid-worker 0.1.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,893 @@
1
+ import { DATAGRID_WORKER_ROW_MODEL_PAYLOAD_SCHEMA_VERSION, DATAGRID_WORKER_ROW_MODEL_PROTOCOL_VERSION, createDataGridWorkerRowModelCommandMessage, isDataGridWorkerRowModelUpdateMessage, } from "./workerOwnedRowModelProtocol.js";
2
+ function createDefaultSnapshot() {
3
+ return {
4
+ kind: "client",
5
+ rowCount: 0,
6
+ loading: false,
7
+ error: null,
8
+ viewportRange: { start: 0, end: 0 },
9
+ pagination: {
10
+ enabled: false,
11
+ pageSize: 0,
12
+ currentPage: 0,
13
+ pageCount: 0,
14
+ totalRowCount: 0,
15
+ startIndex: 0,
16
+ endIndex: 0,
17
+ },
18
+ sortModel: [],
19
+ filterModel: null,
20
+ groupBy: null,
21
+ pivotModel: null,
22
+ pivotColumns: [],
23
+ groupExpansion: {
24
+ expandedByDefault: false,
25
+ toggledGroupKeys: [],
26
+ },
27
+ };
28
+ }
29
+ function cloneRowNode(row) {
30
+ return {
31
+ ...row,
32
+ groupMeta: row.groupMeta
33
+ ? {
34
+ ...row.groupMeta,
35
+ aggregates: row.groupMeta.aggregates
36
+ ? { ...row.groupMeta.aggregates }
37
+ : undefined,
38
+ }
39
+ : undefined,
40
+ state: { ...row.state },
41
+ };
42
+ }
43
+ function cloneSnapshot(snapshot) {
44
+ var _a;
45
+ return {
46
+ ...snapshot,
47
+ viewportRange: {
48
+ start: snapshot.viewportRange.start,
49
+ end: snapshot.viewportRange.end,
50
+ },
51
+ pagination: {
52
+ ...snapshot.pagination,
53
+ },
54
+ sortModel: snapshot.sortModel.map(sort => ({ ...sort })),
55
+ filterModel: snapshot.filterModel
56
+ ? {
57
+ columnFilters: { ...snapshot.filterModel.columnFilters },
58
+ advancedFilters: { ...snapshot.filterModel.advancedFilters },
59
+ advancedExpression: (_a = snapshot.filterModel.advancedExpression) !== null && _a !== void 0 ? _a : null,
60
+ }
61
+ : null,
62
+ groupBy: snapshot.groupBy
63
+ ? {
64
+ fields: [...snapshot.groupBy.fields],
65
+ expandedByDefault: snapshot.groupBy.expandedByDefault,
66
+ }
67
+ : null,
68
+ pivotModel: snapshot.pivotModel
69
+ ? {
70
+ rows: [...snapshot.pivotModel.rows],
71
+ columns: [...snapshot.pivotModel.columns],
72
+ values: snapshot.pivotModel.values.map(value => ({ ...value })),
73
+ ...(snapshot.pivotModel.rowSubtotals ? { rowSubtotals: true } : {}),
74
+ ...(snapshot.pivotModel.columnSubtotals ? { columnSubtotals: true } : {}),
75
+ ...(snapshot.pivotModel.columnGrandTotal ? { columnGrandTotal: true } : {}),
76
+ ...(snapshot.pivotModel.columnSubtotalPosition
77
+ ? { columnSubtotalPosition: snapshot.pivotModel.columnSubtotalPosition }
78
+ : {}),
79
+ ...(snapshot.pivotModel.columnGrandTotalPosition
80
+ ? { columnGrandTotalPosition: snapshot.pivotModel.columnGrandTotalPosition }
81
+ : {}),
82
+ ...(snapshot.pivotModel.grandTotal ? { grandTotal: true } : {}),
83
+ }
84
+ : null,
85
+ pivotColumns: snapshot.pivotColumns
86
+ ? snapshot.pivotColumns.map(column => ({
87
+ ...column,
88
+ columnPath: column.columnPath.map(segment => ({ ...segment })),
89
+ }))
90
+ : [],
91
+ groupExpansion: {
92
+ expandedByDefault: snapshot.groupExpansion.expandedByDefault,
93
+ toggledGroupKeys: [...snapshot.groupExpansion.toggledGroupKeys],
94
+ },
95
+ };
96
+ }
97
+ function cloneAggregationModel(model) {
98
+ if (!model) {
99
+ return null;
100
+ }
101
+ return {
102
+ columns: model.columns.map(column => ({
103
+ ...column,
104
+ })),
105
+ ...(model.basis ? { basis: model.basis } : {}),
106
+ };
107
+ }
108
+ function cloneFormulaExecutionPlan(plan) {
109
+ if (!plan) {
110
+ return null;
111
+ }
112
+ return {
113
+ order: [...plan.order],
114
+ levels: plan.levels.map(level => [...level]),
115
+ nodes: plan.nodes.map(node => ({
116
+ name: node.name,
117
+ field: node.field,
118
+ level: node.level,
119
+ fieldDeps: [...node.fieldDeps],
120
+ computedDeps: [...node.computedDeps],
121
+ dependents: [...node.dependents],
122
+ })),
123
+ };
124
+ }
125
+ function cloneFormulaComputeStageDiagnostics(diagnostics) {
126
+ if (!diagnostics) {
127
+ return null;
128
+ }
129
+ return {
130
+ strategy: diagnostics.strategy,
131
+ rowsTouched: diagnostics.rowsTouched,
132
+ changedRows: diagnostics.changedRows,
133
+ fieldsTouched: [...diagnostics.fieldsTouched],
134
+ evaluations: diagnostics.evaluations,
135
+ skippedByObjectIs: diagnostics.skippedByObjectIs,
136
+ dirtyRows: diagnostics.dirtyRows,
137
+ dirtyNodes: [...diagnostics.dirtyNodes],
138
+ };
139
+ }
140
+ export function createDataGridWorkerOwnedRowModel(options) {
141
+ var _a;
142
+ const MAX_WINDOW_CACHE_SIZE = 8;
143
+ const INITIAL_SYNC_PREFETCH_MAX_ROWS = 25;
144
+ const PATCH_BURST_IMMEDIATE_FLUSH_THRESHOLD = 1024;
145
+ const viewportCoalescingStrategy = (_a = options.viewportCoalescingStrategy) !== null && _a !== void 0 ? _a : "split";
146
+ let disposed = false;
147
+ let nextRequestId = 1;
148
+ let lastAppliedRequestId = 0;
149
+ let initialWindowPrefetchResolved = false;
150
+ let loadingViewport = false;
151
+ let pendingViewportRequestId = 0;
152
+ let pendingViewportRange = null;
153
+ let requestedViewportRange = null;
154
+ let dispatchCount = 0;
155
+ let updatesReceived = 0;
156
+ let updatesApplied = 0;
157
+ let updatesDroppedStale = 0;
158
+ let updatesDroppedInitialAfterApplied = 0;
159
+ let commandsCoalesced = 0;
160
+ let patchCommandsCoalesced = 0;
161
+ let patchUpdatesReceived = 0;
162
+ let patchUpdatesDispatched = 0;
163
+ let patchUpdatesMergedAway = 0;
164
+ let immediateFlushCount = 0;
165
+ let queuePeak = 0;
166
+ let lastPayloadSchemaVersion = null;
167
+ let payloadSchemaMismatches = 0;
168
+ let loadingSetCount = 0;
169
+ let loadingClearCount = 0;
170
+ let snapshot = options.initialSnapshot
171
+ ? cloneSnapshot(options.initialSnapshot)
172
+ : createDefaultSnapshot();
173
+ let aggregationModel = null;
174
+ let formulaFields = [];
175
+ let formulaExecutionPlan = null;
176
+ let formulaComputeStageDiagnostics = null;
177
+ let visibleRange = {
178
+ start: snapshot.viewportRange.start,
179
+ end: snapshot.viewportRange.end,
180
+ };
181
+ let visibleRows = [];
182
+ const visibleWindowCache = new Map();
183
+ const visibleWindowCacheOrder = [];
184
+ const queuedCommands = [];
185
+ const queuedCommandIndexByKey = new Map();
186
+ let flushScheduled = false;
187
+ const listeners = new Set();
188
+ const rangeKey = (range) => `${range.start}:${range.end}`;
189
+ const parseRangeKey = (key) => {
190
+ const [startRaw, endRaw] = key.split(":");
191
+ const start = Number.parseInt(startRaw !== null && startRaw !== void 0 ? startRaw : "", 10);
192
+ const end = Number.parseInt(endRaw !== null && endRaw !== void 0 ? endRaw : "", 10);
193
+ if (!Number.isFinite(start) || !Number.isFinite(end)) {
194
+ return null;
195
+ }
196
+ return {
197
+ start,
198
+ end,
199
+ };
200
+ };
201
+ const rangeContains = (container, target) => {
202
+ if (!container) {
203
+ return false;
204
+ }
205
+ return container.start <= target.start && container.end >= target.end;
206
+ };
207
+ const isSameRange = (left, right) => {
208
+ return Boolean(left) && left.start === right.start && left.end === right.end;
209
+ };
210
+ const normalizeRequestedRange = (range) => {
211
+ if (range.start <= range.end) {
212
+ return range;
213
+ }
214
+ return {
215
+ start: range.end,
216
+ end: range.start,
217
+ };
218
+ };
219
+ const normalizeDispatchViewportRange = (range) => {
220
+ const normalized = normalizeRequestedRange(range);
221
+ const requestedSize = Math.max(1, normalized.end - normalized.start + 1);
222
+ const prefetchPad = Math.max(24, Math.min(96, Math.ceil(requestedSize * 0.75)));
223
+ const rowCount = Math.max(0, snapshot.rowCount);
224
+ const expandedStart = Math.max(0, normalized.start - prefetchPad);
225
+ const expandedEndRaw = normalized.end + prefetchPad;
226
+ const expandedEnd = rowCount > 0 ? Math.min(rowCount - 1, expandedEndRaw) : expandedEndRaw;
227
+ return {
228
+ start: expandedStart,
229
+ end: Math.max(expandedStart, expandedEnd),
230
+ };
231
+ };
232
+ const isRangeCoveredByVisibleWindow = (range) => {
233
+ if (visibleRows.length <= 0) {
234
+ return false;
235
+ }
236
+ return rangeContains(visibleRange, range);
237
+ };
238
+ const clampRangeToRowCount = (range, rowCount) => {
239
+ const normalized = normalizeRequestedRange(range);
240
+ if (rowCount <= 0) {
241
+ return { start: 0, end: 0 };
242
+ }
243
+ const start = Math.max(0, Math.min(rowCount - 1, normalized.start));
244
+ const end = Math.max(start, Math.min(rowCount - 1, normalized.end));
245
+ return { start, end };
246
+ };
247
+ const buildPublicSnapshot = () => {
248
+ const nextSnapshot = cloneSnapshot(snapshot);
249
+ if (loadingViewport) {
250
+ nextSnapshot.loading = true;
251
+ }
252
+ return nextSnapshot;
253
+ };
254
+ const emit = () => {
255
+ if (listeners.size === 0) {
256
+ return;
257
+ }
258
+ const nextSnapshot = buildPublicSnapshot();
259
+ for (const listener of listeners) {
260
+ listener(nextSnapshot);
261
+ }
262
+ };
263
+ const mergePatchRowOptions = (left, right) => {
264
+ var _a;
265
+ if (!left && !right) {
266
+ return undefined;
267
+ }
268
+ const merged = {};
269
+ if ((left === null || left === void 0 ? void 0 : left.recomputeSort) === true || (right === null || right === void 0 ? void 0 : right.recomputeSort) === true) {
270
+ merged.recomputeSort = true;
271
+ }
272
+ if ((left === null || left === void 0 ? void 0 : left.recomputeFilter) === true || (right === null || right === void 0 ? void 0 : right.recomputeFilter) === true) {
273
+ merged.recomputeFilter = true;
274
+ }
275
+ if ((left === null || left === void 0 ? void 0 : left.recomputeGroup) === true || (right === null || right === void 0 ? void 0 : right.recomputeGroup) === true) {
276
+ merged.recomputeGroup = true;
277
+ }
278
+ if (right && "emit" in right && right.emit !== undefined) {
279
+ merged.emit = right.emit;
280
+ }
281
+ if (right && "signal" in right) {
282
+ merged.signal = (_a = right.signal) !== null && _a !== void 0 ? _a : null;
283
+ }
284
+ return Object.keys(merged).length > 0 ? merged : undefined;
285
+ };
286
+ const mergePatchRowUpdates = (left, right) => {
287
+ if (left.length === 0) {
288
+ return right;
289
+ }
290
+ if (right.length === 0) {
291
+ return left;
292
+ }
293
+ const mergedByRowId = new Map();
294
+ const order = [];
295
+ const addUpdates = (updates) => {
296
+ var _a;
297
+ for (const update of updates) {
298
+ if (!mergedByRowId.has(update.rowId)) {
299
+ order.push(update.rowId);
300
+ mergedByRowId.set(update.rowId, update.data);
301
+ continue;
302
+ }
303
+ const previous = (_a = mergedByRowId.get(update.rowId)) !== null && _a !== void 0 ? _a : {};
304
+ mergedByRowId.set(update.rowId, { ...previous, ...update.data });
305
+ }
306
+ };
307
+ addUpdates(left);
308
+ addUpdates(right);
309
+ return order.map((rowId) => {
310
+ var _a;
311
+ return ({
312
+ rowId,
313
+ data: (_a = mergedByRowId.get(rowId)) !== null && _a !== void 0 ? _a : {},
314
+ });
315
+ });
316
+ };
317
+ const mergeCommandPayload = (left, right) => {
318
+ if (left.type === "patch-rows" && right.type === "patch-rows") {
319
+ const mergedOptions = mergePatchRowOptions(left.options, right.options);
320
+ const mergedUpdates = mergePatchRowUpdates(left.updates, right.updates);
321
+ return {
322
+ type: "patch-rows",
323
+ updates: mergedUpdates,
324
+ ...(mergedOptions ? { options: mergedOptions } : {}),
325
+ };
326
+ }
327
+ return right;
328
+ };
329
+ const getCoalesceKey = (payload) => {
330
+ var _a;
331
+ switch (payload.type) {
332
+ case "set-rows":
333
+ return payload.type;
334
+ case "patch-rows":
335
+ return payload.type;
336
+ case "set-viewport-range":
337
+ if (viewportCoalescingStrategy === "simple") {
338
+ return "set-viewport-range";
339
+ }
340
+ return `set-viewport-range:${(_a = payload.coalesceScope) !== null && _a !== void 0 ? _a : "user"}`;
341
+ case "set-pagination":
342
+ case "set-page-size":
343
+ case "set-current-page":
344
+ case "set-sort-model":
345
+ case "set-filter-model":
346
+ case "set-sort-and-filter-model":
347
+ case "set-group-by":
348
+ case "set-pivot-model":
349
+ case "set-aggregation-model":
350
+ case "set-group-expansion":
351
+ case "register-formula-field":
352
+ case "refresh":
353
+ return payload.type;
354
+ default:
355
+ return null;
356
+ }
357
+ };
358
+ const cloneForTransport = (value, context) => {
359
+ if (typeof structuredClone === "function") {
360
+ try {
361
+ return structuredClone(value);
362
+ }
363
+ catch {
364
+ // Fallback below for reactive proxies and other non-structured values.
365
+ }
366
+ }
367
+ try {
368
+ return JSON.parse(JSON.stringify(value));
369
+ }
370
+ catch (error) {
371
+ const reason = error instanceof Error ? error.message : String(error);
372
+ throw new Error(`[AffinoDataGrid worker] Unable to serialize ${context}: ${reason}`);
373
+ }
374
+ };
375
+ const ensureAggregationModelSerializable = (model) => {
376
+ if (!model) {
377
+ return;
378
+ }
379
+ for (const column of model.columns) {
380
+ if (typeof column.createState === "function"
381
+ || typeof column.add === "function"
382
+ || typeof column.remove === "function"
383
+ || typeof column.finalize === "function"
384
+ || typeof column.coerce === "function") {
385
+ throw new Error("[AffinoDataGrid worker] aggregationModel with function callbacks is not supported in worker-owned mode.");
386
+ }
387
+ }
388
+ };
389
+ const validateCommandPayload = (payload) => {
390
+ if (payload.type === "set-aggregation-model") {
391
+ ensureAggregationModelSerializable(payload.aggregationModel);
392
+ }
393
+ };
394
+ const isDataCloneError = (error) => {
395
+ return Boolean(error
396
+ && typeof error === "object"
397
+ && "name" in error
398
+ && error.name === "DataCloneError");
399
+ };
400
+ const pushWindowCache = (range, rows) => {
401
+ const key = rangeKey(range);
402
+ if (visibleWindowCache.has(key)) {
403
+ const existingIndex = visibleWindowCacheOrder.indexOf(key);
404
+ if (existingIndex >= 0) {
405
+ visibleWindowCacheOrder.splice(existingIndex, 1);
406
+ }
407
+ }
408
+ visibleWindowCache.set(key, rows.map(cloneRowNode));
409
+ visibleWindowCacheOrder.push(key);
410
+ while (visibleWindowCacheOrder.length > MAX_WINDOW_CACHE_SIZE) {
411
+ const oldestKey = visibleWindowCacheOrder.shift();
412
+ if (!oldestKey) {
413
+ break;
414
+ }
415
+ visibleWindowCache.delete(oldestKey);
416
+ }
417
+ };
418
+ const markViewportLoading = (range, requestId) => {
419
+ pendingViewportRequestId = Math.max(pendingViewportRequestId, requestId);
420
+ pendingViewportRange = { start: range.start, end: range.end };
421
+ if (loadingViewport) {
422
+ return;
423
+ }
424
+ loadingViewport = true;
425
+ loadingSetCount += 1;
426
+ emit();
427
+ };
428
+ const flushQueuedCommands = () => {
429
+ if (disposed || queuedCommands.length === 0) {
430
+ return;
431
+ }
432
+ const commands = queuedCommands.splice(0, queuedCommands.length);
433
+ queuedCommandIndexByKey.clear();
434
+ for (const command of commands) {
435
+ try {
436
+ let message = createDataGridWorkerRowModelCommandMessage(command.requestId, command.payload, options.channel);
437
+ if (command.payload.type === "patch-rows") {
438
+ patchUpdatesDispatched += command.payload.updates.length;
439
+ }
440
+ dispatchCount += 1;
441
+ try {
442
+ options.target.postMessage(message);
443
+ }
444
+ catch (error) {
445
+ if (!isDataCloneError(error)) {
446
+ throw error;
447
+ }
448
+ message = createDataGridWorkerRowModelCommandMessage(command.requestId, cloneForTransport(command.payload, `command '${command.payload.type}'`), options.channel);
449
+ options.target.postMessage(message);
450
+ }
451
+ if (command.payload.type === "set-viewport-range") {
452
+ markViewportLoading(command.payload.range, command.requestId);
453
+ }
454
+ }
455
+ catch (error) {
456
+ loadingViewport = false;
457
+ pendingViewportRequestId = 0;
458
+ pendingViewportRange = null;
459
+ const reason = error instanceof Error ? error.message : String(error);
460
+ snapshot = {
461
+ ...snapshot,
462
+ error: new Error(`[AffinoDataGrid worker] dispatch failed: ${reason}`),
463
+ };
464
+ emit();
465
+ }
466
+ }
467
+ };
468
+ const flushNow = () => {
469
+ immediateFlushCount += 1;
470
+ flushScheduled = false;
471
+ flushQueuedCommands();
472
+ };
473
+ const scheduleFlush = () => {
474
+ if (flushScheduled || disposed) {
475
+ return;
476
+ }
477
+ flushScheduled = true;
478
+ queueMicrotask(() => {
479
+ flushScheduled = false;
480
+ flushQueuedCommands();
481
+ });
482
+ };
483
+ const dispatchCommand = (payload) => {
484
+ if (disposed) {
485
+ return 0;
486
+ }
487
+ validateCommandPayload(payload);
488
+ if (payload.type === "patch-rows") {
489
+ patchUpdatesReceived += payload.updates.length;
490
+ }
491
+ const requestId = nextRequestId++;
492
+ const coalesceKey = getCoalesceKey(payload);
493
+ if (coalesceKey) {
494
+ const existingIndex = queuedCommandIndexByKey.get(coalesceKey);
495
+ if (typeof existingIndex === "number" && queuedCommands[existingIndex]) {
496
+ const existingCommand = queuedCommands[existingIndex];
497
+ const mergedPayload = mergeCommandPayload(existingCommand.payload, payload);
498
+ commandsCoalesced += 1;
499
+ if (existingCommand.payload.type === "patch-rows"
500
+ && payload.type === "patch-rows"
501
+ && mergedPayload.type === "patch-rows") {
502
+ patchCommandsCoalesced += 1;
503
+ const mergedAway = (existingCommand.payload.updates.length
504
+ + payload.updates.length
505
+ - mergedPayload.updates.length);
506
+ if (mergedAway > 0) {
507
+ patchUpdatesMergedAway += mergedAway;
508
+ }
509
+ }
510
+ queuedCommands[existingIndex] = {
511
+ requestId,
512
+ payload: mergedPayload,
513
+ coalesceKey,
514
+ };
515
+ if (mergedPayload.type === "patch-rows"
516
+ && mergedPayload.updates.length >= PATCH_BURST_IMMEDIATE_FLUSH_THRESHOLD) {
517
+ flushNow();
518
+ return requestId;
519
+ }
520
+ scheduleFlush();
521
+ return requestId;
522
+ }
523
+ }
524
+ const command = {
525
+ requestId,
526
+ payload,
527
+ coalesceKey,
528
+ };
529
+ queuedCommands.push(command);
530
+ if (coalesceKey) {
531
+ queuedCommandIndexByKey.set(coalesceKey, queuedCommands.length - 1);
532
+ }
533
+ if (queuedCommands.length > queuePeak) {
534
+ queuePeak = queuedCommands.length;
535
+ }
536
+ if (payload.type === "patch-rows"
537
+ && payload.updates.length >= PATCH_BURST_IMMEDIATE_FLUSH_THRESHOLD) {
538
+ flushNow();
539
+ return requestId;
540
+ }
541
+ scheduleFlush();
542
+ return requestId;
543
+ };
544
+ const dispatchViewportRange = (range, coalesceScope) => {
545
+ const normalizedRequestedRange = normalizeRequestedRange(range);
546
+ const syncRequestedViewportSnapshot = () => {
547
+ if (coalesceScope !== "user" || isSameRange(snapshot.viewportRange, normalizedRequestedRange)) {
548
+ return;
549
+ }
550
+ snapshot = {
551
+ ...snapshot,
552
+ viewportRange: { ...normalizedRequestedRange },
553
+ };
554
+ emit();
555
+ };
556
+ if (coalesceScope === "user") {
557
+ requestedViewportRange = { ...normalizedRequestedRange };
558
+ }
559
+ if (isRangeCoveredByVisibleWindow(normalizedRequestedRange)
560
+ || rangeContains(pendingViewportRange, normalizedRequestedRange)) {
561
+ syncRequestedViewportSnapshot();
562
+ return 0;
563
+ }
564
+ const dispatchRange = normalizeDispatchViewportRange(normalizedRequestedRange);
565
+ if (isRangeCoveredByVisibleWindow(dispatchRange)
566
+ || rangeContains(pendingViewportRange, dispatchRange)) {
567
+ syncRequestedViewportSnapshot();
568
+ return 0;
569
+ }
570
+ const normalizedScope = viewportCoalescingStrategy === "simple"
571
+ ? "user"
572
+ : coalesceScope;
573
+ const requestId = dispatchCommand({
574
+ type: "set-viewport-range",
575
+ range: dispatchRange,
576
+ coalesceScope: normalizedScope,
577
+ });
578
+ if (requestId > 0) {
579
+ markViewportLoading(dispatchRange, requestId);
580
+ }
581
+ return requestId;
582
+ };
583
+ const onMessage = (event) => {
584
+ if (disposed) {
585
+ return;
586
+ }
587
+ if (!isDataGridWorkerRowModelUpdateMessage(event.data, options.channel)) {
588
+ return;
589
+ }
590
+ updatesReceived += 1;
591
+ const requestId = event.data.requestId;
592
+ if (requestId === 0 && lastAppliedRequestId > 0) {
593
+ updatesDroppedInitialAfterApplied += 1;
594
+ return;
595
+ }
596
+ if (requestId > 0 && requestId < lastAppliedRequestId) {
597
+ updatesDroppedStale += 1;
598
+ return;
599
+ }
600
+ if (requestId > 0) {
601
+ lastAppliedRequestId = requestId;
602
+ }
603
+ const update = event.data.payload;
604
+ const payloadSchemaVersion = typeof update.schemaVersion === "number"
605
+ ? update.schemaVersion
606
+ : 1;
607
+ lastPayloadSchemaVersion = payloadSchemaVersion;
608
+ if (payloadSchemaVersion !== DATAGRID_WORKER_ROW_MODEL_PAYLOAD_SCHEMA_VERSION) {
609
+ payloadSchemaMismatches += 1;
610
+ }
611
+ snapshot = update.snapshot;
612
+ if (requestedViewportRange) {
613
+ snapshot = {
614
+ ...snapshot,
615
+ viewportRange: clampRangeToRowCount(requestedViewportRange, snapshot.rowCount),
616
+ };
617
+ }
618
+ aggregationModel = cloneAggregationModel(update.aggregationModel);
619
+ formulaFields = Array.isArray(update.formulaFields)
620
+ ? update.formulaFields.map(field => ({
621
+ name: field.name,
622
+ field: field.field,
623
+ formula: field.formula,
624
+ deps: [...field.deps],
625
+ contextKeys: [...field.contextKeys],
626
+ }))
627
+ : [];
628
+ formulaExecutionPlan = cloneFormulaExecutionPlan(update.formulaExecutionPlan);
629
+ formulaComputeStageDiagnostics = cloneFormulaComputeStageDiagnostics(update.formulaComputeStageDiagnostics);
630
+ visibleRange = {
631
+ start: update.visibleRange.start,
632
+ end: update.visibleRange.end,
633
+ };
634
+ visibleRows = update.visibleRows;
635
+ pushWindowCache(visibleRange, visibleRows);
636
+ if (!initialWindowPrefetchResolved && requestId === 0) {
637
+ const totalRows = snapshot.rowCount;
638
+ if (totalRows > 0 && totalRows <= INITIAL_SYNC_PREFETCH_MAX_ROWS) {
639
+ const prefetchedRange = {
640
+ start: 0,
641
+ end: totalRows - 1,
642
+ };
643
+ const expectedCount = prefetchedRange.end - prefetchedRange.start + 1;
644
+ const hasFullInitialWindow = visibleRange.start === prefetchedRange.start
645
+ && visibleRange.end === prefetchedRange.end
646
+ && visibleRows.length >= expectedCount;
647
+ if (!hasFullInitialWindow && !isSameRange(pendingViewportRange, prefetchedRange)) {
648
+ dispatchViewportRange(prefetchedRange, "prefetch");
649
+ }
650
+ }
651
+ initialWindowPrefetchResolved = true;
652
+ }
653
+ const matchesPendingRange = isSameRange(pendingViewportRange, visibleRange);
654
+ if (loadingViewport && (requestId >= pendingViewportRequestId || matchesPendingRange)) {
655
+ loadingViewport = false;
656
+ pendingViewportRequestId = 0;
657
+ pendingViewportRange = null;
658
+ loadingClearCount += 1;
659
+ }
660
+ updatesApplied += 1;
661
+ emit();
662
+ };
663
+ options.source.addEventListener("message", onMessage);
664
+ if (options.requestInitialSync !== false) {
665
+ dispatchCommand({ type: "sync" });
666
+ }
667
+ const readVisibleRowByIndex = (index) => {
668
+ if (index < visibleRange.start || index > visibleRange.end) {
669
+ return undefined;
670
+ }
671
+ return visibleRows[index - visibleRange.start];
672
+ };
673
+ const readCachedRowByIndex = (index) => {
674
+ const visibleRow = readVisibleRowByIndex(index);
675
+ if (visibleRow) {
676
+ return cloneRowNode(visibleRow);
677
+ }
678
+ for (let orderIndex = visibleWindowCacheOrder.length - 1; orderIndex >= 0; orderIndex -= 1) {
679
+ const key = visibleWindowCacheOrder[orderIndex];
680
+ if (!key) {
681
+ continue;
682
+ }
683
+ const range = parseRangeKey(key);
684
+ if (!range || index < range.start || index > range.end) {
685
+ continue;
686
+ }
687
+ const cachedRows = visibleWindowCache.get(key);
688
+ const cachedRow = cachedRows === null || cachedRows === void 0 ? void 0 : cachedRows[index - range.start];
689
+ if (cachedRow) {
690
+ return cloneRowNode(cachedRow);
691
+ }
692
+ }
693
+ return undefined;
694
+ };
695
+ return {
696
+ kind: "client",
697
+ getSnapshot() {
698
+ return buildPublicSnapshot();
699
+ },
700
+ getRowCount() {
701
+ return snapshot.rowCount;
702
+ },
703
+ getRow(index) {
704
+ const row = readVisibleRowByIndex(index);
705
+ if (row) {
706
+ return cloneRowNode(row);
707
+ }
708
+ const requestedRange = { start: index, end: index };
709
+ if (!isSameRange(pendingViewportRange, requestedRange)) {
710
+ dispatchViewportRange(requestedRange, "user");
711
+ }
712
+ return undefined;
713
+ },
714
+ getRowsInRange(range) {
715
+ if (range.start !== visibleRange.start || range.end !== visibleRange.end) {
716
+ if (!isSameRange(pendingViewportRange, range)) {
717
+ dispatchViewportRange(range, "user");
718
+ }
719
+ }
720
+ const key = rangeKey(range);
721
+ const cachedRows = visibleWindowCache.get(key);
722
+ if (cachedRows) {
723
+ return [...cachedRows];
724
+ }
725
+ const rows = [];
726
+ for (let index = range.start; index <= range.end; index += 1) {
727
+ const row = readCachedRowByIndex(index);
728
+ if (row) {
729
+ rows.push(row);
730
+ }
731
+ }
732
+ return rows;
733
+ },
734
+ setViewportRange(range) {
735
+ dispatchViewportRange(range, "user");
736
+ },
737
+ setPagination(pagination) {
738
+ dispatchCommand({ type: "set-pagination", pagination });
739
+ },
740
+ setPageSize(pageSize) {
741
+ dispatchCommand({ type: "set-page-size", pageSize });
742
+ },
743
+ setCurrentPage(page) {
744
+ dispatchCommand({ type: "set-current-page", page });
745
+ },
746
+ setSortModel(sortModel) {
747
+ dispatchCommand({ type: "set-sort-model", sortModel: sortModel.map(sort => ({ ...sort })) });
748
+ },
749
+ setFilterModel(filterModel) {
750
+ dispatchCommand({ type: "set-filter-model", filterModel });
751
+ },
752
+ setSortAndFilterModel(input) {
753
+ dispatchCommand({
754
+ type: "set-sort-and-filter-model",
755
+ input: {
756
+ sortModel: input.sortModel.map(sort => ({ ...sort })),
757
+ filterModel: input.filterModel,
758
+ },
759
+ });
760
+ },
761
+ setGroupBy(groupBy) {
762
+ dispatchCommand({ type: "set-group-by", groupBy });
763
+ },
764
+ setPivotModel(pivotModel) {
765
+ dispatchCommand({ type: "set-pivot-model", pivotModel });
766
+ },
767
+ getPivotModel() {
768
+ if (!snapshot.pivotModel) {
769
+ return null;
770
+ }
771
+ return {
772
+ rows: [...snapshot.pivotModel.rows],
773
+ columns: [...snapshot.pivotModel.columns],
774
+ values: snapshot.pivotModel.values.map(value => ({ ...value })),
775
+ ...(snapshot.pivotModel.rowSubtotals ? { rowSubtotals: true } : {}),
776
+ ...(snapshot.pivotModel.columnSubtotals ? { columnSubtotals: true } : {}),
777
+ ...(snapshot.pivotModel.columnGrandTotal ? { columnGrandTotal: true } : {}),
778
+ ...(snapshot.pivotModel.columnSubtotalPosition
779
+ ? { columnSubtotalPosition: snapshot.pivotModel.columnSubtotalPosition }
780
+ : {}),
781
+ ...(snapshot.pivotModel.columnGrandTotalPosition
782
+ ? { columnGrandTotalPosition: snapshot.pivotModel.columnGrandTotalPosition }
783
+ : {}),
784
+ ...(snapshot.pivotModel.grandTotal ? { grandTotal: true } : {}),
785
+ };
786
+ },
787
+ setAggregationModel(nextAggregationModel) {
788
+ const nextModel = cloneAggregationModel(nextAggregationModel);
789
+ aggregationModel = nextModel;
790
+ dispatchCommand({ type: "set-aggregation-model", aggregationModel: nextModel });
791
+ },
792
+ getAggregationModel() {
793
+ return cloneAggregationModel(aggregationModel);
794
+ },
795
+ setGroupExpansion(expansion) {
796
+ dispatchCommand({ type: "set-group-expansion", expansion });
797
+ },
798
+ toggleGroup(groupKey) {
799
+ dispatchCommand({ type: "toggle-group", groupKey });
800
+ },
801
+ expandGroup(groupKey) {
802
+ dispatchCommand({ type: "expand-group", groupKey });
803
+ },
804
+ collapseGroup(groupKey) {
805
+ dispatchCommand({ type: "collapse-group", groupKey });
806
+ },
807
+ expandAllGroups() {
808
+ dispatchCommand({ type: "expand-all-groups" });
809
+ },
810
+ collapseAllGroups() {
811
+ dispatchCommand({ type: "collapse-all-groups" });
812
+ },
813
+ refresh(reason) {
814
+ dispatchCommand({ type: "refresh", reason });
815
+ },
816
+ subscribe(listener) {
817
+ listeners.add(listener);
818
+ return () => {
819
+ listeners.delete(listener);
820
+ };
821
+ },
822
+ setRows(rows) {
823
+ dispatchCommand({ type: "set-rows", rows });
824
+ },
825
+ patchRows(updates, options) {
826
+ dispatchCommand({ type: "patch-rows", updates, options });
827
+ },
828
+ registerFormulaField(definition) {
829
+ dispatchCommand({ type: "register-formula-field", definition });
830
+ },
831
+ getFormulaFields() {
832
+ return formulaFields.map(field => ({
833
+ name: field.name,
834
+ field: field.field,
835
+ formula: field.formula,
836
+ deps: [...field.deps],
837
+ contextKeys: [...field.contextKeys],
838
+ }));
839
+ },
840
+ getFormulaExecutionPlan() {
841
+ return cloneFormulaExecutionPlan(formulaExecutionPlan);
842
+ },
843
+ getFormulaComputeStageDiagnostics() {
844
+ return cloneFormulaComputeStageDiagnostics(formulaComputeStageDiagnostics);
845
+ },
846
+ getComputeDiagnostics() {
847
+ return {
848
+ configuredMode: "worker",
849
+ effectiveMode: "worker",
850
+ transportKind: "custom",
851
+ dispatchCount,
852
+ fallbackCount: 0,
853
+ };
854
+ },
855
+ getWorkerProtocolDiagnostics() {
856
+ return {
857
+ updatesReceived,
858
+ updatesApplied,
859
+ updatesDroppedStale,
860
+ updatesDroppedInitialAfterApplied,
861
+ commandsCoalesced,
862
+ patchCommandsCoalesced,
863
+ patchUpdatesReceived,
864
+ patchUpdatesDispatched,
865
+ patchUpdatesMergedAway,
866
+ immediateFlushCount,
867
+ queuePeak,
868
+ protocolVersion: DATAGRID_WORKER_ROW_MODEL_PROTOCOL_VERSION,
869
+ expectedPayloadSchemaVersion: DATAGRID_WORKER_ROW_MODEL_PAYLOAD_SCHEMA_VERSION,
870
+ lastPayloadSchemaVersion,
871
+ payloadSchemaMismatches,
872
+ loadingSetCount,
873
+ loadingClearCount,
874
+ };
875
+ },
876
+ dispose() {
877
+ if (disposed) {
878
+ return;
879
+ }
880
+ disposed = true;
881
+ options.source.removeEventListener("message", onMessage);
882
+ listeners.clear();
883
+ visibleRows = [];
884
+ visibleWindowCache.clear();
885
+ visibleWindowCacheOrder.length = 0;
886
+ queuedCommands.length = 0;
887
+ queuedCommandIndexByKey.clear();
888
+ formulaFields = [];
889
+ formulaExecutionPlan = null;
890
+ formulaComputeStageDiagnostics = null;
891
+ },
892
+ };
893
+ }