@genesislcap/grid-pro 15.30.0 → 15.30.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.
- package/dist/custom-elements.json +8196 -7794
- package/dist/dts/column-selection/column-selection.d.ts +4 -3
- package/dist/dts/column-selection/column-selection.d.ts.map +1 -1
- package/dist/dts/column-selection/column-selection.styles.d.ts.map +1 -1
- package/dist/dts/datasource/base.datasource.d.ts +69 -8
- package/dist/dts/datasource/base.datasource.d.ts.map +1 -1
- package/dist/dts/datasource/base.types.d.ts +10 -0
- package/dist/dts/datasource/base.types.d.ts.map +1 -1
- package/dist/dts/datasource/dataserver-result.filter.d.ts +17 -0
- package/dist/dts/datasource/dataserver-result.filter.d.ts.map +1 -0
- package/dist/dts/datasource/delivered-block.ledger.d.ts +37 -0
- package/dist/dts/datasource/delivered-block.ledger.d.ts.map +1 -0
- package/dist/dts/datasource/infinite.datasource.d.ts +65 -1
- package/dist/dts/datasource/infinite.datasource.d.ts.map +1 -1
- package/dist/dts/datasource/infinite.resource.d.ts +136 -6
- package/dist/dts/datasource/infinite.resource.d.ts.map +1 -1
- package/dist/dts/datasource/server-side.datasource.d.ts +8 -4
- package/dist/dts/datasource/server-side.datasource.d.ts.map +1 -1
- package/dist/dts/datasource/server-side.resource-base.d.ts +23 -0
- package/dist/dts/datasource/server-side.resource-base.d.ts.map +1 -1
- package/dist/dts/datasource/server-side.resource-dataserver.d.ts +28 -0
- package/dist/dts/datasource/server-side.resource-dataserver.d.ts.map +1 -1
- package/dist/dts/datasource/server-side.resource-reqrep.d.ts +1 -1
- package/dist/dts/datasource/server-side.resource-reqrep.d.ts.map +1 -1
- package/dist/dts/grid-pro-beta.d.ts +6 -0
- package/dist/dts/grid-pro-beta.d.ts.map +1 -1
- package/dist/dts/grid-pro-genesis-datasource/datasource-events.types.d.ts +4 -0
- package/dist/dts/grid-pro-genesis-datasource/datasource-events.types.d.ts.map +1 -1
- package/dist/dts/grid-pro.d.ts.map +1 -1
- package/dist/dts/react.d.ts +20 -20
- package/dist/esm/column-selection/column-selection.js +11 -9
- package/dist/esm/column-selection/column-selection.styles.js +0 -2
- package/dist/esm/column-selection/column-selection.template.js +1 -1
- package/dist/esm/datasource/base.datasource.js +182 -27
- package/dist/esm/datasource/dataserver-result.filter.js +28 -0
- package/dist/esm/datasource/delivered-block.ledger.js +52 -0
- package/dist/esm/datasource/infinite.datasource.js +131 -20
- package/dist/esm/datasource/infinite.resource.js +221 -16
- package/dist/esm/datasource/server-side.datasource.js +80 -29
- package/dist/esm/datasource/server-side.resource-base.js +39 -0
- package/dist/esm/datasource/server-side.resource-dataserver.js +110 -23
- package/dist/esm/datasource/server-side.resource-reqrep.js +25 -3
- package/dist/esm/grid-pro-beta.js +20 -2
- package/dist/esm/grid-pro.js +4 -1
- package/dist/grid-pro.api.json +66 -66
- package/dist/grid-pro.d.ts +294 -21
- package/dist/react.cjs +23 -23
- package/dist/react.mjs +21 -21
- package/package.json +13 -13
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { __awaiter, __decorate } from "tslib";
|
|
2
2
|
import { Auth, Connect } from '@genesislcap/foundation-comms';
|
|
3
3
|
import { UUID } from '@genesislcap/foundation-utils';
|
|
4
|
+
import { DeliveredBlockLedger } from './delivered-block.ledger';
|
|
4
5
|
import { convertDateFilterToCriteria, convertNumberFilterToCriteria, convertSetFilterToCriteria, convertTextFilterToCriteria, } from './filter.utils';
|
|
5
6
|
/**
|
|
6
7
|
* Base class for server-side resource datasources used in Grid Pro SSRM implementations.
|
|
@@ -11,6 +12,12 @@ import { convertDateFilterToCriteria, convertNumberFilterToCriteria, convertSetF
|
|
|
11
12
|
export class BaseServerSideDatasource {
|
|
12
13
|
constructor(options) {
|
|
13
14
|
this.rowData = new Map();
|
|
15
|
+
/**
|
|
16
|
+
* Bumped whenever the rows the grid holds are dropped (destroy, refresh, reload). A req/rep
|
|
17
|
+
* reply that resolves for an earlier generation belongs to a cache the grid no longer has.
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
this.loadGeneration = 0;
|
|
14
21
|
/**
|
|
15
22
|
* The number of rows in the grid. THIS DO NOT CONSIDER CRITERIA_MATCH scenarios.
|
|
16
23
|
* @internal
|
|
@@ -28,6 +35,8 @@ export class BaseServerSideDatasource {
|
|
|
28
35
|
this.errorHandlerFunc = options.errorHandlerFunc;
|
|
29
36
|
this.onNoDataAvailableFunc = options.onNoDataAvailableFunc;
|
|
30
37
|
this.onDataAvailableFunc = options.onDataAvailableFunc;
|
|
38
|
+
this.onRowsDeliveredFunc = options.onRowsDeliveredFunc;
|
|
39
|
+
this.onRowsWithdrawnFunc = options.onRowsWithdrawnFunc;
|
|
31
40
|
this.resourceName = options.resourceName;
|
|
32
41
|
this.resourceParams = options.resourceParams;
|
|
33
42
|
this.originalCriteriaMatch = this.getResourceParam('CRITERIA_MATCH');
|
|
@@ -36,6 +45,7 @@ export class BaseServerSideDatasource {
|
|
|
36
45
|
this.maxRows = options.maxRows;
|
|
37
46
|
this.maxView = options.maxView;
|
|
38
47
|
this.rowId = options.rowId;
|
|
48
|
+
this.ledger = new DeliveredBlockLedger(options.rowId);
|
|
39
49
|
}
|
|
40
50
|
/**
|
|
41
51
|
* Gets a resource parameter, handling both REQUEST_SERVER (DETAILS structure) and DATASERVER (flat structure)
|
|
@@ -116,6 +126,7 @@ export class BaseServerSideDatasource {
|
|
|
116
126
|
});
|
|
117
127
|
}
|
|
118
128
|
destroy() {
|
|
129
|
+
this.invalidatePendingLoads();
|
|
119
130
|
this.rowData = new Map();
|
|
120
131
|
this.clientRowsCount = 0;
|
|
121
132
|
this.calculatedRowsCount = 0;
|
|
@@ -125,6 +136,15 @@ export class BaseServerSideDatasource {
|
|
|
125
136
|
this.sourceRef = undefined;
|
|
126
137
|
this.lastSuccessRowData = undefined;
|
|
127
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Marks every block request still in flight as stale: the grid is dropping the cache they were
|
|
141
|
+
* read for, so their replies must not be handed to the host as held rows.
|
|
142
|
+
* @internal
|
|
143
|
+
*/
|
|
144
|
+
invalidatePendingLoads() {
|
|
145
|
+
this.loadGeneration += 1;
|
|
146
|
+
this.ledger.clear();
|
|
147
|
+
}
|
|
128
148
|
refreshDatasource(params) {
|
|
129
149
|
return __awaiter(this, void 0, void 0, function* () {
|
|
130
150
|
this.destroy();
|
|
@@ -287,6 +307,9 @@ export class BaseServerSideDatasource {
|
|
|
287
307
|
completeEmptyResult(params) {
|
|
288
308
|
var _a, _b;
|
|
289
309
|
if ((_b = (_a = this.lastSuccessRowData) === null || _a === void 0 ? void 0 : _a.rowData) === null || _b === void 0 ? void 0 : _b.length) {
|
|
310
|
+
// Re-serving the previous block's snapshot keeps the SSRM request from hanging; it is not a
|
|
311
|
+
// delivery of new rows, so it is not reported - the snapshot may predate a poll that has
|
|
312
|
+
// since reported those rows changed or removed.
|
|
290
313
|
params.success(this.lastSuccessRowData);
|
|
291
314
|
return;
|
|
292
315
|
}
|
|
@@ -295,6 +318,22 @@ export class BaseServerSideDatasource {
|
|
|
295
318
|
params.success(successRowData);
|
|
296
319
|
this.notifyNoDataAvailableIfEmpty(params, successRowData);
|
|
297
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* Reports rows handed to the grid for the block at `startRow`, then the rows a re-read of that
|
|
323
|
+
* block no longer carries and the rows whose block the grid evicted.
|
|
324
|
+
* @internal
|
|
325
|
+
*/
|
|
326
|
+
deliverRows(startRow, rows) {
|
|
327
|
+
var _a, _b;
|
|
328
|
+
const delivered = rows !== null && rows !== void 0 ? rows : [];
|
|
329
|
+
const { withdrawnRowIds } = this.ledger.record(startRow, delivered);
|
|
330
|
+
if (delivered.length > 0) {
|
|
331
|
+
(_a = this.onRowsDeliveredFunc) === null || _a === void 0 ? void 0 : _a.call(this, delivered);
|
|
332
|
+
}
|
|
333
|
+
if (withdrawnRowIds.length > 0) {
|
|
334
|
+
(_b = this.onRowsWithdrawnFunc) === null || _b === void 0 ? void 0 : _b.call(this, withdrawnRowIds);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
298
337
|
/** Invokes `onNoDataAvailableFunc` when the first SSRM block has no rows. @internal */
|
|
299
338
|
notifyNoDataAvailableIfEmpty(params, successRowData) {
|
|
300
339
|
var _a;
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { __awaiter } from "tslib";
|
|
2
|
-
import { MessageType,
|
|
2
|
+
import { MessageType, logger, } from '@genesislcap/foundation-comms';
|
|
3
3
|
import { DOM } from '@microsoft/fast-element';
|
|
4
4
|
import { getAvailableIndexes, getAvailableIndexFields } from '../utils';
|
|
5
|
+
import { filterDataserverResult } from './dataserver-result.filter';
|
|
5
6
|
import { BaseServerSideDatasource } from './server-side.resource-base';
|
|
6
7
|
/**
|
|
7
8
|
* The DATASERVER IServerSideDatasource implementation, used for SSRM (Server-Side Row Model) in the grid.
|
|
8
9
|
* @beta
|
|
9
10
|
*/
|
|
10
11
|
export class DataserverServerSideDatasource extends BaseServerSideDatasource {
|
|
12
|
+
/**
|
|
13
|
+
* Whether the cache holds a change the grid's rows do not reflect (a tombstoned DELETE), in
|
|
14
|
+
* which case a block must not be served from it: the slice would hand back a deleted row.
|
|
15
|
+
* @remarks Derived rather than a flag of its own, so an INSERT that brings a tombstoned id
|
|
16
|
+
* back clears it along with the tombstone instead of leaving the cache marked stale forever.
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
get cacheOutOfStep() {
|
|
20
|
+
return this.retainedDeletedRowIds.size > 0 || this.structuralPushPending;
|
|
21
|
+
}
|
|
11
22
|
constructor(options) {
|
|
12
23
|
super(options);
|
|
13
24
|
/**
|
|
@@ -23,6 +34,24 @@ export class DataserverServerSideDatasource extends BaseServerSideDatasource {
|
|
|
23
34
|
* @internal
|
|
24
35
|
*/
|
|
25
36
|
this.deliveredRowCount = 0;
|
|
37
|
+
/**
|
|
38
|
+
* Ids of rows the server deleted that stay in the cache because the grid was not told
|
|
39
|
+
* (`live-updates` off) and they sit below the delivered watermark - see
|
|
40
|
+
* handleCurrentStreamLoad. They are not reported to the host as rows the grid holds, and
|
|
41
|
+
* their presence marks the cache out of step, so a re-read purges rather than serves them.
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
this.retainedDeletedRowIds = new Set();
|
|
45
|
+
/**
|
|
46
|
+
* Set when a pushed INSERT landed after rows were handed to the grid, so the cache's insertion
|
|
47
|
+
* order no longer matches the grid's row indices. Cleared when the cache is rebuilt.
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
this.structuralPushPending = false;
|
|
51
|
+
/** Whether the first batch of the current subscription has arrived. @internal */
|
|
52
|
+
this.streamStarted = false;
|
|
53
|
+
/** Whether a MORE_ROWS request is awaiting its batch. @internal */
|
|
54
|
+
this.moreRowsInFlight = false;
|
|
26
55
|
this.createDataserverStreamFunc = options.createDataserverStreamFunc;
|
|
27
56
|
}
|
|
28
57
|
refreshDatasource(params) {
|
|
@@ -36,6 +65,10 @@ export class DataserverServerSideDatasource extends BaseServerSideDatasource {
|
|
|
36
65
|
this.dataserverStreamSubscription = undefined;
|
|
37
66
|
this.dataserverStream = undefined;
|
|
38
67
|
this.deliveredRowCount = 0;
|
|
68
|
+
this.retainedDeletedRowIds.clear();
|
|
69
|
+
this.structuralPushPending = false;
|
|
70
|
+
this.streamStarted = false;
|
|
71
|
+
this.moreRowsInFlight = false;
|
|
39
72
|
yield _super.refreshDatasource.call(this, params);
|
|
40
73
|
});
|
|
41
74
|
}
|
|
@@ -69,6 +102,16 @@ export class DataserverServerSideDatasource extends BaseServerSideDatasource {
|
|
|
69
102
|
// when it already covers this block, or the stream ended, no further stream messages
|
|
70
103
|
// will arrive for this request - resolve it synchronously from the cache.
|
|
71
104
|
const blockAlreadyCovered = this.currentSequenceId >= 1 && (this.rowData.size >= blockEndRow || !this.moreRows);
|
|
105
|
+
// A re-read is the moment the grid asks for current rows. When the cache holds a change the
|
|
106
|
+
// grid never saw (a DELETE it was not told about), serving the slice would hand back the
|
|
107
|
+
// deleted row, so refresh instead: the grid drops every block and re-reads from the server.
|
|
108
|
+
// fail() releases the request slot, as for a superseded filter or sort change above.
|
|
109
|
+
if (blockAlreadyCovered && this.cacheOutOfStep) {
|
|
110
|
+
logger.debug(`SSRM block ${blockStartRow}-${blockEndRow} for ${this.resourceName} re-read while the cache is out of step with the grid; refreshing`);
|
|
111
|
+
yield this.refreshDatasource(params);
|
|
112
|
+
params.fail();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
72
115
|
if (this.currentSequenceId >= 1 &&
|
|
73
116
|
!blockAlreadyCovered &&
|
|
74
117
|
(this.moreRows || params.request.startRow >= Number(this.maxRows))) {
|
|
@@ -76,6 +119,7 @@ export class DataserverServerSideDatasource extends BaseServerSideDatasource {
|
|
|
76
119
|
? Number(this.resourceParams.VIEW_NUMBER)
|
|
77
120
|
: 0;
|
|
78
121
|
if (Number.isFinite(viewNumber)) {
|
|
122
|
+
this.moreRowsInFlight = true;
|
|
79
123
|
this.connect.getMoreRows(this.sourceRef, viewNumber);
|
|
80
124
|
}
|
|
81
125
|
}
|
|
@@ -109,6 +153,7 @@ export class DataserverServerSideDatasource extends BaseServerSideDatasource {
|
|
|
109
153
|
successRowData.rowCount = this.getCorrectRowCount(params);
|
|
110
154
|
this.lastSuccessRowData = successRowData;
|
|
111
155
|
params.success(successRowData);
|
|
156
|
+
this.deliverRows(blockStartRow, successRowData.rowData.filter((row) => !this.retainedDeletedRowIds.has(row === null || row === void 0 ? void 0 : row[this.rowId])));
|
|
112
157
|
this.notifyNoDataAvailableIfEmpty(params, successRowData);
|
|
113
158
|
};
|
|
114
159
|
const failBlock = (timer) => {
|
|
@@ -191,7 +236,7 @@ export class DataserverServerSideDatasource extends BaseServerSideDatasource {
|
|
|
191
236
|
* @internal
|
|
192
237
|
*/
|
|
193
238
|
dispatchStreamMessage(dataserverResult) {
|
|
194
|
-
var _a;
|
|
239
|
+
var _a, _b, _c, _d, _e;
|
|
195
240
|
const messageType = dataserverResult === null || dataserverResult === void 0 ? void 0 : dataserverResult.MESSAGE_TYPE;
|
|
196
241
|
if (messageType &&
|
|
197
242
|
(messageType === MessageType.LOGOFF_ACK || messageType === MessageType.MSG_NACK)) {
|
|
@@ -204,13 +249,28 @@ export class DataserverServerSideDatasource extends BaseServerSideDatasource {
|
|
|
204
249
|
this.moreRows = dataserverResult.MORE_ROWS;
|
|
205
250
|
}
|
|
206
251
|
if (dataserverResult === null || dataserverResult === void 0 ? void 0 : dataserverResult.ROW) {
|
|
207
|
-
const nextMessage =
|
|
208
|
-
|
|
252
|
+
const nextMessage = filterDataserverResult(dataserverResult, this.rowId);
|
|
253
|
+
// A DATASERVER subscription is live by definition, so every batch is either the page a
|
|
254
|
+
// block asked for or a change the server pushed. Replies only ever carry view rows
|
|
255
|
+
// (INSERTs), so a batch holding a MODIFY or a DELETE is a push whenever it arrives.
|
|
256
|
+
// Otherwise the batch is the reply we are waiting on: a request is outstanding
|
|
257
|
+
// (`moreRowsInFlight`), a block is pending, or this is the logon batch.
|
|
258
|
+
//
|
|
259
|
+
// `pendingBlocks.size === 0` alone is not enough: the 10s safety valve settles a block
|
|
260
|
+
// short while its MORE_ROWS is still outstanding, and GSF splits one reply across
|
|
261
|
+
// messages, so the real page can arrive with no block waiting. Treating that as a push
|
|
262
|
+
// prepended it to the cache and shifted every later block's slice against the grid.
|
|
263
|
+
const carriesPushedChanges = ((_b = (_a = nextMessage.updates) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0 || ((_d = (_c = nextMessage.deletes) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) > 0;
|
|
264
|
+
const solicited = !carriesPushedChanges &&
|
|
265
|
+
(this.moreRowsInFlight || this.pendingBlocks.size > 0 || !this.streamStarted);
|
|
266
|
+
this.moreRowsInFlight = false;
|
|
267
|
+
this.streamStarted = true;
|
|
268
|
+
this.handleCurrentStreamLoad(nextMessage, solicited);
|
|
209
269
|
this.currentSequenceId = dataserverResult.SEQUENCE_ID;
|
|
210
270
|
if (this.currentSequenceId === 1) {
|
|
211
271
|
this.sourceRef = dataserverResult.SOURCE_REF;
|
|
212
272
|
if (this.serverRowsCount === 0) {
|
|
213
|
-
this.serverRowsCount = (
|
|
273
|
+
this.serverRowsCount = (_e = dataserverResult.ROWS_COUNT) !== null && _e !== void 0 ? _e : this.rowData.size;
|
|
214
274
|
}
|
|
215
275
|
}
|
|
216
276
|
}
|
|
@@ -290,22 +350,40 @@ export class DataserverServerSideDatasource extends BaseServerSideDatasource {
|
|
|
290
350
|
(_a = this.dataserverStreamSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
|
|
291
351
|
this.dataserverStreamSubscription = undefined;
|
|
292
352
|
this.deliveredRowCount = 0;
|
|
353
|
+
this.retainedDeletedRowIds.clear();
|
|
354
|
+
this.structuralPushPending = false;
|
|
355
|
+
this.streamStarted = false;
|
|
356
|
+
this.moreRowsInFlight = false;
|
|
293
357
|
yield _super.destroy.call(this);
|
|
294
358
|
});
|
|
295
359
|
}
|
|
296
|
-
handleCurrentStreamLoad(result) {
|
|
360
|
+
handleCurrentStreamLoad(result, solicited) {
|
|
297
361
|
var _a, _b, _c, _d, _e;
|
|
298
362
|
if (!result)
|
|
299
363
|
return;
|
|
300
364
|
// Infinite scroll accumulates a session-cumulative cache, as GSF splits batches across
|
|
301
365
|
// messages and later batches append. Map preserves insertion order, so the cache can be
|
|
302
|
-
// sliced by block range
|
|
366
|
+
// sliced by block range - which only works while the cache and the grid agree on where
|
|
367
|
+
// every delivered row sits, so a pushed change below the delivered watermark is kept out of
|
|
368
|
+
// the delivered region and deferred to a refresh.
|
|
303
369
|
const rows = new Map(this.rowData);
|
|
304
370
|
(_a = result.inserts) === null || _a === void 0 ? void 0 : _a.forEach((insertData) => {
|
|
305
|
-
|
|
371
|
+
const key = insertData[this.rowId];
|
|
372
|
+
this.retainedDeletedRowIds.delete(key);
|
|
373
|
+
// A key the cache already holds keeps its slot, so nothing shifts - that is how a DELETE
|
|
374
|
+
// that was tombstoned and then re-inserted puts the cache back in step with the grid.
|
|
375
|
+
const isNewToCache = !rows.has(key);
|
|
376
|
+
rows.set(key, insertData);
|
|
377
|
+
if (!solicited && isNewToCache && this.deliveredRowCount > 0) {
|
|
378
|
+
// A page appends in view order, so it keeps the cache and the grid aligned. A push does
|
|
379
|
+
// not: nothing in the batch says where the server sorts the new row, and the host puts
|
|
380
|
+
// it at the top of a live grid, so from here on the cache cannot be sliced against the
|
|
381
|
+
// grid's indices. Mark it, and let the next read of a covered block refresh instead.
|
|
382
|
+
this.structuralPushPending = true;
|
|
383
|
+
}
|
|
306
384
|
});
|
|
307
385
|
// Live QUERY_UPDATE messages can arrive while a block is pending: MODIFY rows may be
|
|
308
|
-
// partial, so merge into the cached row instead of clobbering it
|
|
386
|
+
// partial, so merge into the cached row instead of clobbering it.
|
|
309
387
|
(_b = result.updates) === null || _b === void 0 ? void 0 : _b.forEach((updateData) => {
|
|
310
388
|
const key = updateData === null || updateData === void 0 ? void 0 : updateData[this.rowId];
|
|
311
389
|
if (key === undefined) {
|
|
@@ -316,28 +394,37 @@ export class DataserverServerSideDatasource extends BaseServerSideDatasource {
|
|
|
316
394
|
});
|
|
317
395
|
(_c = result.deletes) === null || _c === void 0 ? void 0 : _c.forEach((deleteData) => {
|
|
318
396
|
const key = deleteData === null || deleteData === void 0 ? void 0 : deleteData[this.rowId];
|
|
319
|
-
if (key === undefined) {
|
|
397
|
+
if (key === undefined || !rows.has(key)) {
|
|
320
398
|
return;
|
|
321
399
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
let
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
index += 1;
|
|
334
|
-
}
|
|
400
|
+
const index = this.cacheIndexOf(rows, key);
|
|
401
|
+
if (index < this.deliveredRowCount) {
|
|
402
|
+
// The grid still shows the row at this index. Dropping it would shift every later
|
|
403
|
+
// block's slice against the grid, so keep its slot and let the next re-read refresh.
|
|
404
|
+
//
|
|
405
|
+
// Whether the host also removed it from the grid is not knowable here: the host applies
|
|
406
|
+
// live updates from a second, independent DATA_LOGON whose view is not paged with this
|
|
407
|
+
// one, so a delete this stream sees need not have reached the grid at all. Keeping the
|
|
408
|
+
// slot is correct either way - the refresh reconciles both.
|
|
409
|
+
this.retainedDeletedRowIds.add(key);
|
|
410
|
+
return;
|
|
335
411
|
}
|
|
336
412
|
rows.delete(key);
|
|
337
413
|
});
|
|
338
414
|
this.clientRowsCount += (_e = (_d = result.inserts) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0;
|
|
339
415
|
this.rowData = rows;
|
|
340
416
|
}
|
|
417
|
+
/** Position of a key in the cache's insertion order. @internal */
|
|
418
|
+
cacheIndexOf(rows, key) {
|
|
419
|
+
let index = 0;
|
|
420
|
+
for (const mapKey of rows.keys()) {
|
|
421
|
+
if (mapKey === key) {
|
|
422
|
+
return index;
|
|
423
|
+
}
|
|
424
|
+
index += 1;
|
|
425
|
+
}
|
|
426
|
+
return -1;
|
|
427
|
+
}
|
|
341
428
|
}
|
|
342
429
|
/**
|
|
343
430
|
* Max time a non-paginated SSRM block may stay pending before the safety valve in
|
|
@@ -39,6 +39,9 @@ export class ReqRepServerSideDatasource extends BaseServerSideDatasource {
|
|
|
39
39
|
}
|
|
40
40
|
getRows(params) {
|
|
41
41
|
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
// Taken before the first await: a reload that lands anywhere between here and the reply
|
|
43
|
+
// means the block cache this request was read for is gone.
|
|
44
|
+
const generation = this.loadGeneration;
|
|
42
45
|
if (yield this.setupFiltering(params)) {
|
|
43
46
|
params.fail();
|
|
44
47
|
return;
|
|
@@ -54,6 +57,14 @@ export class ReqRepServerSideDatasource extends BaseServerSideDatasource {
|
|
|
54
57
|
DETAILS: this.buildRequestDetails(),
|
|
55
58
|
};
|
|
56
59
|
const requestResult = yield this.createReqRepRequestFunc(requestParams);
|
|
60
|
+
// The cache this block was read for was dropped while the request was in flight (a filter,
|
|
61
|
+
// sort or criteria change): the reply must not be handed to the host as rows the grid holds.
|
|
62
|
+
// fail() still frees AG Grid's concurrent-request slot; the purge already dropped the block.
|
|
63
|
+
if (generation !== this.loadGeneration) {
|
|
64
|
+
logger.debug('SSRM req/rep reply discarded: the block cache was reset while it was in flight');
|
|
65
|
+
params.fail();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
57
68
|
// Apply the result to the grid
|
|
58
69
|
this.applyServerSideData(params, requestResult);
|
|
59
70
|
// If polling enabled, schedule stream creation after user stops scrolling
|
|
@@ -117,13 +128,18 @@ export class ReqRepServerSideDatasource extends BaseServerSideDatasource {
|
|
|
117
128
|
successRowData.rowCount = this.getCorrectRowCount(params);
|
|
118
129
|
this.lastSuccessRowData = successRowData;
|
|
119
130
|
params.success(successRowData);
|
|
131
|
+
this.deliverRows(params.request.startRow, successRowData.rowData);
|
|
120
132
|
this.notifyNoDataAvailableIfEmpty(params, successRowData);
|
|
121
133
|
}
|
|
122
134
|
getCorrectRowCount(params) {
|
|
123
135
|
let rowCount;
|
|
124
136
|
if (this.moreRows) {
|
|
125
|
-
// More data
|
|
126
|
-
rowCount
|
|
137
|
+
// More data may follow, so the total is not known yet. It has to be left undefined:
|
|
138
|
+
// `rowCount` is a row count, not the infinite row model's `lastRow`, so -1 is taken
|
|
139
|
+
// literally. A store told it holds -1 rows never reaches the block it just received,
|
|
140
|
+
// so AG Grid asks for the same blocks again as fast as it can - a request loop that
|
|
141
|
+
// pins the main thread and floods the server with REQUEST_SERVER snapshots.
|
|
142
|
+
rowCount = undefined;
|
|
127
143
|
}
|
|
128
144
|
else {
|
|
129
145
|
// Use discovered total if available, otherwise calculate from current position
|
|
@@ -281,7 +297,13 @@ export class ReqRepServerSideDatasource extends BaseServerSideDatasource {
|
|
|
281
297
|
return {
|
|
282
298
|
CRITERIA_MATCH: this.resourceParams.DETAILS.CRITERIA_MATCH,
|
|
283
299
|
MAX_ROWS: this.resourceParams.DETAILS.MAX_ROWS,
|
|
284
|
-
|
|
300
|
+
// Fall back to the row id when the user has not sorted, as the infinite datasource does.
|
|
301
|
+
// OFFSET paging is only correct over a stable order: an unsorted req/rep returns rows in
|
|
302
|
+
// an unspecified order that differs between requests, so consecutive pages overlap and
|
|
303
|
+
// skip. AG Grid then receives the same row id in two blocks, refuses the store it just
|
|
304
|
+
// built, and re-requests both blocks - a loop that runs as fast as the server can answer.
|
|
305
|
+
// The row id is unique and always sortable, giving every page a deterministic window.
|
|
306
|
+
ORDER_BY: this.resourceParams.DETAILS.ORDER_BY || `${this.rowId} ASC`,
|
|
285
307
|
OFFSET: this.resourceParams.DETAILS.OFFSET,
|
|
286
308
|
};
|
|
287
309
|
}
|
|
@@ -484,11 +484,25 @@ export class GridProBeta extends LifecycleMixin(FoundationElement) {
|
|
|
484
484
|
*/
|
|
485
485
|
handleDataChanged(event) {
|
|
486
486
|
const { changes } = event.detail;
|
|
487
|
-
|
|
487
|
+
// Every datasource emits this for consumers, but only the client-side row model takes row
|
|
488
|
+
// transactions: the infinite and server-side datasources drive the grid through
|
|
489
|
+
// refresh-infinite-cache / apply-server-side-transaction, and applyTransaction on those
|
|
490
|
+
// models is a no-op that logs an AG Grid warning per event.
|
|
491
|
+
if (!this.gridApi || !this.isClientSideRowModel)
|
|
488
492
|
return;
|
|
489
493
|
const result = this.gridApi.applyTransaction(changes);
|
|
490
494
|
this.flashAddedCells(result === null || result === void 0 ? void 0 : result.add);
|
|
491
495
|
}
|
|
496
|
+
/**
|
|
497
|
+
* Whether the live grid runs AG Grid's client-side row model (the default when the datasource
|
|
498
|
+
* set no `rowModelType`).
|
|
499
|
+
* @internal
|
|
500
|
+
*/
|
|
501
|
+
get isClientSideRowModel() {
|
|
502
|
+
var _a, _b, _c;
|
|
503
|
+
const rowModelType = (_c = (_b = (_a = this.gridApi) === null || _a === void 0 ? void 0 : _a.getGridOption) === null || _b === void 0 ? void 0 : _b.call(_a, 'rowModelType')) !== null && _c !== void 0 ? _c : 'clientSide';
|
|
504
|
+
return rowModelType === 'clientSide';
|
|
505
|
+
}
|
|
492
506
|
flashAddedCells(rowNodes) {
|
|
493
507
|
if (this.enableRowFlashing && (rowNodes === null || rowNodes === void 0 ? void 0 : rowNodes.length)) {
|
|
494
508
|
this.gridApi.flashCells({ rowNodes });
|
|
@@ -601,7 +615,11 @@ export class GridProBeta extends LifecycleMixin(FoundationElement) {
|
|
|
601
615
|
if (includeSchema) {
|
|
602
616
|
this.gridApi.setGridOption('columnDefs', []);
|
|
603
617
|
}
|
|
604
|
-
|
|
618
|
+
// rowData belongs to the client-side row model; the infinite and server-side caches are
|
|
619
|
+
// cleared by their own refresh / transaction events.
|
|
620
|
+
if (this.isClientSideRowModel) {
|
|
621
|
+
this.gridApi.setGridOption('rowData', []);
|
|
622
|
+
}
|
|
605
623
|
this.gridApi.refreshCells({ force: true });
|
|
606
624
|
}
|
|
607
625
|
/**
|
package/dist/esm/grid-pro.js
CHANGED
|
@@ -414,7 +414,10 @@ export class GridPro extends LifecycleMixin(FoundationElement) {
|
|
|
414
414
|
handleDataChanged(event) {
|
|
415
415
|
var _a, _b, _c;
|
|
416
416
|
const { changes } = event.detail;
|
|
417
|
-
|
|
417
|
+
// Every datasource emits this for consumers, but row transactions are a client-side row
|
|
418
|
+
// model API: the server-side datasource drives the grid through apply-server-side-transaction,
|
|
419
|
+
// and applyTransaction on that model is a no-op that logs an AG Grid warning per event.
|
|
420
|
+
if (!this.gridApi || this.isServerSide)
|
|
418
421
|
return;
|
|
419
422
|
if (((_a = changes.add) === null || _a === void 0 ? void 0 : _a.length) > 0) {
|
|
420
423
|
if (this.asyncAdd) {
|