@rickcedwhat/playwright-smart-table 6.20.1 → 6.21.0-next.3ab7a18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/engine/rowFinder.d.ts +1 -0
  2. package/dist/engine/rowFinder.js +73 -75
  3. package/dist/engine/scanPages.d.ts +58 -0
  4. package/dist/engine/scanPages.js +46 -0
  5. package/dist/engine/tableIteration.d.ts +1 -0
  6. package/dist/engine/tableIteration.js +40 -22
  7. package/dist/filterEngine.d.ts +4 -0
  8. package/dist/filterEngine.js +15 -4
  9. package/dist/index.d.ts +6 -2
  10. package/dist/index.js +5 -1
  11. package/dist/packageVersion.d.ts +1 -1
  12. package/dist/packageVersion.js +1 -1
  13. package/dist/plugins/index.d.ts +51 -10
  14. package/dist/plugins/index.js +14 -6
  15. package/dist/presets/glide/index.d.ts +1 -1
  16. package/dist/presets/glide/index.js +2 -2
  17. package/dist/presets/mui.js +15 -29
  18. package/dist/presets/rdg.d.ts +1 -1
  19. package/dist/presets/rdg.js +4 -4
  20. package/dist/smartRow.d.ts +2 -2
  21. package/dist/smartRow.js +163 -51
  22. package/dist/strategies/columns.d.ts +5 -9
  23. package/dist/strategies/columns.js +0 -10
  24. package/dist/strategies/contentReady.d.ts +20 -0
  25. package/dist/strategies/contentReady.js +62 -0
  26. package/dist/strategies/filter.d.ts +0 -4
  27. package/dist/strategies/filter.js +0 -16
  28. package/dist/strategies/headers.d.ts +5 -0
  29. package/dist/strategies/headers.js +17 -9
  30. package/dist/strategies/index.d.ts +23 -15
  31. package/dist/strategies/index.js +9 -5
  32. package/dist/strategies/pagination.js +11 -2
  33. package/dist/strategies/viewport.d.ts +4 -1
  34. package/dist/strategies/viewport.js +62 -32
  35. package/dist/typeContext.d.ts +1 -1
  36. package/dist/typeContext.js +77 -17
  37. package/dist/types.d.ts +74 -17
  38. package/dist/useTable.js +125 -56
  39. package/dist/utils/elementTracker.js +6 -3
  40. package/dist/utils/loadingWait.d.ts +15 -0
  41. package/dist/utils/loadingWait.js +43 -0
  42. package/dist/utils/pageIndex.d.ts +12 -0
  43. package/dist/utils/pageIndex.js +19 -0
  44. package/dist/utils/resolveCellLocator.d.ts +35 -0
  45. package/dist/utils/resolveCellLocator.js +52 -0
  46. package/package.json +1 -1
@@ -284,6 +284,9 @@ export type SmartRow<T = any> = Locator & {
284
284
  * Scrolls/paginates to bring this row into view.
285
285
  * Works when row position metadata is known (e.g., from getRowByIndex, findRow,
286
286
  * findRows, filter, or async iteration).
287
+ *
288
+ * Prefers \`strategies.viewport.scrollToRow\` when configured; otherwise falls back to
289
+ * Playwright \`scrollIntoViewIfNeeded()\`.
287
290
  * @throws Error if row position metadata is unknown
288
291
  */
289
292
  bringIntoView(): Promise<void>;
@@ -309,6 +312,12 @@ export type SmartRow<T = any> = Locator & {
309
312
 
310
313
  /**
311
314
  * Get the resolved value of any column — real, override, or synthetic.
315
+ *
316
+ * When \`strategies.viewport\` or \`strategies.navigation\` is configured, runs the same
317
+ * cell-navigation pipeline as \`toJSON\` / \`getCell().bringIntoView()\` so off-screen
318
+ * virtualized cells are mounted before reading. Without those strategies, reads the
319
+ * current DOM cell (Playwright auto-wait).
320
+ *
312
321
  * @param column - Column name (case-sensitive)
313
322
  * @returns The column value as a string
314
323
  */
@@ -440,6 +449,8 @@ export type PaginationStrategy = PaginationPrimitives;
440
449
 
441
450
  export type DedupeStrategy = (row: SmartRow) => string | number | Promise<string | number>;
442
451
 
452
+ export type ContentReadyStrategy = (row: Locator, page: Page) => Promise<void>;
453
+
443
454
 
444
455
 
445
456
  export type FillStrategy = (options: {
@@ -509,6 +520,7 @@ export interface ColumnOverride<TValue = any> {
509
520
  }
510
521
 
511
522
  export type { HeaderStrategy } from './strategies/headers';
523
+ export type { NavigationPrimitives, CellNavigationStrategy } from './strategies/columns';
512
524
 
513
525
  /**
514
526
  * Strategy to resolve column names (string or regex) to their index.
@@ -583,9 +595,16 @@ export interface LoadingStrategy {
583
595
  | 'throw'
584
596
  | ((cell: import('@playwright/test').Locator, columnName: string, row: SmartRow) => Promise<string>);
585
597
 
586
- /** Max ms to wait for sort stabilization when isTableLoading is set. @default 10000 */
598
+ /**
599
+ * Max ms to wait while \`isTableLoading\` returns true (countRows, findRow(s),
600
+ * page-ready gates, and sort when \`sortStabilizationTimeout\` is unset).
601
+ * @default 10000
602
+ */
603
+ loadingTimeout?: number;
604
+
605
+ /** Max ms to wait for sort stabilization when isTableLoading is set. Overrides \`loadingTimeout\` for sort only. @default 10000 */
587
606
  sortStabilizationTimeout?: number;
588
- /** Polling interval (ms) while waiting for sort stabilization. @default 100 */
607
+ /** Polling interval (ms) while waiting for sort / table-ready stabilization. @default 100 (sort) / 200 (table-ready) */
589
608
  sortStabilizationPollInterval?: number;
590
609
  /** Fallback delay (ms) after sort when no isTableLoading is configured. @default 200 */
591
610
  sortStabilizationFallbackDelay?: number;
@@ -654,6 +673,24 @@ export interface TableStrategies {
654
673
  */
655
674
  resolveRowIndex?: (row: Locator) => Promise<RowIndexResult | undefined>;
656
675
 
676
+ /**
677
+ * Waits until a row's content has stabilized before cloning in
678
+ * \`toJSON({ atomic: true })\`. Needed for recycling virtualizers where the
679
+ * framework updates element positions synchronously but renders cell content
680
+ * asynchronously (e.g. react-window, react-virtuoso with React concurrent mode).
681
+ *
682
+ * Without this, the clone captures stale content from the previous occupant of the
683
+ * DOM slot — the element is at the correct position but React hasn't re-rendered yet.
684
+ *
685
+ * Use \`Strategies.ContentReady.textStable()\` for the built-in text-polling strategy.
686
+ *
687
+ * @example
688
+ * strategies: {
689
+ * contentReady: Strategies.ContentReady.textStable({ timeout: 500 }),
690
+ * }
691
+ */
692
+ contentReady?: ContentReadyStrategy;
693
+
657
694
  /**
658
695
  * Viewport oracle strategies for 2D virtualized tables (e.g. MUI DataGrid, AG Grid,
659
696
  * Braintrust-style grids where both rows and columns are virtualized simultaneously).
@@ -675,11 +712,17 @@ export interface TableConfig<T = any> {
675
712
  rowSelector?: string;
676
713
  /** Selector for the cells within a row */
677
714
  cellSelector?: string | ((row: Locator) => Locator);
678
- /** Number of pages to scan for verification */
715
+ /**
716
+ * Number of pages to scan for verification / iteration.
717
+ * Defaults to \`1\` — set explicitly (e.g. \`maxPages: 5\`) when using a pagination
718
+ * strategy, or you will never leave page 1. Init logs a warning when pagination
719
+ * is configured and this is still \`1\`.
720
+ */
679
721
  maxPages?: number;
680
722
  /**
681
723
  * Default concurrency strategy for iteration methods.
682
724
  * Can be overridden by the options passed to forEach, map, or filter.
725
+ * Defaults to sequential when unset (safer for UI interactions).
683
726
  */
684
727
  concurrency?: RowIterationMode;
685
728
  /** Hook to rename columns dynamically */
@@ -715,6 +758,9 @@ export interface TableConfig<T = any> {
715
758
  emptyState?: Locator;
716
759
  }
717
760
 
761
+ /**
762
+ * @internal Resolved config after defaults are applied. Prefer {@link TableConfig} in public code.
763
+ */
718
764
  export interface FinalTableConfig<T = any> extends TableConfig<T> {
719
765
  headerSelector: string | ((root: Locator) => Locator);
720
766
  rowSelector: string;
@@ -786,10 +832,17 @@ export type RowIterationOptions = {
786
832
  useBulkPagination?: boolean;
787
833
  };
788
834
 
835
+ /**
836
+ * Result of {@link useTable}. Implements \`AsyncIterable\` — \`for await (const { row } of table)\`
837
+ * uses the same page-walk as \`map\` / \`forEach\` (\`scanPages\` + runMap): overscan, loading,
838
+ * dedupe, and EOF final scan (#427).
839
+ */
789
840
  export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>; rowIndex: number; index: number; pageIndex: number }> {
790
841
  /**
791
- * Represents the current page index of the table's DOM.
792
- * Starts at 0. Automatically maintained by the library during pagination and bringIntoView.
842
+ * Current DOM page index (0-based). Automatically maintained during pagination
843
+ * and \`bringIntoView\`. Treat as **read-only** — assigning manually can desync
844
+ * the path planner. Manual writes log a warning; prefer \`reset()\` / library
845
+ * navigation. Writable access may become read-only in v7.
793
846
  */
794
847
  currentPageIndex: number;
795
848
 
@@ -821,6 +874,9 @@ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>;
821
874
  * @note The sync path cannot compute a real \`rowIndex\`, so the returned SmartRow's
822
875
  * \`rowIndex\` is \`undefined\` (virtual-scroll positioning via \`bringIntoView()\` is limited).
823
876
  * Use \`findRow()\` (async) when you need a row with an accurate \`rowIndex\`.
877
+ * @note Cannot filter by \`syntheticColumns\` or \`columnOverrides.read\` keys — those need
878
+ * async evaluation via \`findRow()\` / \`findRows()\`. DOM filters use \`strategies.getCellLocator\`
879
+ * when configured (column-virtualized grids), otherwise \`cellSelector\` + column index.
824
880
  */
825
881
  getRow: (
826
882
  filters: Record<string, FilterValue>,
@@ -890,7 +946,9 @@ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>;
890
946
  ) => Promise<SmartRowArray<T>>;
891
947
 
892
948
  /**
893
- * Navigates to a specific column using the configured CellNavigationStrategy.
949
+ * Resolves the named column and scrolls it into view.
950
+ * Prefers \`strategies.viewport.scrollToColumn\` when configured; otherwise scrolls the
951
+ * header cell via Playwright \`scrollIntoViewIfNeeded()\`.
894
952
  */
895
953
  scrollToColumn: (columnName: string) => Promise<void>;
896
954
 
@@ -910,6 +968,7 @@ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>;
910
968
  mapColumn<R = string>(columnName: string, options?: RowIterationOptions): Promise<R[]>;
911
969
 
912
970
  /**
971
+ * @deprecated Use \`mapColumn\` (or \`map\`) instead. Will be removed in v7.0.0.
913
972
  * Iterates over rows and extracts the value of a single column as strings.
914
973
  * @param columnName - The name of the column to extract
915
974
  * @param options - Iteration options
@@ -949,28 +1008,29 @@ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>;
949
1008
 
950
1009
  /**
951
1010
  * Transforms every row across all pages into a value. Returns a flat array.
952
- * Execution is parallel within each page by default (safe for reads).
1011
+ * Defaults to \`concurrency: 'sequential'\` (safe for clicks/fills). Pass
1012
+ * \`concurrency: 'parallel'\` for read-only extraction, or \`'synchronized'\` when
1013
+ * navigation must stay lock-step on virtualized grids.
953
1014
  * Call \`stop()\` to halt after the current page finishes.
954
1015
  *
955
- * > **⚠️ UI Interactions:** \`map\` defaults to \`concurrency: 'parallel'\`. If your callback opens popovers,
956
- * > fills inputs, or otherwise mutates UI state, pass \`concurrency: 'sequential'\` (or \`'synchronized'\`
957
- * > when navigation must stay lock-step) to avoid overlapping interactions.
958
- *
959
1016
  * @param callback - Function receiving { row, rowIndex, stop }
960
1017
  * @param options - maxPages, concurrency, dedupe, useBulkPagination
961
1018
  *
962
1019
  * @example
963
- * // Data extraction — parallel is safe
964
- * const emails = await table.map(({ row }) => row.getCell('Email').innerText());
965
- *
966
- * @example
967
- * // UI interactions — use sequential (or synchronized) concurrency
1020
+ * // Default sequential — safe for UI interactions
968
1021
  * const assignees = await table.map(async ({ row }) => {
969
1022
  * await row.getCell('Assignee').locator('button').click();
970
1023
  * const name = await page.locator('.popover .name').innerText();
971
1024
  * await page.keyboard.press('Escape');
972
1025
  * return name;
973
- * }, { concurrency: 'sequential' });
1026
+ * });
1027
+ *
1028
+ * @example
1029
+ * // Read-only extraction — opt into parallel
1030
+ * const emails = await table.map(
1031
+ * ({ row }) => row.getCell('Email').innerText(),
1032
+ * { concurrency: 'parallel' }
1033
+ * );
974
1034
  */
975
1035
  map<R>(
976
1036
  callback: (ctx: RowIterationContext<T>) => R | Promise<R>,
package/dist/types.d.ts CHANGED
@@ -269,6 +269,9 @@ export type SmartRow<T = any> = Locator & {
269
269
  * Scrolls/paginates to bring this row into view.
270
270
  * Works when row position metadata is known (e.g., from getRowByIndex, findRow,
271
271
  * findRows, filter, or async iteration).
272
+ *
273
+ * Prefers `strategies.viewport.scrollToRow` when configured; otherwise falls back to
274
+ * Playwright `scrollIntoViewIfNeeded()`.
272
275
  * @throws Error if row position metadata is unknown
273
276
  */
274
277
  bringIntoView(): Promise<void>;
@@ -292,6 +295,12 @@ export type SmartRow<T = any> = Locator & {
292
295
  smartFill: (data: Partial<T> | Record<string, any>, options?: FillOptions) => Promise<void>;
293
296
  /**
294
297
  * Get the resolved value of any column — real, override, or synthetic.
298
+ *
299
+ * When `strategies.viewport` or `strategies.navigation` is configured, runs the same
300
+ * cell-navigation pipeline as `toJSON` / `getCell().bringIntoView()` so off-screen
301
+ * virtualized cells are mounted before reading. Without those strategies, reads the
302
+ * current DOM cell (Playwright auto-wait).
303
+ *
295
304
  * @param column - Column name (case-sensitive)
296
305
  * @returns The column value as a string
297
306
  */
@@ -403,6 +412,7 @@ export interface PaginationPrimitives {
403
412
  }
404
413
  export type PaginationStrategy = PaginationPrimitives;
405
414
  export type DedupeStrategy = (row: SmartRow) => string | number | Promise<string | number>;
415
+ export type ContentReadyStrategy = (row: Locator, page: Page) => Promise<void>;
406
416
  export type FillStrategy = (options: {
407
417
  row: SmartRow;
408
418
  columnName: string;
@@ -467,6 +477,7 @@ export interface ColumnOverride<TValue = any> {
467
477
  import { HeaderStrategy } from './strategies/headers';
468
478
  export type { HeaderStrategy } from './strategies/headers';
469
479
  import { NavigationPrimitives } from './strategies/columns';
480
+ export type { NavigationPrimitives, CellNavigationStrategy } from './strategies/columns';
470
481
  /**
471
482
  * Strategy to resolve column names (string or regex) to their index.
472
483
  */
@@ -530,9 +541,15 @@ export interface LoadingStrategy {
530
541
  * Defaults to 'read-as-is' when cellLoadingTimeout is set.
531
542
  */
532
543
  onCellLoadingTimeout?: 'skip' | 'read-as-is' | 'throw' | ((cell: import('@playwright/test').Locator, columnName: string, row: SmartRow) => Promise<string>);
533
- /** Max ms to wait for sort stabilization when isTableLoading is set. @default 10000 */
544
+ /**
545
+ * Max ms to wait while `isTableLoading` returns true (countRows, findRow(s),
546
+ * page-ready gates, and sort when `sortStabilizationTimeout` is unset).
547
+ * @default 10000
548
+ */
549
+ loadingTimeout?: number;
550
+ /** Max ms to wait for sort stabilization when isTableLoading is set. Overrides `loadingTimeout` for sort only. @default 10000 */
534
551
  sortStabilizationTimeout?: number;
535
- /** Polling interval (ms) while waiting for sort stabilization. @default 100 */
552
+ /** Polling interval (ms) while waiting for sort / table-ready stabilization. @default 100 (sort) / 200 (table-ready) */
536
553
  sortStabilizationPollInterval?: number;
537
554
  /** Fallback delay (ms) after sort when no isTableLoading is configured. @default 200 */
538
555
  sortStabilizationFallbackDelay?: number;
@@ -597,6 +614,23 @@ export interface TableStrategies {
597
614
  * }
598
615
  */
599
616
  resolveRowIndex?: (row: Locator) => Promise<RowIndexResult | undefined>;
617
+ /**
618
+ * Waits until a row's content has stabilized before cloning in
619
+ * `toJSON({ atomic: true })`. Needed for recycling virtualizers where the
620
+ * framework updates element positions synchronously but renders cell content
621
+ * asynchronously (e.g. react-window, react-virtuoso with React concurrent mode).
622
+ *
623
+ * Without this, the clone captures stale content from the previous occupant of the
624
+ * DOM slot — the element is at the correct position but React hasn't re-rendered yet.
625
+ *
626
+ * Use `Strategies.ContentReady.textStable()` for the built-in text-polling strategy.
627
+ *
628
+ * @example
629
+ * strategies: {
630
+ * contentReady: Strategies.ContentReady.textStable({ timeout: 500 }),
631
+ * }
632
+ */
633
+ contentReady?: ContentReadyStrategy;
600
634
  /**
601
635
  * Viewport oracle strategies for 2D virtualized tables (e.g. MUI DataGrid, AG Grid,
602
636
  * Braintrust-style grids where both rows and columns are virtualized simultaneously).
@@ -616,11 +650,17 @@ export interface TableConfig<T = any> {
616
650
  rowSelector?: string;
617
651
  /** Selector for the cells within a row */
618
652
  cellSelector?: string | ((row: Locator) => Locator);
619
- /** Number of pages to scan for verification */
653
+ /**
654
+ * Number of pages to scan for verification / iteration.
655
+ * Defaults to `1` — set explicitly (e.g. `maxPages: 5`) when using a pagination
656
+ * strategy, or you will never leave page 1. Init logs a warning when pagination
657
+ * is configured and this is still `1`.
658
+ */
620
659
  maxPages?: number;
621
660
  /**
622
661
  * Default concurrency strategy for iteration methods.
623
662
  * Can be overridden by the options passed to forEach, map, or filter.
663
+ * Defaults to sequential when unset (safer for UI interactions).
624
664
  */
625
665
  concurrency?: RowIterationMode;
626
666
  /** Hook to rename columns dynamically */
@@ -657,6 +697,9 @@ export interface TableConfig<T = any> {
657
697
  */
658
698
  emptyState?: Locator;
659
699
  }
700
+ /**
701
+ * @internal Resolved config after defaults are applied. Prefer {@link TableConfig} in public code.
702
+ */
660
703
  export interface FinalTableConfig<T = any> extends TableConfig<T> {
661
704
  headerSelector: string | ((root: Locator) => Locator);
662
705
  rowSelector: string;
@@ -725,6 +768,11 @@ export type RowIterationOptions = {
725
768
  */
726
769
  useBulkPagination?: boolean;
727
770
  };
771
+ /**
772
+ * Result of {@link useTable}. Implements `AsyncIterable` — `for await (const { row } of table)`
773
+ * uses the same page-walk as `map` / `forEach` (`scanPages` + runMap): overscan, loading,
774
+ * dedupe, and EOF final scan (#427).
775
+ */
728
776
  export interface TableResult<T = any> extends AsyncIterable<{
729
777
  row: SmartRow<T>;
730
778
  rowIndex: number;
@@ -732,8 +780,10 @@ export interface TableResult<T = any> extends AsyncIterable<{
732
780
  pageIndex: number;
733
781
  }> {
734
782
  /**
735
- * Represents the current page index of the table's DOM.
736
- * Starts at 0. Automatically maintained by the library during pagination and bringIntoView.
783
+ * Current DOM page index (0-based). Automatically maintained during pagination
784
+ * and `bringIntoView`. Treat as **read-only** — assigning manually can desync
785
+ * the path planner. Manual writes log a warning; prefer `reset()` / library
786
+ * navigation. Writable access may become read-only in v7.
737
787
  */
738
788
  currentPageIndex: number;
739
789
  /**
@@ -762,6 +812,9 @@ export interface TableResult<T = any> extends AsyncIterable<{
762
812
  * @note The sync path cannot compute a real `rowIndex`, so the returned SmartRow's
763
813
  * `rowIndex` is `undefined` (virtual-scroll positioning via `bringIntoView()` is limited).
764
814
  * Use `findRow()` (async) when you need a row with an accurate `rowIndex`.
815
+ * @note Cannot filter by `syntheticColumns` or `columnOverrides.read` keys — those need
816
+ * async evaluation via `findRow()` / `findRows()`. DOM filters use `strategies.getCellLocator`
817
+ * when configured (column-virtualized grids), otherwise `cellSelector` + column index.
765
818
  */
766
819
  getRow: (filters: Record<string, FilterValue>, options?: {
767
820
  exact?: boolean;
@@ -823,7 +876,9 @@ export interface TableResult<T = any> extends AsyncIterable<{
823
876
  useBulkPagination?: boolean;
824
877
  }) => Promise<SmartRowArray<T>>;
825
878
  /**
826
- * Navigates to a specific column using the configured CellNavigationStrategy.
879
+ * Resolves the named column and scrolls it into view.
880
+ * Prefers `strategies.viewport.scrollToColumn` when configured; otherwise scrolls the
881
+ * header cell via Playwright `scrollIntoViewIfNeeded()`.
827
882
  */
828
883
  scrollToColumn: (columnName: string) => Promise<void>;
829
884
  /**
@@ -843,6 +898,7 @@ export interface TableResult<T = any> extends AsyncIterable<{
843
898
  */
844
899
  mapColumn<R = string>(columnName: string, options?: RowIterationOptions): Promise<R[]>;
845
900
  /**
901
+ * @deprecated Use `mapColumn` (or `map`) instead. Will be removed in v7.0.0.
846
902
  * Iterates over rows and extracts the value of a single column as strings.
847
903
  * @param columnName - The name of the column to extract
848
904
  * @param options - Iteration options
@@ -874,28 +930,29 @@ export interface TableResult<T = any> extends AsyncIterable<{
874
930
  forEach(callback: (ctx: RowIterationContext<T>) => void | Promise<void>, options?: RowIterationOptions): Promise<void>;
875
931
  /**
876
932
  * Transforms every row across all pages into a value. Returns a flat array.
877
- * Execution is parallel within each page by default (safe for reads).
933
+ * Defaults to `concurrency: 'sequential'` (safe for clicks/fills). Pass
934
+ * `concurrency: 'parallel'` for read-only extraction, or `'synchronized'` when
935
+ * navigation must stay lock-step on virtualized grids.
878
936
  * Call `stop()` to halt after the current page finishes.
879
937
  *
880
- * > **⚠️ UI Interactions:** `map` defaults to `concurrency: 'parallel'`. If your callback opens popovers,
881
- * > fills inputs, or otherwise mutates UI state, pass `concurrency: 'sequential'` (or `'synchronized'`
882
- * > when navigation must stay lock-step) to avoid overlapping interactions.
883
- *
884
938
  * @param callback - Function receiving { row, rowIndex, stop }
885
939
  * @param options - maxPages, concurrency, dedupe, useBulkPagination
886
940
  *
887
941
  * @example
888
- * // Data extraction — parallel is safe
889
- * const emails = await table.map(({ row }) => row.getCell('Email').innerText());
890
- *
891
- * @example
892
- * // UI interactions — use sequential (or synchronized) concurrency
942
+ * // Default sequential — safe for UI interactions
893
943
  * const assignees = await table.map(async ({ row }) => {
894
944
  * await row.getCell('Assignee').locator('button').click();
895
945
  * const name = await page.locator('.popover .name').innerText();
896
946
  * await page.keyboard.press('Escape');
897
947
  * return name;
898
- * }, { concurrency: 'sequential' });
948
+ * });
949
+ *
950
+ * @example
951
+ * // Read-only extraction — opt into parallel
952
+ * const emails = await table.map(
953
+ * ({ row }) => row.getCell('Email').innerText(),
954
+ * { concurrency: 'parallel' }
955
+ * );
899
956
  */
900
957
  map<R>(callback: (ctx: RowIterationContext<T>) => R | Promise<R>, options?: RowIterationOptions): Promise<R[]>;
901
958
  /**