@rickcedwhat/playwright-smart-table 6.25.0-next.c784f2c → 6.25.0-next.d8860d0
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/engine/cellNavigation.d.ts +24 -0
- package/dist/engine/cellNavigation.js +329 -0
- package/dist/engine/countRows.d.ts +28 -0
- package/dist/engine/countRows.js +149 -0
- package/dist/engine/rowByIndex.d.ts +20 -0
- package/dist/engine/rowByIndex.js +79 -0
- package/dist/engine/rowFinder.d.ts +3 -1
- package/dist/engine/rowFinder.js +5 -7
- package/dist/engine/rowResolution.d.ts +2 -2
- package/dist/engine/rowResolution.js +11 -7
- package/dist/engine/rowSnapshot.d.ts +28 -0
- package/dist/engine/rowSnapshot.js +422 -0
- package/dist/engine/scanPages.d.ts +7 -2
- package/dist/engine/scanPages.js +11 -0
- package/dist/engine/sorting.d.ts +11 -0
- package/dist/engine/sorting.js +51 -0
- package/dist/engine/tableIteration.js +1 -0
- package/dist/engine/tableMapper.js +16 -5
- package/dist/index.d.ts +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/smartRow.js +12 -692
- package/dist/strategies/columns.d.ts +1 -1
- package/dist/strategies/contentReady.d.ts +19 -1
- package/dist/strategies/contentReady.js +81 -0
- package/dist/strategies/dedupe.d.ts +5 -2
- package/dist/strategies/dedupe.js +5 -2
- package/dist/strategies/index.d.ts +5 -0
- package/dist/types.d.ts +36 -8
- package/dist/useTable.js +41 -289
- package/dist/utils/deadline.d.ts +2 -0
- package/dist/utils/deadline.js +19 -0
- package/dist/utils/memoizeViewport.d.ts +6 -0
- package/dist/utils/memoizeViewport.js +28 -0
- package/dist/utils/navigationBarrier.js +0 -1
- package/dist/utils/safeStringify.d.ts +2 -0
- package/dist/utils/safeStringify.js +18 -0
- package/dist/utils/smartRowArray.d.ts +1 -1
- package/dist/utils/smartRowArray.js +4 -1
- package/package.json +1 -1
package/dist/useTable.js
CHANGED
|
@@ -27,29 +27,15 @@ const filterEngine_1 = require("./filterEngine");
|
|
|
27
27
|
const tableMapper_1 = require("./engine/tableMapper");
|
|
28
28
|
const rowFinder_1 = require("./engine/rowFinder");
|
|
29
29
|
const tableIteration_1 = require("./engine/tableIteration");
|
|
30
|
-
const
|
|
31
|
-
const
|
|
30
|
+
const countRows_1 = require("./engine/countRows");
|
|
31
|
+
const sorting_1 = require("./engine/sorting");
|
|
32
|
+
const rowByIndex_1 = require("./engine/rowByIndex");
|
|
33
|
+
const safeStringify_1 = require("./utils/safeStringify");
|
|
32
34
|
const debugUtils_1 = require("./utils/debugUtils");
|
|
33
35
|
const smartRowArray_1 = require("./utils/smartRowArray");
|
|
34
|
-
const elementTracker_1 = require("./utils/elementTracker");
|
|
35
|
-
const loadingWait_1 = require("./utils/loadingWait");
|
|
36
36
|
const pageIndex_1 = require("./utils/pageIndex");
|
|
37
37
|
const smartRowInternals_1 = require("./utils/smartRowInternals");
|
|
38
38
|
const locatorDescription_1 = require("./utils/locatorDescription");
|
|
39
|
-
// Helper to safely serialize objects containing functions for logging
|
|
40
|
-
const safeStringify = (obj) => {
|
|
41
|
-
try {
|
|
42
|
-
return JSON.stringify(obj, (_key, value) => {
|
|
43
|
-
if (typeof value === 'function') {
|
|
44
|
-
return '[Function]';
|
|
45
|
-
}
|
|
46
|
-
return value;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
catch (e) {
|
|
50
|
-
return '[unserializable]';
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
39
|
/**
|
|
54
40
|
* Main hook to interact with a table.
|
|
55
41
|
*/
|
|
@@ -67,52 +53,6 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
67
53
|
}
|
|
68
54
|
};
|
|
69
55
|
const config = Object.assign(Object.assign({ rowSelector: "tbody tr", headerSelector: "thead th", cellSelector: "td", maxPages: 1, headerTransformer: ({ text }) => text, autoScroll: true, onReset: async () => { } }, configOptions), { strategies: Object.assign(Object.assign({}, defaultStrategies), configOptions.strategies) });
|
|
70
|
-
// Automatically memoize viewport range oracles so user-provided functions are
|
|
71
|
-
// pure DOM queries with no cache bookkeeping. Invalidated after each scroll call.
|
|
72
|
-
let _clearViewportCache = null;
|
|
73
|
-
if (config.strategies.viewport && !config.strategies.viewport.disableCache) {
|
|
74
|
-
const vp = config.strategies.viewport;
|
|
75
|
-
let colRangeCache = null;
|
|
76
|
-
let rowRangeCache = null;
|
|
77
|
-
let rowIndicesCache = null;
|
|
78
|
-
_clearViewportCache = () => {
|
|
79
|
-
colRangeCache = null;
|
|
80
|
-
rowRangeCache = null;
|
|
81
|
-
rowIndicesCache = null;
|
|
82
|
-
};
|
|
83
|
-
config.strategies.viewport = Object.assign(Object.assign({}, vp), { getVisibleColumnRange: vp.getVisibleColumnRange
|
|
84
|
-
? async (ctx) => {
|
|
85
|
-
if (!colRangeCache)
|
|
86
|
-
colRangeCache = await vp.getVisibleColumnRange(ctx);
|
|
87
|
-
return colRangeCache;
|
|
88
|
-
}
|
|
89
|
-
: undefined, getVisibleRowRange: vp.getVisibleRowRange
|
|
90
|
-
? async (ctx) => {
|
|
91
|
-
if (!rowRangeCache)
|
|
92
|
-
rowRangeCache = await vp.getVisibleRowRange(ctx);
|
|
93
|
-
return rowRangeCache;
|
|
94
|
-
}
|
|
95
|
-
: undefined, getVisibleRowIndices: vp.getVisibleRowIndices
|
|
96
|
-
? async (ctx) => {
|
|
97
|
-
if (!rowIndicesCache)
|
|
98
|
-
rowIndicesCache = await vp.getVisibleRowIndices(ctx);
|
|
99
|
-
return rowIndicesCache;
|
|
100
|
-
}
|
|
101
|
-
: undefined, scrollToColumn: vp.scrollToColumn
|
|
102
|
-
? async (ctx, colIndex) => {
|
|
103
|
-
colRangeCache = null;
|
|
104
|
-
rowRangeCache = null;
|
|
105
|
-
rowIndicesCache = null;
|
|
106
|
-
await vp.scrollToColumn(ctx, colIndex);
|
|
107
|
-
}
|
|
108
|
-
: undefined, scrollToRow: vp.scrollToRow
|
|
109
|
-
? async (ctx, rowIndex) => {
|
|
110
|
-
rowRangeCache = null;
|
|
111
|
-
rowIndicesCache = null;
|
|
112
|
-
await vp.scrollToRow(ctx, rowIndex);
|
|
113
|
-
}
|
|
114
|
-
: undefined });
|
|
115
|
-
}
|
|
116
56
|
const resolve = (item, parent) => {
|
|
117
57
|
if (typeof item === 'string')
|
|
118
58
|
return parent.locator(item);
|
|
@@ -227,10 +167,6 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
227
167
|
if (pagesJumped > 0) {
|
|
228
168
|
tableState.currentPageIndex += pagesJumped;
|
|
229
169
|
log(`_advancePage: ${primitive} advanced ${pagesJumped} page(s) — now at page ${tableState.currentPageIndex}`);
|
|
230
|
-
// Invalidate viewport caches: after a page advance the row set changes and the
|
|
231
|
-
// column renderer may recalculate (e.g. scrollbar appearance changes available width).
|
|
232
|
-
if (_clearViewportCache)
|
|
233
|
-
_clearViewportCache();
|
|
234
170
|
await (0, debugUtils_1.debugDelay)(config, 'pagination');
|
|
235
171
|
}
|
|
236
172
|
else {
|
|
@@ -238,6 +174,11 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
238
174
|
}
|
|
239
175
|
return didAdvance;
|
|
240
176
|
};
|
|
177
|
+
const sortingEnv = () => ({
|
|
178
|
+
config,
|
|
179
|
+
getPage: () => rootLocator.page(),
|
|
180
|
+
getContext: () => (Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell })),
|
|
181
|
+
});
|
|
241
182
|
const result = {
|
|
242
183
|
get currentPageIndex() { return tableState.currentPageIndex; },
|
|
243
184
|
set currentPageIndex(v) {
|
|
@@ -326,133 +267,24 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
326
267
|
return resolve(config.headerSelector, rootLocator).nth(idx);
|
|
327
268
|
},
|
|
328
269
|
countRows: async (filters, options) => {
|
|
329
|
-
var _a, _b, _c, _d;
|
|
330
|
-
var _e;
|
|
331
270
|
if (tableState.empty)
|
|
332
271
|
return 0;
|
|
333
272
|
await _autoInit();
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
else if ((_c = (_b = config.columnOverrides) === null || _b === void 0 ? void 0 : _b[key]) === null || _c === void 0 ? void 0 : _c.read) {
|
|
348
|
-
overrideFilters[key] = value;
|
|
349
|
-
}
|
|
350
|
-
else {
|
|
351
|
-
domFilters[key] = value;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
const hasPostFilters = Object.keys(overrideFilters).length > 0 || Object.keys(syntheticFilters).length > 0;
|
|
356
|
-
const resolveRows = () => {
|
|
357
|
-
var _a;
|
|
358
|
-
let rows = resolve(config.rowSelector, rootLocator);
|
|
359
|
-
if (Object.keys(domFilters).length > 0) {
|
|
360
|
-
const map = tableMapper.getMapSync();
|
|
361
|
-
if (!map)
|
|
362
|
-
throw new Error('Initialization Error: Table map not available. Call "await table.init()" first.');
|
|
363
|
-
rows = filterEngine.applyFilters(rows, domFilters, map, (_a = options === null || options === void 0 ? void 0 : options.exact) !== null && _a !== void 0 ? _a : false, rootLocator.page(), rootLocator);
|
|
364
|
-
}
|
|
365
|
-
return rows;
|
|
366
|
-
};
|
|
367
|
-
const countPostFilterMatches = async (candidates, indices) => {
|
|
368
|
-
var _a;
|
|
369
|
-
if (!hasPostFilters)
|
|
370
|
-
return indices.length;
|
|
371
|
-
const map = tableMapper.getMapSync();
|
|
372
|
-
if (!map)
|
|
373
|
-
throw new Error('Initialization Error: Table map not available. Call "await table.init()" first.');
|
|
374
|
-
const exact = (_a = options === null || options === void 0 ? void 0 : options.exact) !== null && _a !== void 0 ? _a : false;
|
|
375
|
-
const hasOverrides = Object.keys(overrideFilters).length > 0;
|
|
376
|
-
const hasSynthetics = Object.keys(syntheticFilters).length > 0;
|
|
377
|
-
let count = 0;
|
|
378
|
-
for (const idx of indices) {
|
|
379
|
-
const row = candidates[idx];
|
|
380
|
-
if (hasOverrides && !await rowFinder.matchesOverrideFilters(row, overrideFilters, map, exact))
|
|
381
|
-
continue;
|
|
382
|
-
if (hasSynthetics && !await rowFinder.matchesSyntheticFilters(row, syntheticFilters, map, exact))
|
|
383
|
-
continue;
|
|
384
|
-
count++;
|
|
385
|
-
}
|
|
386
|
-
return count;
|
|
387
|
-
};
|
|
388
|
-
// Wait for table to finish loading before counting
|
|
389
|
-
const isTableLoading = (_d = config.strategies.loading) === null || _d === void 0 ? void 0 : _d.isTableLoading;
|
|
390
|
-
if (isTableLoading) {
|
|
391
|
-
const ctx = createStrategyContext();
|
|
392
|
-
await (0, loadingWait_1.waitWhileTableLoading)(config, ctx, rootLocator.page(), 'countRows');
|
|
393
|
-
}
|
|
394
|
-
if (!hasPagination) {
|
|
395
|
-
log(`countRows: counting rows in current viewport (no pagination)${hasFilters ? ` filters=${safeStringify(filtersRecord)}` : ''}`);
|
|
396
|
-
if (!hasPostFilters)
|
|
397
|
-
return resolveRows().count();
|
|
398
|
-
const all = await resolveRows().all();
|
|
399
|
-
return countPostFilterMatches(all, all.map((_, i) => i));
|
|
400
|
-
}
|
|
401
|
-
log(`countRows: paginating up to ${effectiveMaxPages} page(s)${hasFilters ? ` filters=${safeStringify(filtersRecord)}` : ''}`);
|
|
402
|
-
const tracker = new elementTracker_1.ElementTracker('countRows');
|
|
403
|
-
let total = 0;
|
|
404
|
-
let pagesScanned = 1;
|
|
405
|
-
let paginationError;
|
|
406
|
-
try {
|
|
407
|
-
const scanned = await (0, scanPages_1.scanPages)({
|
|
408
|
-
advancePage: _advancePage,
|
|
409
|
-
getCurrentPageIndex: () => tableState.currentPageIndex,
|
|
410
|
-
config,
|
|
411
|
-
waitForReady: async () => {
|
|
412
|
-
if (!isTableLoading)
|
|
413
|
-
return;
|
|
414
|
-
const ctx = createStrategyContext();
|
|
415
|
-
await (0, loadingWait_1.waitWhileTableLoading)(config, ctx, rootLocator.page(), 'countRows');
|
|
416
|
-
},
|
|
417
|
-
}, async ({ pagesScanned: pageNum }) => {
|
|
418
|
-
const rowLocators = resolveRows();
|
|
419
|
-
const newIndices = await tracker.getUnseenIndices(rowLocators);
|
|
420
|
-
const candidates = await rowLocators.all();
|
|
421
|
-
const matched = await countPostFilterMatches(candidates, newIndices);
|
|
422
|
-
total += matched;
|
|
423
|
-
log(`countRows: page ${pageNum} — ${matched} row(s) (running total: ${total})`);
|
|
424
|
-
return 'continue';
|
|
425
|
-
}, {
|
|
426
|
-
maxPages: effectiveMaxPages,
|
|
427
|
-
useBulk: false,
|
|
428
|
-
label: 'countRows',
|
|
429
|
-
finalScanOnEof: true,
|
|
430
|
-
});
|
|
431
|
-
pagesScanned = scanned.pagesScanned;
|
|
432
|
-
}
|
|
433
|
-
catch (e) {
|
|
434
|
-
paginationError = e;
|
|
435
|
-
throw e;
|
|
436
|
-
}
|
|
437
|
-
finally {
|
|
438
|
-
await tracker.cleanup(rootLocator.page());
|
|
439
|
-
// Always restore page 1, even if pagination threw mid-count (#348), so the table
|
|
440
|
-
// stays usable for callers that catch the error. If reset() itself fails, only let
|
|
441
|
-
// it surface when pagination succeeded — otherwise preserve the original error.
|
|
442
|
-
log(`countRows: ${total} total row(s) across ${pagesScanned} page(s) — resetting`);
|
|
443
|
-
try {
|
|
444
|
-
await result.reset();
|
|
445
|
-
}
|
|
446
|
-
catch (resetError) {
|
|
447
|
-
if (paginationError === undefined)
|
|
448
|
-
throw resetError;
|
|
449
|
-
(0, debugUtils_1.logDebug)(config, 'error', 'countRows: reset() failed after pagination error', resetError);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
return total;
|
|
273
|
+
return (0, countRows_1.countRows)({
|
|
274
|
+
config,
|
|
275
|
+
rootLocator,
|
|
276
|
+
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
277
|
+
tableMapper,
|
|
278
|
+
filterEngine,
|
|
279
|
+
rowFinder,
|
|
280
|
+
advancePage: _advancePage,
|
|
281
|
+
getCurrentPageIndex: () => tableState.currentPageIndex,
|
|
282
|
+
getContext: () => createStrategyContext(),
|
|
283
|
+
reset: () => result.reset(),
|
|
284
|
+
}, filters, options);
|
|
453
285
|
},
|
|
454
286
|
mapColumn: async (columnName, options = {}) => {
|
|
455
|
-
log(`mapColumn: column="${columnName}" options=${safeStringify(options)}`);
|
|
287
|
+
log(`mapColumn: column="${columnName}" options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
456
288
|
await _autoInit();
|
|
457
289
|
const map = await tableMapper.getMap();
|
|
458
290
|
if (!map.has(columnName))
|
|
@@ -477,7 +309,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
477
309
|
* Iterates over rows and extracts the value of a single column as strings.
|
|
478
310
|
*/
|
|
479
311
|
getColumnValues: async (columnName, options = {}) => {
|
|
480
|
-
log(`getColumnValues: column="${columnName}" options=${safeStringify(options)}`);
|
|
312
|
+
log(`getColumnValues: column="${columnName}" options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
481
313
|
const values = await result.mapColumn(columnName, options);
|
|
482
314
|
return values.map(v => String(v));
|
|
483
315
|
},
|
|
@@ -505,7 +337,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
505
337
|
log("Table revalidated.");
|
|
506
338
|
},
|
|
507
339
|
getRow: (filters, options = { exact: false }) => {
|
|
508
|
-
log(`getRow: filters=${safeStringify(filters)} exact=${options.exact}`);
|
|
340
|
+
log(`getRow: filters=${(0, safeStringify_1.safeStringify)(filters)} exact=${options.exact}`);
|
|
509
341
|
const syntheticKeys = Object.keys(filters).filter(k => { var _a; return (_a = config.syntheticColumns) === null || _a === void 0 ? void 0 : _a[k]; });
|
|
510
342
|
if (syntheticKeys.length > 0) {
|
|
511
343
|
throw new Error(`getRow() cannot filter by synthetic column(s): ${syntheticKeys.join(', ')}. ` +
|
|
@@ -533,67 +365,23 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
533
365
|
return _makeSmart(rowLocator, map, index, undefined, undefined, undefined, true);
|
|
534
366
|
},
|
|
535
367
|
findRowByIndex: async (index, options) => {
|
|
536
|
-
var _a, _b;
|
|
537
|
-
log(`findRowByIndex: index=${index} options=${safeStringify(options)}`);
|
|
538
368
|
await _autoInit();
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}
|
|
548
|
-
let foundSelector;
|
|
549
|
-
// The currently-mounted row whose logical index === index, if any.
|
|
550
|
-
const findMounted = async () => {
|
|
551
|
-
const rows = await resolve(config.rowSelector, rootLocator).all();
|
|
552
|
-
for (const r of rows) {
|
|
553
|
-
const result = await resolveRI(r);
|
|
554
|
-
if (result === undefined)
|
|
555
|
-
continue;
|
|
556
|
-
const normalized = (0, rowResolution_1.normalizeRowIndexResult)(result);
|
|
557
|
-
if (normalized.index === index) {
|
|
558
|
-
foundSelector = normalized.selector;
|
|
559
|
-
return r;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
return null;
|
|
563
|
-
};
|
|
564
|
-
// 1. Already mounted (no navigation needed)?
|
|
565
|
-
let loc = await findMounted();
|
|
566
|
-
// 2. Fast path: jump straight to it via the viewport's random-access scroll.
|
|
567
|
-
if (!loc && (viewport === null || viewport === void 0 ? void 0 : viewport.scrollToRow)) {
|
|
568
|
-
log(`findRowByIndex: scrolling to row ${index}`);
|
|
569
|
-
await viewport.scrollToRow(createStrategyContext(), index);
|
|
570
|
-
loc = await findMounted();
|
|
571
|
-
}
|
|
572
|
-
// 3. Fallback: advance pages (on infinite-scroll tables, a "page" is a scroll step) until
|
|
573
|
-
// the row is reached. Bounded by maxPages so an unreachable index can't loop forever.
|
|
574
|
-
if (!loc && config.strategies.pagination) {
|
|
575
|
-
const effectiveMaxPages = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
576
|
-
let pagesScanned = 1;
|
|
577
|
-
while (!loc && pagesScanned < effectiveMaxPages) {
|
|
578
|
-
if (!await _advancePage(false))
|
|
579
|
-
break;
|
|
580
|
-
pagesScanned++;
|
|
581
|
-
loc = await findMounted();
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
if (!loc) {
|
|
585
|
-
throw new Error(`findRowByIndex(${index}): could not reach row ${index}. ` +
|
|
586
|
-
`Ensure a viewport.scrollToRow or pagination can bring it into view (searched up to maxPages=${(_b = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _b !== void 0 ? _b : config.maxPages}).`);
|
|
587
|
-
}
|
|
588
|
-
await (0, debugUtils_1.debugDelay)(config, 'findRow');
|
|
589
|
-
return _makeSmart(loc, map, index, tableState.currentPageIndex, undefined, foundSelector);
|
|
369
|
+
return (0, rowByIndex_1.findRowByIndex)({
|
|
370
|
+
config,
|
|
371
|
+
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
372
|
+
getMap: () => tableMapper.getMapSync(),
|
|
373
|
+
advancePage: _advancePage,
|
|
374
|
+
getCurrentPageIndex: () => tableState.currentPageIndex,
|
|
375
|
+
getContext: () => createStrategyContext(),
|
|
376
|
+
makeSmartRow: (loc, map, idx, pageIdx, sel) => _makeSmart(loc, map, idx, pageIdx, undefined, sel),
|
|
377
|
+
}, index, options);
|
|
590
378
|
},
|
|
591
379
|
findRow: async (filters, options) => {
|
|
592
|
-
log(`findRow: filters=${safeStringify(filters)} options=${safeStringify(options)}`);
|
|
380
|
+
log(`findRow: filters=${(0, safeStringify_1.safeStringify)(filters)} options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
593
381
|
return rowFinder.findRow(filters, options);
|
|
594
382
|
},
|
|
595
383
|
findRows: async (filters, options) => {
|
|
596
|
-
log(`findRows: filters=${safeStringify(filters !== null && filters !== void 0 ? filters : {})} options=${safeStringify(options)}`);
|
|
384
|
+
log(`findRows: filters=${(0, safeStringify_1.safeStringify)(filters !== null && filters !== void 0 ? filters : {})} options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
597
385
|
return rowFinder.findRows(filters !== null && filters !== void 0 ? filters : {}, options);
|
|
598
386
|
},
|
|
599
387
|
isInitialized: () => {
|
|
@@ -606,49 +394,13 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
606
394
|
},
|
|
607
395
|
sorting: {
|
|
608
396
|
apply: async (columnName, direction) => {
|
|
609
|
-
var _a, _b;
|
|
610
|
-
var _c, _d, _e, _f;
|
|
611
397
|
await _autoInit();
|
|
612
|
-
|
|
613
|
-
throw new Error('No sorting strategy has been configured.');
|
|
614
|
-
log(`Applying sort for column "${columnName}" (${direction})`);
|
|
615
|
-
const context = Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell });
|
|
616
|
-
const maxRetries = 3;
|
|
617
|
-
for (let i = 0; i < maxRetries; i++) {
|
|
618
|
-
const currentState = await config.strategies.sorting.getSortState({ columnName, context });
|
|
619
|
-
if (currentState === direction) {
|
|
620
|
-
log(`Sort for "${columnName}" is already "${direction}".`);
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
await config.strategies.sorting.doSort({ columnName, direction, context });
|
|
624
|
-
if ((_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading) {
|
|
625
|
-
const timeout = (_d = (_c = config.strategies.loading.sortStabilizationTimeout) !== null && _c !== void 0 ? _c : config.strategies.loading.loadingTimeout) !== null && _d !== void 0 ? _d : 10000;
|
|
626
|
-
const poll = (_e = config.strategies.loading.sortStabilizationPollInterval) !== null && _e !== void 0 ? _e : 100;
|
|
627
|
-
await (0, loadingWait_1.waitWhileTableLoading)(config, context, rootLocator.page(), 'sorting.apply', {
|
|
628
|
-
timeout,
|
|
629
|
-
pollInterval: poll,
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
else {
|
|
633
|
-
const fallback = (_f = (_b = config.strategies.loading) === null || _b === void 0 ? void 0 : _b.sortStabilizationFallbackDelay) !== null && _f !== void 0 ? _f : 200;
|
|
634
|
-
await rootLocator.page().waitForTimeout(fallback);
|
|
635
|
-
}
|
|
636
|
-
await (0, debugUtils_1.debugDelay)(config, 'default');
|
|
637
|
-
const newState = await config.strategies.sorting.getSortState({ columnName, context });
|
|
638
|
-
if (newState === direction) {
|
|
639
|
-
log(`Successfully sorted "${columnName}" to "${direction}".`);
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
throw new Error(`Failed to sort column "${columnName}" to "${direction}" after ${maxRetries} attempts.`);
|
|
398
|
+
return (0, sorting_1.applySort)(sortingEnv(), columnName, direction);
|
|
644
399
|
},
|
|
645
400
|
getState: async (columnName) => {
|
|
646
401
|
log(`sorting.getState: column="${columnName}"`);
|
|
647
402
|
await _autoInit();
|
|
648
|
-
|
|
649
|
-
throw new Error('No sorting strategy has been configured.');
|
|
650
|
-
const context = Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell });
|
|
651
|
-
return config.strategies.sorting.getSortState({ columnName, context });
|
|
403
|
+
return (0, sorting_1.getSortState)(sortingEnv(), columnName);
|
|
652
404
|
}
|
|
653
405
|
},
|
|
654
406
|
// ─── Shared async row iterator ───────────────────────────────────────────
|
|
@@ -716,7 +468,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
716
468
|
},
|
|
717
469
|
// ─── Row iteration (delegated to engine/tableIteration) ──────────────────
|
|
718
470
|
forEach: async (callback, options = {}) => {
|
|
719
|
-
log(`forEach: options=${safeStringify(options)}`);
|
|
471
|
+
log(`forEach: options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
720
472
|
await _autoInit();
|
|
721
473
|
await (0, tableIteration_1.runForEach)({
|
|
722
474
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
@@ -731,7 +483,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
731
483
|
}, callback, options);
|
|
732
484
|
},
|
|
733
485
|
map: async (callback, options = {}) => {
|
|
734
|
-
log(`map: options=${safeStringify(options)}`);
|
|
486
|
+
log(`map: options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
735
487
|
await _autoInit();
|
|
736
488
|
return (0, tableIteration_1.runMap)({
|
|
737
489
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
@@ -746,7 +498,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
746
498
|
}, callback, options);
|
|
747
499
|
},
|
|
748
500
|
toArray: async (callback, options = {}) => {
|
|
749
|
-
log(`toArray: options=${safeStringify(options)}`);
|
|
501
|
+
log(`toArray: options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
750
502
|
const cb = callback !== null && callback !== void 0 ? callback : (({ row }) => row.toJSON());
|
|
751
503
|
try {
|
|
752
504
|
return await result.map(cb, options);
|
|
@@ -756,7 +508,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
756
508
|
}
|
|
757
509
|
},
|
|
758
510
|
filter: async (predicate, options = {}) => {
|
|
759
|
-
log(`filter: options=${safeStringify(options)}`);
|
|
511
|
+
log(`filter: options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
760
512
|
await _autoInit();
|
|
761
513
|
return (0, tableIteration_1.runFilter)({
|
|
762
514
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withinDeadline = withinDeadline;
|
|
4
|
+
/** Bound one asynchronous check by an existing deadline, without cancelling its underlying work. */
|
|
5
|
+
async function withinDeadline(check, deadline, expiredValue) {
|
|
6
|
+
if (!Number.isFinite(deadline))
|
|
7
|
+
return check();
|
|
8
|
+
let timer;
|
|
9
|
+
try {
|
|
10
|
+
const expired = new Promise(resolve => {
|
|
11
|
+
timer = setTimeout(() => resolve(expiredValue), Math.max(0, deadline - Date.now()));
|
|
12
|
+
});
|
|
13
|
+
// A late rejection is handled by the race even after the deadline wins.
|
|
14
|
+
return await Promise.race([check(), expired]);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
clearTimeout(timer);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ViewportStrategy } from '../types';
|
|
2
|
+
/** Cache geometry only for one navigation; scans and later reads use the original oracles. */
|
|
3
|
+
export declare function memoizeViewport(viewport: ViewportStrategy): {
|
|
4
|
+
viewport: ViewportStrategy;
|
|
5
|
+
clear: () => void;
|
|
6
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.memoizeViewport = memoizeViewport;
|
|
4
|
+
/** Cache geometry only for one navigation; scans and later reads use the original oracles. */
|
|
5
|
+
function memoizeViewport(viewport) {
|
|
6
|
+
if (viewport.disableCache)
|
|
7
|
+
return { viewport, clear: () => { } };
|
|
8
|
+
let columns;
|
|
9
|
+
let rows;
|
|
10
|
+
const clear = () => { columns = undefined; rows = undefined; };
|
|
11
|
+
const wrapScroll = (scroll) => scroll && (async (context, index) => {
|
|
12
|
+
clear();
|
|
13
|
+
try {
|
|
14
|
+
await scroll.call(viewport, context, index);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
clear();
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
clear,
|
|
22
|
+
viewport: Object.assign(Object.assign({}, viewport), { getVisibleColumnRange: viewport.getVisibleColumnRange
|
|
23
|
+
? context => columns !== null && columns !== void 0 ? columns : (columns = viewport.getVisibleColumnRange(context))
|
|
24
|
+
: undefined, getVisibleRowRange: viewport.getVisibleRowRange
|
|
25
|
+
? context => rows !== null && rows !== void 0 ? rows : (rows = viewport.getVisibleRowRange(context))
|
|
26
|
+
: undefined, scrollToColumn: wrapScroll(viewport.scrollToColumn), scrollToRow: wrapScroll(viewport.scrollToRow) }),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -51,7 +51,6 @@ class NavigationBarrier {
|
|
|
51
51
|
* The fn receives a fresh `targetReached` check; if the previous row's scroll
|
|
52
52
|
* already brought this row into view, fn can no-op immediately.
|
|
53
53
|
*/
|
|
54
|
-
// fallow-ignore-next-line unused-class-member
|
|
55
54
|
async runExclusiveRecovery(fn) {
|
|
56
55
|
if (!this.recoveryMutex)
|
|
57
56
|
this.recoveryMutex = new mutex_1.Mutex();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.safeStringify = void 0;
|
|
4
|
+
/** JSON.stringify for debug logs: functions become "[Function]" and cycles don't throw. */
|
|
5
|
+
const safeStringify = (obj) => {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.stringify(obj, (_key, value) => {
|
|
8
|
+
if (typeof value === 'function') {
|
|
9
|
+
return '[Function]';
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
return '[unserializable]';
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
exports.safeStringify = safeStringify;
|
|
@@ -4,7 +4,7 @@ import { SmartRow } from '../types';
|
|
|
4
4
|
*/
|
|
5
5
|
export interface SmartRowArray<T = any> extends Array<SmartRow<T>> {
|
|
6
6
|
/**
|
|
7
|
-
* Converts all rows to JSON objects
|
|
7
|
+
* Converts all rows to JSON objects, reading sequentially to avoid competing viewport moves.
|
|
8
8
|
*/
|
|
9
9
|
toJSON(): Promise<T[]>;
|
|
10
10
|
}
|
|
@@ -7,7 +7,10 @@ exports.createSmartRowArray = createSmartRowArray;
|
|
|
7
7
|
function createSmartRowArray(rows) {
|
|
8
8
|
const arr = rows;
|
|
9
9
|
arr.toJSON = async () => {
|
|
10
|
-
|
|
10
|
+
const results = [];
|
|
11
|
+
for (const row of rows)
|
|
12
|
+
results.push(await row.toJSON());
|
|
13
|
+
return results;
|
|
11
14
|
};
|
|
12
15
|
return arr;
|
|
13
16
|
}
|
package/package.json
CHANGED