@rickcedwhat/playwright-smart-table 6.24.0 → 6.25.0-next.1e71a54
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 +326 -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 +5 -1
- package/dist/engine/rowFinder.js +17 -10
- 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 +3 -1
- 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/presets/glide/index.js +0 -1
- package/dist/presets/rdg.js +0 -2
- package/dist/smartRow.js +51 -721
- package/dist/strategies/columns.d.ts +1 -1
- package/dist/strategies/contentReady.d.ts +19 -2
- package/dist/strategies/contentReady.js +81 -0
- package/dist/strategies/index.d.ts +7 -3
- package/dist/strategies/resolution.d.ts +1 -3
- package/dist/strategies/resolution.js +0 -28
- package/dist/types.d.ts +40 -7
- package/dist/useTable.js +51 -241
- package/dist/utils/deadline.d.ts +2 -0
- package/dist/utils/deadline.js +19 -0
- package/dist/utils/locatorDescription.d.ts +15 -0
- package/dist/utils/locatorDescription.js +56 -0
- package/dist/utils/navigationBarrier.d.ts +0 -2
- package/dist/utils/navigationBarrier.js +0 -4
- 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 +5 -6
- package/dist/typeContext.d.ts +0 -6
- package/dist/typeContext.js +0 -1133
- package/dist/utils.d.ts +0 -7
- package/dist/utils.js +0 -21
|
@@ -2,7 +2,7 @@ import type { Locator } from '@playwright/test';
|
|
|
2
2
|
import { StrategyContext } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* Primitive navigation functions that define HOW to move within a table.
|
|
5
|
-
* The orchestration logic (WHEN to move) lives in
|
|
5
|
+
* The orchestration logic (WHEN to move) lives in navigateToCell (src/engine/cellNavigation.ts).
|
|
6
6
|
*/
|
|
7
7
|
export interface NavigationPrimitives {
|
|
8
8
|
goUp?: (context: StrategyContext) => Promise<void>;
|
|
@@ -1,6 +1,23 @@
|
|
|
1
|
-
import type { ContentReadyStrategy } from '../types';
|
|
2
|
-
export type { ContentReadyStrategy };
|
|
1
|
+
import type { ContentReadyStrategy, RowReadyPredicate } from '../types';
|
|
3
2
|
export declare const ContentReadyStrategies: {
|
|
3
|
+
/**
|
|
4
|
+
* Waits in the browser until `predicate(rowElement, arg)` returns a truthy value, e.g. until
|
|
5
|
+
* a skeleton placeholder is gone or every cell has rendered. Resolves when `timeout` expires
|
|
6
|
+
* even if the predicate never passed, like the other content-ready strategies.
|
|
7
|
+
*
|
|
8
|
+
* Uses `locator.waitForFunction` (Playwright 1.62+), which re-resolves the row on each retry
|
|
9
|
+
* and polls without a round-trip per check. Older versions poll via `locator.evaluate`.
|
|
10
|
+
* `timeout` and `interval` must be positive finite milliseconds. `interval` only controls
|
|
11
|
+
* the older-version fallback; native polling is controlled by Playwright.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* contentReady: Strategies.ContentReady.until(row => !row.querySelector('.skeleton'))
|
|
15
|
+
*/
|
|
16
|
+
until: <Arg = undefined>(predicate: RowReadyPredicate<Arg>, options?: {
|
|
17
|
+
arg?: Arg;
|
|
18
|
+
timeout?: number;
|
|
19
|
+
interval?: number;
|
|
20
|
+
}) => ContentReadyStrategy;
|
|
4
21
|
/**
|
|
5
22
|
* Polls the row's text content until two consecutive reads match.
|
|
6
23
|
*/
|
|
@@ -1,7 +1,88 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ContentReadyStrategies = void 0;
|
|
4
|
+
function isTimeout(error) {
|
|
5
|
+
return (error === null || error === void 0 ? void 0 : error.name) === 'TimeoutError';
|
|
6
|
+
}
|
|
7
|
+
/** Playwright < 1.62 has no `locator.waitForFunction`: poll with one `evaluate` per check. */
|
|
8
|
+
async function pollPredicate(row, page, predicate, arg, timeout, interval) {
|
|
9
|
+
const deadline = Date.now() + timeout;
|
|
10
|
+
let source = predicate.toString();
|
|
11
|
+
// Build a serializable callback; the user predicate executes only inside the browser.
|
|
12
|
+
const wrap = (expression) => new Function(`return async (row, arg) => Boolean(await (${expression})(row, arg))`)();
|
|
13
|
+
let evaluateBoolean;
|
|
14
|
+
try {
|
|
15
|
+
evaluateBoolean = wrap(source);
|
|
16
|
+
}
|
|
17
|
+
catch (_a) {
|
|
18
|
+
// Like Playwright's serializer, normalize method shorthand into a function expression.
|
|
19
|
+
source = source.startsWith('async ') ? source.replace(/^async\s+/, 'async function ') : `function ${source}`;
|
|
20
|
+
evaluateBoolean = wrap(source);
|
|
21
|
+
}
|
|
22
|
+
let expired = false;
|
|
23
|
+
let deadlineTimer;
|
|
24
|
+
const timeoutReached = new Promise(resolve => {
|
|
25
|
+
deadlineTimer = setTimeout(() => { expired = true; resolve(); }, timeout);
|
|
26
|
+
});
|
|
27
|
+
const poll = async () => {
|
|
28
|
+
while (!expired && Date.now() < deadline) {
|
|
29
|
+
// Convert truthiness before serialization, including async DOM-element results.
|
|
30
|
+
if (await row.evaluate(evaluateBoolean, arg, { timeout: Math.max(1, deadline - Date.now()) }))
|
|
31
|
+
return;
|
|
32
|
+
if (expired || Date.now() >= deadline)
|
|
33
|
+
return;
|
|
34
|
+
await page.waitForTimeout(Math.min(interval, Math.max(0, deadline - Date.now())));
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
try {
|
|
38
|
+
// evaluate's timeout only bounds locator resolution, not an async predicate's promise.
|
|
39
|
+
await Promise.race([poll(), timeoutReached]);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
clearTimeout(deadlineTimer);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
4
45
|
exports.ContentReadyStrategies = {
|
|
46
|
+
/**
|
|
47
|
+
* Waits in the browser until `predicate(rowElement, arg)` returns a truthy value, e.g. until
|
|
48
|
+
* a skeleton placeholder is gone or every cell has rendered. Resolves when `timeout` expires
|
|
49
|
+
* even if the predicate never passed, like the other content-ready strategies.
|
|
50
|
+
*
|
|
51
|
+
* Uses `locator.waitForFunction` (Playwright 1.62+), which re-resolves the row on each retry
|
|
52
|
+
* and polls without a round-trip per check. Older versions poll via `locator.evaluate`.
|
|
53
|
+
* `timeout` and `interval` must be positive finite milliseconds. `interval` only controls
|
|
54
|
+
* the older-version fallback; native polling is controlled by Playwright.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* contentReady: Strategies.ContentReady.until(row => !row.querySelector('.skeleton'))
|
|
58
|
+
*/
|
|
59
|
+
until: (predicate, options = {}) => {
|
|
60
|
+
var _a, _b;
|
|
61
|
+
const timeout = (_a = options.timeout) !== null && _a !== void 0 ? _a : 500;
|
|
62
|
+
const interval = (_b = options.interval) !== null && _b !== void 0 ? _b : 50;
|
|
63
|
+
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
64
|
+
throw new Error('ContentReady.until: timeout must be a positive finite number');
|
|
65
|
+
}
|
|
66
|
+
if (!Number.isFinite(interval) || interval <= 0) {
|
|
67
|
+
throw new Error('ContentReady.until: interval must be a positive finite number');
|
|
68
|
+
}
|
|
69
|
+
const fn = predicate;
|
|
70
|
+
const arg = options.arg;
|
|
71
|
+
return async (row, page) => {
|
|
72
|
+
try {
|
|
73
|
+
if (typeof row.waitForFunction === 'function') {
|
|
74
|
+
await row.waitForFunction(fn, arg, { timeout });
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
await pollPredicate(row, page, fn, arg, timeout, interval);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (!isTimeout(error))
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
},
|
|
5
86
|
/**
|
|
6
87
|
* Polls the row's text content until two consecutive reads match.
|
|
7
88
|
*/
|
|
@@ -3,7 +3,6 @@ export * from './sorting';
|
|
|
3
3
|
export * from './columns';
|
|
4
4
|
export * from './headers';
|
|
5
5
|
export * from './fill';
|
|
6
|
-
export type { ColumnResolutionStrategy } from './resolution';
|
|
7
6
|
export * from './dedupe';
|
|
8
7
|
export * from './loading';
|
|
9
8
|
export * from './stabilization';
|
|
@@ -99,13 +98,18 @@ export declare const Strategies: {
|
|
|
99
98
|
dataAttribute: (options?: import("./viewport").DataAttributeViewportOptions) => import("..").ViewportStrategy;
|
|
100
99
|
};
|
|
101
100
|
ContentReady: {
|
|
101
|
+
until: <Arg = undefined>(predicate: import("..").RowReadyPredicate<Arg>, options?: {
|
|
102
|
+
arg?: Arg;
|
|
103
|
+
timeout?: number;
|
|
104
|
+
interval?: number;
|
|
105
|
+
}) => import("..").ContentReadyStrategy;
|
|
102
106
|
textStable: (options?: {
|
|
103
107
|
timeout?: number;
|
|
104
108
|
interval?: number;
|
|
105
|
-
}) => import("
|
|
109
|
+
}) => import("..").ContentReadyStrategy;
|
|
106
110
|
mutationSettled: (options?: {
|
|
107
111
|
timeout?: number;
|
|
108
112
|
quietPeriod?: number;
|
|
109
|
-
}) => import("
|
|
113
|
+
}) => import("..").ContentReadyStrategy;
|
|
110
114
|
};
|
|
111
115
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StrategyContext } from '../types';
|
|
2
|
+
/** @deprecated Not used by the library. Will be removed in v7. */
|
|
2
3
|
export interface ColumnResolutionStrategy {
|
|
3
4
|
/**
|
|
4
5
|
* Resolves a column name (string or Regex) to a column index.
|
|
@@ -17,6 +18,3 @@ export interface ColumnResolutionStrategy {
|
|
|
17
18
|
headerMap: Map<string, number>;
|
|
18
19
|
}): string;
|
|
19
20
|
}
|
|
20
|
-
export declare const ResolutionStrategies: {
|
|
21
|
-
default: ColumnResolutionStrategy;
|
|
22
|
-
};
|
|
@@ -1,30 +1,2 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ResolutionStrategies = void 0;
|
|
4
|
-
exports.ResolutionStrategies = {
|
|
5
|
-
default: {
|
|
6
|
-
resolveIndex: ({ query, headerMap }) => {
|
|
7
|
-
// 1. Exact / String Match
|
|
8
|
-
if (typeof query === 'string') {
|
|
9
|
-
if (headerMap.has(query))
|
|
10
|
-
return headerMap.get(query);
|
|
11
|
-
}
|
|
12
|
-
// 2. Regex Match
|
|
13
|
-
if (query instanceof RegExp) {
|
|
14
|
-
for (const [colName, idx] of headerMap.entries()) {
|
|
15
|
-
if (query.test(colName))
|
|
16
|
-
return idx;
|
|
17
|
-
}
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
20
|
-
// 3. (Optional) Fuzzy String Match fallback could go here
|
|
21
|
-
// But for strict default strategy, we might want to keep it simple first
|
|
22
|
-
// The original code didn't do fuzzy *resolution* logic inside the get(), it just did strict get().
|
|
23
|
-
// The fuzzy logic was only for *suggestions* on error.
|
|
24
|
-
return undefined;
|
|
25
|
-
},
|
|
26
|
-
resolveName: ({ query }) => {
|
|
27
|
-
return query.toString();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
};
|
package/dist/types.d.ts
CHANGED
|
@@ -428,6 +428,8 @@ export interface PaginationPrimitives {
|
|
|
428
428
|
export type PaginationStrategy = PaginationPrimitives;
|
|
429
429
|
export type DedupeStrategy = (row: SmartRow) => string | number | Promise<string | number>;
|
|
430
430
|
export type ContentReadyStrategy = (row: Locator, page: Page) => Promise<void>;
|
|
431
|
+
/** Predicate for `Strategies.ContentReady.until`. Runs in the browser with the row element, so it must not close over test-side variables; pass values via `arg`. */
|
|
432
|
+
export type RowReadyPredicate<Arg = undefined> = (row: HTMLElement | SVGElement, arg: Arg) => unknown;
|
|
431
433
|
export type FillStrategy = (options: {
|
|
432
434
|
row: SmartRow;
|
|
433
435
|
columnName: string;
|
|
@@ -524,6 +526,7 @@ export interface LoadingStrategy {
|
|
|
524
526
|
* How long (ms) to wait for a loading row to resolve before applying onRowLoadingTimeout.
|
|
525
527
|
* Honored by findRows AND by map/forEach/filter, where the wait runs before the dedupe
|
|
526
528
|
* strategy so content-based dedupe keys see the row's final loaded state.
|
|
529
|
+
* Includes time spent awaiting the loading predicate, even if it never settles.
|
|
527
530
|
* When not set (backward-compatible behavior): findRows skips loading rows immediately;
|
|
528
531
|
* map/forEach/filter process them as-is.
|
|
529
532
|
*/
|
|
@@ -544,6 +547,7 @@ export interface LoadingStrategy {
|
|
|
544
547
|
isCellLoading?: (cell: import('@playwright/test').Locator, columnName: string, row: SmartRow) => Promise<boolean>;
|
|
545
548
|
/**
|
|
546
549
|
* How long (ms) to wait for a loading cell to resolve before applying onCellLoadingTimeout.
|
|
550
|
+
* Includes predicate evaluation and any initial rendering grace period.
|
|
547
551
|
* When not set and isCellLoading returns true, the cell is read as-is immediately.
|
|
548
552
|
*/
|
|
549
553
|
cellLoadingTimeout?: number;
|
|
@@ -638,12 +642,18 @@ export interface TableStrategies {
|
|
|
638
642
|
* Without this, the clone captures stale content from the previous occupant of the
|
|
639
643
|
* DOM slot — the element is at the correct position but React hasn't re-rendered yet.
|
|
640
644
|
*
|
|
641
|
-
*
|
|
645
|
+
* Built-ins: `Strategies.ContentReady.textStable()` (text stops changing),
|
|
646
|
+
* `mutationSettled()` (no DOM mutations for a quiet period), and `until(predicate)`
|
|
647
|
+
* (an in-browser condition holds, e.g. no skeleton placeholder).
|
|
642
648
|
*
|
|
643
649
|
* @example
|
|
644
650
|
* strategies: {
|
|
645
651
|
* contentReady: Strategies.ContentReady.textStable({ timeout: 500 }),
|
|
646
652
|
* }
|
|
653
|
+
* @example
|
|
654
|
+
* strategies: {
|
|
655
|
+
* contentReady: Strategies.ContentReady.until(row => !row.querySelector('.skeleton')),
|
|
656
|
+
* }
|
|
647
657
|
*/
|
|
648
658
|
contentReady?: ContentReadyStrategy;
|
|
649
659
|
/**
|
|
@@ -760,6 +770,14 @@ export type RowIterationContext<T = any> = {
|
|
|
760
770
|
index: number;
|
|
761
771
|
/** 0-based page index — which page this row was collected from. */
|
|
762
772
|
pageIndex: number;
|
|
773
|
+
/**
|
|
774
|
+
* Ends the iteration. No further pages are loaded, and the result of `map` / `filter`
|
|
775
|
+
* includes this row and nothing after it. If several rows call `stop()`, the earliest one wins.
|
|
776
|
+
*
|
|
777
|
+
* In `'sequential'` mode (the default) no callback after this row runs. In `'parallel'` and
|
|
778
|
+
* `'synchronized'` mode the rest of the current page is already in flight, so later rows'
|
|
779
|
+
* callbacks may still run (and their side effects happen) even though their results are dropped.
|
|
780
|
+
*/
|
|
763
781
|
stop: () => void;
|
|
764
782
|
};
|
|
765
783
|
/** Concurrency modes for row iteration. */
|
|
@@ -786,6 +804,11 @@ export type RowIterationOptions = {
|
|
|
786
804
|
* @default false — uses goNext for one-page-at-a-time advancement
|
|
787
805
|
*/
|
|
788
806
|
useBulkPagination?: boolean;
|
|
807
|
+
/**
|
|
808
|
+
* Cancels the iteration between pages. When aborted, the call rejects with
|
|
809
|
+
* `signal.reason` (rows already processed are not returned).
|
|
810
|
+
*/
|
|
811
|
+
signal?: AbortSignal;
|
|
789
812
|
};
|
|
790
813
|
/**
|
|
791
814
|
* Result of {@link useTable}. Implements `AsyncIterable` — `for await (const { row } of table)`
|
|
@@ -868,31 +891,37 @@ export interface TableResult<T = any> extends AsyncIterable<{
|
|
|
868
891
|
* is not configured. Also throws if the row cannot be reached (no silent wrong-row fallback).
|
|
869
892
|
*
|
|
870
893
|
* @param index 0-based logical/data-model row index
|
|
871
|
-
* @param options - `maxPages` bounds how far to scroll/paginate (defaults to config.maxPages)
|
|
894
|
+
* @param options - `maxPages` bounds how far to scroll/paginate (defaults to config.maxPages).
|
|
895
|
+
* `signal` cancels the page scan; the call rejects with `signal.reason`.
|
|
872
896
|
*/
|
|
873
897
|
findRowByIndex: (index: number, options?: {
|
|
874
898
|
maxPages?: number;
|
|
899
|
+
signal?: AbortSignal;
|
|
875
900
|
}) => Promise<SmartRow<T>>;
|
|
876
901
|
/**
|
|
877
902
|
* ASYNC: Searches for a single row across pages using pagination.
|
|
878
903
|
* Auto-initializes the table if not already initialized.
|
|
879
904
|
* @param filters - The filter criteria to match
|
|
880
|
-
* @param options - Search options including exact match and max pages
|
|
905
|
+
* @param options - Search options including exact match and max pages. `signal` cancels the page scan; the call rejects with `signal.reason`.
|
|
881
906
|
*/
|
|
882
907
|
findRow: (filters: Record<string, FilterValue>, options?: {
|
|
883
908
|
exact?: boolean;
|
|
884
909
|
maxPages?: number;
|
|
910
|
+
signal?: AbortSignal;
|
|
885
911
|
}) => Promise<SmartRow<T>>;
|
|
886
912
|
/**
|
|
887
913
|
* ASYNC: Searches for all matching rows across pages using pagination.
|
|
888
914
|
* Auto-initializes the table if not already initialized.
|
|
915
|
+
* Returned rows can be read independently, including with `concurrency: 'synchronized'`.
|
|
916
|
+
* Use synchronized `map()` for coordinated column scrolling across virtualized rows.
|
|
889
917
|
* @param filters - The filter criteria to match (omit or pass {} for all rows)
|
|
890
|
-
* @param options - Search options (exact, maxPages). `useBulkPagination` defaults to `false`: pages advance one at a time via `goNext` so no intermediate page is skipped. Set it to `true` to opt into `goNextBulk` (faster, but skips the rows on jumped-over pages).
|
|
918
|
+
* @param options - Search options (exact, maxPages). `useBulkPagination` defaults to `false`: pages advance one at a time via `goNext` so no intermediate page is skipped. Set it to `true` to opt into `goNextBulk` (faster, but skips the rows on jumped-over pages). `signal` cancels the page scan; the call rejects with `signal.reason`.
|
|
891
919
|
*/
|
|
892
920
|
findRows: (filters?: Record<string, FilterValue>, options?: {
|
|
893
921
|
exact?: boolean;
|
|
894
922
|
maxPages?: number;
|
|
895
923
|
useBulkPagination?: boolean;
|
|
924
|
+
signal?: AbortSignal;
|
|
896
925
|
}) => Promise<SmartRowArray<T>>;
|
|
897
926
|
/**
|
|
898
927
|
* Resolves the named column and scrolls it into view.
|
|
@@ -903,11 +932,13 @@ export interface TableResult<T = any> extends AsyncIterable<{
|
|
|
903
932
|
/**
|
|
904
933
|
* Counts the number of rows, optionally filtered.
|
|
905
934
|
* Without arguments, counts all rows. With filters, counts only matching rows.
|
|
906
|
-
* Paginates when pagination is configured.
|
|
935
|
+
* Paginates when pagination is configured. `signal` cancels the page scan; the call
|
|
936
|
+
* rejects with `signal.reason` (the table is still reset to page 1).
|
|
907
937
|
*/
|
|
908
938
|
countRows: (filters?: Record<string, FilterValue>, options?: {
|
|
909
939
|
exact?: boolean;
|
|
910
940
|
maxPages?: number;
|
|
941
|
+
signal?: AbortSignal;
|
|
911
942
|
}) => Promise<number>;
|
|
912
943
|
/**
|
|
913
944
|
* Iterates over rows and extracts the value of a single column.
|
|
@@ -935,7 +966,8 @@ export interface TableResult<T = any> extends AsyncIterable<{
|
|
|
935
966
|
/**
|
|
936
967
|
* Iterates every row across all pages, calling the callback for side effects.
|
|
937
968
|
* Execution is sequential by default (safe for interactions like clicking/filling).
|
|
938
|
-
* Call `stop()` in the callback to end iteration early.
|
|
969
|
+
* Call `stop()` in the callback to end iteration early. In sequential mode no later row runs;
|
|
970
|
+
* in parallel mode the rest of the current page may still run (see `RowIterationContext.stop`).
|
|
939
971
|
*
|
|
940
972
|
* @param callback - Function receiving { row, rowIndex, stop }
|
|
941
973
|
* @param options - maxPages, concurrency, dedupe, useBulkPagination
|
|
@@ -952,7 +984,8 @@ export interface TableResult<T = any> extends AsyncIterable<{
|
|
|
952
984
|
* Defaults to `concurrency: 'sequential'` (safe for clicks/fills). Pass
|
|
953
985
|
* `concurrency: 'parallel'` for read-only extraction, or `'synchronized'` when
|
|
954
986
|
* navigation must stay lock-step on virtualized grids.
|
|
955
|
-
* Call `stop()` to
|
|
987
|
+
* Call `stop()` to end early: the result includes the row that called it and nothing after it
|
|
988
|
+
* (in parallel mode, later rows on the same page may still run; see `RowIterationContext.stop`).
|
|
956
989
|
*
|
|
957
990
|
* @param callback - Function receiving { row, rowIndex, stop }
|
|
958
991
|
* @param options - maxPages, concurrency, dedupe, useBulkPagination
|