@rickcedwhat/playwright-smart-table 6.8.2 → 6.10.0

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 (38) hide show
  1. package/README.md +0 -2
  2. package/dist/engine/rowFinder.js +153 -168
  3. package/dist/engine/tableIteration.js +105 -123
  4. package/dist/engine/tableMapper.js +88 -103
  5. package/dist/packageVersion.d.ts +1 -1
  6. package/dist/packageVersion.js +1 -1
  7. package/dist/presets/glide/columns.js +23 -32
  8. package/dist/presets/glide/headers.js +18 -27
  9. package/dist/presets/glide/index.d.ts +9 -9
  10. package/dist/presets/glide/index.js +24 -33
  11. package/dist/presets/mui.js +197 -124
  12. package/dist/presets/rdg.d.ts +1 -1
  13. package/dist/presets/rdg.js +32 -41
  14. package/dist/smartRow.js +163 -96
  15. package/dist/strategies/columns.js +2 -11
  16. package/dist/strategies/dedupe.js +3 -12
  17. package/dist/strategies/fill.js +20 -29
  18. package/dist/strategies/headers.js +22 -31
  19. package/dist/strategies/index.d.ts +4 -0
  20. package/dist/strategies/index.js +3 -0
  21. package/dist/strategies/loading.js +24 -33
  22. package/dist/strategies/pagination.js +21 -30
  23. package/dist/strategies/sorting.js +18 -31
  24. package/dist/strategies/stabilization.js +28 -37
  25. package/dist/strategies/viewport.d.ts +52 -0
  26. package/dist/strategies/viewport.js +123 -0
  27. package/dist/typeContext.d.ts +1 -1
  28. package/dist/typeContext.js +100 -9
  29. package/dist/types.d.ts +100 -9
  30. package/dist/useTable.js +98 -76
  31. package/dist/utils/debugUtils.js +5 -16
  32. package/dist/utils/elementTracker.js +27 -40
  33. package/dist/utils/mutex.js +5 -16
  34. package/dist/utils/navigationBarrier.js +25 -36
  35. package/dist/utils/paginationPath.js +102 -115
  36. package/dist/utils/smartRowArray.js +2 -11
  37. package/dist/utils.js +4 -13
  38. package/package.json +9 -5
package/dist/types.d.ts CHANGED
@@ -28,10 +28,13 @@ export type FilterValue = string | RegExp | number | ((cell: Locator) => Locator
28
28
  */
29
29
  export type GetCellLocatorFn = (args: {
30
30
  row: Locator;
31
+ /** The root locator passed to useTable(). Useful for re-querying stale row locators. */
32
+ root: Locator;
31
33
  columnName: string;
32
34
  columnIndex: number;
33
35
  rowIndex?: number;
34
36
  page: Page;
37
+ config: FinalTableConfig<any>;
35
38
  }) => Locator;
36
39
  /**
37
40
  * Hook called before each cell value is read in toJSON (and columnOverrides.read).
@@ -68,6 +71,89 @@ export type GetActiveCellFn = (args: TableContext) => Promise<{
68
71
  columnName?: string;
69
72
  locator: Locator;
70
73
  } | null>;
74
+ /**
75
+ * Viewport oracle strategies for virtualized tables.
76
+ *
77
+ * These tell the library *what is currently in the DOM* rather than *how to move*.
78
+ * When present, the cell-reading engine uses them to jump directly to a target
79
+ * row/column instead of stepping blindly with navigation primitives.
80
+ *
81
+ * This is the primary fix for the 2D scroll hazard: scrolling right to bring a
82
+ * column into view can knock rows out of the vertical viewport (and vice versa).
83
+ * With `getVisibleRowRange` the engine detects this and calls `scrollToRow` to
84
+ * restore the row before reading — eliminating the hazard without polling loops.
85
+ *
86
+ * All members are optional. Supply whichever the underlying grid exposes:
87
+ * - Range oracles alone enable hazard detection with graceful fallback to navigation primitives.
88
+ * - Scroll primitives alone enable direct jumps without range awareness.
89
+ * - Both together give the most reliable, fastest path.
90
+ *
91
+ * @example
92
+ * // MUI DataGrid via apiRef
93
+ * strategies: {
94
+ * viewport: {
95
+ * getVisibleRowRange: async ({ root }) =>
96
+ * root.evaluate(el => {
97
+ * const api = (el as any).__muiDataGrid__;
98
+ * return { first: api.getFirstVisibleRow(), last: api.getLastVisibleRow() };
99
+ * }),
100
+ * scrollToRow: async ({ root }, rowIndex) =>
101
+ * root.evaluate((el, idx) =>
102
+ * (el as any).__muiDataGrid__.scrollToIndexes({ rowIndex: idx }), rowIndex),
103
+ * scrollToColumn: async ({ root }, colIndex) =>
104
+ * root.evaluate((el, idx) =>
105
+ * (el as any).__muiDataGrid__.scrollToIndexes({ colIndex: idx }), colIndex),
106
+ * }
107
+ * }
108
+ *
109
+ * @example
110
+ * // aria-rowindex / aria-colindex DOM fallback (works without internal API access)
111
+ * strategies: {
112
+ * viewport: {
113
+ * getVisibleRowRange: async ({ root }) =>
114
+ * root.evaluate(el => {
115
+ * const rows = [...el.querySelectorAll('[role="row"][aria-rowindex]')];
116
+ * const idxs = rows.map(r => Number(r.getAttribute('aria-rowindex')) - 1);
117
+ * return { first: Math.min(...idxs), last: Math.max(...idxs) };
118
+ * }),
119
+ * }
120
+ * }
121
+ */
122
+ export interface ViewportStrategy {
123
+ /**
124
+ * Returns the 0-based index range of rows currently rendered in the DOM.
125
+ * Used to detect when a target row has been scrolled out of view and needs recovery.
126
+ */
127
+ getVisibleRowRange?: (context: TableContext) => Promise<{
128
+ first: number;
129
+ last: number;
130
+ }>;
131
+ /**
132
+ * Returns the 0-based index range of columns currently rendered in the DOM.
133
+ * Used to detect when a target column is not yet mounted before reading.
134
+ */
135
+ getVisibleColumnRange?: (context: TableContext) => Promise<{
136
+ first: number;
137
+ last: number;
138
+ }>;
139
+ /**
140
+ * Scrolls or jumps directly to make a row visible by 0-based index.
141
+ * Replaces blind goDown()/goUp() step loops for grids that expose a scroll API.
142
+ */
143
+ scrollToRow?: (context: TableContext, rowIndex: number) => Promise<void>;
144
+ /**
145
+ * Scrolls or jumps directly to make a column visible by 0-based index.
146
+ * Replaces blind goRight()/goLeft() step loops for grids that expose a scroll API.
147
+ */
148
+ scrollToColumn?: (context: TableContext, colIndex: number) => Promise<void>;
149
+ /**
150
+ * When true, disables the automatic memoization of getVisibleColumnRange and
151
+ * getVisibleRowRange results. By default the library caches each range value and
152
+ * invalidates it after the corresponding scroll call, eliminating redundant DOM
153
+ * evaluate() round-trips between scrolls.
154
+ */
155
+ disableCache?: boolean;
156
+ }
71
157
  /**
72
158
  * SmartRow - A Playwright Locator with table-aware methods.
73
159
  *
@@ -319,6 +405,17 @@ export interface TableStrategies {
319
405
  * E.g. after sort/pagination, the engine uses loading.isTableLoading when present.
320
406
  */
321
407
  loading?: LoadingStrategy;
408
+ /**
409
+ * Viewport oracle strategies for 2D virtualized tables (e.g. MUI DataGrid, AG Grid,
410
+ * Braintrust-style grids where both rows and columns are virtualized simultaneously).
411
+ *
412
+ * When present, the cell-reading engine uses these to jump directly to the target
413
+ * row/column and to detect the 2D scroll hazard (scrolling to reveal a column can
414
+ * knock rows out of the vertical viewport, and vice versa).
415
+ *
416
+ * Completely optional and additive — existing `navigation` primitives remain the fallback.
417
+ */
418
+ viewport?: ViewportStrategy;
322
419
  }
323
420
  export interface TableConfig<T = any> {
324
421
  /** Selector for the table headers */
@@ -391,12 +488,6 @@ export type RowIterationMode = 'parallel' | 'sequential' | 'synchronized';
391
488
  export type RowIterationOptions = {
392
489
  /** Maximum number of pages to iterate. Defaults to config.maxPages. */
393
490
  maxPages?: number;
394
- /**
395
- * @deprecated Use 'concurrency' mode instead.
396
- * - parallel: true -> concurrency: 'parallel'
397
- * - parallel: false -> concurrency: 'sequential'
398
- */
399
- parallel?: boolean;
400
491
  /**
401
492
  * Concurrency strategy for iteration.
402
493
  * - 'parallel': Full parallel execution of both navigation and actions.
@@ -493,7 +584,7 @@ export interface TableResult<T = any> extends AsyncIterable<{
493
584
  * Call `stop()` in the callback to end iteration early.
494
585
  *
495
586
  * @param callback - Function receiving { row, rowIndex, stop }
496
- * @param options - maxPages, concurrency, dedupe, useBulkPagination (`parallel` is deprecated; use `concurrency`)
587
+ * @param options - maxPages, concurrency, dedupe, useBulkPagination
497
588
  *
498
589
  * @example
499
590
  * await table.forEach(async ({ row, stop }) => {
@@ -512,7 +603,7 @@ export interface TableResult<T = any> extends AsyncIterable<{
512
603
  * > when navigation must stay lock-step) to avoid overlapping interactions.
513
604
  *
514
605
  * @param callback - Function receiving { row, rowIndex, stop }
515
- * @param options - maxPages, concurrency, dedupe, useBulkPagination (`parallel` is deprecated; use `concurrency`)
606
+ * @param options - maxPages, concurrency, dedupe, useBulkPagination
516
607
  *
517
608
  * @example
518
609
  * // Data extraction — parallel is safe
@@ -534,7 +625,7 @@ export interface TableResult<T = any> extends AsyncIterable<{
534
625
  * Execution is sequential by default.
535
626
  *
536
627
  * @param predicate - Function receiving { row, rowIndex, stop }
537
- * @param options - maxPages, concurrency, dedupe, useBulkPagination (`parallel` is deprecated; use `concurrency`)
628
+ * @param options - maxPages, concurrency, dedupe, useBulkPagination
538
629
  *
539
630
  * @example
540
631
  * const active = await table.filter(async ({ row }) =>
package/dist/useTable.js CHANGED
@@ -1,13 +1,4 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
12
3
  var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
13
4
  if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
@@ -56,7 +47,38 @@ const useTable = (rootLocator, configOptions = {}) => {
56
47
  isHeaderLoading: loading_1.LoadingStrategies.Headers.stable(200)
57
48
  }
58
49
  };
59
- const config = Object.assign(Object.assign({ rowSelector: "tbody tr", headerSelector: "thead th", cellSelector: "td", maxPages: 1, headerTransformer: ({ text }) => text, autoScroll: true, onReset: () => __awaiter(void 0, void 0, void 0, function* () { }) }, configOptions), { strategies: Object.assign(Object.assign({}, defaultStrategies), configOptions.strategies) });
50
+ 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) });
51
+ // Automatically memoize viewport range oracles so user-provided functions are
52
+ // pure DOM queries with no cache bookkeeping. Invalidated after each scroll call.
53
+ if (config.strategies.viewport && !config.strategies.viewport.disableCache) {
54
+ const vp = config.strategies.viewport;
55
+ let colRangeCache = null;
56
+ let rowRangeCache = null;
57
+ config.strategies.viewport = Object.assign(Object.assign({}, vp), { getVisibleColumnRange: vp.getVisibleColumnRange
58
+ ? async (ctx) => {
59
+ if (!colRangeCache)
60
+ colRangeCache = await vp.getVisibleColumnRange(ctx);
61
+ return colRangeCache;
62
+ }
63
+ : undefined, getVisibleRowRange: vp.getVisibleRowRange
64
+ ? async (ctx) => {
65
+ if (!rowRangeCache)
66
+ rowRangeCache = await vp.getVisibleRowRange(ctx);
67
+ return rowRangeCache;
68
+ }
69
+ : undefined, scrollToColumn: vp.scrollToColumn
70
+ ? async (ctx, colIndex) => {
71
+ colRangeCache = null;
72
+ rowRangeCache = null;
73
+ await vp.scrollToColumn(ctx, colIndex);
74
+ }
75
+ : undefined, scrollToRow: vp.scrollToRow
76
+ ? async (ctx, rowIndex) => {
77
+ rowRangeCache = null;
78
+ await vp.scrollToRow(ctx, rowIndex);
79
+ }
80
+ : undefined });
81
+ }
60
82
  const resolve = (item, parent) => {
61
83
  if (typeof item === 'string')
62
84
  return parent.locator(item);
@@ -102,7 +124,7 @@ const useTable = (rootLocator, configOptions = {}) => {
102
124
  const rowFinder = new rowFinder_1.RowFinder(rootLocator, config, resolve, filterEngine, tableMapper, _makeSmart, tableState);
103
125
  /** Builds a full TableContext/StrategyContext with getHeaderCell, getHeaders, scrollToColumn. Set after result is created. */
104
126
  let createStrategyContext = () => ({ root: rootLocator, config, page: rootLocator.page(), resolve });
105
- const _getCleanHtml = (loc) => __awaiter(void 0, void 0, void 0, function* () {
127
+ const _getCleanHtml = async (loc) => {
106
128
  return loc.evaluate((el) => {
107
129
  const clone = el.cloneNode(true);
108
130
  const removeSelectors = 'script, style, svg, path, circle, rect, noscript, [hidden]';
@@ -123,33 +145,33 @@ const useTable = (rootLocator, configOptions = {}) => {
123
145
  }
124
146
  return clone.outerHTML;
125
147
  });
126
- });
127
- const _handlePrompt = (promptName, content) => __awaiter(void 0, void 0, void 0, function* () {
148
+ };
149
+ const _handlePrompt = async (promptName, content) => {
128
150
  let finalPrompt = content;
129
151
  finalPrompt += `\n\n👇 Useful TypeScript Definitions 👇\n\`\`\`typescript\n${minimalConfigContext_1.MINIMAL_CONFIG_CONTEXT}\n\`\`\`\n`;
130
152
  console.log(`⚠️ Throwing error to display [${promptName}] cleanly...`);
131
153
  throw new Error(finalPrompt);
132
- });
133
- const _autoInit = () => __awaiter(void 0, void 0, void 0, function* () {
134
- yield tableMapper.getMap();
135
- });
154
+ };
155
+ const _autoInit = async () => {
156
+ await tableMapper.getMap();
157
+ };
136
158
  // Default: goNext (one page). Pass useBulk true to prefer goNextBulk. "How far" uses numeric return when strategy provides it.
137
- const _advancePage = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (useBulk = false) {
159
+ const _advancePage = async (useBulk = false) => {
138
160
  const context = createStrategyContext();
139
161
  const pagination = config.strategies.pagination;
140
162
  let rawResult;
141
163
  let primitive;
142
164
  if (useBulk && (pagination === null || pagination === void 0 ? void 0 : pagination.goNextBulk)) {
143
165
  primitive = 'goNextBulk';
144
- rawResult = yield pagination.goNextBulk(context);
166
+ rawResult = await pagination.goNextBulk(context);
145
167
  }
146
168
  else if (pagination === null || pagination === void 0 ? void 0 : pagination.goNext) {
147
169
  primitive = 'goNext';
148
- rawResult = yield pagination.goNext(context);
170
+ rawResult = await pagination.goNext(context);
149
171
  }
150
172
  else if (pagination === null || pagination === void 0 ? void 0 : pagination.goNextBulk) {
151
173
  primitive = 'goNextBulk (fallback)';
152
- rawResult = yield pagination.goNextBulk(context);
174
+ rawResult = await pagination.goNextBulk(context);
153
175
  }
154
176
  else {
155
177
  log('_advancePage: no pagination primitive available — returning false');
@@ -165,47 +187,47 @@ const useTable = (rootLocator, configOptions = {}) => {
165
187
  log(`_advancePage: ${primitive} returned false — end of data`);
166
188
  }
167
189
  return didAdvance;
168
- });
190
+ };
169
191
  const result = {
170
192
  get currentPageIndex() { return tableState.currentPageIndex; },
171
193
  set currentPageIndex(v) { tableState.currentPageIndex = v; },
172
- init: (options) => __awaiter(void 0, void 0, void 0, function* () {
194
+ init: async (options) => {
173
195
  if (tableMapper.isInitialized())
174
196
  return result;
175
197
  (0, debugUtils_1.warnIfDebugInCI)(config);
176
198
  (0, debugUtils_1.logDebug)(config, 'info', 'Initializing table');
177
- const map = yield tableMapper.getMap(options === null || options === void 0 ? void 0 : options.timeout);
199
+ const map = await tableMapper.getMap(options === null || options === void 0 ? void 0 : options.timeout);
178
200
  (0, debugUtils_1.logDebug)(config, 'info', `Table initialized with ${map.size} columns`, Array.from(map.keys()));
179
- yield (0, debugUtils_1.debugDelay)(config, 'default');
201
+ await (0, debugUtils_1.debugDelay)(config, 'default');
180
202
  return result;
181
- }),
182
- scrollToColumn: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
183
- const map = yield tableMapper.getMap();
203
+ },
204
+ scrollToColumn: async (columnName) => {
205
+ const map = await tableMapper.getMap();
184
206
  const idx = map.get(columnName);
185
207
  if (idx === undefined)
186
208
  throw _createColumnError(columnName, map);
187
209
  // Use header cell for scrolling
188
210
  const headerCell = resolve(config.headerSelector, rootLocator).nth(idx);
189
- yield headerCell.scrollIntoViewIfNeeded();
190
- }),
191
- getHeaders: () => __awaiter(void 0, void 0, void 0, function* () {
192
- const map = yield tableMapper.getMap();
211
+ await headerCell.scrollIntoViewIfNeeded();
212
+ },
213
+ getHeaders: async () => {
214
+ const map = await tableMapper.getMap();
193
215
  return Array.from(map.keys());
194
- }),
195
- getHeaderCell: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
196
- const map = yield tableMapper.getMap();
216
+ },
217
+ getHeaderCell: async (columnName) => {
218
+ const map = await tableMapper.getMap();
197
219
  const idx = map.get(columnName);
198
220
  if (idx === undefined)
199
221
  throw _createColumnError(columnName, map, 'header cell');
200
222
  return resolve(config.headerSelector, rootLocator).nth(idx);
201
- }),
202
- reset: () => __awaiter(void 0, void 0, void 0, function* () {
223
+ },
224
+ reset: async () => {
203
225
  var _a;
204
226
  log("Resetting table...");
205
- yield config.onReset(createStrategyContext());
227
+ await config.onReset(createStrategyContext());
206
228
  if ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goToFirst) {
207
229
  log("Auto-navigating to first page...");
208
- yield config.strategies.pagination.goToFirst(createStrategyContext());
230
+ await config.strategies.pagination.goToFirst(createStrategyContext());
209
231
  }
210
232
  else if (hasPaginationInConfig) {
211
233
  log("No goToFirst strategy configured. Table may not be on page 1.");
@@ -214,13 +236,13 @@ const useTable = (rootLocator, configOptions = {}) => {
214
236
  tableState.currentPageIndex = 0;
215
237
  tableMapper.clear();
216
238
  log("Table reset complete. Calling autoInit to restore state.");
217
- yield _autoInit();
218
- }),
219
- revalidate: () => __awaiter(void 0, void 0, void 0, function* () {
239
+ await _autoInit();
240
+ },
241
+ revalidate: async () => {
220
242
  log("Revalidating table structure...");
221
- yield tableMapper.remapHeaders();
243
+ await tableMapper.remapHeaders();
222
244
  log("Table revalidated.");
223
- }),
245
+ },
224
246
  getRow: (filters, options = { exact: false }) => {
225
247
  const map = tableMapper.getMapSync();
226
248
  if (!map)
@@ -237,53 +259,53 @@ const useTable = (rootLocator, configOptions = {}) => {
237
259
  const rowLocator = resolve(config.rowSelector, rootLocator).nth(index);
238
260
  return _makeSmart(rowLocator, map, index);
239
261
  },
240
- findRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
262
+ findRow: async (filters, options) => {
241
263
  return rowFinder.findRow(filters, options);
242
- }),
243
- findRows: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
264
+ },
265
+ findRows: async (filters, options) => {
244
266
  return rowFinder.findRows(filters !== null && filters !== void 0 ? filters : {}, options);
245
- }),
267
+ },
246
268
  isInitialized: () => {
247
269
  return tableMapper.isInitialized();
248
270
  },
249
271
  sorting: {
250
- apply: (columnName, direction) => __awaiter(void 0, void 0, void 0, function* () {
272
+ apply: async (columnName, direction) => {
251
273
  var _a;
252
- yield _autoInit();
274
+ await _autoInit();
253
275
  if (!config.strategies.sorting)
254
276
  throw new Error('No sorting strategy has been configured.');
255
277
  log(`Applying sort for column "${columnName}" (${direction})`);
256
278
  const context = Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell });
257
279
  const maxRetries = 3;
258
280
  for (let i = 0; i < maxRetries; i++) {
259
- const currentState = yield config.strategies.sorting.getSortState({ columnName, context });
281
+ const currentState = await config.strategies.sorting.getSortState({ columnName, context });
260
282
  if (currentState === direction) {
261
283
  log(`Sort for "${columnName}" is already "${direction}".`);
262
284
  return;
263
285
  }
264
- yield config.strategies.sorting.doSort({ columnName, direction, context });
286
+ await config.strategies.sorting.doSort({ columnName, direction, context });
265
287
  if ((_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading) {
266
- yield config.strategies.loading.isTableLoading(context);
288
+ await config.strategies.loading.isTableLoading(context);
267
289
  }
268
290
  else {
269
- yield rootLocator.page().waitForTimeout(200);
291
+ await rootLocator.page().waitForTimeout(200);
270
292
  }
271
- yield (0, debugUtils_1.debugDelay)(config, 'default');
272
- const newState = yield config.strategies.sorting.getSortState({ columnName, context });
293
+ await (0, debugUtils_1.debugDelay)(config, 'default');
294
+ const newState = await config.strategies.sorting.getSortState({ columnName, context });
273
295
  if (newState === direction) {
274
296
  log(`Successfully sorted "${columnName}" to "${direction}".`);
275
297
  return;
276
298
  }
277
299
  }
278
300
  throw new Error(`Failed to sort column "${columnName}" to "${direction}" after ${maxRetries} attempts.`);
279
- }),
280
- getState: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
281
- yield _autoInit();
301
+ },
302
+ getState: async (columnName) => {
303
+ await _autoInit();
282
304
  if (!config.strategies.sorting)
283
305
  throw new Error('No sorting strategy has been configured.');
284
306
  const context = Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell });
285
307
  return config.strategies.sorting.getSortState({ columnName, context });
286
- })
308
+ }
287
309
  },
288
310
  // ─── Shared async row iterator ───────────────────────────────────────────
289
311
  [Symbol.asyncIterator]() {
@@ -318,9 +340,9 @@ const useTable = (rootLocator, configOptions = {}) => {
318
340
  });
319
341
  },
320
342
  // ─── Row iteration (delegated to engine/tableIteration) ──────────────────
321
- forEach: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
322
- yield _autoInit();
323
- yield (0, tableIteration_1.runForEach)({
343
+ forEach: async (callback, options = {}) => {
344
+ await _autoInit();
345
+ await (0, tableIteration_1.runForEach)({
324
346
  getRowLocators: () => resolve(config.rowSelector, rootLocator),
325
347
  getMap: () => tableMapper.getMapSync(),
326
348
  advancePage: _advancePage,
@@ -329,9 +351,9 @@ const useTable = (rootLocator, configOptions = {}) => {
329
351
  config,
330
352
  getPage: () => rootLocator.page(),
331
353
  }, callback, options);
332
- }),
333
- map: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
334
- yield _autoInit();
354
+ },
355
+ map: async (callback, options = {}) => {
356
+ await _autoInit();
335
357
  return (0, tableIteration_1.runMap)({
336
358
  getRowLocators: () => resolve(config.rowSelector, rootLocator),
337
359
  getMap: () => tableMapper.getMapSync(),
@@ -341,9 +363,9 @@ const useTable = (rootLocator, configOptions = {}) => {
341
363
  config,
342
364
  getPage: () => rootLocator.page(),
343
365
  }, callback, options);
344
- }),
345
- filter: (predicate_1, ...args_1) => __awaiter(void 0, [predicate_1, ...args_1], void 0, function* (predicate, options = {}) {
346
- yield _autoInit();
366
+ },
367
+ filter: async (predicate, options = {}) => {
368
+ await _autoInit();
347
369
  return (0, tableIteration_1.runFilter)({
348
370
  getRowLocators: () => resolve(config.rowSelector, rootLocator),
349
371
  getMap: () => tableMapper.getMapSync(),
@@ -353,17 +375,17 @@ const useTable = (rootLocator, configOptions = {}) => {
353
375
  config,
354
376
  getPage: () => rootLocator.page(),
355
377
  }, predicate, options);
356
- }),
357
- generateConfig: () => __awaiter(void 0, void 0, void 0, function* () {
358
- const html = yield _getCleanHtml(rootLocator);
378
+ },
379
+ generateConfig: async () => {
380
+ const html = await _getCleanHtml(rootLocator);
359
381
  const separator = "=".repeat(50);
360
382
  const content = `\n${separator} \n🤖 COPY INTO GEMINI / ChatGPT 🤖\n${separator} \nI am using 'playwright-smart-table'.\nTarget Table Locator: ${rootLocator.toString()} \nGenerate config for: \n\`\`\`html\n${html.substring(0, 10000)} ...\n\`\`\`\n${separator}\n`;
361
- yield _handlePrompt('Smart Table Config', content);
362
- }),
363
- generateConfigPrompt: () => __awaiter(void 0, void 0, void 0, function* () {
383
+ await _handlePrompt('Smart Table Config', content);
384
+ },
385
+ generateConfigPrompt: async () => {
364
386
  console.warn('⚠️ [playwright-smart-table] generateConfigPrompt() is deprecated and will be removed in v7.0.0. Please use generateConfig() instead.');
365
387
  return result.generateConfig();
366
- }),
388
+ },
367
389
  };
368
390
  createStrategyContext = () => ({
369
391
  root: rootLocator,
@@ -1,13 +1,4 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
3
  exports.getDebugDelay = getDebugDelay;
13
4
  exports.debugDelay = debugDelay;
@@ -28,13 +19,11 @@ function getDebugDelay(config, actionType) {
28
19
  /**
29
20
  * Add debug delay for specific action type
30
21
  */
31
- function debugDelay(config, actionType) {
32
- return __awaiter(this, void 0, void 0, function* () {
33
- const delay = getDebugDelay(config, actionType);
34
- if (delay > 0) {
35
- yield new Promise(resolve => setTimeout(resolve, delay));
36
- }
37
- });
22
+ async function debugDelay(config, actionType) {
23
+ const delay = getDebugDelay(config, actionType);
24
+ if (delay > 0) {
25
+ await new Promise(resolve => setTimeout(resolve, delay));
26
+ }
38
27
  }
39
28
  /**
40
29
  * Log debug message based on log level
@@ -1,13 +1,4 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
3
  exports.ElementTracker = void 0;
13
4
  class ElementTracker {
@@ -19,42 +10,38 @@ class ElementTracker {
19
10
  * in a WeakMap. This gracefully handles both append-only DOMs (by identity) and
20
11
  * virtualized DOMs (by text signature if nodes are recycled).
21
12
  */
22
- getUnseenIndices(locators) {
23
- return __awaiter(this, void 0, void 0, function* () {
24
- return yield locators.evaluateAll((elements, trackerId) => {
25
- const win = window;
26
- if (!win[trackerId]) {
27
- win[trackerId] = new WeakMap();
13
+ async getUnseenIndices(locators) {
14
+ return await locators.evaluateAll((elements, trackerId) => {
15
+ const win = window;
16
+ if (!win[trackerId]) {
17
+ win[trackerId] = new WeakMap();
18
+ }
19
+ const seenMap = win[trackerId];
20
+ const newIndices = [];
21
+ elements.forEach((el, index) => {
22
+ // Determine a lightweight signature for the row (textContent strips HTML, fast)
23
+ const signature = el.textContent || '';
24
+ // If it's a new element, OR a recycled element with new data
25
+ if (seenMap.get(el) !== signature) {
26
+ seenMap.set(el, signature);
27
+ newIndices.push(index);
28
28
  }
29
- const seenMap = win[trackerId];
30
- const newIndices = [];
31
- elements.forEach((el, index) => {
32
- // Determine a lightweight signature for the row (textContent strips HTML, fast)
33
- const signature = el.textContent || '';
34
- // If it's a new element, OR a recycled element with new data
35
- if (seenMap.get(el) !== signature) {
36
- seenMap.set(el, signature);
37
- newIndices.push(index);
38
- }
39
- });
40
- return newIndices;
41
- }, this.id);
42
- });
29
+ });
30
+ return newIndices;
31
+ }, this.id);
43
32
  }
44
33
  /**
45
34
  * Cleans up the tracking map from the browser window object.
46
35
  */
47
- cleanup(page) {
48
- return __awaiter(this, void 0, void 0, function* () {
49
- try {
50
- yield page.evaluate((trackerId) => {
51
- delete window[trackerId];
52
- }, this.id);
53
- }
54
- catch (e) {
55
- // Ignore context destroyed errors during cleanup
56
- }
57
- });
36
+ async cleanup(page) {
37
+ try {
38
+ await page.evaluate((trackerId) => {
39
+ delete window[trackerId];
40
+ }, this.id);
41
+ }
42
+ catch (e) {
43
+ // Ignore context destroyed errors during cleanup
44
+ }
58
45
  }
59
46
  }
60
47
  exports.ElementTracker = ElementTracker;
@@ -1,13 +1,4 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
3
  exports.Mutex = void 0;
13
4
  /**
@@ -18,14 +9,12 @@ class Mutex {
18
9
  constructor() {
19
10
  this.queue = Promise.resolve();
20
11
  }
21
- run(action) {
22
- return __awaiter(this, void 0, void 0, function* () {
23
- const next = this.queue.then(() => __awaiter(this, void 0, void 0, function* () {
24
- return yield action();
25
- }));
26
- this.queue = next.catch(() => { }); // prevent chain breakage
27
- return yield next;
12
+ async run(action) {
13
+ const next = this.queue.then(async () => {
14
+ return await action();
28
15
  });
16
+ this.queue = next.catch(() => { }); // prevent chain breakage
17
+ return await next;
29
18
  }
30
19
  }
31
20
  exports.Mutex = Mutex;